241 lines
6.5 KiB
JavaScript
241 lines
6.5 KiB
JavaScript
/* =====================================================
|
||
GLOBAL STATE
|
||
===================================================== */
|
||
|
||
let index = 0;
|
||
let track;
|
||
let dots = [];
|
||
let currentLang = "de";
|
||
|
||
|
||
/* =====================================================
|
||
DOM CONTENT LOADED
|
||
===================================================== */
|
||
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
const saved = localStorage.getItem("lang") || "de";
|
||
setLang(saved, false);
|
||
initServices();
|
||
initNavigation();
|
||
initNewsSlider();
|
||
});
|
||
|
||
|
||
/* =====================================================
|
||
i18n – LANGUAGE SWITCHING
|
||
===================================================== */
|
||
|
||
async function setLang(lang, save = true) {
|
||
|
||
try {
|
||
const res = await fetch(`locales/${lang}.json`);
|
||
const translations = await res.json();
|
||
|
||
currentLang = lang;
|
||
document.documentElement.lang = lang;
|
||
|
||
if (save) localStorage.setItem("lang", lang);
|
||
|
||
/* Plain text nodes */
|
||
document.querySelectorAll("[data-i18n]").forEach(el => {
|
||
const key = el.getAttribute("data-i18n");
|
||
if (translations[key] !== undefined) {
|
||
el.textContent = translations[key];
|
||
}
|
||
});
|
||
|
||
/* HTML nodes (for content with <strong>, <br> etc.) */
|
||
document.querySelectorAll("[data-i18n-html]").forEach(el => {
|
||
const key = el.getAttribute("data-i18n-html");
|
||
if (translations[key] !== undefined) {
|
||
el.innerHTML = translations[key];
|
||
}
|
||
});
|
||
|
||
/* Update active button */
|
||
document.getElementById("lang-de").classList.toggle("active", lang === "de");
|
||
document.getElementById("lang-en").classList.toggle("active", lang === "en");
|
||
|
||
} catch (err) {
|
||
console.error("Language file could not be loaded:", err);
|
||
}
|
||
}
|
||
|
||
|
||
/* =====================================================
|
||
SERVICES EXPAND / COLLAPSE
|
||
===================================================== */
|
||
|
||
function initServices() {
|
||
|
||
const boxes = document.querySelectorAll(".service-box");
|
||
|
||
boxes.forEach(box => {
|
||
|
||
const arrow = box.querySelector(".service-arrow");
|
||
const text = box.querySelector(".service-text");
|
||
|
||
let closeTimeout;
|
||
|
||
arrow.addEventListener("click", () => {
|
||
boxes.forEach(b => {
|
||
if (b !== box) b.classList.remove("open");
|
||
if (b.closeTimeout) clearTimeout(b.closeTimeout);
|
||
});
|
||
box.classList.toggle("open");
|
||
});
|
||
|
||
/* AUTO CLOSE */
|
||
box.addEventListener("mouseleave", () => {
|
||
if (box.classList.contains("open")) {
|
||
box.classList.add("closing");
|
||
closeTimeout = setTimeout(() => {
|
||
box.classList.remove("open");
|
||
}, 600);
|
||
box.closeTimeout = closeTimeout;
|
||
}
|
||
});
|
||
|
||
/* CANCEL CLOSE */
|
||
box.addEventListener("mouseenter", () => {
|
||
if (closeTimeout) clearTimeout(closeTimeout);
|
||
box.classList.remove("closing");
|
||
});
|
||
|
||
/* CLEANUP CLASS */
|
||
text.addEventListener("transitionend", e => {
|
||
if (e.propertyName === "max-height" && box.classList.contains("closing")) {
|
||
box.classList.remove("closing");
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
|
||
/* =====================================================
|
||
SCROLL TO TOP
|
||
===================================================== */
|
||
|
||
function scrollToTop() {
|
||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||
}
|
||
|
||
|
||
/* =====================================================
|
||
MOBILE BURGER MENU
|
||
===================================================== */
|
||
|
||
function toggleBurger() {
|
||
document.querySelector(".nav").classList.toggle("open");
|
||
}
|
||
|
||
|
||
/* =====================================================
|
||
ACTIVE NAVIGATION SCROLL
|
||
===================================================== */
|
||
|
||
function initNavigation() {
|
||
|
||
const sections = document.querySelectorAll("section");
|
||
const navLinks = document.querySelectorAll(".nav-links a");
|
||
|
||
window.addEventListener("scroll", () => {
|
||
let current = "";
|
||
sections.forEach(section => {
|
||
const top = section.offsetTop - 120;
|
||
if (window.scrollY >= top) current = section.getAttribute("id");
|
||
});
|
||
navLinks.forEach(link => {
|
||
link.classList.remove("active");
|
||
if (link.getAttribute("href") === "#" + current) link.classList.add("active");
|
||
});
|
||
});
|
||
}
|
||
|
||
|
||
/* =====================================================
|
||
NEWS SLIDER
|
||
===================================================== */
|
||
|
||
function initNewsSlider() {
|
||
|
||
track = document.querySelector(".news-track");
|
||
const slider = document.querySelector(".news-slider");
|
||
const dotsContainer = document.querySelector(".news-dots");
|
||
const slides = document.querySelectorAll(".news-slide");
|
||
|
||
if (!track || !dotsContainer || slides.length === 0) return;
|
||
|
||
let autoSlide;
|
||
const INTERVAL = 8000;
|
||
|
||
/* CREATE DOTS */
|
||
dotsContainer.innerHTML = "";
|
||
dots = [];
|
||
|
||
slides.forEach((slide, i) => {
|
||
const dot = document.createElement("div");
|
||
dot.classList.add("news-dot");
|
||
if (i === 0) dot.classList.add("active");
|
||
dot.addEventListener("click", () => {
|
||
index = i;
|
||
updateSlider();
|
||
restartAutoSlide();
|
||
});
|
||
dotsContainer.appendChild(dot);
|
||
dots.push(dot);
|
||
});
|
||
|
||
/* UPDATE SLIDER */
|
||
function updateSlider() {
|
||
track.style.transform = `translateX(-${index * 100}%)`;
|
||
dots.forEach((dot, i) => dot.classList.toggle("active", i === index));
|
||
}
|
||
|
||
/* AUTO LOOP */
|
||
function startAutoSlide() {
|
||
autoSlide = setInterval(() => {
|
||
index = (index + 1) >= slides.length ? 0 : index + 1;
|
||
updateSlider();
|
||
}, INTERVAL);
|
||
}
|
||
|
||
function stopAutoSlide() { clearInterval(autoSlide); }
|
||
function restartAutoSlide() { stopAutoSlide(); startAutoSlide(); }
|
||
|
||
/* PAUSE ON HOVER */
|
||
const newsSection = document.querySelector("#news");
|
||
newsSection.addEventListener("mouseenter", stopAutoSlide);
|
||
newsSection.addEventListener("mouseleave", startAutoSlide);
|
||
|
||
/* TOUCH SUPPORT */
|
||
let startX = 0;
|
||
let isDragging = false;
|
||
|
||
track.addEventListener("touchstart", e => {
|
||
startX = e.touches[0].clientX;
|
||
isDragging = true;
|
||
stopAutoSlide();
|
||
});
|
||
|
||
track.addEventListener("touchmove", e => {
|
||
if (!isDragging) return;
|
||
const delta = e.touches[0].clientX - startX;
|
||
track.style.transition = "none";
|
||
track.style.transform = `translateX(calc(-${index * 100}% + ${delta}px))`;
|
||
});
|
||
|
||
track.addEventListener("touchend", e => {
|
||
isDragging = false;
|
||
track.style.transition = "transform 0.6s ease";
|
||
const delta = e.changedTouches[0].clientX - startX;
|
||
if (delta > 50 && index > 0) index--;
|
||
else if (delta < -50 && index < slides.length - 1) index++;
|
||
updateSlider();
|
||
restartAutoSlide();
|
||
});
|
||
|
||
updateSlider();
|
||
startAutoSlide();
|
||
}
|