☰

Highlight Text Search Buttons

Show search buttons when text is highlighted on specific websites

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Highlight Text Search Buttons
// @namespace    http://tampermonkey.net/
// @version      2.1
// @description  Show search buttons when text is highlighted on specific websites
// @match        *://onlyfans.com/*
// @match        *://camwhores.tv/*
// @match        https://www.camwhores.tv/*
// @match         https://www.showcamrips.com/*
// @match        *://livecamrips.to/*
// @match        *://simpcity.cr/*
// @match        *://forums.socialmediagirls.com/*
// @match        *://camgirlfinder.net/*
// @match        *://nrtool.st/*
// @match         *://archivebate.com/*
// @match         *://nobodyhome.ws/*
// @match         *://camsmut.com/*
// @match         *://stripchat.com/*
// @match         *://*.bongocams.com/*
// @match         *://www.sexycamx.com/*
// @match         *://chaturbate.com/*
// @match         *://striptube.cc/*
// @match         *://forums.socialmediagirls.com/*
// @match         *://recu.me/*
// @match         *://camripvault.site/*
// @grant        GM_openInTab
// @grant        unsafeWindow
// @license MIT
// ==/UserScript==
(function() {
    'use strict';

    // ============ CONFIGURATION ============

    // Default search sites configuration
    const defaultSites = [
        { name: 'camripvault', url: 'https://camripvault.site/browse?query={username}' },
        { name: 'showcamrips', url: 'https://showcamrips.com/model/en/{username}/' },
        { name: 'livecamrips', url: 'https://www.livecamrips.to/search/{username}/1' },
        { name: 'archivebate', url: 'https://archivebate.com/profile/{username}' },
        { name: 'camsmut', url: 'https://camsmut.com/search?q={username}//' },
        { name: 'camwhores', url: 'https://camwhores.tv/search/{username}/' },
        { name: 'nobodyhome', url: 'https://nobodyhome.ws/search.php?action=do_search&postthread=1&keywords={username}' },
        { name: 'camgirlfinder', url: 'https://camgirlfinder.net/models?model={username}&platform=&gender=' },
        { name: 'nrtool', url: 'https://nrtool.st/nrtool/search?s={username}' },
        { name: 'simpcity', url: 'https://simpcity.cr/search/1/?q={username}' },
        { name: 'socialmediagirls', url: 'https://forums.socialmediagirls.com/search/579063442/?q={username}&o=relevance' },
        { name: 'bongocams', url: 'https://ru3.bongocams.com/{username}' },
        { name: 'stripchat', url: 'https://stripchat.com/{username}' },
        { name: 'onlyfans', url: 'https://onlyfans.com/{username}' },
        { name: 'sexycamx', url: 'https://www.sexycamx.com/{username}' },
        { name: 'recume', url: 'https://recu.me/performer/{username}' },
        { name: 'striphub', url: 'https://striptube.cc/performer/{username}' }
    ];


    // ============ SCRIPT LOGIC ============

    let buttonContainer = null;
    let selectedText = '';
    let useLowercase = false;

    // Enhanced function to open URL in new tab (bypasses site restrictions)
    function openUrlInNewTab(url) {
        // Method 1: Try GM_openInTab if available (most reliable)
        if (typeof GM_openInTab !== 'undefined') {
            try {
                GM_openInTab(url, { active: false, insert: true });
                return;
            } catch (e) {
                console.log('GM_openInTab failed, trying alternatives');
            }
        }

        // Method 2: Create a temporary anchor element with download attribute trick
        try {
            const a = document.createElement('a');
            a.href = url;
            a.target = '_blank';
            a.rel = 'noopener noreferrer';

            // This helps bypass some restrictions
            a.style.display = 'none';
            document.body.appendChild(a);

            // Dispatch a real click event
            const evt = new MouseEvent('click', {
                bubbles: true,
                cancelable: true,
                view: window
            });
            a.dispatchEvent(evt);

            // Clean up
            setTimeout(() => document.body.removeChild(a), 100);
            return;
        } catch (e) {
            console.log('Anchor method failed, trying window.open');
        }

        // Method 3: Standard window.open with enhanced parameters
        try {
            const newWindow = window.open(url, '_blank', 'noopener,noreferrer');
            if (newWindow) {
                newWindow.opener = null;
            }
        } catch (e) {
            // Method 4: Last resort - use location.href in a timeout
            console.log('All methods failed, using location fallback');
            setTimeout(() => {
                window.location.href = url;
            }, 100);
        }
    }

    // Create button container
    function createButtonContainer() {
        const container = document.createElement('div');
        container.id = 'highlight-search-container';
        container.style.cssText = `
            position: fixed;
            bottom: 20px;
            right: 20px;
            display: none;
            flex-direction: column;
            gap: 6px;
            z-index: 999999;
            padding: 10px;
            background: rgba(255, 255, 255, 0.98);
            border-radius: 10px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
            backdrop-filter: blur(10px);
            border: 1px solid rgba(0, 0, 0, 0.1);
            max-width: 180px;
            max-height: 80vh;
            overflow-y: auto;
        `;

        // Add title
        const title = document.createElement('div');
        title.textContent = 'Search with:';
        title.style.cssText = `
            font-size: 11px;
            font-weight: 600;
            color: #666;
            margin-bottom: 2px;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
        `;
        container.appendChild(title);

        // Add lowercase checkbox
        const checkboxContainer = document.createElement('label');
        checkboxContainer.style.cssText = `
            display: flex;
            align-items: center;
            gap: 6px;
            font-size: 11px;
            color: #666;
            margin-bottom: 4px;
            cursor: pointer;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
        `;

        const checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.checked = useLowercase;
        checkbox.style.cssText = `
            cursor: pointer;
            width: 14px;
            height: 14px;
        `;

        checkbox.addEventListener('change', (e) => {
            useLowercase = e.target.checked;
        });

        const checkboxLabel = document.createElement('span');
        checkboxLabel.textContent = 'Lowercase';

        checkboxContainer.appendChild(checkbox);
        checkboxContainer.appendChild(checkboxLabel);
        container.appendChild(checkboxContainer);

        // Create buttons
        defaultSites.forEach(site => {
            const button = document.createElement('button');
            button.textContent = site.name;
            button.style.cssText = `
                padding: 6px 10px;
                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                color: white;
                border: none;
                border-radius: 6px;
                cursor: pointer;
                font-size: 12px;
                font-weight: 500;
                font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
                transition: all 0.2s ease;
                white-space: nowrap;
                text-align: center;
            `;

            button.addEventListener('mouseenter', () => {
                button.style.transform = 'translateY(-1px)';
                button.style.boxShadow = '0 2px 8px rgba(0, 0, 0, 0.2)';
            });

            button.addEventListener('mouseleave', () => {
                button.style.transform = 'translateY(0)';
                button.style.boxShadow = 'none';
            });

            button.addEventListener('click', (e) => {
                e.preventDefault();
                e.stopPropagation();

                const username = useLowercase ? selectedText.toLowerCase() : selectedText;
                const finalUrl = site.url.replace('{username}', encodeURIComponent(username));

                openUrlInNewTab(finalUrl);
            });

            container.appendChild(button);
        });

        // Add close button
        const closeBtn = document.createElement('button');
        closeBtn.textContent = '✕';
        closeBtn.style.cssText = `
            position: absolute;
            top: 6px;
            right: 6px;
            width: 20px;
            height: 20px;
            border: none;
            background: rgba(0, 0, 0, 0.1);
            border-radius: 50%;
            cursor: pointer;
            font-size: 12px;
            color: #666;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.2s ease;
        `;

        closeBtn.addEventListener('mouseenter', () => {
            closeBtn.style.background = 'rgba(0, 0, 0, 0.2)';
        });

        closeBtn.addEventListener('mouseleave', () => {
            closeBtn.style.background = 'rgba(0, 0, 0, 0.1)';
        });

        closeBtn.addEventListener('click', () => {
            container.style.display = 'none';
        });

        container.appendChild(closeBtn);
        document.body.appendChild(container);
        return container;
    }

    // Handle text selection
    function handleTextSelection() {
        const selection = window.getSelection();
        const text = selection.toString().trim();

        if (text.length > 0) {
            selectedText = text;
            if (!buttonContainer) {
                buttonContainer = createButtonContainer();
            }
            buttonContainer.style.display = 'flex';
        } else {
            if (buttonContainer) {
                buttonContainer.style.display = 'none';
            }
        }
    }

    // Event listeners
    document.addEventListener('mouseup', handleTextSelection);
    document.addEventListener('keyup', handleTextSelection);
    document.addEventListener('selectionchange', handleTextSelection);

    // Hide buttons when clicking outside
    document.addEventListener('click', (e) => {
        if (buttonContainer && !buttonContainer.contains(e.target)) {
            const selection = window.getSelection();
            if (selection.toString().trim().length === 0) {
                buttonContainer.style.display = 'none';
            }
        }
    });

})();