Greasy Fork

来自缓存

Greasy Fork is available in English.

noemoji

Blocks emojis on all websites (with special handling for X.com and YouTube)

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         noemoji
// @namespace    http://tampermonkey.net/
// @version      2.0
// @description  Blocks emojis on all websites (with special handling for X.com and YouTube)
// @author       You
// @match        *://*/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // Unicode ranges for emojis
    const emojiRanges = [
        { start: 0x1F600, end: 0x1F64F }, // Emoticons
        { start: 0x1F300, end: 0x1F5FF }, // Misc Symbols and Pictographs
        { start: 0x1F680, end: 0x1F6FF }, // Transport and Map
        { start: 0x1F700, end: 0x1F77F }, // Alchemical Symbols
        { start: 0x1F780, end: 0x1F7FF }, // Geometric Shapes
        { start: 0x1F800, end: 0x1F8FF }, // Supplemental Arrows-C
        { start: 0x1F900, end: 0x1F9FF }, // Supplemental Symbols and Pictographs
        { start: 0x1FA00, end: 0x1FA6F }, // Chess Symbols
        { start: 0x1FA70, end: 0x1FAFF }, // Symbols and Pictographs Extended-A
        { start: 0x2600, end: 0x26FF },   // Misc symbols
        { start: 0x2700, end: 0x27BF },   // Dingbats
        { start: 0x2300, end: 0x23FF },   // Miscellaneous Technical
        { start: 0x2B00, end: 0x2BFF },   // Miscellaneous Symbols and Arrows
        { start: 0x3000, end: 0x303F }    // CJK Symbols and Punctuation (includes some emoji-like symbols)
    ];

    // Check if we're on specific sites that need special handling
    const isYouTube = () => window.location.hostname.includes('youtube');
    const isTwitter = () => window.location.hostname.includes('twitter') || window.location.hostname.includes('x.com');
    const isFacebook = () => window.location.hostname.includes('facebook') || window.location.hostname.includes('fb.com');
    const isInstagram = () => window.location.hostname.includes('instagram');
    const isReddit = () => window.location.hostname.includes('reddit');
    const isDiscord = () => window.location.hostname.includes('discord');
    const isWhatsApp = () => window.location.hostname.includes('whatsapp');
    const isTelegram = () => window.location.hostname.includes('telegram');

    // Check if a character is an emoji
    function isEmoji(char) {
        const code = char.codePointAt(0);
        if (!code) return false;

        return emojiRanges.some(range => code >= range.start && code <= range.end);
    }

    // Check if text contains any emoji
    function containsEmoji(text) {
        if (!text) return false;

        for (let i = 0; i < text.length; i++) {
            const charCode = text.codePointAt(i);
            if (charCode !== text.charCodeAt(i)) {
                // This is a surrogate pair
                const char = String.fromCodePoint(charCode);
                if (isEmoji(char)) {
                    return true;
                }
                i++; // Skip the second part of the surrogate pair
            } else {
                const char = text.charAt(i);
                if (isEmoji(char)) {
                    return true;
                }
            }
        }
        return false;
    }

    // Replace emojis with empty string
    function removeEmojis(text) {
        if (!text) return text;

        let result = '';
        for (let i = 0; i < text.length; i++) {
            // Handle surrogate pairs correctly for emojis
            const charCode = text.codePointAt(i);
            if (charCode !== text.charCodeAt(i)) {
                // This is a surrogate pair (likely an emoji)
                const char = String.fromCodePoint(charCode);
                if (isEmoji(char)) {
                    result += ''; // Remove emoji completely
                    i++; // Skip the second part of the surrogate pair
                } else {
                    result += char;
                    i++; // Skip the second part of the surrogate pair
                }
            } else {
                const char = text.charAt(i);
                if (isEmoji(char)) {
                    result += ''; // Remove emoji completely
                } else {
                    result += char;
                }
            }
        }
        return result;
    }

    // Process text nodes and elements
    function processNode(node) {
        if (node.nodeType === Node.TEXT_NODE) {
            const originalText = node.nodeValue;
            const newText = removeEmojis(originalText);
            if (originalText !== newText) {
                node.nodeValue = newText;
            }
        } else if (node.nodeType === Node.ELEMENT_NODE) {
            // Skip script, style, and svg elements
            if (['SCRIPT', 'STYLE', 'SVG'].includes(node.tagName)) {
                return;
            }

            // Check for img elements that might be emoji
            if (node.tagName === 'IMG') {
                const alt = node.getAttribute('alt') || '';
                const src = node.getAttribute('src') || '';
                const className = node.className || '';

                // Hide image if it's likely an emoji
                if (containsEmoji(alt) ||
                    src.includes('emoji') ||
                    src.includes('twemoji') ||
                    className.includes('emoji') ||
                    (node.width > 0 && node.height > 0 && node.width < 32 && node.height < 32)) {
                    node.style.display = 'none';
                }
            }

            // Process elements that might contain emoji
            if (['SPAN', 'DIV', 'P', 'A', 'BUTTON', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6'].includes(node.tagName)) {
                const aria = node.getAttribute('aria-label') || '';
                const title = node.getAttribute('title') || '';
                const className = node.className || '';

                if ((aria && containsEmoji(aria)) ||
                    (title && containsEmoji(title)) ||
                    className.includes('emoji')) {

                    // For X.com and other platforms that use spans to wrap emojis
                    if (node.childNodes.length === 0 ||
                        (node.childNodes.length === 1 && node.childNodes[0].nodeType === Node.TEXT_NODE && containsEmoji(node.childNodes[0].nodeValue))) {
                        node.style.display = 'none';
                    }
                }
            }

            // Process children recursively
            Array.from(node.childNodes).forEach(processNode);
        }
    }

    // Process elements specific to certain platforms
    function processPlatformSpecificElements() {
        // YouTube specific processing
        if (isYouTube()) {
            const youtubeSelectors = [
                'yt-live-chat-text-message-renderer img',
                'yt-formatted-string img',
                'ytd-comment-renderer #content-text img',
                'img[src*="emoji"]',
                'img.emoji',
                'yt-emoji',
                'yt-emoji-picker-renderer'
            ];

            youtubeSelectors.forEach(selector => {
                document.querySelectorAll(selector).forEach(el => {
                    el.style.display = 'none';
                });
            });
        }

        // Twitter/X.com specific processing
        if (isTwitter()) {
            const twitterSelectors = [
                'span[aria-label*="Emoji"]',
                'img.Emoji',
                'img[alt*="Emoji"]',
                '.r-4qtqp9.r-1bwzh9t.r-dnmrzs.r-bnwqim',
                '[data-testid="tweetText"] span:not(.r-18u37iz):not(:has(*))'
            ];

            twitterSelectors.forEach(selector => {
                try {
                    document.querySelectorAll(selector).forEach(el => {
                        el.style.display = 'none';
                    });
                } catch (e) {
                    // Some complex CSS selectors might not be supported in all browsers
                }
            });
        }

        // Facebook specific processing
        if (isFacebook()) {
            const fbSelectors = [
                'span[aria-label*="emoji"]',
                'img[alt*="emoji"]',
                'img[src*="emoji"]',
                '.x1lliihq', // Facebook's emoji class
                'img[alt*="Emoticon"]'
            ];

            fbSelectors.forEach(selector => {
                document.querySelectorAll(selector).forEach(el => {
                    el.style.display = 'none';
                });
            });
        }

        // Instagram specific processing
        if (isInstagram()) {
            const igSelectors = [
                'span[aria-label*="emoji"]',
                'img[alt*="emoji"]',
                'img[src*="emoji"]',
                'img._7yuu' // Instagram's emoji class
            ];

            igSelectors.forEach(selector => {
                document.querySelectorAll(selector).forEach(el => {
                    el.style.display = 'none';
                });
            });
        }

        // Reddit specific processing
        if (isReddit()) {
            const redditSelectors = [
                'span[aria-label*="emoji"]',
                'img.emoji',
                'img[alt*="emoji"]',
                'img[src*="emoji"]'
            ];

            redditSelectors.forEach(selector => {
                document.querySelectorAll(selector).forEach(el => {
                    el.style.display = 'none';
                });
            });
        }

        // Discord specific processing
        if (isDiscord()) {
            const discordSelectors = [
                'img.emoji',
                'img[alt*="emoji"]',
                'img[src*="emoji"]',
                'span.emoji'
            ];

            discordSelectors.forEach(selector => {
                document.querySelectorAll(selector).forEach(el => {
                    el.style.display = 'none';
                });
            });
        }
    }

    // Add custom CSS for universal emoji blocking
    function addCustomStyle() {
        const style = document.createElement('style');

        style.textContent = `
            /* Universal emoji hiding */
            img.emoji,
            img[src*="emoji"],
            img[alt*="emoji"],
            span[aria-label*="emoji"],
            span.emoji,
            .emoji,
            [role="img"][aria-label*="emoji"],
            [role="img"][aria-label*="Emoji"],
            [role="img"][title*="emoji"],
            [role="img"][title*="Emoji"] {
                display: none !important;
            }

            /* Site-specific emoji hiding */

            /* YouTube */
            yt-emoji-picker-renderer,
            yt-emoji,
            yt-img-shadow:has(img[src*="emoji"]),
            img[src*="emojiasset"],
            yt-live-chat-paid-message-renderer img,
            yt-live-chat-paid-sticker-renderer img,
            ytd-comment-renderer #content-text img {
                display: none !important;
            }

            /* X.com / Twitter */
            .r-4qtqp9.r-1bwzh9t.r-dnmrzs.r-bnwqim,
            span[aria-label*="Emoji"],
            span[aria-label*="emoji"] {
                display: none !important;
            }

            /* Facebook */
            .x1lliihq,
            img[alt*="Emoticon"] {
                display: none !important;
            }

            /* WhatsApp Web */
            span.selectable-text img,
            div.selectable-text img {
                display: none !important;
            }

            /* Slack */
            .c-emoji,
            .emoji {
                display: none !important;
            }

            /* Gmail */
            img[src*="emojione"],
            img[src*="goomoji"] {
                display: none !important;
            }

            /* Discord */
            img.emoji-4QhjQqh,
            span.emojiContainer-3X8SvE {
                display: none !important;
            }

            /* Common classes across platforms */
            .emoji-mart-emoji,
            .emoji-picker,
            .emoji-container,
            .emoji-wrapper {
                display: none !important;
            }
        `;

        document.head.appendChild(style);
    }

    // Main initialization function
    function init() {
        addCustomStyle();

        // Process the entire document
        processNode(document.body);

        // Process platform-specific elements
        processPlatformSpecificElements();

        // Set up a MutationObserver to handle dynamically added content
        const observer = new MutationObserver(mutations => {
            mutations.forEach(mutation => {
                mutation.addedNodes.forEach(node => {
                    processNode(node);
                });
            });

            // Re-process platform specific elements after DOM changes
            processPlatformSpecificElements();
        });

        // Observe the entire document for changes
        observer.observe(document.body, {
            childList: true,
            subtree: true,
            characterData: true
        });
    }

    // Initialize as soon as possible
    if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", init);
    } else {
        init();
    }

    // Handle SPA navigation
    let lastUrl = location.href;
    new MutationObserver(() => {
        const url = location.href;
        if (url !== lastUrl) {
            lastUrl = url;
            // Wait a bit for content to load after navigation
            setTimeout(() => {
                processNode(document.body);
                processPlatformSpecificElements();
            }, 1000);
        }
    }).observe(document, {subtree: true, childList: true});

    // Apply initial styling even before full DOM load
    // This helps catch emoji that load very early
    (function earlyInit() {
        // Add basic style to prevent flash of emoji
        const earlyStyle = document.createElement('style');
        earlyStyle.textContent = `
            img[src*="emoji"], img.emoji, .emoji {
                display: none !important;
            }
        `;

        // Try to insert style as early as possible
        if (document.head) {
            document.head.appendChild(earlyStyle);
        } else {
            // If head isn't available yet, wait for it
            const observer = new MutationObserver(() => {
                if (document.head) {
                    document.head.appendChild(earlyStyle);
                    observer.disconnect();
                }
            });
            observer.observe(document.documentElement, { childList: true, subtree: true });
        }
    })();
})();