Загрузка данных


// ==UserScript==
// @name         Avatar Builder+++ (Con soporte GIF)
// @namespace    avatar builder
// @version      2.7.1
// @description  Adds an upload image input to the avatar builder with performance improvements
// @author       gatogamer123xd
// @license      MIT
// @match        https://*.drawaria.online/avatar/builder/
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js
// @icon         https://www.google.com/s2/favicons?domain=drawaria.online
// @grant        none
// ==/UserScript==

(($) => {
    'use strict';

    // Función auxiliar para convertir y comprimir realmente a JPEG usando un Canvas
    const convertToJpeg = (imgElement) => {
        return new Promise((resolve) => {
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');

            canvas.width = imgElement.width;
            canvas.height = imgElement.height;

            // Fondo blanco por si la imagen original tiene transparencias (PNG/WEBP)
            ctx.fillStyle = '#FFFFFF';
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            ctx.drawImage(imgElement, 0, 0);

            // Exporta a JPEG real con una calidad óptima del 90%
            resolve(canvas.toDataURL('image/jpeg', 0.9));
        });
    };

    const setupAvatarUpload = () => {
        // Evita duplicados en la interfaz
        if ($('#imageInput').length) return;

        const $header = $('header');
        if (!$header.length) return;

        // Inyecta el botón y el input oculto de forma limpia
        $header.append(`
            <label class="Button" id="uploadBtn" for="imageInput" style="cursor: pointer; user-select: none;">Upload Image</label>
            <input style="display:none" id="imageInput" type="file" accept="image/jpeg, image/png, image/webp, image/gif">
        `);

        const $imageInput = $('#imageInput');
        const $labelButton = $('#uploadBtn');

        // Función centralizada para enviar la imagen al servidor
        const sendImageToServer = (uploadedImage) => {
            $labelButton.text('Uploading...').css('pointer-events', 'none');

            const targetUrl = window.LOGGEDIN ? '/saveavatar' : '/uploadavatarimage';
            const payload = {
                'avatarsave_builder': JSON.stringify(window.ACCOUNT_AVATARSAVE || {}),
                'imagedata': uploadedImage,
                'fromeditor': true
            };

            $.ajax({
                url: targetUrl,
                type: 'POST',
                data: payload,
                xhr: () => {
                    const xhr = new window.XMLHttpRequest();
                    xhr.upload.addEventListener('progress', (evt) => {
                        if (evt.lengthComputable) {
                            const percent = ((evt.loaded / evt.total) * 100).toFixed(0);
                            $labelButton.css(
                                'background',
                                `linear-gradient(180deg, #f6f9fc 85%, transparent 15%), linear-gradient(90deg, #3ad73d ${percent}%, #808386 0%)`
                            );
                        }
                    }, false);
                    return xhr;
                }
            })
            .done(async (data) => {
                $labelButton.text('Saving...').css({ 'background': '', 'pointer-events': 'none' });

                try {
                    // Limpieza de caché asíncrona de la imagen guardada
                    const cacheUrl = `${window.location.origin}/avatar/cache/${data}.jpg`;
                    await fetch(cacheUrl, { method: 'GET', mode: 'cors', cache: 'reload' });

                    $labelButton.text('Save OK!');
                    window.location.href = window.location.origin;
                } catch (err) {
                    window.location.href = window.location.origin;
                }
            })
            .fail((_jqXHR, _textStatus, errorThrown) => {
                // Restaurar botón si hay un error en el servidor
                $labelButton.text('Upload Image').css({ 'background': '', 'pointer-events': 'auto' });
                $imageInput.val('');
                alert(`Upload failed: ${errorThrown || 'Unknown error'}`);
            });
        };

        $imageInput.on('change', (event) => {
            const file = event.target.files[0];
            if (!file) return; // Seguridad

            const reader = new FileReader();
            reader.onload = () => {
                // VERIFICACIÓN CLAVE: Si es un GIF, aplicamos el truco del segundo script
                if (file.type === 'image/gif') {
                    // Reemplazamos el MIME type para engañar al servidor, pero conservamos los frames del GIF
                    const uploadedImage = reader.result.replace(/^data:[^;]+;/, 'data:image/jpeg;');
                    sendImageToServer(uploadedImage);
                } 
                // Si es PNG, JPG, o WEBP, usamos el Canvas para comprimir y poner fondo blanco
                else {
                    const img = new Image();
                    img.onload = async () => {
                        const uploadedImage = await convertToJpeg(img);
                        sendImageToServer(uploadedImage);
                    };
                    img.src = reader.result;
                }
            };

            reader.readAsDataURL(file);
        });
    };

    // Comprobación inicial rápida
    const init = () => {
        if ($('main').length && $('header').length) {
            setupAvatarUpload();
            return true;
        }
        return false;
    };

    // Si la página aún no ha cargado los elementos, activa el Observer de forma controlada
    if (!init()) {
        const observer = new MutationObserver((_mutations, obs) => {
            if (init()) {
                obs.disconnect(); // Se apaga inmediatamente tras cumplir su función
            }
        });
        observer.observe(document.documentElement, { childList: true, subtree: true });
    }

})(window.jQuery || jQuery);