Upload popup.js via GUI

This commit is contained in:
2026-02-09 21:05:51 +00:00
parent b4ee8b0564
commit 700e6026b6

View File

@@ -1,3 +1,4 @@
//popup.js
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);
@@ -50,7 +51,37 @@ async function init() {
adjustDetailLayout(); adjustDetailLayout();
} }
// --- Settings Visibility --- // --- NEU: Minecraft Color Parser (Eingebaut ohne Löschung) ---
function parseMinecraftColors(text) {
if (!text) return '';
const map = {
'&0': '#000000', '&1': '#0000AA', '&2': '#00AA00', '&3': '#00AAAA',
'&4': '#AA0000', '&5': '#AA00AA', '&6': '#FFAA00', '&7': '#AAAAAA',
'&8': '#555555', '&9': '#5555FF', '&a': '#55FF55', '&b': '#55FFFF',
'&c': '#FF5555', '&d': '#FF55FF', '&e': '#FFFF55', '&f': '#FFFFFF'
};
let html = text.replace(/[<>]/g, '');
Object.keys(map).forEach(code => {
const color = map[code];
const regex = new RegExp(code, 'g');
html = html.replace(regex, `</span><span style="color: ${color}">`);
});
return `<span>${html}</span>`.replace(/&l/g, '<b>').replace(/&r/g, '</b>');
}
// --- 3D Avatar URL Generator ---
function get3DAvatarUrl(playerName, uuid = null) {
const isBedrock = playerName.includes('.') || (uuid && uuid.startsWith('xuid'));
if (isBedrock) {
if (uuid && uuid.length > 0) {
return `https://mc-heads.net/head/${encodeURIComponent(uuid)}/64`;
}
return `https://mc-heads.net/head/${encodeURIComponent(playerName)}/64`;
} else {
return `https://mc-heads.net/head/${encodeURIComponent(playerName)}/64`;
}
}
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;
@@ -69,7 +100,6 @@ async function toggleSettings() {
adjustDetailLayout(); adjustDetailLayout();
} }
// --- 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 || [];
@@ -86,7 +116,6 @@ async function loadStatusesFromStorage() {
statuses = obj.serverStatuses || {}; statuses = obj.serverStatuses || {};
} }
// --- 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);
@@ -95,51 +124,44 @@ function adjustDetailLayout() {
btnDelete.style.display = settingsHidden ? 'none' : 'inline-block'; btnDelete.style.display = settingsHidden ? 'none' : 'inline-block';
} }
// --- 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'); const name = document.createElement('div');
name.className = 'name'; name.className = 'name';
name.textContent = s.name || '(Kein Name)'; name.textContent = s.name || '(Kein Name)';
let listUrl = s.url || (s.wpSite ? s.wpSite + ' (WP)' : ''); let listUrl = s.url || (s.wpSite ? s.wpSite + ' (WP)' : '');
listUrl = listUrl.replace(':9191', ''); listUrl = listUrl.replace(':9191', '');
const url = document.createElement('div'); const url = document.createElement('div');
url.className = 'url'; url.className = 'url';
url.textContent = listUrl; url.textContent = listUrl;
meta.append(name, url); meta.append(name, url);
const statusBubble = document.createElement('div'); const statusBubble = document.createElement('div');
statusBubble.className = 'status-bubble'; statusBubble.className = 'status-bubble';
statusBubble.textContent = '—'; statusBubble.textContent = '—';
statusBubble.style.backgroundColor = 'transparent'; statusBubble.style.backgroundColor = 'transparent';
item.statusBubble = statusBubble; item.statusBubble = statusBubble;
item.append(meta, statusBubble); item.append(meta, statusBubble);
item.addEventListener('click', () => { item.addEventListener('click', () => {
selectedId = s.id; selectedId = s.id;
renderDetail(selectedId); renderDetail(selectedId);
}); });
serversContainer.appendChild(item); serversContainer.appendChild(item);
} }
updateServerListStatuses(false); updateServerListStatuses();
} }
// --- Render Detail --- // --- Fortsetzung popup.js ---
function renderDetail(id) { function renderDetail(id) {
if (!id) { if (!id) {
noSelection.classList.remove('hidden'); noSelection.classList.remove('hidden');
@@ -154,26 +176,17 @@ function renderDetail(id) {
} }
noSelection.classList.add('hidden'); noSelection.classList.add('hidden');
detailContent.classList.remove('hidden'); detailContent.classList.remove('hidden');
detailName.textContent = srv.name || 'Unbenannter Server'; detailName.textContent = srv.name || 'Unbenannter Server';
let urlToShow = srv.url || (srv.wpSite ? srv.wpSite : 'Lokal'); let urlToShow = srv.url || (srv.wpSite ? srv.wpSite : 'Lokal');
urlToShow = urlToShow.replace(':9191', ''); urlToShow = urlToShow.replace(':9191', '');
detailUrlText.textContent = urlToShow; detailUrlText.textContent = urlToShow;
updateDetailForServer(srv, true); updateDetailForServer(srv, true);
} }
// --- Update Detail ---
function updateDetailForServer(srv, force = false) { function updateDetailForServer(srv, force = false) {
const st = statuses[srv.id]; const st = statuses[srv.id];
const prevSt = previousStatuses[srv.id]; const prevSt = previousStatuses[srv.id];
const statusChanged = force || !prevSt || (prevSt.ok !== st?.ok) || (prevSt.data?.online !== st?.data?.online);
const statusChanged = force ||
!prevSt ||
(prevSt.ok !== st?.ok) ||
(prevSt.data?.online !== st?.data?.online);
if (!st || !st.ok || !st.data) { if (!st || !st.ok || !st.data) {
if (statusChanged) { if (statusChanged) {
@@ -186,105 +199,80 @@ function updateDetailForServer(srv, force = false) {
} }
} else { } else {
const d = st.data; const d = st.data;
if (statusChanged) { if (statusChanged) {
detailStatus.textContent = d.online ? 'Online' : 'Offline'; detailStatus.textContent = d.online ? 'Online' : 'Offline';
if (d.online) { if (d.online) detailPulse.classList.add('online');
detailPulse.classList.add('online'); else detailPulse.classList.remove('online');
} else {
detailPulse.classList.remove('online');
} }
}
const newVersion = d.version || 'unknown'; const newVersion = d.version || 'unknown';
if (force || detailVersion.textContent !== newVersion) { if (force || detailVersion.textContent !== newVersion) detailVersion.textContent = newVersion;
detailVersion.textContent = newVersion;
}
const playersCount = Array.isArray(d.players) ? d.players.length : (typeof d.players === 'number' ? d.players : 0); const playersCount = Array.isArray(d.players) ? d.players.length : (typeof d.players === 'number' ? d.players : 0);
const maxPlayers = d.max_players; const maxPlayers = d.max_players;
let newPlayersText = String(playersCount); let newPlayersText = String(playersCount);
if (maxPlayers && maxPlayers !== '-1') { if (maxPlayers && maxPlayers !== '-1') newPlayersText += ` / ${maxPlayers}`;
newPlayersText += ` / ${maxPlayers}`; if (force || detailPlayers.textContent !== newPlayersText) detailPlayers.textContent = newPlayersText;
}
if (force || detailPlayers.textContent !== newPlayersText) {
detailPlayers.textContent = newPlayersText;
}
let pingVal = d.ping || d.latency || '-'; let pingVal = d.ping || d.latency || '-';
if (pingVal !== '-' && typeof pingVal === 'number') { if (pingVal !== '-' && typeof pingVal === 'number') pingVal = pingVal + ' ms';
pingVal = pingVal + ' ms'; if (force || (pingVal !== '-' && detailPing.textContent !== pingVal)) detailPing.textContent = pingVal;
}
if (force || (pingVal !== '-' && detailPing.textContent !== pingVal)) {
detailPing.textContent = pingVal;
}
const currentPlayers = Array.isArray(d.players) ? d.players : []; const currentPlayers = Array.isArray(d.players) ? d.players : [];
const prevPlayers = (prevSt && Array.isArray(prevSt.data?.players)) ? prevSt.data.players : []; const prevPlayers = (prevSt && Array.isArray(prevSt.data?.players)) ? prevSt.data.players : [];
let playersChanged = force || JSON.stringify(currentPlayers) !== JSON.stringify(prevPlayers);
let playersChanged = false; if (playersChanged) updatePlayerList(currentPlayers);
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; previousStatuses[srv.id] = st ? JSON.parse(JSON.stringify(st)) : null;
} }
// --- Spielerliste --- // --- Spielerliste mit Prefix-Hover und Farbsupport ---
function updatePlayerList(players) { function updatePlayerList(players) {
detailPlayerList.innerHTML = ''; detailPlayerList.innerHTML = '';
if (!players || players.length === 0) { if (!players || players.length === 0) {
detailPlayerList.innerHTML = '<li class="placeholder">Keine Spieler online.</li>'; detailPlayerList.innerHTML = '<li class="placeholder">Keine Spieler online.</li>';
return; return;
} }
for (const p of players) { for (const p of players) {
const li = document.createElement('li'); const li = document.createElement('li');
const name = typeof p === 'object' ? p.name || p.username || p.player || '' : String(p); li.className = 'player-item'; // CSS Klasse für den Hover-Container
if (typeof p === 'object' && p.avatar) { let name = '';
const img = document.createElement('img'); let uuid = null;
img.src = p.avatar; let prefix = '';
img.className = 'player-avatar';
img.title = name; if (typeof p === 'object') {
li.appendChild(img); name = p.name || p.username || p.player || '';
uuid = p.uuid || null;
prefix = p.prefix || '';
} else { } else {
// Falls kein Avatar vorhanden, generiere einen von mc-heads.net name = String(p);
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);
} }
const img = document.createElement('img');
img.src = (typeof p === 'object' && p.avatar) ? p.avatar : get3DAvatarUrl(name, uuid);
img.className = 'player-avatar';
img.loading = 'lazy';
img.onerror = function() {
this.onerror = null;
this.src = `https://mc-heads.net/avatar/${encodeURIComponent(name)}/64`;
};
// Das Hover-Element mit farbigem Prefix + Name
const info = document.createElement('div');
info.className = 'player-hover-info';
info.innerHTML = `${parseMinecraftColors(prefix)} ${name}`.trim();
li.appendChild(img);
li.appendChild(info);
detailPlayerList.appendChild(li); detailPlayerList.appendChild(li);
} }
} }
// --- Update Server List Statuses ---
function updateServerListStatuses() { function updateServerListStatuses() {
const items = serversContainer.querySelectorAll('.server-item'); const items = serversContainer.querySelectorAll('.server-item');
items.forEach(item => { items.forEach(item => {
const s = servers.find(x => x.id === item.dataset.id); const s = servers.find(x => x.id === item.dataset.id);
if (!s) return; if (!s) return;
const st = statuses[s.id]; 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) { if (!st || !st.ok || !st.data) {
item.statusBubble.textContent = 'Offline'; item.statusBubble.textContent = 'Offline';
item.statusBubble.style.backgroundColor = 'var(--offline)'; item.statusBubble.style.backgroundColor = 'var(--offline)';
@@ -295,39 +283,19 @@ function updateServerListStatuses() {
item.statusBubble.textContent = 'Offline'; item.statusBubble.textContent = 'Offline';
item.statusBubble.style.backgroundColor = 'var(--offline)'; item.statusBubble.style.backgroundColor = 'var(--offline)';
} }
previousStatuses[s.id] = st ? JSON.parse(JSON.stringify(st)) : null;
}); });
} }
// --- Add / Edit / Delete ---
async function handleAdd() { async function handleAdd() {
const name = inputName.value.trim(); const name = inputName.value.trim();
const url = inputUrl.value.trim(); const url = inputUrl.value.trim();
const wpSite = inputWpSite.value.trim(); const wpSite = inputWpSite.value.trim();
const wpServerId = inputWpServerId.value.trim(); const wpServerId = inputWpServerId.value.trim();
if (!url && !wpSite) return;
if (!url && !wpSite) { const s = { id: uid(), name: name || url || wpSite, url: url || null, wpSite: wpSite || null, wpServerId: wpServerId || null };
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); servers.push(s);
await saveServersToStorage(); await saveServersToStorage();
inputName.value = inputUrl.value = inputWpSite.value = inputWpServerId.value = '';
inputName.value = '';
inputUrl.value = '';
inputWpSite.value = '';
inputWpServerId.value = '';
renderServerList(); renderServerList();
} }
@@ -335,43 +303,26 @@ async function handleEdit() {
if (!selectedId) return; if (!selectedId) return;
const srv = servers.find(s => s.id === selectedId); const srv = servers.find(s => s.id === selectedId);
if (!srv) return; if (!srv) return;
const newName = prompt('Name:', srv.name) || srv.name; 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.name = newName.trim();
srv.url = newUrl.trim();
srv.wpSite = newWpSite.trim();
srv.wpServerId = newWpServerId.trim();
await saveServersToStorage(); await saveServersToStorage();
renderServerList(); renderServerList();
renderDetail(selectedId); renderDetail(selectedId);
} }
async function handleDelete() { async function handleDelete() {
if (!selectedId) return; if (!selectedId || !confirm('Server wirklich löschen?')) return;
if (!confirm('Server wirklich löschen?')) return;
servers = servers.filter(s => s.id !== selectedId); servers = servers.filter(s => s.id !== selectedId);
selectedId = null; selectedId = null;
await saveServersToStorage(); await saveServersToStorage();
renderServerList(); renderServerList();
renderDetail(null); renderDetail(null);
} }
async function manualRefresh() { async function manualRefresh() {
try { try { chrome.runtime.sendMessage({ cmd: 'refreshNow' }); } catch(e) {}
chrome.runtime.sendMessage({ cmd: 'refreshNow' });
} catch(e) {
console.error('Refresh fehlgeschlagen:', e);
}
} }
// --- Storage Listener ---
chrome.storage.onChanged.addListener((changes, area) => { chrome.storage.onChanged.addListener((changes, area) => {
if (area === 'local' && changes.serverStatuses) { if (area === 'local' && changes.serverStatuses) {
statuses = changes.serverStatuses.newValue || {}; statuses = changes.serverStatuses.newValue || {};