diff --git a/Jasbee Gebäude-Prozentanzeige.js b/Jasbee Gebäude-Prozentanzeige.js new file mode 100644 index 0000000..2e09b9e --- /dev/null +++ b/Jasbee Gebäude-Prozentanzeige.js @@ -0,0 +1,306 @@ +// ==UserScript== +// @name Jasbee Gebäude-Prozentanzeige +// @namespace http://tampermonkey.net/ +// @version 3.2 +// @description Erlaubt das Scannen aller Gebäude im Hintergrund per Klick auf city.php (Mit optischen CSS-Fixes) +// @author Daniel + Gemini +// @match https://www.jasbee.de/module/* +// @grant none +// ==/UserScript== + +(function() { + 'use strict'; + + // Liste der erlaubten Dateinamen + const allowedPages = [ + 'city.php', 'lager.php', 'baumarkt.php', 'sportsbar.php', + 'aliengeb.php', 'sauna.php', 'haus.php', 'fussball.php', + 'schwimmhalle.php', 'fabrik.php', 'fitness.php', 'bazar.php' + ]; + + const currentPath = window.location.pathname; + const currentPage = currentPath.substring(currentPath.lastIndexOf('/') + 1).toLowerCase(); + + if (!allowedPages.includes(currentPage)) { + return; + } + + // 1. Popup-Element erstellen und stylen + const popup = document.createElement('div'); + popup.id = 'gebaeude-prozent-popup'; + popup.style.position = 'fixed'; + popup.style.top = '10px'; + popup.style.right = '10px'; + popup.style.backgroundColor = 'rgba(20, 20, 20, 0.95)'; + popup.style.color = '#fff'; + popup.style.padding = '15px'; + popup.style.borderRadius = '8px'; + popup.style.boxShadow = '0 4px 15px rgba(0,0,0,0.6)'; + popup.style.zIndex = '2147483647'; + popup.style.fontFamily = 'Segoe UI, Arial, sans-serif'; + popup.style.fontSize = '13px'; + popup.style.maxHeight = '400px'; + popup.style.overflowY = 'auto'; + popup.style.minWidth = '290px'; + popup.style.border = '1px solid #444'; + popup.style.userSelect = 'none'; + + popup.innerHTML = ` + + + `; + + document.body.appendChild(popup); + + // Scan-Button nur auf city.php einblenden + const scanBtn = document.getElementById('scan-jasbee-buildings'); + if (currentPage === 'city.php') { + scanBtn.style.display = 'inline-block'; + } + + // Drag-and-Drop Steuerung + const header = document.getElementById('popup-header'); + let isDragging = false; + let currentX, currentY, initialX, initialY; + let xOffset = 0, yOffset = 0; + + const savedPos = localStorage.getItem('jasbee_popup_pos'); + if (savedPos) { + const pos = JSON.parse(savedPos); + popup.style.transform = `translate3d(${pos.x}px, ${pos.y}px, 0)`; + xOffset = pos.x; + yOffset = pos.y; + } + + header.addEventListener('mousedown', (e) => { + if (e.target === header) { + initialX = e.clientX - xOffset; + initialY = e.clientY - yOffset; + isDragging = true; + } + }); + + document.addEventListener('mousemove', (e) => { + if (isDragging) { + e.preventDefault(); + currentX = e.clientX - initialX; + currentY = e.clientY - initialY; + xOffset = currentX; + yOffset = currentY; + popup.style.transform = `translate3d(${currentX}px, ${currentY}px, 0)`; + } + }); + + document.addEventListener('mouseup', () => { + if (isDragging) { + isDragging = false; + localStorage.setItem('jasbee_popup_pos', JSON.stringify({x: currentX, y: currentY})); + } + }); + + document.getElementById('clear-jasbee-data').addEventListener('click', () => { + if (confirm('Möchtest du alle gespeicherten Zustandswerte löschen?')) { + localStorage.removeItem('jasbee_building_data'); + updateBuildingStatus(); + } + }); + + scanBtn.addEventListener('click', startBackgroundScan); + + function getStoredData() { + const data = localStorage.getItem('jasbee_building_data'); + return data ? JSON.parse(data) : {}; + } + + function saveStoredData(data) { + localStorage.setItem('jasbee_building_data', JSON.stringify(data)); + } + + // Hintergrund-Scanner Funktion + function startBackgroundScan() { + const links = Array.from(document.querySelectorAll('a[href]')); + const targets = []; + + links.forEach(link => { + const href = link.getAttribute('href'); + allowedPages.forEach(page => { + if (page !== 'city.php' && href.includes(page) && !targets.includes(href)) { + targets.push(href); + } + }); + }); + + if (targets.length === 0) { + alert('Keine Gebäude-Links auf der Seite gefunden!'); + return; + } + + scanBtn.disabled = true; + scanBtn.style.background = '#555'; + + let index = 0; + const iframe = document.createElement('iframe'); + iframe.style.display = 'none'; + document.body.appendChild(iframe); + + function loadNext() { + if (index >= targets.length) { + document.body.removeChild(iframe); + scanBtn.disabled = false; + scanBtn.style.background = '#ff9800'; + scanBtn.innerText = 'Alle scannen'; + updateBuildingStatus(); + return; + } + + scanBtn.innerText = `Scan ${index + 1}/${targets.length}`; + iframe.src = targets[index]; + index++; + } + + iframe.addEventListener('load', () => { + try { + const doc = iframe.contentDocument || iframe.contentWindow.document; + + let buildingName = ''; + const breadcrumbItems = doc.querySelectorAll('.breadcrumb li'); + if (breadcrumbItems.length > 0) { + buildingName = breadcrumbItems[breadcrumbItems.length - 1].textContent.trim().replace(/[\t\r\n]/g, ''); + } + + const percentElements = doc.querySelectorAll('.pie-number'); + let stateValue = ''; + + for (let el of percentElements) { + const txt = el.textContent.trim(); + if (txt.includes('%')) { + const parentCard = el.closest('.card'); + if (parentCard) { + const cardHeader = parentCard.querySelector('h3'); + if (cardHeader && cardHeader.textContent.trim() === 'Gebäudezustand') { + stateValue = txt; + break; + } + } + } + } + + if (buildingName && stateValue) { + let currentStorage = getStoredData(); + // Wir speichern Zustand und die genaue URL als Objekt ab + currentStorage[buildingName] = { + value: stateValue, + url: iframe.src + }; + saveStoredData(currentStorage); + updateBuildingStatus(); + } + } catch (e) { + console.error("Fehler beim Auslesen des Iframes:", e); + } + + setTimeout(loadNext, 500); + }); + + loadNext(); + } + + // Hauptfunktion zur Anzeige + function updateBuildingStatus() { + const contentDiv = document.getElementById('popup-content'); + let currentStorage = getStoredData(); + let hasNewData = false; + + let buildingName = ''; + const breadcrumbItems = document.querySelectorAll('.breadcrumb li'); + if (breadcrumbItems.length > 0) { + buildingName = breadcrumbItems[breadcrumbItems.length - 1].textContent.trim().replace(/[\t\r\n]/g, ''); + } + + const percentElements = document.querySelectorAll('.pie-number'); + + if (buildingName && buildingName.toLowerCase() !== 'city' && percentElements.length > 0) { + let stateValue = ''; + for (let el of percentElements) { + const txt = el.textContent.trim(); + if (txt.includes('%')) { + const parentCard = el.closest('.card'); + if (parentCard) { + const cardHeader = parentCard.querySelector('h3'); + if (cardHeader && cardHeader.textContent.trim() === 'Gebäudezustand') { + stateValue = txt; + break; + } + } + } + } + + if (stateValue) { + // Abwärtskompatibilität prüfen (falls alte Datenstruktur ohne .value existiert) + const oldVal = (typeof currentStorage[buildingName] === 'object') ? currentStorage[buildingName].value : currentStorage[buildingName]; + + if (oldVal !== stateValue) { + currentStorage[buildingName] = { + value: stateValue, + url: window.location.href + }; + hasNewData = true; + } + } + } + + if (hasNewData) { + saveStoredData(currentStorage); + } + + let buildingList = []; + for (let name in currentStorage) { + const entry = currentStorage[name]; + // Abwärtskompatibler Check für die Datenstruktur + const val = (typeof entry === 'object') ? entry.value : entry; + const url = (typeof entry === 'object') ? entry.url : ''; + + buildingList.push({ + name: name, + value: val, + url: url, + numeric: parseFloat(val) || 0 + }); + } + + if (buildingList.length === 0) { + contentDiv.innerHTML = 'Noch keine Zustände erfasst. Klicke auf "Alle scannen" oder besuche deine Gebäude.'; + } else { + buildingList.sort((a, b) => a.numeric - b.numeric); + + let html = ''; + buildingList.forEach(item => { + let color = '#4caf50'; + if (item.numeric < 50) color = '#f44336'; + else if (item.numeric < 90) color = '#ffeb3b'; + + const isCurrent = (item.name === buildingName) ? 'background-color: rgba(255,255,255,0.08); font-style: italic;' : ''; + + // Falls eine URL vorhanden ist, betten wir den Namen in einen Link ein + const nameDisplay = item.url + ? `${item.name}` + : item.name; + + html += ` + + + `; + }); + html += '
${nameDisplay}${item.value}
'; + contentDiv.innerHTML = html; + } + } + + updateBuildingStatus(); +})(); \ No newline at end of file