168 lines
5.4 KiB
JavaScript
168 lines
5.4 KiB
JavaScript
/* chat.js */
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const CHAT_WIDGET_HTML = `
|
|
<div id="chat-widget" class="collapsed">
|
|
<div id="chat-header">
|
|
<span><span id="chat-status-dot" class="status-offline">●</span> MESSAGE BOARD</span>
|
|
<button id="chat-toggle" aria-label="Toggle message board">▲</button>
|
|
</div>
|
|
<div id="chat-body" style="display: none;">
|
|
<div id="chat-messages"></div>
|
|
<div id="chat-input-area">
|
|
<div class="chat-settings">
|
|
<label for="chat-nick-input" style="font-size:0.8rem; color:#ccc;">Name:</label>
|
|
<input type="text" id="chat-nick-input" placeholder="visitor" maxlength="20">
|
|
</div>
|
|
<div id="chat-controls">
|
|
<input type="text" id="chat-input" placeholder="Post a message..." maxlength="200">
|
|
<button id="chat-send">Post</button>
|
|
</div>
|
|
<div id="chat-status">Connecting…</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
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();
|
|
});
|