559 lines
20 KiB
HTML
Executable File
559 lines
20 KiB
HTML
Executable File
<!doctype html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title>CANCOM Simple Signage Player</title>
|
||
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||
|
||
<style>
|
||
/* ─── Player-Grundlayout ───
|
||
Alle Medien (img/video/iframe) werden zentriert auf Vollbild angezeigt.
|
||
Standardmäßig ausgeblendet – nur das aktive Element ist sichtbar. */
|
||
html, body {
|
||
font-family: system-ui, sans-serif;
|
||
margin: 0;
|
||
padding: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: black;
|
||
overflow: hidden;
|
||
}
|
||
|
||
img, video, #iframe {
|
||
width: 100vw;
|
||
height: 100vh;
|
||
background: black;
|
||
display: none;
|
||
position: fixed;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
z-index: 1;
|
||
}
|
||
|
||
img, #iframe {
|
||
object-fit: contain;
|
||
border: 0;
|
||
}
|
||
|
||
video {
|
||
object-fit: contain;
|
||
}
|
||
|
||
/* ─── Newsticker (Laufband) ─── */
|
||
.newsticker-text {
|
||
flex: 1;
|
||
overflow: hidden;
|
||
position: relative;
|
||
height: 100%;
|
||
}
|
||
|
||
.newsticker-track {
|
||
display: inline-block;
|
||
white-space: nowrap;
|
||
position: absolute;
|
||
left: 100%;
|
||
top: 0;
|
||
height: 40px;
|
||
line-height: 40px;
|
||
animation: ticker-scroll 40s linear infinite;
|
||
}
|
||
|
||
.newsticker-track span {
|
||
padding-right: 4rem;
|
||
}
|
||
|
||
@keyframes ticker-scroll {
|
||
from { transform: translateX(0); }
|
||
to { transform: translateX(-100vw); }
|
||
}
|
||
</style>
|
||
</head>
|
||
|
||
<body>
|
||
|
||
<!-- ═══════════════════════════════════════════════════
|
||
OVERLAY / Custom-URL (iframe-Modus)
|
||
Wenn der Aktions-Button angeklickt wird, öffnet
|
||
sich dieser Overlay-Bereich mit Zurück-Button.
|
||
═══════════════════════════════════════════════════ -->
|
||
<div id="overlay" style="display:none;position:fixed;inset:0;z-index:99999;background:#000;">
|
||
<style>
|
||
/* Zurück-Button-Position: 9 Positionen, dynamisch per openOverlay(pos) gesetzt */
|
||
#overlay-back.overlay-back-top-left { top: 16px; left: 16px; }
|
||
#overlay-back.overlay-back-top-center { top: 16px; left: 50%; transform: translateX(-50%); }
|
||
#overlay-back.overlay-back-top-right { top: 16px; right: 16px; }
|
||
#overlay-back.overlay-back-middle-left { top: 50%; left: 16px; transform: translateY(-50%); }
|
||
#overlay-back.overlay-back-middle-center { top: 50%; left: 50%; transform: translate(-50%,-50%); }
|
||
#overlay-back.overlay-back-middle-right { top: 50%; right: 16px; transform: translateY(-50%); }
|
||
#overlay-back.overlay-back-bottom-left { bottom: 16px; left: 16px; }
|
||
#overlay-back.overlay-back-bottom-center { bottom: 16px; left: 50%; transform: translateX(-50%); }
|
||
#overlay-back.overlay-back-bottom-right { bottom: 16px; right: 16px; }
|
||
</style>
|
||
<button id="overlay-back" onclick="closeOverlay()"
|
||
style="position:absolute;z-index:10;
|
||
padding:8px 20px;background:#fff;color:#000;
|
||
border:none;border-radius:20px;font-size:1rem;font-weight:600;
|
||
cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,0.08);">
|
||
← Zurück
|
||
</button>
|
||
<iframe id="overlay-iframe" style="width:100%;height:100%;border:none;display:block;"></iframe>
|
||
</div>
|
||
|
||
<!-- ─── Custom-URL-Button (Position: 9 Positionen oben/mitte/unten × links/mitte/rechts)
|
||
Bottom-Positionen weichen automatisch 16px über dem Newsticker-Balken aus ─── -->
|
||
{% if custom_url and custom_url_enabled %}
|
||
<style>
|
||
/* Einheitliches Button-Styling: Weiß, abgerundet, fetter Text (Custom-URL + Voice-Agent) */
|
||
.btn-pos-{{ screen }} {
|
||
position: fixed;
|
||
z-index: 9999;
|
||
padding: 10px 20px;
|
||
background: #fff;
|
||
color: #000;
|
||
border: none;
|
||
border-radius: 20px;
|
||
font-size: 1rem;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
text-decoration: none;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||
}
|
||
.btn-pos-{{ screen }}-top-left { top: 16px; left: 16px; }
|
||
.btn-pos-{{ screen }}-top-center { top: 16px; left: 50%; transform: translateX(-50%); }
|
||
.btn-pos-{{ screen }}-top-right { top: 16px; right: 16px; }
|
||
.btn-pos-{{ screen }}-middle-left { top: 50%; left: 16px; transform: translateY(-50%); }
|
||
.btn-pos-{{ screen }}-middle-center { top: 50%; left: 50%; transform: translate(-50%,-50%); }
|
||
.btn-pos-{{ screen }}-middle-right { top: 50%; right: 16px; transform: translateY(-50%); }
|
||
.btn-pos-{{ screen }}-bottom-left { bottom: {% if newsticker_enabled %}56px{% else %}16px{% endif %}; left: 16px; }
|
||
.btn-pos-{{ screen }}-bottom-center { bottom: {% if newsticker_enabled %}56px{% else %}16px{% endif %}; left: 50%; transform: translateX(-50%); }
|
||
.btn-pos-{{ screen }}-bottom-right { bottom: {% if newsticker_enabled %}56px{% else %}16px{% endif %}; right: 16px; }
|
||
</style>
|
||
{% if custom_url_target == "redirect" %}
|
||
<a href="{{ custom_url }}"
|
||
class="btn-pos-{{ screen }} btn-pos-{{ screen }}-{{ custom_url_position|default('top-left') }}">
|
||
{{ custom_url_label or "Info" }}
|
||
</a>
|
||
{% else %}
|
||
<button onclick='openOverlay({{ custom_url | tojson }}, "{{ custom_url_position|default('top-left') }}")'
|
||
class="btn-pos-{{ screen }} btn-pos-{{ screen }}-{{ custom_url_position|default('top-left') }}">
|
||
{{ custom_url_label or "Info" }}
|
||
</button>
|
||
{% endif %}
|
||
{% endif %}
|
||
|
||
<!-- ═══════════════════════════════════════════════════
|
||
Voice-Agent-Button (fix positioniert, 9 Positionen)
|
||
Typewriter-Tagline, optionales DVA-Bild, animierte
|
||
Balken, Verhalten overlay/redirect. Bottom-Positionen
|
||
weichen dem Newsticker aus.
|
||
═══════════════════════════════════════════════════ -->
|
||
{% if voice_agent_url and voice_agent_enabled %}
|
||
<style>
|
||
/* Voice-Agent-Wrap: Fixpositionierter Container, Column-Layout für Bild + Button */
|
||
.voice-wrap-{{ screen }} {
|
||
position: fixed;
|
||
z-index: 9998;
|
||
display: inline-flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
.voice-pos-{{ screen }} .voice-text-{{ screen }} {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
justify-content: center;
|
||
line-height: 1.3;
|
||
min-width: 160px;
|
||
}
|
||
/* Typewriter-Tagline: Mehrsprachig, Buchstabe für Buchstabe, 0,8 s Fade-Out */
|
||
.voice-pos-{{ screen }} .voice-text-{{ screen }} .voice-tagline-{{ screen }} {
|
||
display: block;
|
||
font-size: 0.75rem;
|
||
font-weight: 500;
|
||
color: #888;
|
||
height: 1.2em;
|
||
line-height: 1.2em;
|
||
white-space: nowrap;
|
||
transition: opacity 0.8s ease;
|
||
}
|
||
.voice-pos-{{ screen }} .voice-text-{{ screen }} .voice-label-{{ screen }} {
|
||
display: block;
|
||
font-size: 1.05rem;
|
||
font-weight: 600;
|
||
color: #000;
|
||
}
|
||
.voice-pos-{{ screen }} .voice-text-{{ screen }} .voice-tagline-{{ screen }} .cursor {
|
||
display: inline-block;
|
||
width: 2px;
|
||
height: 1em;
|
||
background: #DA002D;
|
||
margin-left: 2px;
|
||
animation: voice-blink-{{ screen }} 0.6s step-end infinite;
|
||
vertical-align: text-bottom;
|
||
}
|
||
@keyframes voice-blink-{{ screen }} {
|
||
50% { opacity: 0; }
|
||
}
|
||
/* Optionales DVA-Bild (dva.png) oberhalb des Buttons, Beta-Feature */
|
||
.voice-wrap-{{ screen }} .voice-img-{{ screen }} {
|
||
width: 200px;
|
||
aspect-ratio: 648 / 596;
|
||
margin-bottom: 6px;
|
||
background: url('/static/dva.png') no-repeat center/contain;
|
||
flex-shrink: 0;
|
||
}
|
||
/* Voice-Agent-Button: Weiß, 20 px Border-Radius, fett – einheitlich mit Aktions-Button */
|
||
.voice-pos-{{ screen }} {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
padding: 8px 20px;
|
||
gap: 12px;
|
||
min-width: 280px;
|
||
height: 56px;
|
||
background: #fff;
|
||
color: #000;
|
||
border: none;
|
||
border-radius: 20px;
|
||
font-size: 1.05rem;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
text-decoration: none;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||
}
|
||
.voice-pos-{{ screen }}:hover {
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.12);
|
||
}
|
||
.voice-pos-{{ screen }} .voice-icon {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 6px;
|
||
width: auto;
|
||
flex-shrink: 0;
|
||
}
|
||
/* Animierte Balken (5 Stück): Welle-Effekt mit abgestuften Verzögerungen */
|
||
.voice-pos-{{ screen }} .voice-icon .bar {
|
||
width: 3px;
|
||
border-radius: 2px;
|
||
background: #DA002D;
|
||
transform-origin: center bottom;
|
||
animation: voice-bar-{{ screen }} 0.6s ease-in-out infinite alternate;
|
||
}
|
||
.voice-pos-{{ screen }} .voice-icon .bar:nth-child(1) { height: 10px; animation-delay: 0.0s; }
|
||
.voice-pos-{{ screen }} .voice-icon .bar:nth-child(2) { height: 16px; animation-delay: 0.2s; }
|
||
.voice-pos-{{ screen }} .voice-icon .bar:nth-child(3) { height: 22px; animation-delay: 0.4s; }
|
||
.voice-pos-{{ screen }} .voice-icon .bar:nth-child(4) { height: 14px; animation-delay: 0.1s; }
|
||
.voice-pos-{{ screen }} .voice-icon .bar:nth-child(5) { height: 8px; animation-delay: 0.3s; }
|
||
@keyframes voice-bar-{{ screen }} {
|
||
0% { transform: scaleY(0.4); }
|
||
100% { transform: scaleY(1.2); }
|
||
}
|
||
/* Voice-Agent-Positionen: 9 Positionen (oben/mitte/unten × links/mitte/rechts), Bottom weicht Newsticker aus */
|
||
.voice-wrap-{{ screen }}-top-left { top: 16px; left: 16px; }
|
||
.voice-wrap-{{ screen }}-top-center { top: 16px; left: 50%; transform: translateX(-50%); }
|
||
.voice-wrap-{{ screen }}-top-right { top: 16px; right: 16px; }
|
||
.voice-wrap-{{ screen }}-middle-left { top: 50%; left: 16px; transform: translateY(-50%); }
|
||
.voice-wrap-{{ screen }}-middle-center { top: 50%; left: 50%; transform: translate(-50%,-50%); }
|
||
.voice-wrap-{{ screen }}-middle-right { top: 50%; right: 16px; transform: translateY(-50%); }
|
||
.voice-wrap-{{ screen }}-bottom-left { bottom: calc({% if newsticker_enabled %}56px{% else %}16px{% endif %}); left: 16px; }
|
||
.voice-wrap-{{ screen }}-bottom-center { bottom: calc({% if newsticker_enabled %}56px{% else %}16px{% endif %}); left: 50%; transform: translateX(-50%); }
|
||
.voice-wrap-{{ screen }}-bottom-right { bottom: calc({% if newsticker_enabled %}56px{% else %}16px{% endif %}); right: 16px; }
|
||
</style>
|
||
<!-- Voice-Agent-Wrap: Fixpositionierter Container mit Button + optionalem Bild -->
|
||
<div class="voice-wrap-{{ screen }} voice-wrap-{{ screen }}-{{ voice_agent_position|default('top-left') }}">
|
||
{% if voice_agent_show_image %}<span class="voice-img-{{ screen }}"></span>{% endif %}
|
||
{% if voice_agent_target == "redirect" %}
|
||
<a href="{{ voice_agent_url }}"
|
||
class="voice-pos-{{ screen }}">
|
||
<span class="voice-icon">
|
||
<span class="bar"></span>
|
||
<span class="bar"></span>
|
||
<span class="bar"></span>
|
||
<span class="bar"></span>
|
||
<span class="bar"></span>
|
||
</span>
|
||
<span class="voice-text-{{ screen }}">
|
||
<span class="voice-tagline-{{ screen }}" id="voice-tagline-{{ screen }}"></span>
|
||
<span class="voice-label-{{ screen }}">{{ voice_agent_label or "Digitaler Assistent" }}</span>
|
||
</span>
|
||
</a>
|
||
{% else %}
|
||
<button onclick='openOverlay({{ voice_agent_url | tojson }}, "{{ voice_agent_position|default('top-left') }}")'
|
||
class="voice-pos-{{ screen }}">
|
||
<span class="voice-icon">
|
||
<span class="bar"></span>
|
||
<span class="bar"></span>
|
||
<span class="bar"></span>
|
||
<span class="bar"></span>
|
||
<span class="bar"></span>
|
||
</span>
|
||
<span class="voice-text-{{ screen }}">
|
||
<span class="voice-tagline-{{ screen }}" id="voice-tagline-{{ screen }}"></span>
|
||
<span class="voice-label-{{ screen }}">{{ voice_agent_label or "Digitaler Assistent" }}</span>
|
||
</span>
|
||
</button>
|
||
{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- ─── Medien-Elemente (eines ist jeweils sichtbar) ─── -->
|
||
<img id="image">
|
||
<video id="video" muted autoplay playsinline></video>
|
||
<iframe id="iframe"></iframe>
|
||
|
||
<!-- ─── Newsticker (optional, Laufband unten) ─── -->
|
||
{% if newsticker_enabled %}
|
||
<div id="newsticker-bar" style="position:fixed;left:0;right:0;bottom:0;height:40px;z-index:10000;background:#DA002D;color:#fff;display:flex;align-items:center;overflow:hidden;">
|
||
<div id="newsticker-text" class="newsticker-text">
|
||
<div class="newsticker-track">
|
||
<span>{{ newsticker_text }}</span>
|
||
</div>
|
||
</div>
|
||
<div id="newsticker-clock" style="padding:0 18px 0 24px;font-size:1.1em;font-family:monospace;min-width:90px;text-align:right;"></div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
|
||
<script>
|
||
// ═════════════════════════════════════════════════════
|
||
// Player-JavaScript
|
||
// ═════════════════════════════════════════════════════
|
||
|
||
let playerTimer = null;
|
||
|
||
// ─── Custom-URL/Voice-Agent-Overlay (open/close mit Zurück-Button-Position-Tracking) ───
|
||
function openOverlay(url, pos) {
|
||
if (playerTimer) clearTimeout(playerTimer);
|
||
document.getElementById("overlay").style.display = "block";
|
||
document.getElementById("overlay-iframe").src = url;
|
||
var backBtn = document.getElementById("overlay-back");
|
||
backBtn.className = "overlay-back-" + (pos || "top-left");
|
||
}
|
||
|
||
function closeOverlay() {
|
||
document.getElementById("overlay").style.display = "none";
|
||
document.getElementById("overlay-iframe").src = "";
|
||
playNext();
|
||
}
|
||
|
||
// ─── Newsticker-Uhr (aktuelle Uhrzeit) ───
|
||
function updateClock() {
|
||
const el = document.getElementById('newsticker-clock');
|
||
if (!el) return;
|
||
const now = new Date();
|
||
el.textContent = now.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
||
}
|
||
setInterval(updateClock, 1000);
|
||
updateClock();
|
||
|
||
// ─── Vom Server übergebene Daten ───
|
||
const normalFiles = {{ normal_files | tojson }};
|
||
const prioFiles = {{ prio_files | tojson }};
|
||
const interval = {{ interval }} * 1000;
|
||
const stayOnFirst = {{ "true" if stay_on_first else "false" }};
|
||
const screen = "{{ screen }}";
|
||
const site = "{{ site }}";
|
||
// stayOnFirst: Player bleibt auf erstem Element (kein Timer/Advance)
|
||
|
||
// ─── Player-Status ───
|
||
let normalIndex = 0;
|
||
let prioIndex = 0;
|
||
let mode = "normal"; // "normal" | "prio"
|
||
const img = document.getElementById("image");
|
||
const vid = document.getElementById("video");
|
||
const iframe = document.getElementById("iframe");
|
||
|
||
// ─── Hilfsfunktionen ───
|
||
function normalizeItem(item) {
|
||
if (typeof item === "string") {
|
||
const lower = item.toLowerCase();
|
||
if (lower.startsWith("http://") || lower.startsWith("https://")) {
|
||
return { kind: "url", url: item, zoom: 1.0 };
|
||
}
|
||
return { kind: "file", name: item };
|
||
}
|
||
if (item.kind === "url" && !("zoom" in item)) {
|
||
item.zoom = 1.0;
|
||
}
|
||
if (!("enabled" in item)) {
|
||
item.enabled = true;
|
||
}
|
||
return item;
|
||
}
|
||
|
||
function isVideo(item) {
|
||
return item.kind === "file" && item.name.toLowerCase().endsWith(".mp4");
|
||
}
|
||
function isImage(item) {
|
||
return item.kind === "file" && /\.(jpg|jpeg|png)$/i.test(item.name);
|
||
}
|
||
function isHtml(item) {
|
||
return item.kind === "file" && /\.(html?|htm)$/i.test(item.name);
|
||
}
|
||
|
||
// ─── Nächstes Element aus der Playlist holen ───
|
||
// Ablauf: Normal-Playlist → Priority → Normal → …
|
||
function getNextItem() {
|
||
const normalizedNormal = normalFiles.map(normalizeItem);
|
||
const normalizedPrio = prioFiles.map(normalizeItem);
|
||
const activeNormal = normalizedNormal.filter(item => item.enabled !== false);
|
||
const activePrio = normalizedPrio.filter(item => item.enabled !== false);
|
||
|
||
// Nur Priority vorhanden
|
||
if (activeNormal.length === 0 && activePrio.length > 0) {
|
||
return { item: activePrio[prioIndex++ % activePrio.length], isPrio: true };
|
||
}
|
||
// Nur normale Playlist vorhanden
|
||
if (activePrio.length === 0 && activeNormal.length > 0) {
|
||
return { item: activeNormal[normalIndex++ % activeNormal.length], isPrio: false };
|
||
}
|
||
// Normal-Phase
|
||
if (mode === "normal") {
|
||
const item = activeNormal[normalIndex++];
|
||
if (normalIndex >= activeNormal.length) {
|
||
normalIndex = 0;
|
||
if (activePrio.length > 0) mode = "prio";
|
||
}
|
||
return { item, isPrio: false };
|
||
}
|
||
// Priority-Phase
|
||
if (mode === "prio") {
|
||
const item = activePrio[prioIndex++];
|
||
if (prioIndex >= activePrio.length) {
|
||
prioIndex = 0;
|
||
mode = "normal";
|
||
}
|
||
return { item, isPrio: true };
|
||
}
|
||
return null;
|
||
}
|
||
|
||
// ─── Aktuelles Medium abspielen ───
|
||
function playNext() {
|
||
const entry = getNextItem();
|
||
if (!entry) return;
|
||
|
||
const item = entry.item;
|
||
const basePath = entry.isPrio ? "priority" : screen;
|
||
|
||
// Alle Elemente ausblenden
|
||
img.style.display = "none";
|
||
vid.style.display = "none";
|
||
iframe.style.display = "none";
|
||
vid.pause();
|
||
vid.src = "";
|
||
iframe.src = "";
|
||
iframe.style.transform = "scale(1)";
|
||
|
||
// URL im iframe anzeigen (mit Zoom-Unterstützung)
|
||
if (item.kind === "url") {
|
||
iframe.style.display = "block";
|
||
iframe.src = item.url;
|
||
const zoom = item.zoom || 1.0;
|
||
if (zoom !== 1.0) {
|
||
const scale = 1 / zoom;
|
||
iframe.style.width = `${100 * scale}vw`;
|
||
iframe.style.height = `${100 * scale}vh`;
|
||
iframe.style.transform = `translate(-50%, -50%) scale(${zoom})`;
|
||
} else {
|
||
iframe.style.width = `100vw`;
|
||
iframe.style.height = `100vh`;
|
||
iframe.style.transform = `translate(-50%, -50%)`;
|
||
}
|
||
if (!stayOnFirst) playerTimer = setTimeout(playNext, interval);
|
||
return;
|
||
}
|
||
|
||
// Datei aus dem Medienverzeichnis
|
||
const src = entry.isPrio ? `/media/priority/${item.name}` : `/media/${site}/${basePath}/${item.name}`;
|
||
if (isVideo(item)) {
|
||
vid.style.display = "block";
|
||
vid.src = src;
|
||
vid.onended = stayOnFirst ? null : playNext;
|
||
vid.play();
|
||
} else if (isHtml(item)) {
|
||
iframe.style.display = "block";
|
||
iframe.src = src;
|
||
iframe.style.width = `100vw`;
|
||
iframe.style.height = `100vh`;
|
||
iframe.style.transform = `translate(-50%, -50%)`;
|
||
if (!stayOnFirst) playerTimer = setTimeout(playNext, interval);
|
||
} else {
|
||
// Bild (jpg/png)
|
||
img.style.display = "block";
|
||
img.src = src;
|
||
if (!stayOnFirst) playerTimer = setTimeout(playNext, interval);
|
||
}
|
||
}
|
||
|
||
// ─── Auto-Reload bei Playlist-Änderungen ───
|
||
let lastHash = null;
|
||
|
||
async function checkForUpdates() {
|
||
try {
|
||
const res = await fetch(`/playlist/${site}/${screen}/hash`, { cache: "no-store" });
|
||
const hash = await res.text();
|
||
if (lastHash && lastHash !== hash) {
|
||
location.reload(true);
|
||
}
|
||
lastHash = hash;
|
||
} catch (e) {
|
||
console.warn("Playlist-Check fehlgeschlagen", e);
|
||
}
|
||
}
|
||
|
||
// ─── Voice-Agent-Tagline Typewriter ───
|
||
// Wechselt mehrsprachige Sätze, Buchstabe für Buchstabe, 2,5 s Pause, 0,8 s Fade-Out
|
||
(function() {
|
||
const el = document.getElementById('voice-tagline-{{ screen }}');
|
||
if (!el) return;
|
||
const phrases = [
|
||
"Always ready to help",
|
||
"How can I assist you?",
|
||
"Questions? Just ask.",
|
||
"Wie kann ich helfen?",
|
||
"Ich bin für Sie da",
|
||
"Sprechen Sie mich einfach an",
|
||
"Comment puis-je vous aider?",
|
||
"¿En qué puedo ayudarle?",
|
||
"Come posso aiutarti?",
|
||
"I'm here for you",
|
||
"Let me know what you need"
|
||
];
|
||
let idx = 0, charIdx = 0, fading = false;
|
||
function type() {
|
||
if (fading) {
|
||
el.style.opacity = '0';
|
||
fading = false;
|
||
setTimeout(function() {
|
||
el.textContent = '';
|
||
el.style.opacity = '1';
|
||
setTimeout(type, 200);
|
||
}, 800);
|
||
return;
|
||
}
|
||
const phrase = phrases[idx % phrases.length];
|
||
if (charIdx < phrase.length) {
|
||
el.textContent = phrase.slice(0, charIdx + 1);
|
||
charIdx++;
|
||
setTimeout(type, 50 + Math.random() * 30);
|
||
} else {
|
||
charIdx = 0;
|
||
idx++;
|
||
fading = true;
|
||
setTimeout(type, 2500);
|
||
}
|
||
}
|
||
type();
|
||
})();
|
||
|
||
// ─── Start ───
|
||
playNext();
|
||
setInterval(checkForUpdates, 5000);
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|