Avatar Builder+

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

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

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