您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
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(); } })();