Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04a73259ba | ||
|
|
9200aac276 | ||
|
|
2214845361 | ||
|
|
de8ea49ee7 | ||
|
|
5b128f9d50 | ||
|
|
ad8cb2b819 | ||
|
|
f86e29ac9a | ||
|
|
77367e86a1 | ||
|
|
0d06c9ab24 | ||
|
|
f3f47aee4d |
No files matched your search
@@ -0,0 +1,7 @@
|
||||
DirectoryIndex launch.html
|
||||
|
||||
# Hide repository meta files behind 404
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteRule ^(README\.md|LICENSE|AGENTS\.md)$ - [NC,R=404,L]
|
||||
</IfModule>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 600">
|
||||
<rect width="900" height="600" fill="#EE1C25"/>
|
||||
<path id="star" d="M0,-1 L0.225,-0.309 L0.951,-0.309 L0.363,0.118 L0.588,0.809 L0,0.382 L-0.588,0.809 L-0.363,0.118 L-0.951,-0.309 L-0.225,-0.309 Z" fill="#FFDE00"/>
|
||||
<use href="#star" transform="translate(150,150) scale(90)"/>
|
||||
<use href="#star" transform="translate(300,60) scale(30)"/>
|
||||
<use href="#star" transform="translate(360,120) scale(30)"/>
|
||||
<use href="#star" transform="translate(360,210) scale(30)"/>
|
||||
<use href="#star" transform="translate(300,270) scale(30)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 602 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 600">
|
||||
<rect width="900" height="200" fill="#000"/>
|
||||
<rect y="200" width="900" height="200" fill="#DD0000"/>
|
||||
<rect y="400" width="900" height="200" fill="#FFCE00"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 233 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 600">
|
||||
<rect width="300" height="600" fill="#0055A4"/>
|
||||
<rect x="300" width="300" height="600" fill="#fff"/>
|
||||
<rect x="600" width="300" height="600" fill="#EF4135"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 233 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6400 1971"><title>bnvb</title><desc>Logo von bnvb planen und beraten Ingenieurgesellschaft mbH</desc><path fill="#55626a" d="M299.46 652.15h52.5c65.84 0 135-28.33 135-121.67V298.8c0-55-22.5-103.34-111.66-103.34h-60c-38.34 0-83.34 15.02-110 42.52V27.15L36.95 33.82v81.66h51.67v526.67h90.83l14.17-34.17c25 27.5 63.33 44.17 105.83 44.17m-10.83-91.67c-29.17 0-63.34-7.5-83.34-19.16V294.65c16.67-5 45-11.68 74.17-11.68h44.17c34.17 0 48.33 10.01 48.33 42.51v187.49c0 33.33-12.5 47.51-45.83 47.51h-37.5Zm280.83 81.67h211.67v-75.83l-47.5-1.67V297.98c14.17-4.18 43.33-9.18 76.67-9.18h38.33c22.5 0 42.5 6.68 42.5 33.35v242.5l-48.33 1.67v75.83h211.66v-75.83l-47.5-1.67V292.98c0-69.16-44.16-97.52-112.5-97.52h-45.83c-48.33 0-88.33 18.36-117.5 47.52l-5-40.83-160.83 4.15v80.85h52.5v277.5l-48.34 1.67v75.83Zm694.98 4.17h136.67l135-358.35 44.17-1.65v-80.85H1381.1v80.85h50l-81.66 240-5.84 30.81h-1.66l-6.67-30.81-80-240h50v-80.85h-222.5v80.85l50 1.65 131.67 358.35Zm601.67 5.83h52.5c65.84 0 135-28.33 135-121.67V298.8c0-55-22.5-103.34-111.66-103.34h-60c-38.34 0-83.34 15.02-110 42.52V27.15l-168.34 6.67v81.66h51.67v526.67h90.83l14.17-34.17c25 27.5 63.33 44.17 105.83 44.17m-10.83-91.67c-29.17 0-63.33-7.5-83.33-19.16V294.65c16.66-5 45-11.68 74.16-11.68h44.17c34.17 0 48.33 10.01 48.33 42.51v187.49c0 33.33-12.5 47.51-45.83 47.51h-37.5ZM60.06 1329.62h120.75v-33.89l-40.87-.94v-77.57c15.33 16.29 35.76 20.9 57.59 20.9h31.1c36.24 0 70.6-19.02 70.6-68.72v-128.16c0-30.21-13.93-56.67-65.02-56.67h-34.36c-22.76 0-45.52 8.84-60.83 24.17l-1.88-19.52-80.8 1.39v35.75h32.97v268.43l-29.25.94v33.89Zm132.36-133.28c-25.08 0-43.66-7.9-52.48-13v-150.47c9.76-3.72 36.68-6.51 48.3-6.51h29.25c12.54 0 31.13.93 31.13 24.61v113.34c0 25.52-11.16 32.03-29.27 32.03h-26.93Zm158.35 36.21h109.15v-34.34l-29.72-.95V891.22L347.06 894v35.76h32.05v267.5l-28.34.95v34.34Zm217.82 4.67h37.15c33.9 0 50.62-20.9 58.52-35.78h.92l5.1 33.9 70.15-2.79v-34.34l-29.73-.95v-150.47c0-46.44-30.66-62.22-72-62.22h-55.72c-20.89 0-44.58 3.24-62.7 10.67v60.84h45.06v-33.9h61.3c22.3 0 32.97 6.5 32.97 28.34v42.24h-82.66c-43.2 0-72 20.92-72 65.96V1181c0 33.92 24.63 56.21 63.64 56.21m16.25-38.1c-16.25 0-29.26-5.58-29.26-24.62v-21.36c0-19.94 10.68-26.94 30.66-26.94h73.37v30.2c0 28.81-20.43 42.72-49.22 42.72h-25.55Zm198.76 33.43h109.14v-34.34l-28.79-.95v-163.9c8.35-2.82 26.93-6.07 49.22-6.07h24.62c13.92 0 27.4 4.17 27.4 21.84v148.13l-29.26.95v34.34h109.13v-34.34l-28.78-.95v-158.33c0-39.97-27.42-54.36-61.77-54.36h-26.95c-24.15 0-48.3 7.88-64.55 24.16l-2.32-19.5-80.33 1.38v35.75h32.5v170.9l-29.26.95v34.34Zm398.46 5.58h62.69c21.37 0 40.42-4.62 52.48-14.4v-26.9h-109.6c-30.19 0-40.4-11.62-40.4-37.63v-35.78h116.56l39.48-6.04v-57.12c0-62.24-42.72-75.7-89.63-75.7h-24.62c-52.47 0-93.8 17.2-93.8 82.2v92.44c0 58.96 39.93 78.93 86.84 78.93m-34.83-150.47v-27.4c0-25.08 10.21-38.09 40.87-38.09h25.54c26.94 0 40.87 6.51 40.87 34.86v30.63h-107.28Zm211.77 144.9h109.13v-34.35l-28.8-.95v-163.9c8.37-2.82 26.95-6.07 49.24-6.07h24.62c13.92 0 27.4 4.17 27.4 21.84v148.13l-29.26.95v34.34h109.13v-34.34l-28.8-.95v-158.33c0-39.97-27.4-54.36-61.75-54.36h-26.95c-24.15 0-48.3 7.88-64.55 24.17l-2.32-19.52-80.35 1.39v35.75h32.52v170.9l-29.26.95v34.34Zm506.18 5.57h74.3c29.28 0 53.88-11.6 69.68-29.7l24.15 24.12h74.3v-37.62h-42.72l-22.3-21.33c7.44-16.27 10.68-36.7 10.68-54.34v-39.95l47.38-.94v-35.3h-90.1v65.5c0 9.75-.92 24.14-2.78 32.96l-122.6-113.78c-10.7-10.21-20.44-22.3-20.44-38.53v-14.4c0-20.91 17.17-30.2 32.5-30.2h32.05c23.21 0 35.77 9.29 35.77 32.99v24.14h50.6V973.9c0-41.34-26-68.75-80.33-68.75h-42.27c-49.22 0-81.27 27.41-81.27 71.55v13.45c0 30.2 19.05 49.7 31.11 61.32-41.79 7.88-63.16 34.8-63.16 83.12v26.46c0 56.18 38.56 77.09 85.45 77.09m11.62-40.87c-28.33 0-43.65-13.92-43.65-45.05v-21.34c.45-34.37 13.46-45.52 38.07-50.17l106.83 99.84c-9.76 10.23-21.84 16.72-38.56 16.72h-62.7Zm508.52 40.87h31.58c34.83 0 72.45-16.25 72.45-67.8V1040.3c0-28.79-13-55.73-63.16-55.73h-32.52c-20.9 0-46.9 6.5-63.63 22.78V891.22l-83.59 2.77v35.77h32.52v302.8h40.4l6.04-18.11c14.4 16.26 37.15 23.68 59.91 23.68m-1.86-41.8c-20.43 0-41.33-5.56-53.42-13.45v-150.01c9.76-2.8 28.34-7.43 48.77-7.43h29.26c22.76 0 30.66 7.43 30.66 26.45v115.65c0 19.51-9.76 28.8-28.34 28.8h-26.93Zm254.95 41.8h62.71c21.36 0 40.4-4.62 52.47-14.4v-26.9h-109.6c-30.18 0-40.4-11.62-40.4-37.63v-35.78h116.56l39.48-6.04v-57.12c0-62.24-42.73-75.7-89.64-75.7h-24.6c-52.48 0-93.82 17.2-93.82 82.2v92.44c0 58.96 39.95 78.93 86.84 78.93m-34.82-150.47v-27.4c0-25.08 10.22-38.09 40.86-38.09H2670c26.95 0 40.87 6.51 40.87 34.86v30.63H2603.6Zm211.77 144.9h123.52v-34.35l-43.18-.95v-155.1c19.03-6.52 43.65-12.54 63.62-12.54v50.14h45.99v-89.62c-7.43-1.4-17.66-1.84-26.01-1.84-32.05 0-62.24 8.82-85.92 22.73l-2.8-21.8-78.47 1.38v35.75h32.5v170.9l-29.25.95v34.34Zm299.08 4.66h37.15c33.89 0 50.6-20.9 58.5-35.78h.93l5.14 33.9 70.1-2.79v-34.34l-29.72-.95v-150.47c0-46.44-30.64-62.22-71.98-62.22h-55.73c-20.9 0-44.58 3.24-62.7 10.68v60.83h45.04v-33.9h61.3c22.3 0 32.98 6.5 32.98Line truncated
|
||||
|
After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 30">
|
||||
<clipPath id="t">
|
||||
<path d="M30,15 h30 v15 z v15 h-30 z h-30 v-15 z v-15 h30 z"/>
|
||||
</clipPath>
|
||||
<path d="M0,0 v30 h60 v-30 z" fill="#012169"/>
|
||||
<path d="M0,0 L60,30 M60,0 L0,30" stroke="#fff" stroke-width="6"/>
|
||||
<path d="M0,0 L60,30 M60,0 L0,30" clip-path="url(#t)" stroke="#C8102E" stroke-width="4"/>
|
||||
<path d="M30,0 v30 M0,15 h60" stroke="#fff" stroke-width="10"/>
|
||||
<path d="M30,0 v30 M0,15 h60" stroke="#C8102E" stroke-width="6"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 513 B |
+660
-73
@@ -7,7 +7,11 @@
|
||||
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
:root.dark {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -27,28 +31,91 @@
|
||||
width: min(700px, calc(100% - 32px));
|
||||
}
|
||||
|
||||
.navbar {
|
||||
position: fixed;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.logo-bar {
|
||||
position: fixed;
|
||||
top: 16px;
|
||||
left: 16px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.logo-bar img {
|
||||
display: block;
|
||||
height: 60px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.lang-menu {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.lang-dropdown {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 100%;
|
||||
min-width: 150px;
|
||||
margin-top: 6px;
|
||||
padding: 6px;
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.lang-dropdown.open {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.lang-dropdown button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 10px 14px;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
background: none;
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.lang-dropdown button:hover {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.lang-dropdown button.active {
|
||||
font-weight: 600;
|
||||
background: #f0f8ff;
|
||||
}
|
||||
|
||||
.lang-flag {
|
||||
width: 20px;
|
||||
height: 13px;
|
||||
margin-right: 6px;
|
||||
border-radius: 2px;
|
||||
vertical-align: -2px;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-sizing: border-box;
|
||||
width: min(600px, calc(100% - 32px));
|
||||
padding: 32px;
|
||||
background: white;
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.10);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.30), 0 12px 40px rgba(0, 0, 0, 0.45);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 25px;
|
||||
box-sizing: border-box;
|
||||
width: min(600px, calc(100% - 32px));
|
||||
padding: 32px;
|
||||
background: white;
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.10);
|
||||
text-align: center;
|
||||
float: right;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
@@ -170,54 +237,101 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
/*
|
||||
* Dark-Theme über die Klasse "dark" am html-Element.
|
||||
* JS setzt sie anhand der gespeicherten Wahl oder der
|
||||
* System-Einstellung (siehe detectTheme).
|
||||
*/
|
||||
:root.dark body {
|
||||
background: #181818;
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.card {
|
||||
:root.dark .card {
|
||||
background: #242424;
|
||||
}
|
||||
|
||||
.footer {
|
||||
:root.dark .lang-dropdown {
|
||||
background: #242424;
|
||||
}
|
||||
|
||||
p {
|
||||
:root.dark .lang-dropdown button:hover {
|
||||
background: #303030;
|
||||
}
|
||||
|
||||
:root.dark .lang-dropdown button.active {
|
||||
background: #1a3a5a;
|
||||
}
|
||||
|
||||
:root.dark p {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
:root.dark .secondary {
|
||||
background: #444;
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.status, .download {
|
||||
:root.dark .status,
|
||||
:root.dark .download {
|
||||
background: #303030;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
:root.dark .spinner {
|
||||
border-color: #555;
|
||||
border-top-color: #ddd;
|
||||
}
|
||||
|
||||
.form-group input,
|
||||
.form-group textarea {
|
||||
:root.dark .form-group label {
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
:root.dark .form-group input,
|
||||
:root.dark .form-group textarea {
|
||||
background: #303030;
|
||||
border-color: #555;
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.success-message {
|
||||
:root.dark .success-message {
|
||||
background: #1a3a5a;
|
||||
color: #99ccff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav id="logoBar" class="logo-bar hidden">
|
||||
<img id="logoImage" src="" alt="Logo">
|
||||
</nav>
|
||||
|
||||
<nav class="navbar">
|
||||
<a class="button secondary" href="https://www.bnvb-ingenieure.de/impressum" target="_blank" rel="noopener noreferrer" data-i18n="impressum">
|
||||
Impressum
|
||||
</a>
|
||||
<div id="langMenu" class="lang-menu">
|
||||
<button id="langButton" class="button secondary" type="button" aria-haspopup="true" aria-expanded="false">
|
||||
<img id="langFlag" class="lang-flag" src="assets/uk.svg" alt="">
|
||||
<span id="langLabel">English</span>
|
||||
</button>
|
||||
<div id="langDropdown" class="lang-dropdown">
|
||||
<button type="button" data-lang="en"><img class="lang-flag" src="assets/uk.svg" alt="">English</button>
|
||||
<button type="button" data-lang="de"><img class="lang-flag" src="assets/de.svg" alt="">Deutsch</button>
|
||||
<button type="button" data-lang="fr"><img class="lang-flag" src="assets/fr.svg" alt="">Français</button>
|
||||
<button type="button" data-lang="zh"><img class="lang-flag" src="assets/cn.svg" alt="">中文</button>
|
||||
</div>
|
||||
</div>
|
||||
<button id="themeButton" class="button secondary" type="button" aria-label="Toggle dark mode">
|
||||
<svg id="themeIconMoon" class="theme-icon" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/>
|
||||
</svg>
|
||||
<svg id="themeIconSun" class="theme-icon hidden" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="4"/>
|
||||
<path d="M12 2v2m0 16v2M4.9 4.9l1.4 1.4m11.4 11.4 1.4 1.4M2 12h2m16 0h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/>
|
||||
</svg>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div class="container">
|
||||
<div class="card">
|
||||
|
||||
@@ -348,11 +462,11 @@
|
||||
|
||||
<!-- FEHLER -->
|
||||
<div id="invalidRoom" class="hidden">
|
||||
<h1 data-i18n="error.title">
|
||||
<h1 id="missingRoomTitle" data-i18n="error.title">
|
||||
No meeting specified
|
||||
</h1>
|
||||
|
||||
<div class="error">
|
||||
<div id="missingRoomError" class="error">
|
||||
<span data-i18n="error.description">
|
||||
Please specify a meeting using the
|
||||
</span>
|
||||
@@ -362,6 +476,20 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h1 id="invalidNameTitle" class="hidden" data-i18n="error.invalidTitle">
|
||||
Invalid meeting name
|
||||
</h1>
|
||||
|
||||
<div id="invalidNameError" class="error hidden">
|
||||
<span data-i18n="error.invalidDescription">
|
||||
Only letters, numbers and hyphens are allowed:
|
||||
</span>
|
||||
<br>
|
||||
<span class="room">
|
||||
<strong id="invalidRoomName"></strong>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 24px;">
|
||||
<button id="createNewMeetingButton" class="button primary hidden" type="button" data-i18n="creator.createNewButton">
|
||||
Create New Meeting
|
||||
@@ -385,12 +513,12 @@
|
||||
|
||||
<div class="form-group">
|
||||
<label for="meetingStart" data-i18n="creator.meetingStartLabel">Start Date & Time</label>
|
||||
<input type="datetime-local" id="meetingStart" />
|
||||
<input type="datetime-local" id="meetingStart" step="900" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="meetingEnd" data-i18n="creator.meetingEndLabel">End Date & Time</label>
|
||||
<input type="datetime-local" id="meetingEnd" />
|
||||
<input type="datetime-local" id="meetingEnd" step="900" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@@ -414,10 +542,6 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
Impressum
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
"use strict";
|
||||
@@ -440,7 +564,14 @@
|
||||
* Timeout trying to start the jitsi-meet-electron client
|
||||
*/
|
||||
launchTimeout: 2000,
|
||||
|
||||
/*
|
||||
* set wallpaper for webpage
|
||||
*/
|
||||
backgroundImage: "assets/background.png",
|
||||
/*
|
||||
* Set logo
|
||||
*/
|
||||
logo: "assets/logo.svg",
|
||||
/*
|
||||
* Download URLs for jitsi-meet-electron client
|
||||
*/
|
||||
@@ -459,6 +590,7 @@
|
||||
const TRANSLATIONS = {
|
||||
en: {
|
||||
pageTitle: "Jitsi Meeting",
|
||||
impressum: "Legal Notice",
|
||||
|
||||
launcher: {
|
||||
title: "Jitsi Meeting",
|
||||
@@ -517,7 +649,9 @@
|
||||
error: {
|
||||
title: "No meeting specified",
|
||||
description: "Please specify a meeting using the",
|
||||
descriptionSuffix: "parameter."
|
||||
descriptionSuffix: "parameter.",
|
||||
invalidTitle: "Invalid meeting name",
|
||||
invalidDescription: "Only letters, numbers and hyphens are allowed:"
|
||||
},
|
||||
|
||||
creator: {
|
||||
@@ -533,6 +667,7 @@
|
||||
downloadButton: "Download ICS File",
|
||||
createNewButton: "Create New Meeting",
|
||||
meetingNameRequired: "Please enter a meeting name",
|
||||
meetingNameInvalid: "Meeting name may only contain letters, numbers and hyphens",
|
||||
startTimeRequired: "Please select a start time",
|
||||
endTimeRequired: "Please select an end time",
|
||||
endAfterStart: "End time must be after start time",
|
||||
@@ -543,6 +678,7 @@
|
||||
|
||||
de: {
|
||||
pageTitle: "Jitsi Meeting öffnen",
|
||||
impressum: "Impressum",
|
||||
|
||||
launcher: {
|
||||
title: "Jitsi Meeting",
|
||||
@@ -603,7 +739,9 @@
|
||||
title: "Kein Meeting angegeben",
|
||||
description:
|
||||
"Bitte gib ein Meeting über den Parameter",
|
||||
descriptionSuffix: "an."
|
||||
descriptionSuffix: "an.",
|
||||
invalidTitle: "Ungültiger Meeting-Name",
|
||||
invalidDescription: "Erlaubt sind nur Buchstaben, Zahlen und Bindestriche:"
|
||||
},
|
||||
|
||||
creator: {
|
||||
@@ -619,6 +757,7 @@
|
||||
downloadButton: "ICS-Datei herunterladen",
|
||||
createNewButton: "Neues Meeting erstellen",
|
||||
meetingNameRequired: "Bitte gib einen Meeting-Namen ein",
|
||||
meetingNameInvalid: "Der Meeting-Name darf nur Buchstaben, Zahlen und Bindestriche enthalten",
|
||||
startTimeRequired: "Bitte wähle eine Startzeit",
|
||||
endTimeRequired: "Bitte wähle eine Endzeit",
|
||||
endAfterStart: "Die Endzeit muss nach der Startzeit liegen",
|
||||
@@ -629,6 +768,7 @@
|
||||
|
||||
fr: {
|
||||
pageTitle: "Réunion Jitsi",
|
||||
impressum: "Mentions légales",
|
||||
|
||||
launcher: {
|
||||
title: "Réunion Jitsi",
|
||||
@@ -688,7 +828,9 @@
|
||||
error: {
|
||||
title: "Aucune réunion spécifiée",
|
||||
description: "Veuillez spécifier une réunion à l'aide du paramètre",
|
||||
descriptionSuffix: "."
|
||||
descriptionSuffix: ".",
|
||||
invalidTitle: "Nom de réunion invalide",
|
||||
invalidDescription: "Seuls les lettres, les chiffres et les tirets sont autorisés :"
|
||||
},
|
||||
|
||||
creator: {
|
||||
@@ -704,12 +846,96 @@
|
||||
downloadButton: "Télécharger le fichier ICS",
|
||||
createNewButton: "Créer une nouvelle réunion",
|
||||
meetingNameRequired: "Veuillez entrer un nom de réunion",
|
||||
meetingNameInvalid: "Le nom de la réunion ne peut contenir que des lettres, des chiffres et des tirets",
|
||||
startTimeRequired: "Veuillez sélectionner une heure de début",
|
||||
endTimeRequired: "Veuillez sélectionner une heure de fin",
|
||||
endAfterStart: "L'heure de fin doit être après l'heure de début",
|
||||
organizerRequired: "Veuillez entrer un email d'organisateur",
|
||||
organizerInvalid: "Veuillez entrer une adresse email d'organisateur valide"
|
||||
}
|
||||
},
|
||||
|
||||
zh: {
|
||||
pageTitle: "Jitsi 会议",
|
||||
impressum: "法律声明",
|
||||
|
||||
launcher: {
|
||||
title: "Jitsi 会议",
|
||||
meetingPrefix: "会议",
|
||||
meetingSuffix: "即将打开。",
|
||||
openButton: "加入会议"
|
||||
},
|
||||
|
||||
starting: {
|
||||
title: "正在启动 Jitsi Meet …",
|
||||
status: "正在打开 Jitsi Meet 桌面客户端。"
|
||||
},
|
||||
|
||||
fallback: {
|
||||
title: "无法打开 Jitsi Meet",
|
||||
description: "Jitsi Meet 桌面客户端可能尚未启动。",
|
||||
retryButton: "重试"
|
||||
},
|
||||
|
||||
downloads: {
|
||||
windows: {
|
||||
title: "适用于 Windows 的 Jitsi Meet",
|
||||
description: "请安装 Jitsi Meet 桌面客户端,然后再次尝试打开会议。",
|
||||
button: "下载 Windows 版 Jitsi"
|
||||
},
|
||||
|
||||
macos: {
|
||||
title: "适用于 macOS 的 Jitsi Meet",
|
||||
description: "请安装 Jitsi Meet 桌面客户端,然后再次尝试打开会议。",
|
||||
button: "下载 macOS 版 Jitsi"
|
||||
},
|
||||
|
||||
linux: {
|
||||
title: "适用于 Linux 的 Jitsi Meet",
|
||||
description: "请安装 Jitsi Meet 桌面客户端,然后再次尝试打开会议。",
|
||||
button: "下载 Linux 版 Jitsi"
|
||||
},
|
||||
|
||||
other: {
|
||||
title: "Jitsi Meet 桌面版",
|
||||
description: "没有为您的操作系统配置桌面版下载。"
|
||||
}
|
||||
},
|
||||
|
||||
browser: {
|
||||
title: "在浏览器中加入",
|
||||
description: "您也可以直接通过浏览器加入会议。",
|
||||
button: "在浏览器中加入"
|
||||
},
|
||||
|
||||
error: {
|
||||
title: "未指定会议",
|
||||
description: "请使用参数指定会议",
|
||||
descriptionSuffix: "。",
|
||||
invalidTitle: "会议名称无效",
|
||||
invalidDescription: "仅允许使用字母、数字和连字符:"
|
||||
},
|
||||
|
||||
creator: {
|
||||
title: "创建新会议",
|
||||
meetingNameLabel: "会议名称",
|
||||
meetingDescriptionLabel: "描述",
|
||||
meetingStartLabel: "开始日期和时间",
|
||||
meetingEndLabel: "结束日期和时间",
|
||||
meetingOrganizerLabel: "组织者邮箱",
|
||||
createButton: "创建会议",
|
||||
cancelButton: "取消",
|
||||
successMessage: "会议已创建!请下载您的日历文件。",
|
||||
downloadButton: "下载 ICS 文件",
|
||||
createNewButton: "创建新会议",
|
||||
meetingNameRequired: "请输入会议名称",
|
||||
meetingNameInvalid: "会议名称只能包含字母、数字和连字符",
|
||||
startTimeRequired: "请选择开始时间",
|
||||
endTimeRequired: "请选择结束时间",
|
||||
endAfterStart: "结束时间必须晚于开始时间",
|
||||
organizerRequired: "请输入组织者邮箱",
|
||||
organizerInvalid: "请输入有效的组织者邮箱地址"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -719,6 +945,21 @@
|
||||
*/
|
||||
|
||||
function detectLanguage() {
|
||||
/*
|
||||
* Gespeicherte Sprachwahl hat Vorrang.
|
||||
*/
|
||||
try {
|
||||
const saved = localStorage.getItem("jitsiLauncherLanguage");
|
||||
|
||||
if (saved && TRANSLATIONS[saved]) {
|
||||
return saved;
|
||||
}
|
||||
} catch (error) {
|
||||
/*
|
||||
* localStorage kann z.B. im Privat-Modus blockiert sein.
|
||||
*/
|
||||
}
|
||||
|
||||
const languages = navigator.languages || [navigator.language];
|
||||
|
||||
for (const language of languages) {
|
||||
@@ -741,8 +982,8 @@
|
||||
return "en";
|
||||
}
|
||||
|
||||
const currentLanguage = detectLanguage();
|
||||
const translation = TRANSLATIONS[currentLanguage];
|
||||
let currentLanguage = detectLanguage();
|
||||
let translation = TRANSLATIONS[currentLanguage];
|
||||
|
||||
/* ============================================================
|
||||
* Apply translations to webpage
|
||||
@@ -796,6 +1037,162 @@
|
||||
return typeof value === "string" ? value : null;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* LANGUAGE MENU
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
const LANGUAGE_NAMES = {
|
||||
en: "English",
|
||||
de: "Deutsch",
|
||||
fr: "Français",
|
||||
zh: "中文"
|
||||
};
|
||||
|
||||
const LANGUAGE_FLAGS = {
|
||||
en: "assets/uk.svg",
|
||||
de: "assets/de.svg",
|
||||
fr: "assets/fr.svg",
|
||||
zh: "assets/cn.svg"
|
||||
};
|
||||
|
||||
function updateLanguageMenu() {
|
||||
langFlag.src = LANGUAGE_FLAGS[currentLanguage];
|
||||
langLabel.textContent = LANGUAGE_NAMES[currentLanguage];
|
||||
|
||||
langDropdown
|
||||
.querySelectorAll("button[data-lang]")
|
||||
.forEach((item) => {
|
||||
item.classList.toggle(
|
||||
"active",
|
||||
item.dataset.lang === currentLanguage
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function setLanguage(language) {
|
||||
if (!TRANSLATIONS[language]) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentLanguage = language;
|
||||
translation = TRANSLATIONS[language];
|
||||
|
||||
try {
|
||||
localStorage.setItem("jitsiLauncherLanguage", language);
|
||||
} catch (error) {
|
||||
/*
|
||||
* localStorage kann z.B. im Privat-Modus blockiert sein.
|
||||
*/
|
||||
}
|
||||
|
||||
applyTranslations();
|
||||
updateLanguageMenu();
|
||||
}
|
||||
|
||||
function toggleLanguageMenu() {
|
||||
const isOpen = langDropdown.classList.toggle("open");
|
||||
|
||||
langButton.setAttribute(
|
||||
"aria-expanded",
|
||||
isOpen ? "true" : "false"
|
||||
);
|
||||
}
|
||||
|
||||
function closeLanguageMenu() {
|
||||
langDropdown.classList.remove("open");
|
||||
langButton.setAttribute("aria-expanded", "false");
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* THEME
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
const THEME_KEY = "jitsiLauncherTheme";
|
||||
|
||||
function savedTheme() {
|
||||
try {
|
||||
const saved = localStorage.getItem(THEME_KEY);
|
||||
|
||||
if (saved === "dark" || saved === "light") {
|
||||
return saved;
|
||||
}
|
||||
} catch (error) {
|
||||
/*
|
||||
* localStorage kann z.B. im Privat-Modus blockiert sein.
|
||||
*/
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function systemIsDark() {
|
||||
return window.matchMedia("(prefers-color-scheme: dark)").matches;
|
||||
}
|
||||
|
||||
function detectTheme() {
|
||||
/*
|
||||
* Gespeicherte Wahl hat Vorrang, sonst System-Einstellung.
|
||||
*/
|
||||
return savedTheme() || (systemIsDark() ? "dark" : "light");
|
||||
}
|
||||
|
||||
let currentTheme = detectTheme();
|
||||
|
||||
function updateThemeToggle() {
|
||||
themeIconSun.classList.toggle("hidden", currentTheme !== "dark");
|
||||
themeIconMoon.classList.toggle("hidden", currentTheme === "dark");
|
||||
}
|
||||
|
||||
function applyTheme() {
|
||||
document.documentElement.classList.toggle("dark", currentTheme === "dark");
|
||||
updateThemeToggle();
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
currentTheme = currentTheme === "dark" ? "light" : "dark";
|
||||
|
||||
try {
|
||||
localStorage.setItem(THEME_KEY, currentTheme);
|
||||
} catch (error) {
|
||||
/*
|
||||
* localStorage kann z.B. im Privat-Modus blockiert sein.
|
||||
*/
|
||||
}
|
||||
|
||||
applyTheme();
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* BACKGROUND IMAGE
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
function applyBackgroundImage() {
|
||||
if (!CONFIG.backgroundImage) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.body.style.backgroundImage = `url("${CONFIG.backgroundImage}")`;
|
||||
document.body.style.backgroundSize = "cover";
|
||||
document.body.style.backgroundPosition = "center";
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* LOGO
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
function applyLogo() {
|
||||
if (!CONFIG.logo) {
|
||||
return;
|
||||
}
|
||||
|
||||
logoImage.src = CONFIG.logo;
|
||||
logoBar.classList.remove("hidden");
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* DOM elements
|
||||
* ============================================================
|
||||
@@ -831,6 +1228,29 @@
|
||||
const icsSuccess = document.getElementById("icsSuccess");
|
||||
const downloadIcsLink = document.getElementById("downloadIcsLink");
|
||||
|
||||
/* Language menu DOM elements */
|
||||
const langMenu = document.getElementById("langMenu");
|
||||
const langButton = document.getElementById("langButton");
|
||||
const langDropdown = document.getElementById("langDropdown");
|
||||
const langFlag = document.getElementById("langFlag");
|
||||
const langLabel = document.getElementById("langLabel");
|
||||
|
||||
/* Invalid room view DOM elements */
|
||||
const missingRoomTitle = document.getElementById("missingRoomTitle");
|
||||
const missingRoomError = document.getElementById("missingRoomError");
|
||||
const invalidNameTitle = document.getElementById("invalidNameTitle");
|
||||
const invalidNameError = document.getElementById("invalidNameError");
|
||||
const invalidRoomName = document.getElementById("invalidRoomName");
|
||||
|
||||
/* Logo DOM elements */
|
||||
const logoBar = document.getElementById("logoBar");
|
||||
const logoImage = document.getElementById("logoImage");
|
||||
|
||||
/* Theme DOM elements */
|
||||
const themeButton = document.getElementById("themeButton");
|
||||
const themeIconSun = document.getElementById("themeIconSun");
|
||||
const themeIconMoon = document.getElementById("themeIconMoon");
|
||||
|
||||
/* ============================================================
|
||||
* GET ROOM FROM QUERY-PARAMETER
|
||||
* ============================================================
|
||||
@@ -851,22 +1271,19 @@
|
||||
window.location.search
|
||||
);
|
||||
|
||||
const room = params.get("room");
|
||||
|
||||
if (!room) {
|
||||
/*
|
||||
* Parameter nicht vorhanden.
|
||||
*/
|
||||
if (!params.has("room")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Leerzeichen etc. verhindern.
|
||||
*
|
||||
* "?room=" ohne Wert ergibt einen leeren String.
|
||||
*/
|
||||
const trimmed = room.trim();
|
||||
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return trimmed;
|
||||
return params.get("room").trim();
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
@@ -882,29 +1299,17 @@
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
const ROOM_PATTERN = /^[A-Za-z0-9-]+$/;
|
||||
|
||||
function isValidRoom(room) {
|
||||
if (!room) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Keine Steuerzeichen.
|
||||
* Nur Buchstaben, Zahlen und Bindestriche.
|
||||
*/
|
||||
if (/[\u0000-\u001F\u007F]/.test(room)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Keine Slash-Zeichen.
|
||||
*
|
||||
* Falls deine Jitsi-Konfiguration Räume mit Slashes
|
||||
* unterstützt, diese Prüfung entsprechend entfernen.
|
||||
*/
|
||||
if (room.includes("/")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return ROOM_PATTERN.test(room);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
@@ -1179,6 +1584,52 @@
|
||||
}, CONFIG.launchTimeout);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* ROOM NAME
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Sonderzeichen in das nächstliegende ASCII-Zeichen
|
||||
* umwandeln.
|
||||
*
|
||||
* Beispiel:
|
||||
*
|
||||
* "Grundstück" ergibt "Grundstueck"
|
||||
* "François" ergibt "Francois"
|
||||
*/
|
||||
function transliterate(text) {
|
||||
const extras = {
|
||||
"ß": "ss",
|
||||
"ẞ": "ss",
|
||||
"æ": "ae",
|
||||
"Æ": "ae",
|
||||
"œ": "oe",
|
||||
"Œ": "oe",
|
||||
"ø": "o",
|
||||
"Ø": "o"
|
||||
};
|
||||
|
||||
return text
|
||||
.normalize("NFD")
|
||||
.replace(/[\u0300-\u036f]/g, "")
|
||||
.replace(/[ßẞæÆœŒøØ]/g, (char) => extras[char]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Aus dem Meeting-Namen einen gültigen Room-Namen ableiten.
|
||||
*
|
||||
* Beispiel:
|
||||
*
|
||||
* "Grund Stück Planung" ergibt "grund-stuck-planung"
|
||||
*/
|
||||
function createRoomName(meetingName) {
|
||||
return transliterate(meetingName)
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, "-")
|
||||
.replace(/[^a-z0-9-]/g, "");
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* ICS FILE GENERATION
|
||||
* ============================================================
|
||||
@@ -1186,7 +1637,7 @@
|
||||
|
||||
function generateICS(meetingName, description, startTime, endTime, organizer) {
|
||||
/* Generate a unique room name from the meeting name */
|
||||
const roomName = meetingName.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
|
||||
const roomName = createRoomName(meetingName);
|
||||
const meetingUrl = `https://${CONFIG.jitsiLauncher}/launch.html?room=${encodeURIComponent(roomName)}`;
|
||||
|
||||
/* Format dates for ICS */
|
||||
@@ -1246,6 +1697,51 @@ END:VCALENDAR`;
|
||||
return url;
|
||||
}
|
||||
|
||||
const MINUTES_STEP = 15;
|
||||
const DEFAULT_DURATION_MINUTES = 30;
|
||||
|
||||
/*
|
||||
* Minuten auf das 15-Minuten-Raster aufrunden.
|
||||
*
|
||||
* Beispiel:
|
||||
*
|
||||
* 14:07 ergibt 14:15
|
||||
* 14:50 ergibt 15:00
|
||||
*/
|
||||
function roundMinutes(date) {
|
||||
const rounded = new Date(date);
|
||||
|
||||
rounded.setSeconds(0, 0);
|
||||
rounded.setMinutes(
|
||||
Math.ceil(date.getMinutes() / MINUTES_STEP) * MINUTES_STEP
|
||||
);
|
||||
|
||||
return rounded;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lokale Zeit für datetime-local formatieren.
|
||||
*
|
||||
* Beispiel:
|
||||
*
|
||||
* 1. Januar 2026, 14:15 ergibt "2026-01-01T14:15"
|
||||
*/
|
||||
function formatLocal(date) {
|
||||
const pad = (num) => num.toString().padStart(2, "0");
|
||||
|
||||
return (
|
||||
date.getFullYear() +
|
||||
"-" +
|
||||
pad(date.getMonth() + 1) +
|
||||
"-" +
|
||||
pad(date.getDate()) +
|
||||
"T" +
|
||||
pad(date.getHours()) +
|
||||
":" +
|
||||
pad(date.getMinutes())
|
||||
);
|
||||
}
|
||||
|
||||
function showMeetingCreator() {
|
||||
/* Hide all other views */
|
||||
launcher.classList.add("hidden");
|
||||
@@ -1265,11 +1761,13 @@ END:VCALENDAR`;
|
||||
icsSuccess.classList.add("hidden");
|
||||
|
||||
/* Set default start and end times */
|
||||
const now = new Date();
|
||||
const inOneHour = new Date(now.getTime() + 60 * 60 * 1000);
|
||||
const start = roundMinutes(new Date());
|
||||
const end = new Date(
|
||||
start.getTime() + DEFAULT_DURATION_MINUTES * 60 * 1000
|
||||
);
|
||||
|
||||
meetingStartInput.value = now.toISOString().slice(0, 16);
|
||||
meetingEndInput.value = inOneHour.toISOString().slice(0, 16);
|
||||
meetingStartInput.value = formatLocal(start);
|
||||
meetingEndInput.value = formatLocal(end);
|
||||
}
|
||||
|
||||
function createMeeting() {
|
||||
@@ -1284,6 +1782,11 @@ END:VCALENDAR`;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ROOM_PATTERN.test(createRoomName(meetingName))) {
|
||||
alert(getTranslation('creator.meetingNameInvalid') || 'Meeting name may only contain letters, numbers and hyphens');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!meetingStartInput.value) {
|
||||
alert(getTranslation('creator.startTimeRequired') || 'Please select a start time');
|
||||
return;
|
||||
@@ -1349,11 +1852,44 @@ END:VCALENDAR`;
|
||||
const room = getRoom();
|
||||
|
||||
/*
|
||||
* Kein room-Parameter
|
||||
* Kein room-Parameter: Meldungen ausblenden,
|
||||
* nur den Create-Button anzeigen.
|
||||
*/
|
||||
if (room === null) {
|
||||
missingRoomTitle.classList.add("hidden");
|
||||
missingRoomError.classList.add("hidden");
|
||||
invalidNameTitle.classList.add("hidden");
|
||||
invalidNameError.classList.add("hidden");
|
||||
invalidRoom.classList.remove("hidden");
|
||||
createNewMeetingButton.classList.remove("hidden");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Leerer room-Parameter ("?room="):
|
||||
* "No meeting specified" anzeigen.
|
||||
*/
|
||||
if (room === "") {
|
||||
missingRoomTitle.classList.remove("hidden");
|
||||
missingRoomError.classList.remove("hidden");
|
||||
invalidNameTitle.classList.add("hidden");
|
||||
invalidNameError.classList.add("hidden");
|
||||
invalidRoom.classList.remove("hidden");
|
||||
createNewMeetingButton.classList.remove("hidden");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ungültiger room-Parameter: nur die
|
||||
* Invalid-Name-Meldung anzeigen.
|
||||
*/
|
||||
if (!isValidRoom(room)) {
|
||||
missingRoomTitle.classList.add("hidden");
|
||||
missingRoomError.classList.add("hidden");
|
||||
invalidNameTitle.classList.remove("hidden");
|
||||
invalidNameError.classList.remove("hidden");
|
||||
invalidRoomName.textContent = room;
|
||||
invalidRoom.classList.remove("hidden");
|
||||
/* Show the create new meeting button when no room is specified */
|
||||
createNewMeetingButton.classList.remove("hidden");
|
||||
return;
|
||||
}
|
||||
@@ -1430,12 +1966,63 @@ END:VCALENDAR`;
|
||||
cancelCreateMeeting
|
||||
);
|
||||
|
||||
/* Language menu events */
|
||||
langButton.addEventListener(
|
||||
"click",
|
||||
toggleLanguageMenu
|
||||
);
|
||||
|
||||
langDropdown.addEventListener(
|
||||
"click",
|
||||
(event) => {
|
||||
const item = event.target.closest("button[data-lang]");
|
||||
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLanguage(item.dataset.lang);
|
||||
closeLanguageMenu();
|
||||
}
|
||||
);
|
||||
|
||||
document.addEventListener(
|
||||
"click",
|
||||
(event) => {
|
||||
if (!langMenu.contains(event.target)) {
|
||||
closeLanguageMenu();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/* Theme events */
|
||||
themeButton.addEventListener(
|
||||
"click",
|
||||
toggleTheme
|
||||
);
|
||||
|
||||
/*
|
||||
* Ohne gespeicherte Wahl dem System folgen.
|
||||
*/
|
||||
window
|
||||
.matchMedia("(prefers-color-scheme: dark)")
|
||||
.addEventListener("change", () => {
|
||||
if (savedTheme() === null) {
|
||||
currentTheme = systemIsDark() ? "dark" : "light";
|
||||
applyTheme();
|
||||
}
|
||||
});
|
||||
|
||||
/* ============================================================
|
||||
* START
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
applyTranslations();
|
||||
applyTheme();
|
||||
applyBackgroundImage();
|
||||
applyLogo();
|
||||
updateLanguageMenu();
|
||||
initialize();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in new issue
Block a user