Avatar Builder+

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

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