Avatar Builder+

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

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

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