From 3be6b0b9a434a82709387c8ff0988c08ff34a8aa Mon Sep 17 00:00:00 2001 From: M_Viper Date: Thu, 1 Jan 2026 22:28:35 +0000 Subject: [PATCH] BungeeCord-Chrome/popup.js aktualisiert --- BungeeCord-Chrome/popup.js | 735 +++++++++++++++++++------------------ 1 file changed, 383 insertions(+), 352 deletions(-) diff --git a/BungeeCord-Chrome/popup.js b/BungeeCord-Chrome/popup.js index f423981..448d8e8 100644 --- a/BungeeCord-Chrome/popup.js +++ b/BungeeCord-Chrome/popup.js @@ -1,353 +1,384 @@ -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) 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 (p.avatar) { - const img = document.createElement('img'); - img.src = p.avatar; - img.className = 'player-avatar'; - img.title = name; - li.appendChild(img); - } - detailPlayerList.appendChild(li); - } -} - -// --- Update List (Fix: Syntaxfehler behoben) --- -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'; - // Klammerfehler korrigiert: ')' zu '}' - 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) {} -} - -// --- 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); - } - } +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); + } + } }); \ No newline at end of file