//PV-Tabelle Monatsübersicht (function(){ var BASE = "0_userdata.0.PV-Tabelle"; var ARCHIVE_BASE = BASE + ".archive."; var DISPLAY_BASE = BASE + ".system.display."; var SELECTED_YEAR_ID = BASE + ".system.selectedYear"; var currentYear = new Date().getFullYear(); var TOGGLE_KEYS = ["production", "consumption", "battery", "export", "import", "gridBalance"]; // ------------------------------------------------------------ // Hilfsfunktion: liefert ALLE Elemente mit einer bestimmten ID. // Normalerweise ist eine ID eindeutig, aber wenn VIS eine View über // eine andere legt (z.B. Popup/Dialog), können beide Views gleich- // zeitig im DOM stehen -> doppelte IDs. document.getElementById() // liefert dann immer nur die ERSTE (evtl. unsichtbare) Instanz. // getVisibleById() filtert stattdessen auf tatsächlich sichtbare // Elemente (offsetParent !== null), damit beide Views unabhängig // voneinander funktionieren. // ------------------------------------------------------------ function getAllById(id){ var nodes = document.querySelectorAll('[id="' + id + '"]'); return Array.prototype.slice.call(nodes); } function getVisibleById(id){ var all = getAllById(id); for(var i = 0; i < all.length; i++){ if(all[i].offsetParent !== null){ return all[i]; } } return all[0] || null; } // ------------------------------------------------------------ // Klick-Anzeige für einen angetippten Balken. Per Event-Delegation // an document.body gebunden - funktioniert auch, wenn das // Chart-Widget (Widget 2) durch seine {oid}-Bindung wiederholt neu // gerendert wird, da wir nie eine feste Referenz auf ein einzelnes // Chart-Element halten müssen. // ------------------------------------------------------------ var tooltipTimer = null; function showTooltip(text){ var el = getVisibleById("pv-tooltip"); if(!el){ return; } el.textContent = text; el.classList.add("visible"); if(tooltipTimer){ clearTimeout(tooltipTimer); } tooltipTimer = setTimeout(function(){ el.classList.remove("visible"); tooltipTimer = null; }, 5000); } function hideTooltip(){ var el = getVisibleById("pv-tooltip"); if(el){ el.classList.remove("visible"); } if(tooltipTimer){ clearTimeout(tooltipTimer); tooltipTimer = null; } } document.body.addEventListener("click", function(e){ var target = e.target; if(target && target.tagName && target.tagName.toLowerCase() === "rect"){ var info = target.getAttribute("data-info"); if(info){ showTooltip(info); return; } } // Klick irgendwo sonst -> falls offen, Anzeige schließen if(!(target && target.id === "pv-tooltip")){ hideTooltip(); } }); // ------------------------------------------------------------ // Checkboxen: per Event-Delegation an document.body gebunden. // Widget 1 (Steuerung) enthält keine {oid}-Bindung und wird von VIS // daher nie neu injiziert - die Delegation ist hier eigentlich nur // eine zusätzliche Absicherung, schadet aber nicht. // ------------------------------------------------------------ document.body.addEventListener("change", function(e){ var target = e.target; if(!target || !target.id){ return; } TOGGLE_KEYS.forEach(function(key){ if(target.id === "pv-tg-" + key){ vis.conn.setState(DISPLAY_BASE + key, target.checked); } }); if(target.id === "pv-month-year-select"){ vis.conn.setState(SELECTED_YEAR_ID, target.value); } }); // ------------------------------------------------------------ // Anfangszustand einmalig laden: Checkboxen ankreuzen, Jahres- // Dropdown befüllen. Da Widget 1 nicht neu gerendert wird, reicht // das einmalige Setzen beim Start. // ------------------------------------------------------------ function loadToggles(){ var done = false; function applyDefaults(){ if(done){ return; } done = true; TOGGLE_KEYS.forEach(function(key){ getAllById("pv-tg-" + key).forEach(function(cb){ if(cb.dataset.pvLoaded !== "1"){ cb.checked = true; } }); }); } var fallbackTimer = setTimeout(applyDefaults, 6000); vis.conn.getStates(DISPLAY_BASE + "*", function(err, states){ if(done){ return; } clearTimeout(fallbackTimer); done = true; TOGGLE_KEYS.forEach(function(key){ var id = DISPLAY_BASE + key; var val = true; if(states && states[id] && states[id].val !== null && states[id].val !== undefined){ val = !!states[id].val; } getAllById("pv-tg-" + key).forEach(function(cb){ cb.checked = val; cb.dataset.pvLoaded = "1"; }); }); }); } function populateYears(){ vis.conn.getStates(ARCHIVE_BASE + "*.year.production", function(err, states){ var years = []; if(states){ Object.keys(states).forEach(function(id){ var m = id.match(/archive\.(\d{4})\.year\.production$/); if(m){ years.push(m[1]); } }); } if(years.indexOf(String(currentYear)) === -1){ years.push(String(currentYear)); } years = years.filter(function(y, i){ return years.indexOf(y) === i; }); years.sort(function(a, b){ return b - a; }); var selects = getAllById("pv-month-year-select"); if(selects.length === 0){ return; } selects.forEach(function(sel){ sel.innerHTML = ""; years.forEach(function(y){ var opt = document.createElement("option"); opt.value = y; opt.textContent = y; sel.appendChild(opt); }); sel.dataset.pvYearInit = "1"; }); vis.conn.getStates(SELECTED_YEAR_ID, function(err2, states2){ var current = String(currentYear); if(states2 && states2[SELECTED_YEAR_ID] && states2[SELECTED_YEAR_ID].val){ current = String(states2[SELECTED_YEAR_ID].val); } if(years.indexOf(current) === -1){ current = String(currentYear); } selects.forEach(function(sel){ sel.value = current; }); }); }); } // ------------------------------------------------------------ // MutationObserver: erkennt, wenn NEUE Instanzen unserer Elemente // im DOM auftauchen - z.B. weil eine View als Popup/"View in View" // erst nachträglich (nach dem initialen Seitenaufbau) geladen wird. // Ein einmaliger setTimeout-Init würde solche später erscheinenden // Widgets nie erreichen. Der Observer prüft bei jeder DOM-Änderung // (entprellt), ob es nicht-initialisierte Checkboxen/Dropdowns gibt, // und ruft dafür populateYears()/loadToggles() erneut auf. // ------------------------------------------------------------ function hasUninitializedInstances(){ var selects = getAllById("pv-month-year-select"); for(var i = 0; i < selects.length; i++){ if(selects[i].dataset.pvYearInit !== "1"){ return true; } } for(var k = 0; k < TOGGLE_KEYS.length; k++){ var boxes = getAllById("pv-tg-" + TOGGLE_KEYS[k]); for(var j = 0; j < boxes.length; j++){ if(boxes[j].dataset.pvLoaded !== "1"){ return true; } } } return false; } var observerTimer = null; var pvObserver = new MutationObserver(function(){ if(observerTimer){ clearTimeout(observerTimer); } observerTimer = setTimeout(function(){ observerTimer = null; if(hasUninitializedInstances()){ populateYears(); loadToggles(); } }, 300); }); pvObserver.observe(document.body, { childList:true, subtree:true }); // ------------------------------------------------------------ // Start // ------------------------------------------------------------ setTimeout(function(){ populateYears(); loadToggles(); }, 1200); })();