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 || '')}
${resp}
`; 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 || []); }); });