luanti-web/site_generator/templates/world_detail_page.template
2025-06-21 15:58:52 +02:00

173 lines
10 KiB
Text
Executable file

<div class='page-title-container'>
<h2 class='world-detail-title'>%%WORLD_DISPLAY_NAME_PAGE%%</h2>
</div>
<div class="close-button-container">
<a href="worlds.html" class="close-button" title="Zurück zur Weltenübersicht"><b>X</b></a>
</div>
<div class="responsive-nav">
<button id="burger-menu-toggle" class="burger-menu" aria-label="Menü öffnen/schließen"><span></span><span></span><span></span></button>
<div class='page-nav-buttons' id="page-nav-buttons-container-%%current_world_key%%">
<a href='#server-info' class='button'>Server-Info</a>
<a href='#server-verbindung' class='button'>Verbindung</a>
<a href='#welt-admin' class='button'>Admin</a>
<a href='#beschreibung' class='button'>Beschreibung</a>
<a href='#spielregeln' class='button'>Spielregeln</a>
<a href='#weltradar' class='button'>Radar</a>
<a href='#mods' class='button'>Mods</a>
<a href='#spielerliste' class='button'>Spielerliste</a>
</div>
</div>
<div id='server-info' class='info-box server-details'>
<h3>Server-Info</h3>
<p><strong>Status:</strong> <span class='status-dot' id='status-dot-%%current_world_key%%'></span><span id='world-status-%%current_world_key%%' class='status-text status-loading'>%%STATUS_TEXT_FALLBACK_PAGE%%</span></p>
<p><strong>Spiel:</strong> <a href='https://content.luanti.org/packages/?type=game&q=%%MT_GAMEID%%' target='_blank' rel='noopener noreferrer'>%%MT_GAMEID%%</a></p>
<p><strong>Kreativmodus:</strong> <span class='status-text-colored %%creative_text_class%%'>%%creative_text%%</span></p>
<p><strong>Schaden:</strong> <span class='status-text-colored %%damage_text_class%%'>%%damage_text%%</span></p>
<div id="weather-info-%%current_world_key%%"></div>
</div>
<div id='server-verbindung' class='info-box server-details'>
<h3>Server-Verbindung</h3>
<p><strong>Adresse:</strong> <span id='addr-%%current_world_key%%'>%%SERVER_ADDRESS_PAGE%%</span><button title='Adresse kopieren' class='copy-button' onclick='copyToClipboard("addr-%%current_world_key%%")'>📋</button></p>
<p><strong>Port:</strong> <span id='port-%%current_world_key%%'>%%SERVER_PORT_PAGE%%</span><button title='Port kopieren' class='copy-button' onclick='copyToClipboard("port-%%current_world_key%%")'>📋</button></p>
<p><strong>Passwort:</strong> <span id='pass-%%current_world_key%%'>%%SERVER_ACCESS_INFO_PAGE%%</span><button title='Info kopieren' class='copy-button' onclick='copyToClipboard("pass-%%current_world_key%%")'>📋</button></p>
</div>
<div id='welt-admin' class='admin-section'>
<h3>Welt-Admin</h3>
<div class='admin-grid'>%%ADMIN_BOXES_HTML%%</div>
</div>
<div id="description-text-wrapper">
<h3 id='beschreibung'>Beschreibung</h3>
<div id="description-text" class="collapsible-text">%%WORLD_LONG_DESCRIPTION_PAGE%%</div>
<div class="read-more-container"><a href="#" class="read-more-link" data-target="description-text">weiterlesen</a></div>
</div>
<div id="gamerules-section">
<h3 id='spielregeln'>Spielregeln</h3>
<div id="gamerules-text-wrapper">
<div id="gamerules-text" class="collapsible-text">%%WORLD_GAME_RULES_PAGE%%</div>
<div class="read-more-container"><a href="#" class="read-more-link" data-target="gamerules-text">weiterlesen</a></div>
</div>
</div>
%%WORLD_RADAR_HTML%%
<h3 id='mods'>Verwendete Mods</h3>
<div class='scrollable-mod-list'>%%MODS_HTML%%</div>
%%WORLD_PLAYERLIST_HTML%%
<script>
// === Globale Hilfsfunktionen & Datenabruf ===
function copyToClipboard(elementId) { const el = document.getElementById(elementId); if(el) { navigator.clipboard.writeText(el.innerText || el.textContent).then(() => { const btn = el.nextElementSibling; if(btn && btn.classList.contains('copy-button')) { const orig_btn_text = btn.innerHTML; btn.innerHTML = '✓'; setTimeout(() => { btn.innerHTML = orig_btn_text; }, 1500); } }).catch(err => console.error('Fehler Kopieren: ', err));}}
function formatTimestampForDisplay(epochSeconds) { if (!epochSeconds || epochSeconds == 0) return 'unbekannt'; const date = new Date(epochSeconds * 1000); return date.toLocaleString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit'}) + ' Uhr';}
function fetchWorldStatus_%%current_world_key%%() {
const statusTextEl = document.getElementById('world-status-%%current_world_key%%');
const statusDotEl = document.getElementById('status-dot-%%current_world_key%%');
if (!statusTextEl || !statusDotEl) return;
const onlineStatusUrl = '/%%web_online_status_rel_path%%?v=%%CACHE_BUSTER%%&t=' + new Date().getTime();
const lastUpdateUrl = '/%%web_last_update_rel_path%%?v=%%CACHE_BUSTER%%&t=' + new Date().getTime();
Promise.all([fetch(onlineStatusUrl).then(res => res.text()), fetch(lastUpdateUrl).then(res => res.text())])
.then(([onlineStatusContent, lastUpdateContent]) => {
const dateObject = new Date(lastUpdateContent.trim());
const lastUpdateEpoch = !isNaN(dateObject.getTime()) ? Math.floor(dateObject.getTime() / 1000) : NaN;
const nowInSeconds = Math.floor(Date.now() / 1000);
if (!isNaN(lastUpdateEpoch) && (nowInSeconds - lastUpdateEpoch > 900)) {
statusDotEl.className = 'status-dot unknown';
statusTextEl.className = 'status-text status-unknown';
statusTextEl.textContent = 'Unbekannt (seit: ' + formatTimestampForDisplay(lastUpdateEpoch) + ')';
return;
}
const onlineStatusParts = onlineStatusContent.split(' - ');
const currentStatus = onlineStatusParts[0].trim();
if (currentStatus === 'online') {
statusDotEl.className = 'status-dot online';
statusTextEl.className = 'status-text status-online';
statusTextEl.textContent = 'Online';
} else {
const offlineSince = onlineStatusParts.length > 1 ? onlineStatusParts.slice(1).join(' - ').trim() : 'unbekannt';
statusDotEl.className = 'status-dot offline';
statusTextEl.className = 'status-text status-offline';
statusTextEl.textContent = 'Offline (seit: ' + offlineSince + ')';
}
}).catch(error => {
statusDotEl.className = 'status-dot offline';
statusTextEl.className = 'status-text status-offline';
statusTextEl.textContent = 'Status nicht abrufbar';
});
}
function fetchDataForElement(elementId, filePath, isRawText, prefixText, suffixText, isJsonPlayerList) {
const el = document.getElementById(elementId);
if (!el && !isJsonPlayerList) return;
fetch('/' + filePath + '?v=%%CACHE_BUSTER%%&t=' + new Date().getTime())
.then(r => { if (!r.ok) throw new Error('Datei ' + filePath + ' nicht erreichbar (' + r.status + ')'); return r.text(); })
.then(t => {
let content = "";
if (t.trim() === "") {
content = (elementId.startsWith("map-last-update") ? "<em>unbekannt</em>" : "<em>Keine Daten verfügbar.</em>");
if (isJsonPlayerList && window.playerListLogic_%%current_world_key%%) {
window.playerListLogic_%%current_world_key%%.masterPlayerData = {};
window.playerListLogic_%%current_world_key%%.updatePlayerMarkers({});
window.playerListLogic_%%current_world_key%%.applyPlayerFiltersAndRender();
}
} else {
if (isJsonPlayerList && window.playerListLogic_%%current_world_key%%) {
try {
const playerData = JSON.parse(t);
const logic = window.playerListLogic_%%current_world_key%%;
logic.masterPlayerData = playerData;
logic.updatePlayerMarkers(playerData);
logic.applyPlayerFiltersAndRender();
} catch (e) { console.error("Fehler beim Parsen der Spielerdaten:", e); }
return;
} else { content = isRawText ? t.replace(/\n/g, '<br>') : t; }
}
if(el) { el.innerHTML = (prefixText || '') + content + (suffixText || ''); }
}).catch(e => {
if(el) { el.innerHTML = (prefixText || '') + '<em>nicht abrufbar</em>' + (suffixText || ''); }
});
}
document.addEventListener('DOMContentLoaded', function() {
// Burger-Menü Logik
const burgerToggle = document.getElementById('burger-menu-toggle');
const navContainer = document.getElementById('page-nav-buttons-container-%%current_world_key%%');
if (burgerToggle && navContainer) {
burgerToggle.addEventListener('click', () => navContainer.classList.toggle('menu-open'));
navContainer.addEventListener('click', e => { if (e.target.classList.contains('button')) { navContainer.classList.remove('menu-open'); }});
}
// "Weiterlesen"-Logik
document.querySelectorAll('.read-more-link').forEach(link => {
const targetElement = document.getElementById(link.dataset.target);
if (targetElement) {
if (targetElement.scrollHeight <= targetElement.clientHeight) {
link.parentElement.style.display = 'none';
} else {
link.addEventListener('click', function(e) {
e.preventDefault();
targetElement.classList.add('expanded');
link.parentElement.style.display = 'none';
});
}
}
});
// Initiale Daten-Ladeaufrufe
fetchWorldStatus_%%current_world_key%%();
fetchDataForElement('player-info-%%current_world_key%%', '%%web_players_txt_rel_path%%', false, '', '', true);
fetchDataForElement('weather-info-%%current_world_key%%', '%%web_weather_txt_rel_path%%', true, '<p><strong>Wetter:</strong> ', '</p>');
fetchDataForElement('map-last-update-text-%%current_world_key%%', '%%web_last_update_rel_path%%', true, '', '');
});
// Periodische Updates
setInterval(fetchWorldStatus_%%current_world_key%%, 60000);
setInterval(() => fetchDataForElement('map-last-update-text-%%current_world_key%%', '%%web_last_update_rel_path%%', true, '', ''), 60000);
setInterval(() => fetchDataForElement('player-info-%%current_world_key%%', '%%web_players_txt_rel_path%%', false, '', '', true), 70000);
setInterval(() => fetchDataForElement('weather-info-%%current_world_key%%', '%%web_weather_txt_rel_path%%', true, '<p><strong>Wetter:</strong> ', '</p>'), 300000);
</script>