// animation speeds cause why not
const TYPING_SPEED = 60;
const ERASE_SPEED = 80;
const STATUS_UPDATE_INTERVAL = 60000; // 1min updates
const TIME_UPDATE_INTERVAL = 1; // 1ms updates (yeah its bad)
// status warning stuff
const LATENCY_WARNING_THRESHOLD = 500000;
// lazy group name mapping
const GROUP_NAMES = {
4: "INFRASTRUCTURE",
5: "SERVICES",
6: "API",
};
// does the fancy typing animation for nav labels
// its way overcomplicated but whatever
function writeoutnavlabel(label) {
if (label.getAttribute("data-animating") === "true") return;
const text = label.getAttribute("data-text") || label.textContent;
label.setAttribute("data-text", text);
label.textContent = "";
label.style.opacity = "1";
label.setAttribute("data-animating", "true");
let i = 0;
let animationInterval = setInterval(() => {
if (i < text.length) {
label.textContent += text.charAt(i);
i++;
} else {
clearInterval(animationInterval);
label.setAttribute("data-animating", "false");
const icon = label.closest(".nav-icon");
if (icon) {
icon.isAnimating = false;
icon.hoverAnimationDone = true;
}
}
}, TYPING_SPEED);
label.setAttribute("data-animation-interval", animationInterval);
}
// erases the nav labels
// just as terrible as the typing but matches
function eraselabel(label) {
if (label.getAttribute("data-animating") === "true") {
const existingInterval = label.getAttribute("data-animation-interval");
if (existingInterval) clearInterval(existingInterval);
}
label.setAttribute("data-animating", "true");
let eraseInterval = setInterval(() => {
const currentText = label.textContent;
if (currentText.length > 0) {
label.textContent = currentText.slice(0, -1);
} else {
clearInterval(eraseInterval);
label.setAttribute("data-animating", "false");
label.style.opacity = "0";
}
}, ERASE_SPEED);
label.setAttribute("data-animation-interval", eraseInterval);
}
// we get the client info from the headers
// unclean but it works
function updateClientInfo() {
fetch(window.location.href)
.then((response) => {
const clientInfo = response.headers.get("X-Client-Info");
if (clientInfo) {
// i hate http why does it do this
// this is a hack to fix the weird  character that shows up
const correctedInfo = clientInfo.replace(/Â?µs/g, "µs");
document.querySelector(".location").textContent = correctedInfo;
}
})
.catch((error) => {
console.error("Error fetching X-Client-Info:", error);
});
}
// updates the time display
// probably updates too much but eh
function updateTime() {
const now = new Date();
const timeString = now
.toLocaleString("en-US", {
month: "2-digit",
day: "2-digit",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
fractionalSecondDigits: 3,
hour12: true,
})
.toUpperCase();
document.getElementById("current-time").textContent = timeString;
}
// bunch of status helper functions for formatting and stuff
function calculateAverageUptime(services) {
if (!services || services.length === 0) return 0;
const totalUptime = services.reduce(
(sum, service) => sum + service.online_24_hours,
0,
);
return (totalUptime / services.length).toFixed(2);
}
function formatLatency(latency) {
return (Math.round((latency / 1000) * 10) / 10).toFixed(1);
}
function formatUptime(uptime) {
return uptime.toFixed(2);
}
function getStatusClass(service) {
if (!service.online) return "status-offline";
if (service.latency > LATENCY_WARNING_THRESHOLD) return "status-warning";
return "status-online";
}
function getGroupName(groupId) {
return GROUP_NAMES[groupId] || `GROUP ${groupId}`;
}
// builds all the html for status page
// groups everything by group_id cause organization
function createStatusHTML(services) {
const groups = services.reduce((acc, service) => {
const groupId = service.group_id;
if (!acc[groupId]) acc[groupId] = [];
acc[groupId].push(service);
return acc;
}, {});
return Object.entries(groups)
.map(
([groupId, groupServices]) => `
${getGroupName(parseInt(groupId))}
${groupServices
.map(
(service) => `
${service.name.toUpperCase()}
${service.online ? "OPERATIONAL" : "OFFLINE"}
Latency: ${formatLatency(service.latency)}ms
Uptime 24h: ${formatUptime(service.online_24_hours)}%
7-day Uptime: ${formatUptime(service.online_7_days)}%
`,
)
.join("")}
`,
)
.join("");
}
// main status updater
// fetches and displays all service statuses
function updateStatus() {
const statusContainer = document.getElementById("status-container");
const averageUptimeElement = document.getElementById("average-uptime");
fetch("https://status.elia.network/api/services")
.then((response) => response.json())
.then((data) => {
statusContainer.innerHTML = createStatusHTML(data);
const averageUptime = calculateAverageUptime(data);
averageUptimeElement.textContent = `UPTIME ${averageUptime}%`;
document.querySelectorAll(".status-item").forEach((item) => {
item.addEventListener("click", function () {
const details = this.querySelector(".status-details");
details.classList.toggle("expanded");
});
});
})
.catch((error) => {
statusContainer.innerHTML = `
UNABLE TO FETCH STATUS DATA
${error.message}
`;
averageUptimeElement.textContent = "UPTIME ERROR";
});
}
// handles expanding/collapsing project items
// closes others when opening new one
document.querySelectorAll(".project-item").forEach((item) => {
item.addEventListener("click", function () {
const content = this.querySelector(".project-content");
const icon = this.querySelector(".expand-icon");
document.querySelectorAll(".project-item").forEach((otherItem) => {
if (otherItem !== item) {
otherItem
.querySelector(".project-content")
.classList.remove("expanded");
otherItem.querySelector(".expand-icon").textContent = "+";
}
});
content.classList.toggle("expanded");
icon.textContent = content.classList.contains("expanded") ? "−" : "+";
});
});
// smooth scrolls to sections when clicking nav
// better than jumping around
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
anchor.addEventListener("click", function (e) {
e.preventDefault();
document.querySelector(this.getAttribute("href")).scrollIntoView({
behavior: "smooth",
});
});
});
// handles active nav icon updates on scroll
// shows which section youre looking at
window.addEventListener("scroll", () => {
let current = "";
document.querySelectorAll("section").forEach((section) => {
const sectionTop = section.offsetTop;
if (scrollY >= sectionTop - 60) {
current = section.getAttribute("id");
}
});
document.querySelectorAll(".nav-icon").forEach((icon) => {
const label = icon.querySelector(".nav-label");
const isCurrentSection = icon.getAttribute("href") === `#${current}`;
const wasActive = icon.classList.contains("active");
if (isCurrentSection && !wasActive) {
icon.classList.add("active");
label.style.opacity = "1";
if (!icon.isAnimating && !icon.hoverAnimationDone) {
writeoutnavlabel(label);
icon.hoverAnimationDone = true;
}
} else if (!isCurrentSection && wasActive) {
icon.classList.remove("active");
if (!icon.isAnimating) {
eraselabel(label);
icon.isAnimating = false;
icon.hoverAnimationDone = false;
}
}
});
});
// nav icon hover stuff
// does the typing animation when hovering
document.querySelectorAll(".nav-icon").forEach((icon) => {
icon.hoverAnimationDone = false;
icon.isAnimating = false;
icon.addEventListener("mouseenter", () => {
if (!icon.classList.contains("active") && !icon.isAnimating) {
const label = icon.querySelector(".nav-label");
writeoutnavlabel(label);
icon.isAnimating = true;
}
});
icon.addEventListener("mouseleave", () => {
if (!icon.classList.contains("active")) {
const label = icon.querySelector(".nav-label");
eraselabel(label);
icon.isAnimating = false;
icon.hoverAnimationDone = false;
}
});
icon.addEventListener("click", () => {
const label = icon.querySelector(".nav-label");
label.style.opacity = "1";
});
});
// sets everything up when page loads
// starts all the intervals and stuff
document.addEventListener("DOMContentLoaded", () => {
updateTime();
setInterval(updateTime, TIME_UPDATE_INTERVAL);
updateStatus();
setInterval(updateStatus, STATUS_UPDATE_INTERVAL);
updateClientInfo();
const activeNavIcon = document.querySelector(".nav-icon.active");
if (activeNavIcon) {
const label = activeNavIcon.querySelector(".nav-label");
if (label) writeoutnavlabel(label);
}
// fancy console log for style points
console.log(
"%cELIA.NETWORK",
"color: #2a5a5a; font-size: 24px; font-weight: bold; text-shadow: 2px 2px 4px rgba(42, 90, 90, 0.3);",
);
});