Test custom.css

This commit is contained in:
2026-03-21 19:28:12 +01:00
parent 24a782f3da
commit 99471fbc52
6 changed files with 12778 additions and 472 deletions
+135 -107
View File
@@ -1,22 +1,23 @@
/* ============================================================================
KalliLab — CUSTOM JAVASCRIPT
Basiert auf LionCityGaming/homepage custom.js (MIT License)
Anpassungen: KalliLab Tab-Namen, keine HA/EPL/Glance iFrames
Home / Media Tabs
Reload-Button entfernen
Aktiven Tab merken
============================================================================ */
const CONFIG = {
STORAGE: {
KEY: "lastFocusedTabId",
KEY: "kallilab-last-focused-tab-id",
},
TIMING: {
RETRY_DELAY: 500,
STANDARD_REFRESH: 1800000, // 30 Minuten
QUICK_REFRESH: 60000, // 1 Minute
STANDARD_REFRESH: 1800000,
QUICK_REFRESH: 60000,
RETRY_ON_ERROR: 30000,
BATCH_DELAY: 100,
},
SERVICES: {
QUICK_REFRESH: [], // keine kritischen Quick-Refresh Widgets
QUICK_REFRESH: [],
},
};
@@ -28,17 +29,10 @@ const RELOAD_BUTTON_SELECTORS = [
'[role="button"][aria-label="Reload"]',
];
// Keine iFrame-Widgets in KalliLab
const IFRAME_CONFIG = [];
// Tab-Mapping für KalliLab Tabs (5 Tabs)
const TAB_MAPPING = {
"#ueberblick": ["#Überblick-tab", "#Überblick"],
"#system": ["#System-tab", "#System"],
"#sicherheit": ["#Sicherheit-tab", "#Sicherheit"],
"#dienste": ["#Dienste-tab", "#Dienste"],
"#backends": ["#Backends-tab", "#Backends"],
"": ["#Überblick-tab", "#Überblick"],
const TAB_HASH_MAP = {
"#home": "Home-tab",
"#media": "Media-tab",
"": "Home-tab",
};
const state = {
@@ -46,7 +40,6 @@ const state = {
currentFocusedTab: null,
observers: {
reloadButton: null,
resize: null,
},
};
@@ -105,7 +98,9 @@ function throttle(func, limit) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
setTimeout(() => {
inThrottle = false;
}, limit);
}
};
}
@@ -141,6 +136,81 @@ function setupReloadButtonObserver() {
return observer;
}
function setTabFocus(tab) {
requestAnimationFrame(() => {
if (state.currentFocusedTab) {
state.currentFocusedTab.classList.remove("tab-focused");
}
state.currentFocusedTab = tab;
if (state.currentFocusedTab) {
state.currentFocusedTab.classList.add("tab-focused");
}
});
}
function showTabContent(contentElement) {
if (!contentElement || !domCache.tabContents) return;
domCache.tabContents.forEach((content) => {
content.classList.remove("active");
content.style.display = "none";
});
contentElement.classList.add("active");
contentElement.style.display = "block";
domCache.updateActiveTab();
}
function resolveTabContentElement(tab) {
if (!tab) return null;
const ariaControls = tab.getAttribute("aria-controls");
if (ariaControls) {
return document.getElementById(ariaControls);
}
const tabText = (tab.textContent || "").trim().toLowerCase();
const directMatch = Array.from(document.querySelectorAll(".tabcontent")).find((content) => {
return content.id.toLowerCase() === tabText;
});
return directMatch || document.querySelector(".tabcontent.active");
}
function activateTab(tab, updateHash = true) {
if (!tab) return;
setTabFocus(tab);
storage.save(tab.id);
const contentToShow = resolveTabContentElement(tab);
showTabContent(contentToShow);
if (updateHash) {
const normalized = (tab.textContent || "").trim().toLowerCase();
if (normalized === "home") {
history.replaceState(null, "", "#home");
} else if (normalized === "media") {
history.replaceState(null, "", "#media");
}
}
}
function handleTabFocusFromURL() {
const hash = window.location.hash.toLowerCase();
const tabId = TAB_HASH_MAP[hash] || TAB_HASH_MAP[""];
const tabToFocus = document.getElementById(tabId);
if (tabToFocus) {
activateTab(tabToFocus, false);
return true;
}
return false;
}
function updateServiceCard(card, data) {
requestAnimationFrame(() => {
const titleElement = card.querySelector(".card-title");
@@ -154,6 +224,7 @@ function updateServiceCard(card, data) {
statusElement.textContent = Array.isArray(data)
? `${data.length} items`
: (data.status ?? (typeof data === "object" ? "Data received" : ""));
statusElement.style.color = "";
}
});
}
@@ -165,7 +236,7 @@ function updateServiceCardError(card, error) {
statusElement.textContent = error.message.includes("404")
? "Service unavailable"
: "Error loading data";
statusElement.style.color = "red";
statusElement.style.color = "#ff7b7b";
}
});
}
@@ -221,53 +292,14 @@ async function batchUpdateServiceCards(cards) {
}
}
function handleTabFocusFromURL() {
const hash = window.location.hash.toLowerCase();
const mapping = TAB_MAPPING[hash] || TAB_MAPPING[""];
const [tabSelector, contentSelector] = mapping;
const tabToFocus = document.querySelector(tabSelector);
const contentToShow = document.querySelector(contentSelector);
if (tabToFocus) {
setTabFocus(tabToFocus);
storage.save(tabToFocus.id);
domCache.tabContents.forEach((content) => {
content.classList.remove("active");
content.style.display = "none";
});
if (contentToShow) {
showTabContent(contentToShow);
}
}
}
function setTabFocus(tab) {
requestAnimationFrame(() => {
if (state.currentFocusedTab) {
state.currentFocusedTab.classList.remove("tab-focused");
}
state.currentFocusedTab = tab;
state.currentFocusedTab.classList.add("tab-focused");
});
}
function showTabContent(contentElement) {
if (!contentElement) return;
contentElement.classList.add("active");
contentElement.style.display = "block";
domCache.updateActiveTab();
}
async function preloadAllTabs() {
const tabContents = document.querySelectorAll(".tab-pane");
const tabContents = document.querySelectorAll(".tabcontent, .tab-pane");
const serviceCards = new Set();
tabContents.forEach((tab) => {
tab.querySelectorAll(".service-card").forEach((card) => serviceCards.add(card));
tab.querySelectorAll(".service-card[data-api-endpoint]").forEach((card) => {
serviceCards.add(card);
});
});
await batchUpdateServiceCards(Array.from(serviceCards));
@@ -295,49 +327,45 @@ function setupPeriodicRefresh() {
});
domCache.myTab?.addEventListener("click", (event) => {
if (event.target.matches('[id$="-tab"]')) {
const tab = event.target.closest('[id$="-tab"]');
if (tab) {
activateTab(tab);
debouncedRefresh();
}
});
}
function initializeTabFocus() {
// KalliLab Tab-Selektoren
const tabs = document.querySelectorAll(
"#Überblick-tab, #System-tab, #Sicherheit-tab, #Dienste-tab, #Backends-tab",
);
const tabs = document.querySelectorAll('#myTab [id$="-tab"]');
if (!tabs.length) return;
handleTabFocusFromURL();
const handledByHash = handleTabFocusFromURL();
if (!window.location.hash) {
if (!handledByHash) {
const savedTabId = storage.get();
const savedTab = savedTabId && document.getElementById(savedTabId);
if (savedTab) {
setTabFocus(savedTab);
activateTab(savedTab, false);
} else {
const activeTab = document.querySelector(".tabcontent.active");
const correspondingTab =
activeTab && document.querySelector(`[aria-controls="${activeTab.id}"]`);
if (correspondingTab) {
setTabFocus(correspondingTab);
const activeTab = document.querySelector('#myTab [aria-selected="true"]');
if (activeTab) {
activateTab(activeTab, false);
} else {
activateTab(tabs[0], false);
}
}
}
tabs.forEach((tab) => {
const handleTabAction = function () {
setTabFocus(this);
storage.save(this.id);
};
tab.addEventListener("click", () => {
activateTab(tab);
});
tab.addEventListener("click", handleTabAction);
tab.addEventListener("keydown", function (e) {
tab.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
this.click();
handleTabAction.call(this);
activateTab(tab);
}
});
});
@@ -347,6 +375,10 @@ function initializeTabFocus() {
storage.save(state.currentFocusedTab.id);
}
});
window.addEventListener("hashchange", () => {
handleTabFocusFromURL();
});
}
function initializeEverything() {
@@ -368,14 +400,21 @@ function initializeEverything() {
} else {
setTimeout(initializeEverything, CONFIG.TIMING.RETRY_DELAY);
}
}
window.addEventListener("orientationchange", () => {
setTimeout(removeReloadButton, 100);
});
function cleanup() {
if (state.observers.reloadButton) {
state.observers.reloadButton.disconnect();
state.observers.reloadButton = null;
}
domCache.clear();
state.currentFocusedTab = null;
}
document.addEventListener("DOMContentLoaded", removeReloadButton);
window.addEventListener("load", initializeEverything);
window.addEventListener("unload", cleanup);
if (typeof window.htmlLoaded === "function") {
const originalHtmlLoaded = window.htmlLoaded;
@@ -386,22 +425,11 @@ if (typeof window.htmlLoaded === "function") {
}
if ("ontouchstart" in window) {
window.addEventListener("touchend", () => {
setTimeout(removeReloadButton, 100);
}, { passive: true });
}
function cleanup() {
if (state.observers.reloadButton) {
state.observers.reloadButton.disconnect();
state.observers.reloadButton = null;
}
if (state.observers.resize) {
state.observers.resize.disconnect();
state.observers.resize = null;
}
domCache.clear();
state.currentFocusedTab = null;
}
window.addEventListener("unload", cleanup);
window.addEventListener(
"touchend",
() => {
setTimeout(removeReloadButton, 100);
},
{ passive: true },
);
}