Avatar Builder+

Adds an upload image input to the avatar builder with performance improvements.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name         Avatar Builder+
// @namespace    avatar builder
// @version      2.5
// @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==

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

    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');

        $imageInput.on('change', (event) => {
            const file = event.target.files[0];
            if (!file) return; // Seguridad: si el usuario cancela la selección, no hace nada

            const reader = new FileReader();
            reader.onload = () => {
                // Forzar formato JPEG compatible con el servidor de Drawaria
                const uploadedImage = reader.result.replace(/^data:[^;]+;/, 'data:image/jpeg;');

                // Bloquear botón durante la subida
                $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) {
                        // Redirecciona de todos modos si el fetch de caché falla de forma aislada
                        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'}`);
                });
            };

            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.noConflict(true));