/* chat.js */ document.addEventListener('DOMContentLoaded', () => { const CHAT_WIDGET_HTML = ` `; document.body.insertAdjacentHTML('beforeend', CHAT_WIDGET_HTML); const widget = document.getElementById('chat-widget'); const header = document.getElementById('chat-header'); const toggleBtn = document.getElementById('chat-toggle'); const body = document.getElementById('chat-body'); const messagesContainer = document.getElementById('chat-messages'); const input = document.getElementById('chat-input'); const sendBtn = document.getElementById('chat-send'); const nickInput = document.getElementById('chat-nick-input'); const statusEl = document.getElementById('chat-status'); const statusDot = document.getElementById('chat-status-dot'); let ws = null; let isCollapsed = true; // Initialize nickname base (server appends IP suffix) let savedNick = localStorage.getItem('gw_chat_nick'); if (!savedNick) { savedNick = 'visitor'; localStorage.setItem('gw_chat_nick', savedNick); } nickInput.value = savedNick; nickInput.addEventListener('change', () => { let val = nickInput.value.trim(); if (!val) val = 'visitor'; nickInput.value = val; localStorage.setItem('gw_chat_nick', val); }); // Toggle chat function toggleChat() { isCollapsed = !isCollapsed; if (isCollapsed) { widget.classList.add('collapsed'); body.style.display = 'none'; toggleBtn.textContent = '▲'; } else { widget.classList.remove('collapsed'); body.style.display = 'flex'; toggleBtn.textContent = '▼'; input.focus(); scrollToBottom(); } } header.addEventListener('click', (e) => { if (e.target !== nickInput && e.target !== input) { toggleChat(); } }); function formatTime(ts) { const d = new Date(ts); return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }); } function appendMessage(msg) { const div = document.createElement('div'); div.className = 'chat-message'; const tsSpan = document.createElement('span'); tsSpan.className = 'chat-ts'; tsSpan.textContent = '[' + formatTime(msg.ts) + ']'; const nickSpan = document.createElement('span'); nickSpan.className = 'chat-nick'; nickSpan.textContent = msg.nick + ':'; const textSpan = document.createElement('span'); textSpan.className = 'chat-text'; textSpan.textContent = msg.text; div.appendChild(tsSpan); div.appendChild(nickSpan); div.appendChild(textSpan); messagesContainer.appendChild(div); scrollToBottom(); } function scrollToBottom() { messagesContainer.scrollTop = messagesContainer.scrollHeight; } function connect() { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const host = window.location.host; // includes port if non-standard ws = new WebSocket(`${protocol}//${host}/chat`); ws.onopen = () => { statusEl.textContent = 'Connected'; statusDot.className = 'status-online'; }; ws.onmessage = (e) => { try { const data = JSON.parse(e.data); if (data.type === 'history') { messagesContainer.innerHTML = ''; data.messages.forEach(appendMessage); } else { appendMessage(data); } } catch (err) { console.error('Chat MS error:', err); } }; ws.onclose = () => { statusEl.textContent = 'Disconnected. Reconnecting...'; statusDot.className = 'status-offline'; setTimeout(connect, 3000); }; ws.onerror = () => { statusEl.textContent = 'Connection Error'; ws.close(); }; } function sendMessage() { const text = input.value.trim(); if (!text || ws.readyState !== WebSocket.OPEN) return; const nick = nickInput.value.trim() || 'visitor'; const msg = { nick, text }; ws.send(JSON.stringify(msg)); input.value = ''; input.focus(); } sendBtn.addEventListener('click', sendMessage); input.addEventListener('keypress', (e) => { if (e.key === 'Enter') sendMessage(); }); // Init connection connect(); });