Betty Modern

Enhanced open directory finder for modern Google search

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name            Betty Modern
// @namespace       mario0318.com
// @author          Raul Torres (Modern adaptation of Betty - Open directory search assistant)
// @version         0.7.0
// @license         GPLv3
// @description     Enhanced open directory finder for modern Google search
// @icon            https://raw.githubusercontent.com/sgeto/Betty/master/betty-space%20invader%20emoji.png
// @match           https://www.google.com/*
// @exclude         https://www.google.com/images/*
// @exclude         https://www.google.com/video/*
// @run-at          document-end
// @require         https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js
// @grant           GM_xmlhttpRequest
// ==/UserScript==
/*
 * Original Betty script created by:
 * - Ali Abdulkadir (sgeto)
 * - Jorge Frisancho (teocci)
 * - jO9GEc
 *
 * Modern adaptation by mario0318
 *
 * This work is derived from:
 * https://github.com/sgeto/Betty (Original repository)
 * Licensed under GPL-3.0 - https://www.gnu.org/licenses/gpl-3.0.html
 */

(function() {
    'use strict';

    // Modern Google search form handling
    function enhanceSearchForm() {
        const searchBox = document.querySelector('textarea[name="q"], input[name="q"]');
        if (!searchBox) return;

        const form = searchBox.closest('form');
        if (!form) return;

        // Preserve original form submission
        const originalSubmit = form.submit.bind(form);
        form.addEventListener('submit', function(e) {
            e.preventDefault();
            originalSubmit();
        });

        // Create radio buttons container
        const radioContainer = document.createElement('div');
        radioContainer.id = 'betty-radio-container';
        radioContainer.style.margin = '10px 0';
        radioContainer.style.display = 'flex';
        radioContainer.style.flexWrap = 'wrap';
        radioContainer.style.gap = '12px';

        // Radio button definitions
        const options = [
            { name: 'Web', value: '' },
            { name: 'Music', value: '+(mp3|wav|ac3|ogg|flac|wma|m4a) -inurl:(jsp|pl|php|html|aspx|htm|cf|shtml) intitle:index.of "last modified" -inurl:(listen77|mp3raid|mp3toss|mp3drug|index_of|wallywashis)' },
            { name: 'Video', value: '+(mkv|mp4|avi|mov|mpg|wmv) -inurl:(jsp|pl|php|html|aspx|htm|cf|shtml) intitle:index.of "last modified" -inurl:(listen77|mp3raid|mp3toss|mp3drug|index_of|wallywashis)' },
            { name: 'Files', value: 'intitle:"index of" -inurl:(jsp|pl|php|html|aspx|htm|cf|shtml) -inurl:(listen77|mp3raid|mp3toss|mp3drug|index_of|wallywashis)' },
            { name: 'Archives', value: '+(rar|zip|tar|tgz|7zip|iso|cso|gz|7z|bz2|gzip) intitle:"index of" -inurl:(jsp|pl|php|html|aspx|htm|cf|shtml) -inurl:(listen77|mp3raid|mp3toss|mp3drug|index_of|wallywashis)' },
            { name: 'Software', value: '+(exe|iso|tar|msi|rar|deb|zip|apk) -inurl:(jsp|pl|php|html|aspx|htm|cf|shtml) intitle:index.of "last modified" -inurl:(listen77|mp3raid|mp3toss|mp3drug|index_of|wallywashis)' },
            { name: 'Books', value: '+(mobi|cbz|cbr|cbc|chm|epub|fb2|lit|lrf|odt|pdf|prc|pdb|pml|rb|rtf|tcr|doc|docx) -inurl:(jsp|pl|php|html|aspx|htm|cf|shtml) intitle:index.of "last modified" -inurl:(listen77|mp3raid|mp3toss|mp3drug|index_of|wallywashis)' }
        ];

        // Create radio buttons
        options.forEach(opt => {
            const wrapper = document.createElement('div');
            wrapper.style.display = 'flex';
            wrapper.style.alignItems = 'center';
            wrapper.style.gap = '4px';

            const radio = document.createElement('input');
            radio.type = 'radio';
            radio.name = 'betty-search-type';
            radio.value = opt.value;
            radio.id = `betty-${opt.name.toLowerCase()}`;
            radio.style.margin = '0';

            if (opt.name === 'Web') radio.checked = true;

            radio.addEventListener('change', () => {
                searchBox.value = opt.value ? opt.value : '';
            });

            const label = document.createElement('label');
            label.htmlFor = radio.id;
            label.textContent = opt.name;
            label.style.color = '#70757a';
            label.style.fontSize = '14px';

            wrapper.appendChild(radio);
            wrapper.appendChild(label);
            radioContainer.appendChild(wrapper);
        });

        // Insert radio buttons after search box
        searchBox.parentElement.insertAdjacentElement('afterend', radioContainer);
    }

    // Enhanced cache link exposure
    function enhanceCacheLinks() {
        const results = document.querySelectorAll('div.g, div[data-snc]');

        results.forEach(result => {
            const menu = result.querySelector('div[role="button"][aria-label="More options"]');
            if (!menu) return;

            const cachedLink = Array.from(menu.parentElement.querySelectorAll('a')).find(a =>
                a.href.includes('webcache.googleusercontent.com')
            );

            if (cachedLink) {
                const clone = cachedLink.cloneNode(true);
                clone.textContent = 'Cached';
                clone.style.marginLeft = '12px';
                clone.style.fontSize = '14px';
                result.querySelector('a[href]:first-child').insertAdjacentElement('afterend', clone);
            }
        });
    }

    // Initialize
    function init() {
        if (window.location.href === 'https://www.google.com/' ||
            window.location.href.startsWith('https://www.google.com/search')) {
            enhanceSearchForm();
        }

        if (window.location.href.startsWith('https://www.google.com/search')) {
            const observer = new MutationObserver(mutations => {
                if (document.querySelector('div#betty-radio-container')) return;
                enhanceCacheLinks();
            });

            observer.observe(document.body, {
                childList: true,
                subtree: true
            });

            enhanceCacheLinks();
        }
    }

    // Wait for DOM and initialize
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();