Quick search - Ctrl+Shift+F - 2.3

Horizontal floating search window with paste & search functionality

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         Quick search - Ctrl+Shift+F - 2.3
// @namespace    http://tampermonkey.net/
// @version      2.3
// @description  Horizontal floating search window with paste & search functionality
// @author       </j0tsarup>
// @match        *://*/*
// @grant        GM_addStyle
// @grant        GM_addElement
// @run-at       document-start
// ==/UserScript==

/*
 * 
 * • Keyboard shortcuts: Ctrl+Shift+F (open), ESC (close)

 * FEATURES:
 * • Floating search window with drag-to-move functionality
 * • Paste & search directly from clipboard
 * • Multiple search engines: Google, Bing, DuckDuckGo, Yahoo
 * • Optional location-based search
 * • Toggle bubble for quick access
 * • Clean, modern UI with smooth animations
 * 
 * CHANGELOG v2.3:
 * • Added version number to script name
 * • Added formatted header with feature list
 * • Updated author information
 * • Improved code documentation
 * 
 * ═══════════════════════════════════════════════════════════════════════════
 */

(function() {
    'use strict';

    // Wait for document to be ready
    function init() {
        // Add CSS styles with higher priority
        GM_addStyle(`
        #floatingSearchWindow {
            position: fixed !important;
            top: 50% !important;
            left: 50% !important;
            transform: translate(-50%, -50%) !important;
            width: 900px !important;
            max-width: 90vw !important;
            background: #ffffff !important;
            border-radius: 12px !important;
            box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15) !important;
            z-index: 2147483647 !important;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif !important;
            display: none !important;
            padding: 20px !important;
        }

        #floatingSearchWindow.active {
            display: block !important;
        }

        .search-header {
            display: flex !important;
            justify-content: space-between !important;
            align-items: center !important;
            margin-bottom: 16px !important;
            padding-bottom: 12px !important;
            border-bottom: 1px solid #e5e5e5 !important;
        }

        .search-title {
            font-size: 14px !important;
            font-weight: 500 !important;
            color: #000000 !important;
            margin: 0 !important;
        }

        .header-controls {
            display: flex !important;
            gap: 8px !important;
            align-items: center !important;
        }

        .bubble-toggle {
            font-size: 11px !important;
            padding: 6px 12px !important;
            background: #f5f5f5 !important;
            border: 1px solid #ddd !important;
            border-radius: 6px !important;
            cursor: pointer !important;
            color: #000000 !important;
            transition: all 0.2s !important;
        }

        .bubble-toggle:hover {
            background: #e8e8e8 !important;
        }

        .bubble-toggle.enabled {
            background: #000000 !important;
            color: #ffffff !important;
            border-color: #000000 !important;
        }

        .close-btn {
            background: none !important;
            border: none !important;
            font-size: 20px !important;
            color: #666 !important;
            cursor: pointer !important;
            padding: 0 !important;
            width: 24px !important;
            height: 24px !important;
            display: flex !important;
            align-items: center !important;
            justify-content: center !important;
            border-radius: 4px !important;
            transition: all 0.2s !important;
        }

        .close-btn:hover {
            background: #f0f0f0 !important;
            color: #000 !important;
        }

        .search-main {
            display: flex !important;
            gap: 12px !important;
            align-items: stretch !important;
            margin-bottom: 16px !important;
        }

        .search-input-wrapper {
            flex: 1 !important;
            position: relative !important;
        }

        .search-input {
            width: 100% !important;
            padding: 14px 16px !important;
            border: 2px solid #e5e5e5 !important;
            border-radius: 8px !important;
            font-size: 14px !important;
            transition: all 0.2s !important;
            box-sizing: border-box !important;
            font-family: inherit !important;
            color: #000000 !important;
        }

        .search-input::placeholder {
            color: #999 !important;
        }

        .search-input:focus {
            outline: none !important;
            border-color: #000000 !important;
        }

        .search-btn {
            padding: 14px 28px !important;
            background: #000000 !important;
            color: #ffffff !important;
            border: none !important;
            border-radius: 8px !important;
            font-size: 14px !important;
            font-weight: 500 !important;
            cursor: pointer !important;
            transition: all 0.2s !important;
            white-space: nowrap !important;
        }

        .search-btn:hover {
            background: #333333 !important;
        }

        .paste-search-btn {
            padding: 14px 24px !important;
            background: #f5f5f5 !important;
            color: #000000 !important;
            border: 2px solid #e5e5e5 !important;
            border-radius: 8px !important;
            font-size: 14px !important;
            font-weight: 500 !important;
            cursor: pointer !important;
            transition: all 0.2s !important;
            white-space: nowrap !important;
        }

        .paste-search-btn:hover {
            background: #e8e8e8 !important;
            border-color: #000000 !important;
        }

        .search-options {
            display: flex !important;
            gap: 8px !important;
            flex-wrap: wrap !important;
        }

        .option-group {
            display: flex !important;
            gap: 6px !important;
        }

        .option-btn {
            padding: 8px 16px !important;
            background: #f5f5f5 !important;
            color: #000000 !important;
            border: 2px solid #e5e5e5 !important;
            border-radius: 6px !important;
            font-size: 13px !important;
            cursor: pointer !important;
            transition: all 0.2s !important;
            font-weight: 500 !important;
        }

        .option-btn:hover {
            background: #e8e8e8 !important;
        }

        .option-btn.active {
            background: #000000 !important;
            color: #ffffff !important;
            border-color: #000000 !important;
        }

        .location-input {
            padding: 8px 14px !important;
            border: 2px solid #e5e5e5 !important;
            border-radius: 6px !important;
            font-size: 13px !important;
            font-family: inherit !important;
            color: #000000 !important;
            min-width: 150px !important;
        }

        .location-input::placeholder {
            color: #999 !important;
        }

        .location-input:focus {
            outline: none !important;
            border-color: #000000 !important;
        }

        .toggle-bubble {
            position: fixed !important;
            bottom: 30px !important;
            right: 30px !important;
            width: 56px !important;
            height: 56px !important;
            background: #000000 !important;
            border: none !important;
            border-radius: 50% !important;
            color: white !important;
            font-size: 20px !important;
            cursor: pointer !important;
            box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2) !important;
            z-index: 2147483646 !important;
            transition: all 0.3s !important;
            display: none !important;
        }

        .toggle-bubble.visible {
            display: flex !important;
            align-items: center !important;
            justify-content: center !important;
        }

        .toggle-bubble:hover {
            transform: scale(1.1) !important;
            box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3) !important;
        }

        .draggable-handle {
            position: absolute !important;
            top: 0 !important;
            left: 0 !important;
            right: 0 !important;
            height: 12px !important;
            cursor: move !important;
        }
    `);

    // Create the floating window HTML
    const searchWindow = document.createElement('div');
    searchWindow.id = 'floatingSearchWindow';
    searchWindow.innerHTML = `
        <div class="draggable-handle"></div>
        <div class="search-header">
            <h2 class="search-title">rumpsy's quick search</h2>
            <div class="header-controls">
                <button class="bubble-toggle" id="bubbleToggle">bubble: off</button>
                <button class="close-btn" id="closeSearch">×</button>
            </div>
        </div>
        <div class="search-main">
            <div class="search-input-wrapper">
                <input type="text" class="search-input" id="searchQuery" placeholder="search query">
            </div>
            <button class="search-btn" id="executeSearch">search</button>
            <button class="paste-search-btn" id="pasteSearch">paste & search</button>
        </div>
        <div class="search-options">
            <div class="option-group">
                <button class="option-btn active" data-engine="google">google</button>
                <button class="option-btn" data-engine="bing">bing</button>
                <button class="option-btn" data-engine="duckduckgo">duckduckgo</button>
                <button class="option-btn" data-engine="yahoo">yahoo</button>
            </div>
            <input type="text" class="location-input" id="searchLocation" placeholder="location (optional)">
        </div>
    `;

    // Create toggle bubble
    const toggleBubble = document.createElement('button');
    toggleBubble.className = 'toggle-bubble';
    toggleBubble.innerHTML = '🔍';
    toggleBubble.title = 'Open Search (Ctrl+Shift+F)';

    // Append to body
    if (document.body) {
        document.body.appendChild(searchWindow);
        document.body.appendChild(toggleBubble);
    } else {
        // Wait for body to be available
        const observer = new MutationObserver(() => {
            if (document.body) {
                document.body.appendChild(searchWindow);
                document.body.appendChild(toggleBubble);
                observer.disconnect();
            }
        });
        observer.observe(document.documentElement, { childList: true });
    }

    // State
    let selectedEngine = 'google';
    let bubbleEnabled = false;

    // Toggle window visibility
    function toggleWindow() {
        searchWindow.classList.toggle('active');
        if (searchWindow.classList.contains('active')) {
            document.getElementById('searchQuery').focus();
        }
    }

    toggleBubble.addEventListener('click', toggleWindow);

    // Close button
    document.getElementById('closeSearch').addEventListener('click', () => {
        searchWindow.classList.remove('active');
    });

    // Bubble toggle
    document.getElementById('bubbleToggle').addEventListener('click', () => {
        bubbleEnabled = !bubbleEnabled;
        const bubbleBtn = document.getElementById('bubbleToggle');
        if (bubbleEnabled) {
            toggleBubble.classList.add('visible');
            bubbleBtn.classList.add('enabled');
            bubbleBtn.textContent = 'bubble: on';
        } else {
            toggleBubble.classList.remove('visible');
            bubbleBtn.classList.remove('enabled');
            bubbleBtn.textContent = 'bubble: off';
        }
    });

    // Engine selection
    document.querySelectorAll('.option-btn[data-engine]').forEach(btn => {
        btn.addEventListener('click', () => {
            document.querySelectorAll('.option-btn[data-engine]').forEach(b => b.classList.remove('active'));
            btn.classList.add('active');
            selectedEngine = btn.dataset.engine;
        });
    });

    // Make window draggable
    let isDragging = false;
    let currentX;
    let currentY;
    let initialX;
    let initialY;

    const dragHandle = searchWindow.querySelector('.draggable-handle');

    dragHandle.addEventListener('mousedown', (e) => {
        isDragging = true;
        initialX = e.clientX - searchWindow.offsetLeft;
        initialY = e.clientY - searchWindow.offsetTop;
    });

    document.addEventListener('mousemove', (e) => {
        if (isDragging) {
            e.preventDefault();
            currentX = e.clientX - initialX;
            currentY = e.clientY - initialY;
            searchWindow.style.transform = 'none';
            searchWindow.style.left = currentX + 'px';
            searchWindow.style.top = currentY + 'px';
        }
    });

    document.addEventListener('mouseup', () => {
        isDragging = false;
    });

    // Search functionality
    function performSearch(query = null) {
        const searchQuery = query || document.getElementById('searchQuery').value.trim();
        const location = document.getElementById('searchLocation').value.trim();

        if (!searchQuery) {
            alert('Please enter a search query');
            return;
        }

        let fullQuery = searchQuery;
        if (location) {
            fullQuery += ' ' + location;
        }

        const encodedQuery = encodeURIComponent(fullQuery);
        let searchURL = '';

        switch(selectedEngine) {
            case 'google':
                searchURL = `https://www.google.com/search?q=${encodedQuery}`;
                break;
            case 'bing':
                searchURL = `https://www.bing.com/search?q=${encodedQuery}`;
                break;
            case 'duckduckgo':
                searchURL = `https://duckduckgo.com/?q=${encodedQuery}`;
                break;
            case 'yahoo':
                searchURL = `https://search.yahoo.com/search?p=${encodedQuery}`;
                break;
        }

        window.open(searchURL, '_blank');
    }

    document.getElementById('executeSearch').addEventListener('click', () => performSearch());

    document.getElementById('searchQuery').addEventListener('keypress', (e) => {
        if (e.key === 'Enter') {
            performSearch();
        }
    });

    // Paste and search functionality
    document.getElementById('pasteSearch').addEventListener('click', async () => {
        try {
            const text = await navigator.clipboard.readText();
            if (text) {
                performSearch(text);
            } else {
                alert('Clipboard is empty');
            }
        } catch (err) {
            alert('Unable to read clipboard. Please paste manually.');
        }
    });

    // Keyboard shortcut: Ctrl+Shift+F to toggle
    document.addEventListener('keydown', (e) => {
        if (e.ctrlKey && e.shiftKey && e.key === 'F') {
            e.preventDefault();
            toggleWindow();
        }
        // ESC to close
        if (e.key === 'Escape' && searchWindow.classList.contains('active')) {
            searchWindow.classList.remove('active');
        }
    });

    // Close window when clicking outside
    searchWindow.addEventListener('click', (e) => {
        if (e.target === searchWindow) {
            searchWindow.classList.remove('active');
        }
    });

}

// Initialize when DOM is ready
if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
} else {
    init();
}

})();