SLITHER.IO STANDALONE CHAT MOD (NOW TALK WITH SLITHER GAMERS!)

Adds the 143X community chat with profiles, GIFs, Discord integration, and settings to Slither.io.

// ==UserScript==
// @name         SLITHER.IO STANDALONE CHAT MOD (NOW TALK WITH SLITHER GAMERS!)
// @namespace    http://tampermonkey.net/
// @version      11.6
// @description  Adds the 143X community chat with profiles, GIFs, Discord integration, and settings to Slither.io.
// @author       dxxthly & waynesg
// @match        http://slither.io/
// @match        https://slither.io/
// @match        http://slither.com/io
// @match        https://slither.com/io
// @grant        none
// @icon         https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQUNcRl2Rh40pZLhgffYGFDRLbYJ4qfMNwddQ&s
// ==/UserScript==

(function() {
    'use strict';

    // --- NEW: THE UNBREAKABLE GUARDIAN ---
    // This injects a high-priority stylesheet to override any malicious CSS.
    (function createGuardianStylesheet() {
        const guardianStyle = document.createElement('style');
        guardianStyle.id = 'mod-guardian-styles';
        // The '!important' flag ensures these rules win against any injected styles.
        guardianStyle.textContent = `
            html {
                filter: none !important;
                transform: none !important;
            }
            body {
                transform: none !important;
            }
        `;
        (document.head || document.documentElement).appendChild(guardianStyle);
        console.log('Guardian Stylesheet is active.');
    })();
    // --- END OF GUARDIAN ---

    let hasInitialized = false;

    // =================================================================================
    // INITIALIZATION CHECKER
    // This is the new, robust way to start the script.
    // It will repeatedly check if the slither.io page is ready.
    // =================================================================================
    const initChecker = setInterval(() => {
        // We check for the 'login' div, which is a reliable indicator that the core page has loaded.
        if (document.getElementById('login') && !hasInitialized) {
            hasInitialized = true; // Prevents the script from running multiple times
            clearInterval(initChecker); // Stop checking once we've started
            main(); // Run the main script logic
        }
    }, 100); // Check every 100 milliseconds

    // REPLACE WITH THIS
    const systemAccounts = [
        "system",
        "discord_bot",
        "system_badge" // <-- THE FIX IS HERE
    ];

    // =================================================================================
    // MAIN SCRIPT FUNCTION
    // All of our chat logic now lives inside this function.
    // =================================================================================
    function main() {
        // --- CONFIGURATION ---
        const config = {
            chatMaxMessages: 75,
            firebaseConfig: {
                apiKey: "AIzaSyCtTloqGNdhmI3Xt0ta11vF0MQJHiKpO7Q",
                authDomain: "chatforslither.firebaseapp.com",
                databaseURL: "https://chatforslither-default-rtdb.firebaseio.com",
                projectId: "chatforslither",
                storageBucket: "chatforslither.appspot.com",
                messagingSenderId: "1045559625491",
                appId: "1:1045559625491:web:79eb8200eb87edac00bce6"
            },
            devList: [{ uid: "CiOpgh1RLBg3l5oXn0SAho66Po93" }, { uid: "PZA5qgKWsPTXc278pyx7NwROf313" }, { uid: "P75eMwh756Rb6h1W6iqQfHN2Dm92" }],
            vipMembers: [{ uid: "crcOY9hoRrfayStCxMVm7Zdx2W92", name: "stevao" }, { uid: "DhGhICAZwkRa7wuMsyquM9a5uO92", name: "LUANBLAYNER" }]
        };

        // --- STYLES (CSS INJECTION) ---
        const style = document.createElement('style');
        style.textContent = `
            #chat-container { position: fixed; left: 20px; top: 100px; width: 380px; height: 400px; z-index: 9999; display: flex; flex-direction: column; background: rgba(28, 28, 32, 0.97); border: 1px solid #4CAF50; border-radius: 8px; box-shadow: 0 5px 20px rgba(0,0,0,0.3); overflow: hidden; user-select: none; resize: both; min-width: 320px; min-height: 250px; }
            #chat-header { display: flex; align-items: center; border-bottom: 1px solid #4CAF50; background: rgba(0,0,0,0.2); cursor: move; }
            .chat-tab { flex: 1; padding: 10px 12px; text-align: center; cursor: pointer; font-weight: 500; transition: background 0.2s, color 0.2s; }
            #chat-tab-main { background: rgba(76, 175, 80, 0.25); color: #fff; } #chat-tab-users { background: transparent; color: #ccc; }
            #chat-settings-btn, #chat-hide-btn { background: none; border: none; color: #ccc; font-size: 20px; padding: 0 12px; cursor: pointer; transition: color 0.2s; border-left: 1px solid rgba(255,255,255,0.1); }
            #chat-settings-btn:hover, #chat-hide-btn:hover { color: white; }
            #chat-area { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
            #chat-body, #online-users { flex: 1; padding: 10px 15px; overflow-y: auto; display: flex; flex-direction: column; gap: 5px; scrollbar-width: thin; scrollbar-color: #4CAF50 rgba(0,0,0,0.2); }
            #online-users { display: none; }
            #chat-input-container { display: flex; align-items: center; border-top: 1px solid #4CAF50; }
            #chat-input { flex-grow: 1; padding: 12px 15px; border: none; background: transparent; color: #e0e0e0; outline: none; font-size: 14px; }
            .chat-username { font-weight: bold; cursor: pointer; text-decoration: underline dotted; }
            .chat-modal-overlay { display: none; position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 10001; background: rgba(0,0,0,0.75); align-items: center; justify-content: center; }
            .chat-modal-content { background: #2E2E34; border-radius: 10px; padding: 25px 30px; min-width: 400px; display: flex; flex-direction: column; gap: 15px; border: 1px solid #555; }
            .chat-modal-content h2 { margin: 0 0 10px 0; color: #4CAF50; text-align: center; }
            .settings-field { display: flex; flex-direction: column; gap: 5px; } .settings-field label { color: #bbb; font-size: 0.9em; }
            .settings-field input[type="text"] { width: 100%; box-sizing: border-box; padding: 10px; background: #222; border: 1px solid #555; border-radius: 5px; color: #eee; font-size: 1em; }
            .color-input-wrapper { display: flex; align-items: center; gap: 10px; padding: 5px; background: #222; border: 1px solid #555; border-radius: 5px; }
            .color-input-wrapper input[type="color"] { width: 30px; height: 30px; border: none; background: none; cursor: pointer; padding: 0; }
            .color-input-wrapper input[type="text"] { flex-grow: 1; border: none; background: none; color: #eee; padding: 5px; }
            .modal-buttons { display: flex; justify-content: flex-end; gap: 10px; margin-top: 10px; }
            .modal-btn { padding: 9px 20px; border: none; border-radius: 5px; font-size: 0.95em; cursor: pointer; }
            #settings-save-btn { background: #4CAF50; color: #fff; } #settings-cancel-btn { background: #555; color: #fff; }
            .profile-popup { position: fixed; z-index: 10002; display: flex; flex-direction: column; align-items: center; left: 50%; top: 50%; transform: translate(-50%, -50%); min-width: 280px; background: #2A2A2F; color: #e0e0e0; border-radius: 12px; border: 1px solid #4CAF50; box-shadow: 0 8px 32px rgba(0,0,0,0.35); padding: 24px 30px; }
            .profile-popup .avatar { width: 72px; height: 72px; border-radius: 50%; object-fit: cover; border: 3px solid #4CAF50; margin-bottom: 16px; }
            .profile-popup .close-btn { position: absolute; top: 12px; right: 12px; background: none; border: none; color: #aaa; font-size: 1.6em; cursor: pointer; }
        `;
        document.head.appendChild(style);

        // --- HELPER FUNCTIONS ---
        const escapeHTML = (str) => { const p = document.createElement('p'); p.appendChild(document.createTextNode(str)); return p.innerHTML; };
        const isDev = (uid) => config.devList.some(dev => dev.uid === uid);
        const isVip = (uid, name) => config.vipMembers.some(vip => vip.uid === uid && vip.name.toLowerCase() === (name || '').toLowerCase());
        const rainbowTextStyle = (name) => name.split('').map((char, i) => `<span style="color:${["#ef3550","#f48fb1","#7e57c2","#2196f3","#26c6da","#43a047","#eeff41","#f9a825","#ff5722"][i % 9]}; font-weight: bold;">${char}</span>`).join('');
        const vipGlowStyle = (name, color) => `<span style="color:#fff;font-weight:bold;text-shadow:0 0 5px #fff, 0 0 10px ${color}, 0 0 15px ${color};">${name}</span>`;

        // --- UI & MODAL CREATION ---
        const chatContainer = document.createElement('div'); chatContainer.id = 'chat-container';
        chatContainer.innerHTML = `
            <div id="chat-header"> <div id="chat-tab-main" class="chat-tab">143X Chat</div> <div id="chat-tab-users" class="chat-tab">Online Users</div> <button id="chat-settings-btn" title="Settings">⚙️</button> <button id="chat-hide-btn" title="Hide Chat">×</button> </div>
            <div id="chat-area"> <div id="chat-body"><p style="color:#888; text-align:center;">Initializing...</p></div> <div id="online-users"></div> <div id="chat-input-container"><input id="chat-input" type="text" placeholder="Connecting..." disabled></div> </div>`;
        document.body.appendChild(chatContainer);
        makeDraggable(chatContainer, document.getElementById('chat-header'));
        const settingsModal = document.createElement('div'); settingsModal.id = 'settings-modal-overlay'; settingsModal.className = 'chat-modal-overlay';
        settingsModal.innerHTML = `
            <div id="settings-modal" class="chat-modal-content">
                <h2>Chat & Profile Settings</h2>
                <div class="settings-field"><label>Nickname</label><input type="text" id="settings-nickname" maxlength="20"></div>
                                <div class="settings-field"><label>Chat Name Color</label>
                    <div class="color-input-wrapper">
                        <input type="color" id="settings-name-color">
                    </div>
                </div>
                <div class="settings-field"><label>Profile Avatar URL</label><input type="text" id="settings-avatar"></div>
                <div class="settings-field"><label>Profile Motto</label><input type="text" id="settings-motto" maxlength="60"></div>
                <div class="modal-buttons"><button id="settings-cancel-btn" class="modal-btn">Cancel</button><button id="settings-save-btn" class="modal-btn">Save</button></div>
            </div>`;
        document.body.appendChild(settingsModal);

        // --- FIREBASE & CHAT LOGIC ---
        function loadFirebaseAndInit() {
            const script1 = document.createElement('script'); script1.src = 'https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js';
            script1.onload = () => { const script2 = document.createElement('script'); script2.src = 'https://www.gstatic.com/firebasejs/8.10.0/firebase-database.js';
                script2.onload = () => { const script3 = document.createElement('script'); script3.src = 'https://www.gstatic.com/firebasejs/8.10.0/firebase-auth.js';
                    script3.onload = () => initializeChat(window.firebase); document.head.appendChild(script3); }; document.head.appendChild(script2); }; document.head.appendChild(script1);
        }

        function initializeChat(firebase) {
            if (!firebase.apps.length) firebase.initializeApp(config.firebaseConfig);
            const auth = firebase.auth(); const db = firebase.database();
            auth.signInAnonymously().catch(err => console.error("Firebase Sign-In Error:", err));
            auth.onAuthStateChanged(user => {
                if (user) {
                    const chatInput = document.getElementById('chat-input'); chatInput.disabled = false; chatInput.placeholder = 'Type and press Enter...'; document.getElementById('chat-body').innerHTML = '';
                    let nickname = localStorage.getItem("slitherChatNickname");
                    if (!nickname) { nickname = prompt("Welcome! Please enter a nickname:", "Player") || "Anon"; localStorage.setItem("slitherChatNickname", nickname); }
                    const userRef = db.ref("onlineUsers/" + user.uid);
                    userRef.onDisconnect().remove();
                    const localData = { name: nickname, chatNameColor: localStorage.getItem("slitherChatNameColor") || "#FFD700", profileAvatar: localStorage.getItem("slitherChatAvatar") || "", profileMotto: localStorage.getItem("slitherChatMotto") || "" };
                    userRef.update({ ...localData, uid: user.uid, lastActive: firebase.database.ServerValue.TIMESTAMP });
                    setInterval(() => userRef.update({ lastActive: Date.now() }), 45000);
                    listenForOnlineUsers(db, user.uid);
                    setupEventListeners(db, user.uid);
                }
            });

            let chatMessagesArray = [];
            let latestTimeLoaded = 0;

            const chatBody = document.getElementById('chat-body');
            const currentUser = auth.currentUser;

            // Step 1: Load initial messages
            db.ref("slitherChat").orderByChild("time").limitToLast(config.chatMaxMessages).once("value", async (snapshot) => {
                if (!snapshot.exists()) return;
                snapshot.forEach(child => { chatMessagesArray.push({ key: child.key, ...child.val() }); });
                chatMessagesArray.sort((a, b) => a.time - b.time);
                if (chatMessagesArray.length > 0) { latestTimeLoaded = chatMessagesArray[chatMessagesArray.length - 1].time; }
                if (chatBody) {
                    chatBody.innerHTML = '';
                    for (const msg of chatMessagesArray) { await renderChatMessage(msg, chatBody, currentUser?.uid); }
                    chatBody.scrollTop = chatBody.scrollHeight;
                }
                // Step 2: Listen for ONLY new messages from now on
                db.ref("slitherChat").orderByChild("time").startAt(latestTimeLoaded + 1).on("child_added", async (newSnapshot) => {
                    const newMsg = { key: newSnapshot.key, ...newSnapshot.val() };
                    if (chatMessagesArray.some(m => m.key === newMsg.key)) return;
                    chatMessagesArray.push(newMsg);
                    if (chatMessagesArray.length > config.chatMaxMessages) chatMessagesArray.shift();
                    if (chatBody) {
                        while (chatBody.children.length >= config.chatMaxMessages) { chatBody.removeChild(chatBody.firstChild); }
                        await renderChatMessage(newMsg, chatBody, currentUser?.uid, true);
                    }
                });
            });
        }

        
        function renderChatMessage(msg, chatBodyElement, currentUid, shouldScroll = false) {
            // Check if user is ignored
            if (window.standaloneState && window.standaloneState.ignoredUsers && window.standaloneState.ignoredUsers[msg.uid]) return;
            if (!msg || !msg.uid) return;

            // --- 1. SETUP & VALIDATION ---
            const isValidHexColor = (color) => /^#([0-9a-fA-F]{3}){1,2}$/.test(color);
            let userColor = (msg.chatNameColor && isValidHexColor(msg.chatNameColor)) ? msg.chatNameColor : '#FFD700';

            const isSystemMessage = systemAccounts.includes(msg.uid);
            const isDiscordBot = msg.uid === 'discord_bot';
            const displayName = escapeHTML(msg.name || 'Anon');

            let nameHtml;
            let roleTagHTML = '';

            // --- 2. BUILD THE NAME HTML BASED ON USER TYPE ---

            if (isDiscordBot) {
                // Discord names are not clickable as they don't have in-game profiles
                nameHtml = `<span style="color:${userColor};font-weight:bold;">${displayName}</span>`;
                roleTagHTML = ` <span style="background: #7289DA; color: #fff; padding: 2px 7px; border-radius: 4px; font-size: 0.8em; font-weight: 700; vertical-align:middle;">DISCORD</span>`;
            } else if (isDev(msg.uid)) {
                // Dev names are clickable
                nameHtml = `<span class="chat-username" data-uid="${msg.uid}" style="cursor:pointer;">${rainbowTextStyle(displayName)}</span>`;
                roleTagHTML = ` <span style="background: #E91E63; color: #fff; padding: 2px 7px; border-radius: 4px; font-size: 0.8em; font-weight: 700; vertical-align:middle;">DEV</span>`;
            } else if (isVip(msg.uid, msg.name)) {
                // VIP names are clickable
                nameHtml = `<span class="chat-username" data-uid="${msg.uid}" style="cursor:pointer;">${vipGlowStyle(displayName, userColor)}</span>`;
                roleTagHTML = ` <span style="background: #9C27B0; color: #fff; padding: 2px 7px; border-radius: 4px; font-size: 0.8em; font-weight: 700; vertical-align:middle;">VIP</span>`;
            } else if (isSystemMessage) {
                // System names are not clickable
                nameHtml = `<span style="color:${userColor};font-weight:bold;">System</span>`;
                roleTagHTML = ` <span style="background: #e74c3c; color: #fff; padding: 2px 7px; border-radius: 4px; font-size: 0.8em; font-weight: 700; vertical-align:middle;">SYSTEM</span>`;
            } else {
                // *** THIS IS THE KEY CHANGE FOR REGULAR USERS ***
                // We add the class and data-uid to make them clickable too.
                nameHtml = `<span class="chat-username" data-uid="${msg.uid}" style="color:${userColor};font-weight:bold;cursor:pointer;">${displayName}</span>`;
            }

            // --- 3. HANDLE MESSAGE CONTENT (IMAGES, HTML, ETC.) ---
            let finalMessage = '';
            const imageRegex = /(https?:\/\/[^\s]+\.(?:png|jpg|jpeg|gif|webp))/i;
            const imageMatch = msg.text.match(imageRegex);

            if (imageMatch) {
                // If there's an image, separate it from the text
                const textPart = escapeHTML(msg.text.replace(imageRegex, '').trim());
                finalMessage = `${textPart}<br><img src="${imageMatch[0]}" style="max-width:90%; border-radius:6px; margin-top:5px; cursor:pointer;" onclick="window.open('${imageMatch[0]}', '_blank')">`;
            } else if (isSystemMessage || isDev(msg.uid)) {
                // Allow HTML for Devs and System messages
                finalMessage = msg.text;
            } else {
                // Escape HTML for everyone else to prevent injection
                finalMessage = escapeHTML(msg.text);
            }

            // --- 4. ASSEMBLE AND RENDER THE FINAL ELEMENT ---
            const el = document.createElement('div');
            const borderColor = (msg.uid === currentUid) ? '#4CAF50' : '#444';
            const bgColor = (msg.uid === currentUid) ? 'rgba(76, 175, 80, 0.12)' : 'rgba(255,255,255,0.04)';

            el.style.cssText = `margin-bottom: 8px; word-break: break-word; background: ${bgColor}; padding: 8px 12px; border-radius: 6px; color: #ddd; font-family: inherit; font-size: 14px; line-height: 1.5; border-left: 3px solid ${borderColor};`;

            const timestamp = new Date(msg.time).toLocaleTimeString([], { hour12: true, hour: '2-digit', minute: '2-digit' });
            el.innerHTML = `<span style="color:#888; font-size:0.9em; margin-right:5px;">${timestamp}</span> <b>${nameHtml}${roleTagHTML}:</b> ${finalMessage}`;

            chatBodyElement.appendChild(el);

            // Auto-scroll logic
            if (shouldScroll || chatBodyElement.scrollTop >= chatBodyElement.scrollHeight - chatBodyElement.clientHeight - 150) {
                chatBodyElement.scrollTop = chatBodyElement.scrollHeight;
            }
        }

        function listenForOnlineUsers(db, currentUid) {
            const onlineUsersRef = db.ref("onlineUsers");
            const onlineUsersDiv = document.getElementById('online-users');
            const onlineUsersTab = document.getElementById('chat-tab-users');
            const tenMinutesAgo = Date.now() - (10 * 60 * 1000);

            onlineUsersRef.orderByChild('lastActive').startAt(tenMinutesAgo).on('value', (snapshot) => {
                if (!onlineUsersDiv) return;
                onlineUsersDiv.innerHTML = '';
                let userCount = 0;
                if (snapshot.exists()) {
                    snapshot.forEach(childSnapshot => {
                        const user = childSnapshot.val();
                        if (!user || !user.name) return;
                        userCount++;

                        const userEl = document.createElement('div');
                        userEl.style.cssText = `padding: 5px; cursor: pointer; border-radius: 4px; display:flex; align-items:center;`;
                        userEl.className = 'chat-username'; // Make it clickable
                        userEl.dataset.uid = user.uid; // Store UID for click event

                        const userColor = (user.chatNameColor && /^#([0-9a-fA-F]{3}){1,2}$/.test(user.chatNameColor)) ? user.chatNameColor : '#FFFFFF';

                        let nameHtml;
                        if (isDev(user.uid)) {
                            nameHtml = rainbowTextStyle(escapeHTML(user.name));
                        } else if (isVip(user.uid, user.name)) {
                            nameHtml = vipGlowStyle(escapeHTML(user.name), userColor);
                        } else {
                            nameHtml = `<span style="color:${userColor};">${escapeHTML(user.name)}</span>`;
                        }

                        userEl.innerHTML = nameHtml;
                        onlineUsersDiv.appendChild(userEl);
                    });
                }
                onlineUsersTab.textContent = `Online Users (${userCount})`;
            });
        }


                async function showUserProfile(db, uid) {
            // Prevent opening multiple popups
            if (document.querySelector('.profile-popup')) return;

            try {
                const userSnapshot = await db.ref(`onlineUsers/${uid}`).once('value');
                if (!userSnapshot.exists()) {
                    console.log("User not found or offline.");
                    return;
                }
                const userData = userSnapshot.val();

                const popup = document.createElement('div');
                popup.className = 'profile-popup';

                const defaultAvatar = 'https://i.imgur.com/M6NYjjO.jpeg';
                const avatarUrl = (userData.profileAvatar || '').trim() || defaultAvatar;

                popup.innerHTML = `
                    <button class="close-btn">×</button>
                    <img src="${escapeHTML(avatarUrl)}" class="avatar" onerror="this.src='${defaultAvatar}'">
                    <h3 style="margin: 0; color: ${escapeHTML(userData.chatNameColor || '#fff')};">${escapeHTML(userData.name || 'Anonymous')}</h3>
                    <p style="color: #bbb; font-style: italic; margin: 5px 0 15px 0;">"${escapeHTML(userData.profileMotto || 'No motto.')}"</p>
                    <div class="profile-actions">
                        <!-- Add buttons for ignore, etc. here if needed -->
                    </div>
                `;

                document.body.appendChild(popup);

                // Add event listener to the close button
                popup.querySelector('.close-btn').addEventListener('click', () => {
                    popup.remove();
                });

            } catch (error) {
                console.error("Error fetching user profile:", error);
            }
        }

        // --- EVENT LISTENERS ---
        function setupEventListeners(db, uid) {
            document.getElementById('chat-tab-main').addEventListener('click', () => { document.getElementById('chat-body').style.display = 'flex'; document.getElementById('online-users').style.display = 'none'; document.getElementById('chat-tab-main').style.background='rgba(76, 175, 80, 0.25)'; document.getElementById('chat-tab-users').style.background='transparent';});
            document.getElementById('chat-tab-users').addEventListener('click', () => { document.getElementById('chat-body').style.display = 'none'; document.getElementById('online-users').style.display = 'flex'; document.getElementById('chat-tab-users').style.background='rgba(76, 175, 80, 0.25)'; document.getElementById('chat-tab-main').style.background='transparent';});
            document.getElementById('chat-input').addEventListener('keydown', (e) => { if (e.key === 'Enter') { const text = e.target.value.trim(); if (text) { const name = localStorage.getItem("slitherChatNickname"); const color = localStorage.getItem("slitherChatNameColor") || "#FFD700"; db.ref("slitherChat").push({ uid, name, text, time: firebase.database.ServerValue.TIMESTAMP, chatNameColor: color }); db.ref("discordBridge").push({ uid, name, text, time: firebase.database.ServerValue.TIMESTAMP }); e.target.value = ''; } } });
            const settingsOverlay = document.getElementById('settings-modal-overlay');
            document.getElementById('chat-settings-btn').addEventListener('click', () => {
                document.getElementById('settings-nickname').value = localStorage.getItem("slitherChatNickname") || '';
                // CORRECTED: Only set the value for the existing color picker
                document.getElementById('settings-name-color').value = localStorage.getItem("slitherChatNameColor") || '#FFD700';
                document.getElementById('settings-avatar').value = localStorage.getItem("slitherChatAvatar") || '';
                document.getElementById('settings-motto').value = localStorage.getItem("slitherChatMotto") || '';
                settingsOverlay.style.display = 'flex';
            });
            document.getElementById('settings-cancel-btn').addEventListener('click', () => settingsOverlay.style.display = 'none');
            const cPicker = document.getElementById('settings-name-color');
            document.getElementById('settings-save-btn').addEventListener('click', () => { const newData = { name: document.getElementById('settings-nickname').value.trim().slice(0, 20) || 'Anon', chatNameColor: cPicker.value, profileAvatar: document.getElementById('settings-avatar').value.trim(), profileMotto: document.getElementById('settings-motto').value.trim() }; localStorage.setItem("slitherChatNickname", newData.name); localStorage.setItem("slitherChatNameColor", newData.chatNameColor); localStorage.setItem("slitherChatAvatar", newData.profileAvatar); localStorage.setItem("slitherChatMotto", newData.profileMotto); db.ref("onlineUsers/" + uid).update(newData); settingsOverlay.style.display = 'none'; });
            document.getElementById('chat-hide-btn').addEventListener('click', () => document.getElementById('chat-container').style.display = 'none');
            document.body.addEventListener('click', e => { if (e.target.closest('.chat-username')) showUserProfile(db, e.target.closest('.chat-username').dataset.uid); });
        }

        // --- UTILITY ---
        function makeDraggable(el, handle) { let p1=0, p2=0, p3=0, p4=0; handle.onmousedown = (e) => { e.preventDefault(); p3 = e.clientX; p4 = e.clientY; document.onmouseup = () => {document.onmouseup=null; document.onmousemove=null;}; document.onmousemove = (e) => { e.preventDefault(); p1=p3-e.clientX; p2=p4-e.clientY; p3=e.clientX; p4=e.clientY; el.style.top=(el.offsetTop-p2)+"px"; el.style.left=(el.offsetLeft-p1)+"px"; }; }; }

        // --- RUN ---
        loadFirebaseAndInit();
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址