document.addEventListener('DOMContentLoaded', function() {
const serviceForm = document.getElementById('add-service-form');
const intervalSelect = document.getElementById('check-interval');
const notifyOnlineCheckbox = document.getElementById('notify-online');
const exportBtn = document.getElementById('export-services');
const importBtn = document.getElementById('import-services');
const importFileInput = document.getElementById('import-file');
const servicesListStat = document.getElementById('services-list-stat');
const emptyState = document.getElementById('empty-state');
const searchInput = document.getElementById('services-search');
const MAX_POPUP_SERVERS = 8;
let currentChart = null;
// NEU: Discord Webhook Input
const discordWebhookInput = document.getElementById('discord-webhook-url');
// --- Tab-Logik ---
const tabButtons = document.querySelectorAll('.tab-btn');
const tabPanels = document.querySelectorAll('.tab-panel');
tabButtons.forEach(button => {
button.addEventListener('click', () => {
const targetTab = button.getAttribute('data-tab');
tabButtons.forEach(btn => btn.classList.remove('active'));
tabPanels.forEach(panel => panel.classList.remove('active'));
button.classList.add('active');
document.getElementById(`${targetTab}-panel`).classList.add('active');
});
});
// --- Utilities ---
function getAllData(callback) {
chrome.storage.sync.get({ services: [], popupServers: [] }, function(syncData) {
chrome.storage.local.get({ serviceStatus: {}, history: {} }, function(localData) {
callback(syncData.services || [], syncData.popupServers || [], localData.serviceStatus || {}, localData.history || {});
});
});
}
// --- NEU: Popup-Server-Auswahl rendern ---
function renderPopupServerSelection(allServices) {
const container = document.getElementById('popup-server-list');
const infoElement = document.getElementById('selection-info');
container.innerHTML = ''; // Leeren
if (allServices.length === 0) {
infoElement.textContent = 'Bitte fügen Sie zuerst Server hinzu.';
return;
}
chrome.storage.sync.get({ popupServers: [] }, (data) => {
const selectedServers = data.popupServers || [];
// Info-Text aktualisieren
infoElement.textContent = `${selectedServers.length} von ${MAX_POPUP_SERVERS} Servern für das Popup ausgewählt.`;
allServices.forEach(service => {
const serverItem = document.createElement('div');
serverItem.className = 'popup-server-item';
const isSelected = selectedServers.some(s => s.name === service.name && s.adresse === service.adresse);
serverItem.innerHTML = `
${escapeHtml(service.name)}
${escapeHtml(service.adresse || '')}
`;
const checkbox = serverItem.querySelector('input[type="checkbox"]');
checkbox.addEventListener('change', () => {
updatePopupServerSelection(service, checkbox.checked, allServices);
});
container.appendChild(serverItem);
});
});
}
// --- NEU: Auswahl aktualisieren ---
function updatePopupServerSelection(service, isSelected, allServices) {
chrome.storage.sync.get({ popupServers: [] }, (data) => {
let selectedServers = data.popupServers || [];
if (isSelected) {
// Verhindere, dass mehr als MAX_POPUP_SERVERS ausgewählt werden
if (selectedServers.length >= MAX_POPUP_SERVERS) {
alert(`Sie können maximal ${MAX_POPUP_SERVERS} Server auswählen.`);
checkbox.checked = false; // Haken entfernen
return;
}
selectedServers.push(service);
} else {
selectedServers = selectedServers.filter(s => !(s.name === service.name && s.adresse === service.adresse));
}
chrome.storage.sync.set({ popupServers: selectedServers }, () => {
// Info-Text und Checkboxen aktualisieren
renderPopupServerSelection(allServices);
});
});
}
// --- Services rendern ---
function renderServices(filter = '') {
getAllData((services, popupServers, statuses, history) => {
servicesListStat.innerHTML = '';
const filtered = services.filter(s => s.name.toLowerCase().includes(filter.toLowerCase()));
if (filtered.length === 0) {
emptyState.style.display = services.length === 0 ? 'block' : 'none';
if (services.length === 0) return;
} else {
emptyState.style.display = 'none';
}
filtered.forEach((service, index) => {
const statusObj = statuses[service.name] || {};
const st = statusObj.status || 'unknown';
const resp = typeof statusObj.responseTime === 'number' ? `${statusObj.responseTime} ms` : '';
const li = document.createElement('li');
li.className = 'service-stat-item';
li.dataset.index = index;
li.innerHTML = `
${escapeHtml(service.name)}
${escapeHtml(service.adresse || '')}
`;
li.querySelector('.service-name').addEventListener('click', () => {
showStatsForService(service);
highlightSelection(li);
});
li.querySelector('.service-name').addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
li.querySelector('.service-name').click();
}
});
li.querySelector('.icon-btn.edit').addEventListener('click', (e) => {
e.stopPropagation();
enterEditMode(li, service, services);
});
li.querySelector('.icon-btn.delete').addEventListener('click', (e) => {
e.stopPropagation();
if (confirm(`Server "${service.name}" wirklich löschen?`)) {
services.splice(index, 1);
chrome.storage.sync.set({ services }, () => {
renderServices(searchInput.value.trim());
renderPopupServerSelection(services); // Auch die Auswahl neu rendern
});
}
});
servicesListStat.appendChild(li);
});
});
}
function highlightSelection(itemEl) {
document.querySelectorAll('.service-stat-item').forEach(it => it.classList.remove('selected'));
itemEl.classList.add('selected');
}
function enterEditMode(li, service, allServices) {
li.classList.add('editing');
li.innerHTML = `
`;
const saveBtn = li.querySelector('.btn-save');
const cancelBtn = li.querySelector('.btn-cancel');
cancelBtn.addEventListener('click', (ev) => {
ev.stopPropagation();
renderServices(searchInput.value.trim());
});
saveBtn.addEventListener('click', (ev) => {
ev.stopPropagation();
const newName = li.querySelector('.edit-name').value.trim();
const newAdresse = li.querySelector('.edit-adresse').value.trim();
if (!newName || !newAdresse) { alert('Name und Adresse dürfen nicht leer sein.'); return; }
chrome.storage.sync.get({ services: [] }, function(data) {
const services = data.services || [];
const idx = services.findIndex(s => s.name === service.name && (s.adresse || '') === (service.adresse || ''));
if (idx === -1) {
alert('Fehler: Dienst nicht gefunden.');
renderServices(searchInput.value.trim());
return;
}
services[idx].name = newName;
services[idx].adresse = newAdresse;
chrome.storage.sync.set({ services }, () => {
renderServices(searchInput.value.trim());
renderPopupServerSelection(services); // Auch die Auswahl neu rendern
});
});
});
}
// --- Add service ---
function addService(name, adresse) {
if (!name || !adresse) return;
chrome.storage.sync.get({ services: [] }, function(data) {
const newService = { name, adresse };
const updatedServices = [...data.services, newService];
chrome.storage.sync.set({ services: updatedServices }, () => {
renderServices(searchInput.value.trim());
renderPopupServerSelection(updatedServices); // Auch die Auswahl neu rendern
});
});
}
serviceForm.addEventListener('submit', function(event) {
event.preventDefault();
const nameInput = document.getElementById('service-name');
const protocolSelect = document.getElementById('service-protocol');
const domainInput = document.getElementById('service-domain');
const fullAdresse = protocolSelect.value + domainInput.value.trim();
addService(nameInput.value.trim(), fullAdresse);
serviceForm.reset();
nameInput.focus();
});
// --- Settings load/save ---
function loadSettings() {
// NEU: discordWebhookUrl zu den abgerufenen Daten hinzufügen
chrome.storage.sync.get({ checkInterval: 1, notifyOnline: false, discordWebhookUrl: '' }, function(data) {
intervalSelect.value = data.checkInterval;
notifyOnlineCheckbox.checked = data.notifyOnline;
discordWebhookInput.value = data.discordWebhookUrl || ''; // NEU
});
}
intervalSelect.addEventListener('change', () => {
const newInterval = parseFloat(intervalSelect.value);
chrome.storage.sync.set({ checkInterval: newInterval }, () => { chrome.runtime.sendMessage({ type: 'updateInterval' }); });
});
notifyOnlineCheckbox.addEventListener('change', () => { chrome.storage.sync.set({ notifyOnline: notifyOnlineCheckbox.checked }); });
// NEU: Event Listener für Discord Webhook URL
discordWebhookInput.addEventListener('change', () => {
chrome.storage.sync.set({ discordWebhookUrl: discordWebhookInput.value.trim() });
});
// --- Import/Export ---
exportBtn.addEventListener('click', () => {
chrome.storage.sync.get({ services: [] }, (data) => {
const dataStr = JSON.stringify(data.services, null, 2);
const blob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(blob); const a = document.createElement('a');
a.href = url; a.download = 'uptime-services.json';
document.body.appendChild(a); a.click();
document.body.removeChild(a); URL.revokeObjectURL(url);
});
});
importBtn.addEventListener('click', () => importFileInput.click());
importFileInput.addEventListener('change', (event) => {
const file = event.target.files[0]; if (!file) return;
const reader = new FileReader();
reader.onload = (e) => {
try { const importedServices = JSON.parse(e.target.result);
if (Array.isArray(importedServices)) {
chrome.storage.sync.set({ services: importedServices }, () => {
renderServices(searchInput.value.trim());
renderPopupServerSelection(importedServices); // Auch die Auswahl neu rendern
});
}
else { alert('Ungültiges Dateiformat.'); }
} catch (error) { alert('Fehler beim Lesen der Datei.'); }
};
reader.readAsText(file);
});
// --- Statistik-Anzeige ---
function showStatsForService(service) {
if (!service) return;
document.getElementById('no-service-selected').style.display = 'none';
const chartCanvas = document.getElementById('uptimeChart');
chartCanvas.style.display = 'block';
chrome.storage.local.get({ history: {} }, (data) => {
const history = data.history[service.name] || {};
const labels = Object.keys(history).sort().slice(-48);
const uptimeData = labels.map(label => {
const h = history[label];
return h.checks > 0 ? (h.up_checks / h.checks * 100).toFixed(2) : 0;
});
if (currentChart) currentChart.destroy();
const ctx = chartCanvas.getContext('2d');
if (labels.length === 0) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.font = "16px -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto";
ctx.fillStyle = getComputedStyle(document.documentElement).getPropertyValue('--text-secondary');
ctx.textAlign = 'center';
ctx.fillText('Noch keine Daten verfügbar.', ctx.canvas.width / 2, ctx.canvas.height / 2);
return;
}
const formattedLabels = labels.map(l => {
const parts = l.split(' ');
return parts.length === 2 ? parts[1] : l;
});
currentChart = new Chart(ctx, {
type: 'line',
data: {
labels: formattedLabels,
datasets: [{
label: `Uptime für ${service.name} (%)`,
data: uptimeData,
borderColor: 'var(--accent-color)',
backgroundColor: 'rgba(24, 119, 242, 0.1)',
fill: true,
tension: 0.3
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: { beginAtZero: true, max: 100, ticks: { callback: value => value + '%' } }
},
plugins: { legend: { display: false } }
}
});
});
}
// --- Search ---
searchInput.addEventListener('input', () => {
renderServices(searchInput.value.trim());
});
// --- Helpers ---
function escapeHtml(str) { if (!str) return ''; return String(str).replace(/[&<>"']/g, s => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[s])); }
function escapeAttr(s) { return (s||'').replace(/"/g, '"'); }
// --- Init ---
loadSettings();
renderServices();
// Initiales Rendern der Popup-Auswahl
chrome.storage.sync.get({ services: [] }, (data) => {
renderPopupServerSelection(data.services || []);
});
});