BungeeCord-Chrome/popup.js aktualisiert

This commit is contained in:
2026-01-01 22:28:35 +00:00
parent f0e197ee8a
commit 3be6b0b9a4

View File

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