Desbloquear Facemojikeyboard

Desbloquea los textos artísticos, previene popups, copia al portapapeles sin notificaciones, sin afectar el scroll, y elimina "@Facemojikeyboard" del texto.

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

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Desbloquear Facemojikeyboard
// @namespace    http://tampermonkey.net/
// @version      1.3.1
// @description  Desbloquea los textos artísticos, previene popups, copia al portapapeles sin notificaciones, sin afectar el scroll, y elimina "@Facemojikeyboard" del texto.
// @author       YouTubeDrawaria
// @match        https://www.facemojikeyboard.com/text-art-collection
// @grant        none
// @license      MIT
// @icon         https://cdn.facemojikeyboard.com/website/favicon.png
// ==/UserScript==

(function() {
    'use strict';

    // Cadena a eliminar de los textos
    const STRING_TO_REMOVE = '@Facemojikeyboard';

    /**
     * Copia el texto dado al portapapeles. Sin notificaciones.
     * @param {string} text El texto a copiar.
     */
    function copyTextToClipboard(text) {
        // Eliminar la cadena no deseada antes de copiar
        const cleanedText = text.replace(new RegExp(STRING_TO_REMOVE, 'g'), '').trim();

        if (navigator.clipboard) {
            navigator.clipboard.writeText(cleanedText).catch(err => {
                console.error('No se pudo copiar el texto usando navigator.clipboard: ', err);
                fallbackCopyTextToClipboard(cleanedText);
            });
        } else {
            fallbackCopyTextToClipboard(cleanedText);
        }
    }

    /**
     * Fallback para copiar texto al portapapeles usando un textarea temporal.
     * @param {string} text El texto a copiar.
     */
    function fallbackCopyTextToClipboard(text) {
        const textarea = document.createElement('textarea');
        textarea.value = text;
        textarea.style.position = 'fixed';
        textarea.style.left = '-9999px';
        textarea.style.top = '-9999px';
        document.body.appendChild(textarea);
        textarea.focus();
        textarea.select();
        try {
            document.execCommand('copy');
        } catch (err) {
            console.error('No se pudo copiar el texto con execCommand: ', err);
        }
        document.body.removeChild(textarea);
    }

    /**
     * Aplica las modificaciones a los elementos de arte de texto (eliminar candado, hacer seleccionable, añadir listener de copia).
     */
    function applyModifications() {
        const textArtWrappers = document.querySelectorAll('.emojiwrapper-list-tool__wrapper');

        textArtWrappers.forEach(item => {
            // 1. Elimina el icono de candado si existe
            const lockIcon = item.querySelector('img.lock');
            if (lockIcon) {
                lockIcon.remove();
            }

            // 2. Asegura que el texto sea seleccionable
            item.style.userSelect = 'text';
            item.style.webkitUserSelect = 'text';
            item.style.mozUserSelect = 'text';
            item.style.msUserSelect = 'text';

            // 3. Añade un listener de clic para copiar texto y prevenir popups
            if (!item.dataset.listenerAdded) {
                item.addEventListener('click', function(event) {
                    event.preventDefault();
                    event.stopPropagation();

                    const textSpan = this.querySelector('span');
                    if (textSpan && textSpan.textContent) {
                        copyTextToClipboard(textSpan.textContent.trim());
                    }
                }, { capture: true });
                item.dataset.listenerAdded = 'true';
            }
        });
    }

    /**
     * Oculta elementos específicos de pop-up y asegura que el scroll no esté bloqueado.
     * Se usa `!important` para asegurar la anulación de estilos existentes.
     */
    function hideSpecificPopupsAndRestoreScroll() {
        const popper = document.querySelector('.el-popper.tooltip-popper-class');
        if (popper) {
            popper.style.setProperty('display', 'none', 'important');
            popper.setAttribute('aria-hidden', 'true');
        }

        const modal = document.querySelector('.dialog-container.default-modal');
        if (modal) {
            modal.style.setProperty('display', 'none', 'important');
        }

        document.body.style.setProperty('overflow', 'auto', 'important');
        document.documentElement.style.setProperty('overflow', 'auto', 'important');
    }

    // --- Lógica de Ejecución y Observación ---

    // 1. Añadir reglas CSS globales para ocultar los popups de forma persistente
    // y asegurar que el scroll siempre funcione.
    function addGlobalCSSRules() {
        const style = document.createElement('style');
        style.type = 'text/css';
        style.innerHTML = `
            /* Ocultar los popups de información/descarga */
            .el-popper.tooltip-popper-class,
            .dialog-container.default-modal {
                display: none !important;
            }
            /* Asegurar que el scroll siempre funcione */
            body, html {
                overflow: auto !important;
            }
        `;
        document.head.appendChild(style);
    }
    addGlobalCSSRules();

    // 2. Ejecutar las modificaciones iniciales y ocultar popups al cargar la página
    applyModifications();
    hideSpecificPopupsAndRestoreScroll();

    // 3. Observar cambios en el DOM para manejar contenido cargado dinámicamente y nuevos popups
    const observer = new MutationObserver((mutationsList, observer) => {
        let needsModifications = false;
        let needsPopupHiding = false;

        for (const mutation of mutationsList) {
            if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType === 1) { // Solo si es un elemento HTML
                        if (node.classList.contains('emojiwrapper-list-tool') || node.querySelector('.emojiwrapper-list-tool__wrapper')) {
                            needsModifications = true;
                        }
                        if (node.classList.contains('el-popper') || node.classList.contains('dialog-container')) {
                             needsPopupHiding = true;
                        }
                    }
                });
            } else if (mutation.type === 'attributes' && (mutation.target === document.body || mutation.target === document.documentElement) && mutation.attributeName === 'style') {
                if (mutation.target.style.overflow === 'hidden') {
                    needsPopupHiding = true;
                }
            }
        }

        if (needsModifications) {
            applyModifications();
        }
        if (needsPopupHiding) {
            hideSpecificPopupsAndRestoreScroll();
        }
    });

    observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style'] });
    observer.observe(document.documentElement, { attributes: true, attributeFilter: ['style'] });

})();