Fix Gmail Email Search

Improved version of the old "Emails" quick search functionality in Gmail, one click to view all emails you've sent or received from any address

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Fix Gmail Email Search
// @namespace    https://github.com/gserafini/fix-gmail-email-search-userscript/
// @version      1.5.1
// @description  Improved version of the old "Emails" quick search functionality in Gmail, one click to view all emails you've sent or received from any address
// @author       Gabriel Serafini
// @license      MIT
// @donate       If you like this, PayPal a little love to [email protected] (or https://paypal.me/SerafiniStudios )
// @screenshot   https://raw.githubusercontent.com/gserafini/fix-gmail-email-search-userscript/master/fix-gmail-email-search-screenshot.png
// @icon         https://raw.githubusercontent.com/gserafini/fix-gmail-email-search-userscript/master/SearchEmails_icon.png
// @match        *://mail.google.com/*
// @match        *://contacts.google.com/*
// @match        *://gmail.com/*
// @grant        none
// ==/UserScript==

// Changelog:
// v1.5.1 - Bug fix: Fixed @match directive for contacts.google.com to support
//          Google Workspace accounts with /u/1/ paths (hovercards now work on all accounts)
// v1.5.0 - Major rewrite: Removed jQuery dependency, now uses pure vanilla JavaScript
//          No longer requires separate TrustedHTML bypass script
//          Uses native DOM methods (createElement, createElementNS) instead of innerHTML
//          Smaller, faster, and bypasses TrustedTypes restrictions without workarounds

(function() {
    'use strict';

    // Add styles using textContent (no TrustedTypes issues)
    var style = document.createElement('style');
    style.textContent = '.email_search_icon { outline: none; position: relative; z-index: 0; padding: 0 4px; margin: 0; top: 2px; cursor: pointer; } ' +
                        '.email_search_icon svg { fill: #600; } ' +
                        '.email_search_icon svg:hover { fill: #f00; }';
    document.head.appendChild(style);

    // Create search icon element using DOM methods (no innerHTML)
    function createEmailSearchIcon(emailAddress) {
        var span = document.createElement('span');
        span.className = 'email_search_icon';
        span.title = 'Click to search for all emails with ' + emailAddress;
        span.setAttribute('email', emailAddress);

        // Create SVG using createElementNS
        var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        svg.setAttribute('focusable', 'false');
        svg.setAttribute('height', '1em');
        svg.setAttribute('viewBox', '0 0 24 24');
        svg.setAttribute('width', '1em');

        var path1 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        path1.setAttribute('d', 'M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z');

        var path2 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        path2.setAttribute('d', 'M0 0h24v24H0z');
        path2.setAttribute('fill', 'none');

        svg.appendChild(path1);
        svg.appendChild(path2);
        span.appendChild(svg);

        return span;
    }

    // Check if element already has icon
    function hasIcon(element) {
        var prev = element.previousElementSibling;
        return prev && prev.classList.contains('email_search_icon');
    }

    function insertEmailSearchIcons() {
        // Find Gmail email elements (.gD class)
        var gdElements = document.querySelectorAll('.gD');
        for (var i = 0; i < gdElements.length; i++) {
            var el = gdElements[i];
            if (!hasIcon(el)) {
                var email = el.getAttribute('email');
                if (email) {
                    var gdIcon = createEmailSearchIcon(email);
                    el.parentNode.insertBefore(gdIcon, el);
                }
            }
        }

        // Find mailto links (excluding contenteditable divs)
        var mailtoLinks = document.querySelectorAll('a[href^="mailto:"]');
        for (var j = 0; j < mailtoLinks.length; j++) {
            var link = mailtoLinks[j];
            // Skip if inside contenteditable div
            var inEditable = link.closest('div[contenteditable="true"]');
            if (!inEditable && !hasIcon(link)) {
                var href = link.getAttribute('href');
                var emailAddr = href.replace('mailto:', '');
                var mailtoIcon = createEmailSearchIcon(emailAddr);
                link.parentNode.insertBefore(mailtoIcon, link);
            }
        }
    }

    // Poll periodically for newly created elements
    setInterval(insertEmailSearchIcons, 300);

    // Handle click events using event delegation
    document.addEventListener('click', function(event) {
        var target = event.target;

        // Find closest .email_search_icon (handles clicks on SVG children)
        var icon = target.closest('.email_search_icon');
        if (!icon) return;

        event.stopPropagation();

        var email = icon.getAttribute('email');

        if (window.self === window.top) {
            // Main window - perform search
            var searchInput = document.querySelector('input[name="q"]');
            var searchButton = document.querySelector('button[aria-label="Search mail"]');

            if (searchInput && searchButton) {
                searchInput.value = email;
                searchButton.click();
            }
        } else {
            // Iframe (hovercard) - send message to parent
            window.parent.postMessage('searchForEmail=' + email, '*');
        }
    });

    // Listen for messages from iframe (hovercard)
    if (window.self === window.top) {
        window.addEventListener('message', function(event) {
            if (event.origin === 'https://contacts.google.com' &&
                typeof event.data === 'string' &&
                event.data.indexOf('searchForEmail=') !== -1) {

                var email = event.data.replace('searchForEmail=', '');
                var searchInput = document.querySelector('input[name="q"]');
                var searchButton = document.querySelector('button[aria-label="Search mail"]');

                if (searchInput && searchButton) {
                    searchInput.value = email;
                    searchButton.click();
                }
            }
        });
    }

})();