const $ = id => document.getElementById(id); const uid = () => 'srv_' + Math.random().toString(36).slice(2,9); const inputName = $('inputName'); const inputUrl = $('inputUrl'); const inputWpSite = $('inputWpSite'); const inputWpServerId = $('inputWpServerId'); const btnAdd = $('btnAddServer'); const serversContainer = $('serversContainer'); const serverListPanel = document.querySelector('.server-list'); const btnRefresh = $('btnRefresh'); const btnToggleSettings = $('btnToggleSettings'); const settingsForm = $('settingsForm'); const noSelection = $('noSelection'); const detailContent = $('detailContent'); const detailName = $('detailName'); const detailUrlText = $('detailUrlText'); const detailStatus = $('detailStatus'); const detailPulse = $('detailPulse'); const detailVersion = $('detailVersion'); const detailPlayers = $('detailPlayers'); const detailPing = $('detailPing'); const detailPlayerList = $('detailPlayerList'); const btnEdit = $('btnEdit'); const btnDelete = $('btnDelete'); let servers = []; let selectedId = null; let statuses = {}; let previousStatuses = {}; let settingsVisible = false; document.addEventListener('DOMContentLoaded', init); btnAdd.addEventListener('click', handleAdd); btnRefresh.addEventListener('click', manualRefresh); btnToggleSettings.addEventListener('click', toggleSettings); btnEdit.addEventListener('click', handleEdit); btnDelete.addEventListener('click', handleDelete); async function init() { await loadServersFromStorage(); await loadStatusesFromStorage(); await loadSettingsVisibility(); renderServerList(); if (servers.length === 1) selectedId = servers[0].id; renderDetail(selectedId); adjustDetailLayout(); } // --- Settings Visibility --- async function loadSettingsVisibility() { const obj = await chrome.storage.local.get(['settingsVisible']); settingsVisible = obj.settingsVisible !== undefined ? obj.settingsVisible : false; if (!settingsVisible) settingsForm.classList.add('hidden'); else settingsForm.classList.remove('hidden'); } async function saveSettingsVisibility() { await chrome.storage.local.set({ settingsVisible }); } async function toggleSettings() { settingsVisible = !settingsVisible; settingsForm.classList.toggle('hidden'); await saveSettingsVisibility(); adjustDetailLayout(); } // --- Storage --- async function loadServersFromStorage() { const obj = await chrome.storage.local.get(['servers']); servers = obj.servers || []; for (const s of servers) if (!s.id) s.id = uid(); await chrome.storage.local.set({ servers }); } async function saveServersToStorage() { await chrome.storage.local.set({ servers }); } async function loadStatusesFromStorage() { const obj = await chrome.storage.local.get(['serverStatuses']); statuses = obj.serverStatuses || {}; } // --- Layout --- function adjustDetailLayout() { const settingsHidden = settingsForm.classList.contains('hidden'); detailContent.classList.toggle('full-width', settingsHidden); serverListPanel.classList.toggle('hidden', settingsHidden); btnEdit.style.display = settingsHidden ? 'none' : 'inline-block'; btnDelete.style.display = settingsHidden ? 'none' : 'inline-block'; } // --- Render Server List --- function renderServerList() { serversContainer.innerHTML = ''; if (servers.length === 0) { serversContainer.innerHTML = '
Noch keine Server hinzugefügt.
'; return; } for (const s of servers) { const item = document.createElement('li'); item.className = 'server-item'; item.dataset.id = s.id; const meta = document.createElement('div'); meta.className = 'meta'; const name = document.createElement('div'); name.className = 'name'; name.textContent = s.name || '(Kein Name)'; let listUrl = s.url || (s.wpSite ? s.wpSite + ' (WP)' : ''); listUrl = listUrl.replace(':9191', ''); const url = document.createElement('div'); url.className = 'url'; url.textContent = listUrl; meta.append(name, url); const statusBubble = document.createElement('div'); statusBubble.className = 'status-bubble'; statusBubble.textContent = '—'; statusBubble.style.backgroundColor = 'transparent'; item.statusBubble = statusBubble; item.append(meta, statusBubble); item.addEventListener('click', () => { selectedId = s.id; renderDetail(selectedId); }); serversContainer.appendChild(item); } updateServerListStatuses(false); } // --- Render Detail --- function renderDetail(id) { if (!id) { noSelection.classList.remove('hidden'); detailContent.classList.add('hidden'); return; } const srv = servers.find(x => x.id === id); if (!srv) { noSelection.classList.remove('hidden'); detailContent.classList.add('hidden'); return; } noSelection.classList.add('hidden'); detailContent.classList.remove('hidden'); detailName.textContent = srv.name || 'Unbenannter Server'; let urlToShow = srv.url || (srv.wpSite ? srv.wpSite : 'Lokal'); urlToShow = urlToShow.replace(':9191', ''); detailUrlText.textContent = urlToShow; updateDetailForServer(srv, true); } // --- Update Detail --- function updateDetailForServer(srv, force = false) { const st = statuses[srv.id]; const prevSt = previousStatuses[srv.id]; const statusChanged = force || !prevSt || (prevSt.ok !== st?.ok) || (prevSt.data?.online !== st?.data?.online); if (!st || !st.ok || !st.data) { if (statusChanged) { detailStatus.textContent = 'Offline'; detailPulse.classList.remove('online'); detailVersion.textContent = '-'; detailPlayers.textContent = '-'; detailPing.textContent = '-'; updatePlayerList([]); } } else { const d = st.data; if (statusChanged) { detailStatus.textContent = d.online ? 'Online' : 'Offline'; if (d.online) { detailPulse.classList.add('online'); } else { detailPulse.classList.remove('online'); } } const newVersion = d.version || 'unknown'; if (force || detailVersion.textContent !== newVersion) { detailVersion.textContent = newVersion; } const playersCount = Array.isArray(d.players) ? d.players.length : (typeof d.players === 'number' ? d.players : 0); const maxPlayers = d.max_players; let newPlayersText = String(playersCount); if (maxPlayers && maxPlayers !== '-1') { newPlayersText += ` / ${maxPlayers}`; } if (force || detailPlayers.textContent !== newPlayersText) { detailPlayers.textContent = newPlayersText; } let pingVal = d.ping || d.latency || '-'; if (pingVal !== '-' && typeof pingVal === 'number') { pingVal = pingVal + ' ms'; } if (force || (pingVal !== '-' && detailPing.textContent !== pingVal)) { detailPing.textContent = pingVal; } const currentPlayers = Array.isArray(d.players) ? d.players : []; const prevPlayers = (prevSt && Array.isArray(prevSt.data?.players)) ? prevSt.data.players : []; let playersChanged = false; if (force) { playersChanged = true; } else { playersChanged = JSON.stringify(currentPlayers) !== JSON.stringify(prevPlayers); } if (playersChanged) { updatePlayerList(currentPlayers); } } previousStatuses[srv.id] = st ? JSON.parse(JSON.stringify(st)) : null; } // --- Spielerliste --- function updatePlayerList(players) { detailPlayerList.innerHTML = ''; if (!players || players.length === 0) { detailPlayerList.innerHTML = '
  • Keine Spieler online.
  • '; return; } for (const p of players) { const li = document.createElement('li'); const name = typeof p === 'object' ? p.name || p.username || p.player || '' : String(p); if (typeof p === 'object' && p.avatar) { const img = document.createElement('img'); img.src = p.avatar; img.className = 'player-avatar'; img.title = name; li.appendChild(img); } else { // Falls kein Avatar vorhanden, generiere einen von mc-heads.net const img = document.createElement('img'); img.src = `https://mc-heads.net/avatar/${encodeURIComponent(name)}/32`; img.className = 'player-avatar'; img.title = name; li.appendChild(img); } detailPlayerList.appendChild(li); } } // --- Update Server List Statuses --- function updateServerListStatuses() { const items = serversContainer.querySelectorAll('.server-item'); items.forEach(item => { const s = servers.find(x => x.id === item.dataset.id); if (!s) return; const st = statuses[s.id]; const prevSt = previousStatuses[s.id]; const statusChanged = !prevSt || (prevSt.ok !== st?.ok) || (prevSt.data?.online !== st?.data?.online); if (!statusChanged) return; if (!st || !st.ok || !st.data) { item.statusBubble.textContent = 'Offline'; item.statusBubble.style.backgroundColor = 'var(--offline)'; } else if (st.data.online) { item.statusBubble.textContent = 'Online'; item.statusBubble.style.backgroundColor = 'var(--online)'; } else { item.statusBubble.textContent = 'Offline'; item.statusBubble.style.backgroundColor = 'var(--offline)'; } previousStatuses[s.id] = st ? JSON.parse(JSON.stringify(st)) : null; }); } // --- Add / Edit / Delete --- async function handleAdd() { const name = inputName.value.trim(); const url = inputUrl.value.trim(); const wpSite = inputWpSite.value.trim(); const wpServerId = inputWpServerId.value.trim(); if (!url && !wpSite) { alert('Bitte URL oder WP Site angeben'); return; } const s = { id: uid(), name: name || url || wpSite, url: url || null, wpSite: wpSite || null, wpServerId: wpServerId || null }; servers.push(s); await saveServersToStorage(); inputName.value = ''; inputUrl.value = ''; inputWpSite.value = ''; inputWpServerId.value = ''; renderServerList(); } async function handleEdit() { if (!selectedId) return; const srv = servers.find(s => s.id === selectedId); if (!srv) return; const newName = prompt('Name:', srv.name) || srv.name; const newUrl = prompt('URL:', srv.url || '') || srv.url; const newWpSite = prompt('WP Site:', srv.wpSite || '') || srv.wpSite; const newWpServerId = prompt('WP Server ID:', srv.wpServerId || '') || srv.wpServerId; srv.name = newName.trim(); srv.url = newUrl.trim(); srv.wpSite = newWpSite.trim(); srv.wpServerId = newWpServerId.trim(); await saveServersToStorage(); renderServerList(); renderDetail(selectedId); } async function handleDelete() { if (!selectedId) return; if (!confirm('Server wirklich löschen?')) return; servers = servers.filter(s => s.id !== selectedId); selectedId = null; await saveServersToStorage(); renderServerList(); renderDetail(null); } async function manualRefresh() { try { chrome.runtime.sendMessage({ cmd: 'refreshNow' }); } catch(e) { console.error('Refresh fehlgeschlagen:', e); } } // --- Storage Listener --- chrome.storage.onChanged.addListener((changes, area) => { if (area === 'local' && changes.serverStatuses) { statuses = changes.serverStatuses.newValue || {}; updateServerListStatuses(); if (selectedId) { const srv = servers.find(s => s.id === selectedId); if (srv) updateDetailForServer(srv); } } });