Files
godot-website/_includes/header.html
2025-10-15 20:09:08 +02:00

338 lines
12 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<input type="checkbox" id="nav_toggle_cb">
<header class="flex column">
<div class="container flex align-center">
<div id="nav_head">
<a href="/" id="logo-link">
<img class="nav-logo" src="/assets/logo.svg" width="136" height="48" alt="Godot Engine">
<img class="nav-logo dark-logo" src="/assets/logo_dark.svg" width="136" height="48" alt="Godot Engine">
</a>
<div class="mobile-links">
<span class="fund mobile"><a href="https://fund.godotengine.org"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="width: 13px; fill: white; margin-right: 4px;"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M47.6 300.4L228.3 469.1c7.5 7 17.4 10.9 27.7 10.9s20.2-3.9 27.7-10.9L464.4 300.4c30.4-28.3 47.6-68 47.6-109.5v-5.8c0-69.9-50.5-129.5-119.4-141C347 36.5 300.6 51.4 268 84L256 96 244 84c-32.6-32.6-79-47.5-124.6-39.9C50.5 55.6 0 115.2 0 185.1v5.8c0 41.5 17.2 81.2 47.6 109.5z"/></svg> Donate</a></span>
<label for="nav_toggle_cb" id="nav_toggle_btn">
<img src="/assets/icons/hamburger.svg" width="24" height="24" alt="Main menu">
</label>
</div>
</div>
<nav id="nav">
<ul class="left">
<!-- On hover, opens a dropdown containing the link to console support page -->
<li><a href="/features/" data-dropdown="features-dropdown">{% t header.features %}</a></li>
<li><a href="/showcase/">{% t header.showcase %}</a></li>
<li><a href="/blog/">{% t header.blog %}</a></li>
<li><a href="/community/" data-dropdown="community-dropdown">{% t header.community %}</a></li>
<li><a href="https://godotengine.org/asset-library/asset">{% t header.assets %}</a></li>
<!-- Show console support link in the mobile hamburger menu -->
<li class="mobile-only"><a href="/consoles/">{% t header.console_support %}</a></li>
</ul>
<ul class="right">
<li><a href="/download/windows/" class="set-os-download-url">{% t header.download %}</a></li>
<li><a href="https://docs.godotengine.org">{% t header.docs %}</a></li>
<li><a href="https://contributing.godotengine.org/en/latest/organization/how_to_contribute.html">{% t header.contribute %}</a></li>
{% if page.localize %}
<li class="language-selector" onclick="this.classList.toggle('open')">
<a class="mobile-language-selector" href="#">{% t header.language %}: </a>
<div class="language-dropdown">
{% for lang in page.localize %}
<div class="language-option"><a href="#" data-lang-path="{% if lang == 'en' %}/{% else %}/{{ lang }}/{% endif %}" data-lang="{{ lang }}" onclick="onSetLanguagePreference(event, this)"><span class="localize-language-label">{{ lang }}</span></a></div>
{% endfor %}
</div>
</li>
{% endif %}
<li class="fund desktop"><a href="https://fund.godotengine.org"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="width: 13px; fill: white; margin-right: 4px; top: 1px;
position: relative;"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M47.6 300.4L228.3 469.1c7.5 7 17.4 10.9 27.7 10.9s20.2-3.9 27.7-10.9L464.4 300.4c30.4-28.3 47.6-68 47.6-109.5v-5.8c0-69.9-50.5-129.5-119.4-141C347 36.5 300.6 51.4 268 84L256 96 244 84c-32.6-32.6-79-47.5-124.6-39.9C50.5 55.6 0 115.2 0 185.1v5.8c0 41.5 17.2 81.2 47.6 109.5z"/></svg> {% t header.donate %}</a></li>
</ul>
</nav>
</div>
{% comment %}
{% if page.path == "pages/home.html" %}
<div class="banner-container" style="background-color: #fb99ff;">
<div class="container">
<div class="banner">
<div class="banner-text">
Godot 4.5 just released!
</div>
<div class="banner-text">
Check out the <a href="/releases/4.5/">release page!</a>
</div>
</div>
</div>
</div>
{% endif %}
{% endcomment %}
</header>
<!-- Dropdown menu positioned outside header to avoid backdrop-filter nesting issues -->
<div class="nav-dropdown-menu" id="features-dropdown">
<!-- The features link is only visible on high-resolution tablets -->
<!-- On desktop we show the dropdown on hover, and the user can navigate to the features page by clicking the link in main menu-->
<!-- And on small screens (width < 1200px) we switch to mobile hamburger menu -->
<!-- So we need this only to cover the edge case of high-resolution tablets -->
<a href="/features/" class="touch-only">{% t header.features %}</a>
<a href="/consoles/">{% t header.console_support %}</a>
</div>
<div class="nav-dropdown-menu" id="community-dropdown">
<a href="/community/" class="touch-only">{% t header.community %}</a>
<a href="https://forum.godotengine.org">{% t header.forum %}</a>
<a href="/events/">{% t header.events %}</a>
</div>
{% if page.localize %}
<script type="module">
let _languageSelector = null;
function isLanguageMatch(langA, langB, lax = false) {
const langALower = (langA ?? "").toLowerCase();
const langBLower = (langB ?? "").toLowerCase();
if (langALower === langBLower) {
return true;
}
if (!lax) {
return false;
}
return langALower.slice(0, 2) === langBLower.slice(0, 2);
}
function redirectTo(path) {
if (path == null) {
return;
}
window.location.href = path;
}
function getLanguageSelector() {
if (_languageSelector != null) {
return _languageSelector;
}
_languageSelector = document.querySelector(".language-selector");
if (_languageSelector == null) {
throw new Error("Could not find `.language-selector`");
}
return _languageSelector;
}
function setPreferredLanguage(lang) {
window.localStorage.setItem("preferred_language", lang);
}
function getPreferredLanguage() {
return window.localStorage.getItem("preferred_language");
}
function setLanguagePreference(path, lang) {
setPreferredLanguage(lang);
redirectTo(path);
}
function getLanguagePath(lang) {
const languageSelector = getLanguageSelector();
const languageAnchors = Array.from(
languageSelector.querySelectorAll(".language-option > a[data-lang]"),
);
for (const languageAnchor of languageAnchors) {
if (isLanguageMatch(lang, languageAnchor.dataset.lang)) {
return languageAnchor.dataset.langPath;
}
}
return null;
}
function getBrowserPreferredLanguage() {
const languageSelector = getLanguageSelector();
const languageAnchors = Array.from(
languageSelector.querySelectorAll(".language-option > a[data-lang]"),
);
const languages = languageAnchors.map(
(languageAnchor) => languageAnchor.dataset.lang,
);
const matchLanguage = (currentLanguage) => {
for (const language of languages) {
if (isLanguageMatch(navigator.language, language)) {
return language;
}
}
for (const language of languages) {
if (isLanguageMatch(navigator.language, language, true)) {
return language;
}
}
return null;
};
let matchedLanguage = null;
if (navigator.language != null) {
matchedLanguage = matchLanguage(navigator.language);
if (matchedLanguage != null) {
return matchedLanguage;
}
}
for (const navigatorLanguage of navigator.languages ?? []) {
matchedLanguage = matchLanguage(navigatorLanguage);
if (matchedLanguage != null) {
return matchedLanguage;
}
}
return matchedLanguage;
}
function main() {
document.addEventListener("click", function (event) {
const languageSelector = getLanguageSelector();
// Check if the click is outside the language selector
if (!languageSelector.contains(event.target)) {
languageSelector.classList.remove("open");
}
});
const currentLanguage = document.documentElement.lang;
const preferredLanguage = getPreferredLanguage();
if (preferredLanguage == null) {
const browserPreferredLanguage = getBrowserPreferredLanguage();
if (browserPreferredLanguage == null) {
setPreferredLanguage("en");
const languagePath = getLanguagePath("en");
if (!window.location.href.startsWith(languagePath)) {
redirectTo(languagePath);
}
} else {
setPreferredLanguage(browserPreferredLanguage);
const languagePath = getLanguagePath(browserPreferredLanguage);
if (!window.location.href.startsWith(languagePath)) {
redirectTo(languagePath);
}
}
} else if (preferredLanguage !== currentLanguage && currentLanguage === "en") {
const languagePath = getLanguagePath(preferredLanguage);
if (!window.location.href.startsWith(languagePath)) {
redirectTo(languagePath);
}
} else {
setPreferredLanguage(currentLanguage);
}
// Language select event listener.
window.onSetLanguagePreference = (event, element) => {
event.preventDefault();
const path = element.dataset.langPath;
const lang = element.dataset.lang;
setLanguagePreference(path, lang);
};
}
main();
</script>
{% endif %}
<script>
// Open dropdown on hover (desktop) or tap (touch devices)
document.addEventListener('DOMContentLoaded', function() {
// Find all dropdown triggers
const dropdownTriggers = document.querySelectorAll('[data-dropdown]');
dropdownTriggers.forEach(trigger => {
const dropdownId = trigger.getAttribute('data-dropdown');
const dropdownMenu = document.getElementById(dropdownId);
if (dropdownMenu) {
let hideTimeout = -1;
const clearHideTimeout = () => {
if (hideTimeout === -1) {
return;
}
clearTimeout(hideTimeout);
hideTimeout = -1;
};
const isDropdownVisible = () => {
return dropdownMenu.style.display === 'block';
};
const showDropdown = () => {
if (isDropdownVisible()) {
return;
}
// Don't show dropdown on small screens (width < 1200px)
if (window.innerWidth < 1200) {
return;
}
clearHideTimeout();
const rect = trigger.getBoundingClientRect();
dropdownMenu.style.top = (rect.bottom) + 'px';
dropdownMenu.style.left = (rect.left) + 'px';
dropdownMenu.style.display = 'block';
trigger.classList.add('dropdown-open');
};
// Hide dropdown after a delay
const hideDropdown = ({ instant = false } = {}) => {
if (!isDropdownVisible()) {
return;
}
const hideDropdownTrigger = () => {
clearHideTimeout();
dropdownMenu.style.display = 'none';
trigger.classList.remove('dropdown-open');
};
if (instant) {
hideDropdownTrigger();
return;
}
hideTimeout = setTimeout(hideDropdownTrigger, 100);
}
// Toggle dropdown on high-resolution tablets
const toggleDropdown = (event) => {
if (window.innerWidth < 1200) {
return;
}
event.preventDefault();
if (isDropdownVisible()) {
hideDropdown({ instant: true })
} else {
showDropdown();
}
}
const onlyOnTouch = (callback) => (event) => {
if (event.pointerType === "touch") {
callback(event);
}
};
const notOnTouch = (callback) => (event) => {
if (event.pointerType !== "touch") {
callback(event);
}
}
// Touch device: use click/tap to toggle dropdown
trigger.addEventListener('pointerup', onlyOnTouch((event) => toggleDropdown(event)));
// Close dropdown when clicking outside (for touch devices)
document.documentElement.addEventListener('pointerup', onlyOnTouch((event) => {
if (!trigger.contains(event.target) && !dropdownMenu.contains(event.target)) {
hideDropdown({ instant: true });
}
}));
// Desktop: use hover
trigger.addEventListener('pointerenter', notOnTouch((_event) => showDropdown()));
trigger.addEventListener('pointerleave', notOnTouch((_event) => hideDropdown()));
// Keep dropdown visible when hovering over it
dropdownMenu.addEventListener('pointerenter', notOnTouch((_event) => clearHideTimeout()));
dropdownMenu.addEventListener('pointerleave', notOnTouch((_event) => hideDropdown()));
}
});
});
</script>
<main>