document.addEventListener('DOMContentLoaded', () => { const revealElements = document.querySelectorAll('[data-reveal]'); const revealObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('revealed'); revealObserver.unobserve(entry.target); } }); }, { rootMargin: '0px 0px -150px 0px', threshold: 0.1 }); revealElements.forEach(el => revealObserver.observe(el)); const nav = document.querySelector('nav'); window.addEventListener('scroll', () => { if (window.scrollY > 50) { nav.style.padding = '0.75rem 0'; nav.style.background = 'rgba(15, 23, 42, 0.9)'; } else { nav.style.padding = '1rem 0'; nav.style.background = 'rgba(30, 41, 59, 0.7)'; } }); document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); const checkInvite = () => { const isJoinPage = window.location.pathname.includes('join'); const urlParams = new URLSearchParams(window.location.search); let devMode = urlParams.get('dev'); if (!devMode) { const hashClean = window.location.hash.startsWith('#') ? window.location.hash.substring(1) : window.location.hash; const hashParams = new URLSearchParams(hashClean); devMode = hashParams.get('dev'); } if (!devMode) { if (window.location.hash.includes('devsuccess') || window.location.search.includes('devsuccess')) devMode = 'success'; if (window.location.hash.includes('devfailure') || window.location.search.includes('devfailure')) devMode = 'failure'; } if (isJoinPage && devMode) { setTimeout(() => { const displayRoom = document.getElementById('display-room-id'); const actions = document.getElementById('join-actions'); if (displayRoom) displayRoom.textContent = 'DEV-ROOM'; if (actions) { actions.innerHTML = `
Simulating connection (DEV)...Verbindung wird simuliert (DEV)...

Simulating status event in 1.5 seconds.Status-Event wird in 1,5 Sekunden simuliert.

`; setTimeout(() => { window.dispatchEvent(new CustomEvent('KOALASYNC_STATUS', { detail: { success: devMode === 'success', message: devMode === 'failure' ? 'Simulated Connection Timeout!' : '' } })); }, 1500); } }, 600); return; } setTimeout(() => { const isInstalled = document.documentElement.dataset.koalasyncInstalled === 'true'; if (window.location.hash.startsWith('#join:')) { const parts = window.location.hash.split(':'); if (parts.length >= 3) { const roomId = parts[1]; const password = parts[2]; const serverFlag = parts[3] || '0'; const serverUrl = parts[4] ? decodeURIComponent(parts[4]) : ''; if (isJoinPage) { const displayRoom = document.getElementById('display-room-id'); const actions = document.getElementById('join-actions'); if (displayRoom) displayRoom.textContent = roomId; if (actions) { if (!isInstalled) { const isFirefox = navigator.userAgent.includes('Firefox'); if (isFirefox) { actions.innerHTML = `
Firefox GET IT ON MOZILLA ADD-ONSIM FIREFOX ADD-ON STORE HERUNTERLADEN Download via GitHubÜber GitHub herunterladen

The extension is required to join and sync videos. Die Erweiterung ist erforderlich, um beizutreten und Videos zu synchronisieren.

`; } else { actions.innerHTML = `
Chrome GET IT ON CHROME WEBSTOREIM CHROME WEB STORE HERUNTERLADEN Download via GitHubÜber GitHub herunterladen

The extension is required to join and sync videos. Die Erweiterung ist erforderlich, um beizutreten und Videos zu synchronisieren.

`; } } else { actions.innerHTML = `
Joining room automatically...Raum wird automatisch betreten...

Your extension is taking care of it.Deine Erweiterung kümmert sich darum.

`; setTimeout(() => { window.dispatchEvent(new CustomEvent('KOALASYNC_JOIN_REQUEST', { detail: { roomId, password, useCustomServer: serverFlag === '1', serverUrl: serverUrl } })); }, 500); } } } else { if (!document.getElementById('koala-banner')) { const banner = document.createElement('div'); banner.className = 'invite-banner'; banner.id = 'koala-banner'; const container = document.createElement('div'); container.className = 'container'; container.style.cssText = 'display:flex; justify-content:space-between; align-items:center;'; const inviteSpan = document.createElement('span'); inviteSpan.appendChild(document.createTextNode('🎫 Invitation for ')); const boldRoom = document.createElement('b'); boldRoom.textContent = roomId; inviteSpan.appendChild(boldRoom); inviteSpan.appendChild(document.createTextNode(' detected!')); const joinLink = document.createElement('a'); joinLink.href = 'join' + window.location.hash; joinLink.className = 'btn-banner'; joinLink.textContent = 'OPEN JOIN PAGE'; container.appendChild(inviteSpan); container.appendChild(joinLink); banner.appendChild(container); document.body.prepend(banner); } } document.addEventListener('click', (e) => { if (e.target && e.target.id === 'webJoinBtn') { e.target.textContent = 'JOINING...'; e.target.disabled = true; window.dispatchEvent(new CustomEvent('KOALASYNC_JOIN_REQUEST', { detail: { roomId, password, useCustomServer: serverFlag === '1', serverUrl: serverUrl } })); } }); } } }, 600); }; window.addEventListener('KOALASYNC_STATUS', (e) => { const { success, message } = e.detail; const isJoinPage = window.location.pathname.includes('join'); if (isJoinPage) { const icon = document.getElementById('join-status-icon'); const title = document.getElementById('join-title'); const actions = document.getElementById('join-actions'); const desc = document.getElementById('join-desc'); const ring = document.getElementById('status-ring'); if (success) { if (ring) { ring.classList.remove('active-pulse'); ring.style.display = 'none'; } if (icon) { icon.innerHTML = 'Success'; icon.style.transform = 'scale(1)'; } const isDE = document.documentElement.classList.contains('lang-de'); title.textContent = isDE ? 'Erfolgreich!' : 'Success!'; desc.innerHTML = isDE ? 'Verbunden!
Wähle jetzt einen Video-Tab in der Erweiterung aus.' : 'Connected!
Now select a video tab in the extension.'; let count = 3; const updateCountdown = () => { if (count <= 0) { window.close(); desc.textContent = isDE ? 'Beitritt erfolgreich! Du kannst diesen Tab jetzt manuell schließen.' : 'Joined successfully! You can close this tab manually.'; } else { count--; setTimeout(updateCountdown, 1000); } }; setTimeout(updateCountdown, 1000); const closeLabel = isDE ? 'TAB JETZT SCHLIESSEN' : 'CLOSE TAB NOW'; actions.innerHTML = `
`; } else { if (ring) { ring.classList.remove('active-pulse'); ring.style.display = 'none'; } if (icon) { icon.innerHTML = 'Error'; icon.style.transform = 'scale(1)'; } const isDE = document.documentElement.classList.contains('lang-de'); title.textContent = isDE ? 'Fehler' : 'Error'; desc.textContent = isDE ? `Beitritt fehlgeschlagen: ${message}` : `Join failed: ${message}`; const retryLabel = isDE ? 'ERNEUT VERSUCHEN' : 'TRY AGAIN'; actions.innerHTML = `
`; } } else { const banner = document.getElementById('koala-banner'); if (banner) { if (success) { banner.style.background = 'var(--success)'; banner.innerHTML = '
✅ Joined! This tab will close in 2s...
'; setTimeout(() => window.close(), 2000); } else { banner.style.background = 'var(--error)'; banner.innerHTML = ''; const errDiv = document.createElement('div'); errDiv.className = 'container'; errDiv.textContent = '❌ Error: ' + message; banner.appendChild(errDiv); } } } }); const updateDynamicVersion = async () => { try { const versionPath = document.documentElement.lang === 'de' ? '../version.json' : 'version.json'; const response = await fetch(versionPath); if (!response.ok) return; const data = await response.json(); const { version, date } = data; if (!version || !date) return; const releaseDate = new Date(date); const now = new Date(); const diffMs = now - releaseDate; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); const diffMins = Math.floor(diffMs / (1000 * 60)); let relativeTimeEn = ''; let relativeTimeDe = ''; if (diffDays > 0) { relativeTimeEn = `${diffDays} ${diffDays === 1 ? 'day' : 'days'} ago`; relativeTimeDe = `vor ${diffDays} ${diffDays === 1 ? 'Tag' : 'Tagen'}`; } else if (diffHours > 0) { relativeTimeEn = `${diffHours} ${diffHours === 1 ? 'hour' : 'hours'} ago`; relativeTimeDe = `vor ${diffHours} ${diffHours === 1 ? 'Stunde' : 'Stunden'}`; } else if (diffMins > 0) { relativeTimeEn = `${diffMins} ${diffMins === 1 ? 'minute' : 'minutes'} ago`; relativeTimeDe = `vor ${diffMins} ${diffMins === 1 ? 'Minute' : 'Minuten'}`; } else { relativeTimeEn = 'just now'; relativeTimeDe = 'gerade eben'; } const badgeEn = document.querySelector('.version-text-en'); const badgeDe = document.querySelector('.version-text-de'); if (badgeEn) { badgeEn.textContent = `v${version} OUT NOW • ${relativeTimeEn}`; } if (badgeDe) { badgeDe.textContent = `v${version} JETZT VERFÜGBAR • ${relativeTimeDe}`; } const schemaScript = document.getElementById('schema-software'); if (schemaScript) { try { const schema = JSON.parse(schemaScript.textContent); schema.softwareVersion = version; schemaScript.textContent = JSON.stringify(schema, null, 2); } catch (err) { console.warn('Failed to dynamically update schema version:', err); } } } catch (e) { console.warn('Failed to fetch dynamic version info:', e); } }; const mockTabs = document.querySelectorAll('.mock-tab'); const mockScreens = document.querySelectorAll('.mock-screen'); mockTabs.forEach(tab => { tab.addEventListener('click', () => { mockTabs.forEach(t => t.classList.remove('active')); mockScreens.forEach(s => s.classList.remove('active')); tab.classList.add('active'); const targetId = tab.getAttribute('data-target'); const targetScreen = document.getElementById(targetId); if (targetScreen) { targetScreen.classList.add('active'); } }); }); const termTabBtns = document.querySelectorAll('.terminal-tab-btn'); const termPanes = document.querySelectorAll('.terminal-pane'); termTabBtns.forEach(btn => { btn.addEventListener('click', () => { termTabBtns.forEach(b => b.classList.remove('active')); termPanes.forEach(p => p.classList.remove('active')); btn.classList.add('active'); const targetPaneId = btn.getAttribute('data-tab'); const targetPane = document.getElementById(targetPaneId); if (targetPane) { targetPane.classList.add('active'); } }); }); const copyBtn = document.querySelector('.terminal-copy-btn'); if (copyBtn) { copyBtn.addEventListener('click', () => { const activePane = document.querySelector('.terminal-pane.active'); if (!activePane) return; const codeElement = activePane.querySelector('code'); if (!codeElement) return; const textToCopy = codeElement.innerText || codeElement.textContent; navigator.clipboard.writeText(textToCopy).then(() => { const isDE = document.documentElement.classList.contains('lang-de'); const originalHTML = copyBtn.innerHTML; copyBtn.innerHTML = isDE ? '✅ Kopiert!' : '✅ Copied!'; copyBtn.disabled = true; setTimeout(() => { copyBtn.innerHTML = originalHTML; copyBtn.disabled = false; }, 2000); }).catch(err => { console.error('Failed to copy text: ', err); }); }); } const hamburger = document.querySelector('.hamburger'); const navLinks = document.querySelector('.nav-links'); if (hamburger && navLinks) { hamburger.setAttribute('aria-expanded', 'false'); hamburger.addEventListener('click', () => { const isOpen = navLinks.classList.toggle('open'); hamburger.setAttribute('aria-expanded', isOpen ? 'true' : 'false'); }); } const localizeHomeLinks = () => { const activeLang = localStorage.getItem('koala_lang') || (navigator.language.startsWith('de') ? 'de' : 'en'); const path = window.location.pathname; const pathSegments = path.split('/'); const isSubdir = pathSegments.some(seg => ['de', 'fr', 'es', 'pt-BR', 'ru'].includes(seg)); if (!isSubdir) { const homeLinks = document.querySelectorAll('a[href="./"], a[href="de/"], a[href="fr/"], a[href="es/"], a[href="pt-BR/"], a[href="ru/"]'); homeLinks.forEach(link => { link.href = (activeLang === 'en') ? './' : `${activeLang}/`; }); } }; const handleLanguageChange = (e) => { const select = e.currentTarget; const newLang = select.value; const path = window.location.pathname; localStorage.setItem('koala_lang', newLang); const isLegalOrJoin = path.includes('impressum') || path.includes('datenschutz') || path.includes('join'); const isIndex = !isLegalOrJoin; if (isIndex) { const pathSegments = path.split('/'); const isSubdir = pathSegments.some(seg => ['de', 'fr', 'es', 'pt-BR', 'ru'].includes(seg)); let targetPath; if (newLang === 'en') { if (isSubdir) { targetPath = '../'; } else { targetPath = './'; } } else { if (isSubdir) { targetPath = '../' + newLang + '/'; } else { targetPath = newLang + '/'; } } window.location.href = targetPath; } else { const html = document.documentElement; html.classList.remove('lang-en', 'lang-de', 'lang-fr', 'lang-es', 'lang-pt-br', 'lang-ru'); const activeDisplayLang = (newLang === 'de') ? 'de' : 'en'; html.classList.add('lang-' + activeDisplayLang); html.lang = activeDisplayLang; document.querySelectorAll('.lang-dropdown').forEach(sel => { sel.value = newLang; }); const isJoin = path.includes('join'); if (isJoin) { const titles = { en: 'Join Room | KoalaSync', de: 'Raum beitreten | KoalaSync' }; document.title = titles[activeDisplayLang] || titles.en; } localizeHomeLinks(); } }; document.querySelectorAll('.lang-dropdown').forEach(select => { select.addEventListener('change', handleLanguageChange); }); const initLanguageSelectorValue = () => { const savedLang = localStorage.getItem('koala_lang'); const browserLang = navigator.language.startsWith('de') ? 'de' : 'en'; const activePref = savedLang || browserLang; document.querySelectorAll('.lang-dropdown').forEach(select => { select.value = activePref; }); }; document.querySelectorAll('.email-reveal').forEach(el => { el.addEventListener('click', function() { const user = this.getAttribute('data-user'); const domain = this.getAttribute('data-domain'); if (user && domain) { this.innerHTML = `${user}@${domain}`; } }); }); const detectBrowserAndElevateBadge = () => { const isFirefox = navigator.userAgent.includes('Firefox'); const isChrome = navigator.userAgent.includes('Chrome') || navigator.userAgent.includes('Chromium'); const chromeBtns = document.querySelectorAll('.btn-primary'); const firefoxBtns = document.querySelectorAll('.btn-firefox'); if (isFirefox && chromeBtns.length > 0 && firefoxBtns.length > 0) { chromeBtns.forEach(btn => { btn.classList.remove('btn-primary'); btn.classList.add('btn-secondary'); }); firefoxBtns.forEach(btn => { btn.style.order = '-1'; btn.style.transform = 'scale(1.05)'; btn.addEventListener('mouseleave', () => { btn.style.transform = 'scale(1)'; }); btn.addEventListener('mouseenter', () => { btn.style.transform = 'scale(1.05) translateY(-2px)'; }); }); } else if (isChrome && chromeBtns.length > 0 && firefoxBtns.length > 0) { firefoxBtns.forEach(btn => { btn.classList.remove('btn-firefox'); btn.classList.add('btn-secondary'); btn.style.color = 'var(--text)'; btn.style.background = 'var(--card)'; btn.style.border = '1px solid var(--glass-border)'; btn.style.boxShadow = 'none'; }); } setTimeout(() => { const isInstalled = document.documentElement.dataset.koalasyncInstalled === 'true'; const navBadge = document.getElementById('nav-extension-status'); if (isInstalled && navBadge) { navBadge.style.display = 'inline-flex'; } const illusChrome = document.querySelectorAll('.illus-store-btn.chrome'); const illusFirefox = document.querySelectorAll('.illus-store-btn.firefox'); if (isFirefox && illusFirefox.length > 0) { illusFirefox.forEach(btn => { btn.style.order = '-1'; if (!isInstalled) { btn.classList.add('install-breathe'); btn.style.cursor = 'pointer'; btn.onclick = () => window.open('https://addons.mozilla.org/de/firefox/addon/koalasync/', '_blank'); } }); illusChrome.forEach(btn => { btn.style.opacity = '0.5'; btn.style.transform = 'scale(0.95)'; }); } else if (isChrome && illusChrome.length > 0) { illusChrome.forEach(btn => { btn.style.order = '-1'; if (!isInstalled) { btn.classList.add('install-breathe'); btn.style.cursor = 'pointer'; btn.onclick = () => window.open('https://chromewebstore.google.com/detail/koalasync/obbnmkmlaaddodakcbdljknjpagklifc', '_blank'); } }); illusFirefox.forEach(btn => { btn.style.opacity = '0.5'; btn.style.transform = 'scale(0.95)'; }); } if (!isInstalled) { const heroBtns = document.querySelectorAll(isFirefox ? '.btn-firefox' : (isChrome ? '.btn-primary' : null)); if (heroBtns && heroBtns.length > 0) { heroBtns.forEach(btn => { const isFF = btn.classList.contains('btn-firefox'); const glowColor = isFF ? 'rgba(249, 115, 22, ' : 'rgba(99, 102, 241, '; btn.animate([ { transform: 'scale(1)', boxShadow: `0 0 15px ${glowColor}0.2)` }, { transform: 'scale(1.05)', boxShadow: `0 0 25px ${glowColor}0.5)` }, { transform: 'scale(1)', boxShadow: `0 0 15px ${glowColor}0.2)` } ], { duration: 2500, iterations: Infinity, easing: 'ease-in-out' }); }); } } }, 600); }; detectBrowserAndElevateBadge(); checkInvite(); updateDynamicVersion(); localizeHomeLinks(); initLanguageSelectorValue(); });