Avatar Builder+

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

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

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