summaryrefslogtreecommitdiff
path: root/templates/layouts
diff options
context:
space:
mode:
Diffstat (limited to 'templates/layouts')
-rw-r--r--templates/layouts/app.html.ep320
-rw-r--r--templates/layouts/default.html.ep193
-rw-r--r--templates/layouts/legacy.html.ep66
3 files changed, 386 insertions, 193 deletions
diff --git a/templates/layouts/app.html.ep b/templates/layouts/app.html.ep
new file mode 100644
index 0000000..c557bee
--- /dev/null
+++ b/templates/layouts/app.html.ep
@@ -0,0 +1,320 @@
+<!DOCTYPE html>
+<html lang="de">
+<head>
+ <title><%= stash('title') // 'DBF' %></title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="keywords" content="Abfahrtsmonitor, Bahnhofstafel, Abfahrten, Abfahrtstafel, Nahverkehr, Regionalverkehr, Fernverkehr, ICE, IC, RE, RB, S-Bahn">
+ <meta name="description" content="<%= stash('description') // 'Inoffizieller Abfahrtsmonitor für Nah-, Reginol- und Fernverkehr' %>">
+ <meta name="theme-color" content="#00838f">
+ <link rel="icon" type="image/png" href="/static/icons/icon-16x16.png" sizes="16x16">
+ <link rel="icon" type="image/png" href="/static/icons/icon-32x32.png" sizes="32x32">
+ <link rel="icon" type="image/png" href="/static/icons/icon-96x96.png" sizes="96x96">
+ <link rel="apple-touch-icon" href="/static/icons/icon-120x120.png">
+ <link rel="apple-touch-icon" sizes="180x180" href="/static/icons/icon-180x180.png">
+ <link rel="apple-touch-icon" sizes="152x152" href="/static/icons/icon-152x152.png">
+ <link rel="apple-touch-icon" sizes="167x167" href="/static/icons/icon-167x167.png">
+% if ($self->stash('refresh_interval')) {
+ <meta http-equiv="refresh" content="<%= $self->stash('refresh_interval') %>"/>
+% }
+
+ % my $av = 'v110'; # asset version
+ % if (session('theme') and session('theme') eq 'dark' or param('dark')) {
+ %= stylesheet "/static/${av}/css/dark.min.css", id => 'theme'
+ % }
+ % else {
+ %= stylesheet "/static/${av}/css/light.min.css", id => 'theme'
+ % }
+ <script>
+ function addStyleSheet(name, id) {
+ const path = '/static/<%=$av%>/css/' + name + '.min.css';
+ const old = document.getElementById(id);
+ if (old && (old.href != path)) {
+ old.href = path;
+ document.cookie = 'theme=' + name + ';SameSite=None;Secure';
+ }
+ }
+ const otherTheme = {
+ 'dark': 'light',
+ 'light': 'dark',
+ };
+ var currentTheme = localStorage.getItem('theme');
+ if (!otherTheme.hasOwnProperty(currentTheme)) {
+ currentTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
+ }
+ addStyleSheet(currentTheme, 'theme');
+ </script>
+ %= stylesheet "/static/${av}/css/material-icons.css"
+ %= stylesheet "/static/${av}/css/jquery-ui.min.css"
+ %= javascript '/static/js/jquery-3.4.1.min.js', defer => undef
+ %= javascript "/static/${av}/js/jquery-ui.min.js", defer => undef
+ %= javascript "/static/${av}/js/dbf.min.js", defer => undef
+ % if (not stash('hide_opts')) {
+ %= javascript "/dyn/${av}/autocomplete.js", defer => undef
+ % }
+ % if (stash('with_geostop')) {
+ %= javascript "/static/${av}/js/geostop.min.js", defer => undef
+ % }
+ % if (stash('with_map')) {
+ %= stylesheet "/static/${av}/leaflet/leaflet.css"
+ %= javascript "/static/${av}/leaflet/leaflet.js"
+ %= javascript "/static/${av}/js/map-refresh.min.js", defer => undef
+ % }
+</head>
+<body>
+
+<div class="navbar-fixed">
+ <nav style="color: #ffffff; background-color: #00838f;">
+ <div class="nav-wrapper container">
+ % if (my $nav_link = stash('nav_link')) {
+ <a class="brand-logo" style="float: left;" href="<%= $nav_link %>">
+ %= stash('title') || 'DBF'
+ </a>
+ % }
+ % else {
+ <span class="brand-logo">
+ %= stash('title') || 'DBF'
+ </span>
+ % }
+ <ul id="nav-mobile" style="float: right;">
+ % if (stash('api_link')) {
+ <li class="waves-effect waves-light">
+ <a href="<%= stash('api_link') %>"><span class="visually-hidden"><%= stash('api_text') %></span><i class="material-icons" aria-hidden="true"><%= stash('api_icon') %></i></a>
+ </li>
+ % }
+ % if (stash('hide_opts')) {
+ <li><a href="/"><span class="visually-hidden">Hauptseite</span><i class="material-icons" aria-hidden="true">edit</i></a></li>
+ % }
+ % else {
+ <li><a href="#stationinput"><span class="visually-hidden">Menü</span><i class="material-icons" aria-hidden="true">edit</i></a></li>
+ % }
+ </ul>
+ </div>
+ </nav>
+</div>
+
+<div class="container">
+% if (my $error = stash 'error') {
+<div class="error"><strong>Fehler:</strong>
+<p>
+%= $error
+</p>
+</div>
+% }
+% elsif (stash('stationlist')) {
+<div class="error"><strong>Mehrdeutige Eingabe.</strong>
+Bitte eine Station aus der Liste auswählen</div>
+% }
+</div>
+
+<div class="content">
+%= content
+</div>
+
+% if (not stash('hide_opts')) {
+<div class="container">
+<div class="input-field">
+
+
+%= form_for _redirect => begin
+%= hidden_field efa => param('efa')
+%= hidden_field hafas => param('hafas')
+<div>
+ <div class="field">
+% if (stash('stationlist')) {
+ %= select_field input => stash('stationlist')
+% }
+% elsif (stash('input')) {
+ %= text_field 'input', class => 'station', placeholder => 'Stationsname oder Fahrtnummer', id => 'stationinput'
+% }
+% else {
+ %= text_field 'input', class => 'station', placeholder => 'Stationsname oder Fahrtnummer', id => 'stationinput', autofocus => 'autofocus'
+% }
+ </div>
+ <div class="field">
+ %= submit_button 'Abfahrtstafel'
+ </div>
+ % if (stash('input')) {
+ <div class="geolink">
+ <a class="button" href="<%= url_for('_autostop')->to_abs->scheme('https')->query({efa => param('efa'), hafas => param('hafas')}) %>">Stationen in der Umgebung suchen</a>
+ </div>
+ % }
+ <div class="backendlink">
+ <a class="button button-light" href="<%= url_for('_backend')->query({efa => param('efa'), hafas => param('hafas')}) %>">Backend: <%= param('efa') ? param('efa') . ' (EFA)' : param('hafas') ? param('hafas') . ' (HAFAS)' : 'DB (IRIS-TTS)' %></a>
+ </div>
+ <div class="break"></div>
+ <div class="moresettings-header moresettings-header-collapsed button button-light">Weitere Einstellungen</div>
+ <div class="moresettings moresettings-collapsed">
+ <div class="field">
+ <div class="desc">
+ %= check_box 'hidelowdelay' => 1, id => 'id_hidelowdelay'
+ <label for="id_hidelowdelay">
+ Verspätungen erst ab 5 Minuten anzeigen
+ </label>
+ </div>
+ </div>
+ <div class="field">
+ <div class="desc">
+ %= check_box 'detailed' => 1, id => 'id_detailed'
+ <label for="id_detailed">
+ Mehr Details
+ </label>
+ </div>
+ </div>
+ <div class="field">
+ <div class="desc">
+ %= check_box 'past' => 1, id => 'past'
+ <label for="past">
+ Fahrten der vergangenen 60 Minuten zeigen
+ </label>
+ </div>
+ </div>
+ <div class="field">
+ <div class="desc">
+ %= check_box 'hide_opts' => 1, id => 'id_hide_opts'
+ <label for="id_hide_opts">
+ Formular verstecken
+ </label>
+ </div>
+ </div>
+ <div class="field">
+ <div class="desc">
+ Nur Fahrten über
+ </div>
+ <div>
+ %= text_field 'via', placeholder => 'Bahnhof 1, Bhf2, ... (oder regulärer Ausdruck)', class => 'station'
+ </div>
+ </div>
+ <div class="field">
+ <div class="desc">
+ Gleise
+ </div>
+ <div>
+ %= text_field 'platforms', placeholder => '1, 2, 5, ...'
+ </div>
+ </div>
+ <div class="field">
+ <div class="desc">
+ Ankunfts- oder Abfahrtszeit anzeigen?
+ </div>
+ <div>
+ %= select_field admode => [['Abfahrt bevorzugen' => 'deparr'], ['Nur Abfahrt' => 'dep'], ['Nur Ankunft' => 'arr']]
+ </div>
+ </div>
+ <div class="field">
+ <div class="desc">
+ Frontend
+ </div>
+ <div>
+ %= select_field mode => [ ['App' => 'app'], ['Infoscreen' => 'infoscreen'], ['Bahnhofstafel (legacy)' => 'multi'], ['Gleisanzeiger (legacy)' => 'single'] ]
+ </div>
+ </div>
+ <div class="field">
+ %= submit_button 'Anzeigen'
+ </div>
+ </div> <!-- moresettings -->
+</div>
+% end
+
+</div> <!-- input-field -->
+
+<div class="notes">
+ <div class="developers-header developers-header-collapsed button button-light">API</div>
+ <div class="developers developers-collapsed">
+ <ul>
+ % if (0) {
+ <li>You're welcome to embed DBF departure boards as iframes or use them
+ in full-screen browser setups. The App frontend works best for
+ small screens, whereas the legacy Infoscreen mode is better suited
+ for large displays.</li>
+ <li>The departure board supports names, EVA IDs, and (in IRIS mode)
+ DS100/Ril100 codes as station identifiers.</li>
+ <li>Requests for train details can optionally be suffixed with the
+ DD.MM.[YYYY] date of the requested trip, e.g. "ICE 921 (1.1.)" or
+ "ICE 921 @ 1.1.". The date refers to the scheduled departure at the
+ train's origin station.</li>
+ <li>A JSON IRIS API is avaliable via
+ <span style="font-family: monospace;">mode=json&amp;version=3</span>
+ (or just <span style="font-family: monospace;">https://dbf.finalrewind.org/Station.json?version=3</span>).
+ Route elements may contain "isAdditional" and "isCancelled"; the rest
+ should be self-explanatory. Please do not send more than 30 requests
+ per minute and only one request per station per minute.</li>
+ <li>There is no JSON API for train details yet.</li>
+ <li>The optional <span style="font-family: monospace;">limit</span>
+ parameter limits the number of returnd departures; e.g.
+ <span style="font-family: monospace;">limit=10</span> will result in no more than ten.</li>
+ <li>DBF is available as Open Source software
+ (<a href="https://github.com/derf/db-fakedisplay/blob/master/README.md">installation instructions</a>).
+ Please use your own installation for automated crawlers that request dozens of stations per minute.</li>
+ % }
+ % else {
+ <li>DBF-Abfahrtstafeln können gerne als iframe eingebunden oder in
+ fest installierten Vollbild-Browserfenstern verwendet werden.
+ Für eine kleine Ansicht (z.B. iframe in einer normalen Website)
+ empfiehlt sich das "App"-Frontend. Für eine große Ansicht
+ (z.B. als alleinstehender Infoscreen) gibt es den "Infoscreen"-Modus.</li>
+ <li>Die Abfahrtstafel unterstützt Namen, EVA-IDs, und (im IRIS-Backend)
+ DS100/Ril100-Codes zur Identifikation von Stationen.</li>
+ <li>Abfahrten werden mit Echtzeitdaten bzw. Prognosen angegeben und
+ danach sortiert. Mit dem Parameter
+ <span style="font-family: monospace;">rt=0</span> wwerden stattdessen
+ Plandaten angegeben und zur Sortierung genutzt.</li>
+ <li>Bei HAFAS-Backends können optional Details für spezifische Fahrten im
+ DD.MM.[YYYY]-Format abgefragt werden, z.B. "ICE 921 (1.1.)" oder
+ "ICE 921 @ 1.1.". Das Datum bezieht sich auf die geplante
+ Abfahrtszeit am Startbahnhof der Fahrt.</li>
+ <li>Viele Seiten sind auch als JSON verfügbar, wahlweise mittels
+ <span style="font-family: monospace;">Accept: application/json</span> oder
+ durch <span style="font-family: monospace;">.json</span> in der URL.
+ HAFAS- und IRIS-Abfahrtstafeln liefern mit dem GET-Parameter <span style="font-family: monospace;">version=3</span> eine stabile JSON-API.
+ Alle anderen Endpunkte (sowie Abfahrtstafeln mit <span style="font-family: monospace;">version=raw</span>) erlauben direkten Zugriff auf die serialisierten Travel::Status::DE::{EFA,HAFAS,IRIS}-Objekte ohne stabile API.</li>
+ <li>Bitte maximal 30 Anfragen pro Minute und insbesondere nur eine Anfrage
+ pro Station und Minute – eine höhere Auflösung haben die Backenddaten
+ ohnehin nicht.</li>
+ <li>Mit <span style="font-family: monospace;">limit</span> kann die Anzahl der
+ angezeigten / im JSON enthaltenen Abfahrten eingeschränkt werden, z.B.
+ <span style="font-family: monospace;">limit=10</span> für die ersten zehn.</li>
+ <li>Dieser Dienst ist Open Source-Software und kann leicht auf eigenen Servern
+ <a href="https://github.com/derf/db-fakedisplay/blob/master/README.md">installiert</a>
+ werden. Automatisierte Crawler, die mehrere Dutzend Stationen pro Minute
+ abfragen, bitte nur auf eigenen Instanzen betreiben.</li>
+ % }
+ </ul>
+ </div> <!-- developers -->
+</div> <!-- notes -->
+
+</div> <!-- container -->
+
+<div class="container">
+<div class="config">
+Farbschema:
+<a onClick="javascript:setTheme('light')">hell</a>
+<a onClick="javascript:setTheme('dark')">dunkel</a>
+<a onClick="javascript:setTheme('default')">automatisch</a>
+<!--Language:
+<br/>
+<a onClick="javascript:setLang('de')">DE</a>
+<a onClick="javascript:setLang('en')">EN</a>
+<a onClick="javascript:setLang('default')">system language</a>
+-->
+</div> <!-- config -->
+</div> <!-- container -->
+% }
+% if (not stash('hide_footer')) {
+<div class="container">
+<div class="about">
+<a href="_about">DBF</a> v<%= stash('version') // '???' %>
+<a href="_datenschutz" rel="nofollow">Datenschutz</a>
+<a href="_impressum" rel="nofollow">Impressum</a>
+</div> <!-- about -->
+</div> <!-- container -->
+% }
+
+</body>
+</html>
diff --git a/templates/layouts/default.html.ep b/templates/layouts/default.html.ep
deleted file mode 100644
index a169699..0000000
--- a/templates/layouts/default.html.ep
+++ /dev/null
@@ -1,193 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
- <title><%= stash('title') // 'db-infoscreen' %></title>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
-% if ($self->stash('refresh_interval')) {
- <meta http-equiv="refresh" content="<%= $self->stash('refresh_interval') %>"/>
-% }
-
-
- %= stylesheet '/default.css'
-% if ($self->browser->mobile) {
- %= stylesheet '/mobile.css'
-% }
- %= javascript '/jquery-1.10.2.min.js'
- %= javascript '/collapse.js'
- %= javascript '/marquee.js'
- %= javascript begin
- $(function () { $('marquee').marquee() });
- % end
-</head>
-<body style="<%= (stash('hide_opts') ? 'margin: 0; padding: 0;' : q{}) %>">
-
-% if (my $error = stash 'error') {
-<div class="error">Backend-Fehler:</div>
-<div>
-<pre>
-%= $error
-</pre>
-</div>
-% }
-
-% if (time() < 1420178400 and param('backend') and (param('backend') eq 'iris' ) ) {
-<div class="error">Note: Due to an unknown problem on the Deutsche Bahn IRIS
-server, no schedule data is available. This service will resume operation
-on 2015-01-02 at 07:00 CET.</div>
-% }
-
-%= content
-
-% if (stash 'show_intro') {
-<p>
- Diese Seite ist ein Frontend zum IRIS (Abfahrtsmonitor)
- der Deutschen Bahn. Sie kann wahlweise die in Bahnhofshallen / an
- Haltestellen montierten Anzeigen nachahmen oder einen Handy- und
- Infoscreen-taugliche Abfahrtsmonitor liefern.
-</p>
-<p>
- Als Backend werden sowohl das RIS ("Reisenden-Informationssystem", d.h. der
- Bahn-Abfahrtsmonitor) als auch das IRIS ("Innerbetriebliches
- Reisenden-Informationssystem"?) unterstützt. Im Normalfall enthält das
- IRIS mehr und detailliertere Angaben.
-</p>
-% }
-
-
-% if (not stash('hide_opts')) {
-<div class="input-field">
-
-
-%= form_for _redirect => begin
-<div>
- <div class="field">
- <div class="desc">Bahnhof / Haltestelle</div>
- <div>
-% if (stash('stationlist')) {
- %= select_field station => stash('stationlist')
-% }
-% else {
- %= text_field 'station'
-% }
- %= submit_button 'Display'
- </div>
- </div>
- <div class="break"></div>
- <span class="optional">Optionale Einstellungen:</span>
- <div class="field">
- <div class="desc">
- Nur Züge über
- </div>
- <div>
- %= text_field 'via'
- </div>
- </div>
- <div class="field">
- <div class="desc">
- Gleise
- </div>
- <div>
- %= text_field 'platforms'
- </div>
- </div>
- <div class="field">
- <div class="desc">
- Verspätungen &lt;5 Minuten ignorieren
- </div>
- <div>
- %= check_box 'hidelowdelay' => 1
- </div>
- </div>
- <div class="field">
- <div class="desc">
- Layout
- </div>
- <div>
- %= select_field mode => [ ['Infoscreen' => 'clean'], ['Bahnhofstafel' => 'multi'], ['Gleis' => 'single'] ]
- </div>
- </div>
- <div class="field">
- <div class="desc">
- Backend
- </div>
- <div>
- %= select_field backend => [ ['IRIS' => 'iris'], ['RIS' => 'ris'] ]
- </div>
- </div>
- <div class="field">
- <div class="desc">
- Formular verstecken
- </div>
- <div>
- %= check_box 'hide_opts' => 1
- </div>
- </div>
- <div class="break"></div>
- <span class="optional">Nur für IRIS-Backend:</span>
- <div class="field">
- <div class="desc">
- Ankunfts- oder Abfahrtszeiten
- </div>
- <div>
- %= select_field admode => [['Abfahrt bevorzugen' => 'deparr'], ['Nur Abfahrt' => 'dep'], ['Nur Ankunft' => 'arr']]
- </div>
- </div>
- <div class="field">
- <div class="desc">
- Erwartete Zeiten statt Fahrplandaten
- </div>
- <div>
- %= check_box 'show_realtime' => 1
- </div>
- </div>
-</div>
-% end
-
-</div> <!-- input-field -->
-
-<div class="notes">
-<span class="notes">Anmerkungen:</span>
-<ul>
-<li>Umlaute werden nicht bei allen Backend/Layout-Kombinationen korrekt unterstützt.
-Wenn etwas nicht funktioniert helfen meist
-Umschreibungen der Art ä → ae oder ä → a</li>
-<li>Das RIS-Backend ist deprecated und wird bald ausgetauscht oder entfernt</li>
-</ul>
-</div> <!-- notes -->
-
-<div class="notes">
-<span class="notes">Beispiele:</span>
-<ul>
-<li><a href="/Essen%20Hbf?mode=multi">Essen HBf</a> (IRIS, Bahnhofstafel)</li>
-<li><a href="/Dortmund/Bochum%7CHamm?mode=multi">Dortmund HBf</a> (IRIS, Bahnhofstafel,
-nur Züge via Bochum oder Hamm)</li>
-<li><a href="/Dortmund%20Uni?mode=clean">Dortmund Universit&auml;t</a> (IRIS, Mobile/Infoscreen)</li>
-<li><a href="/KD?mode=single">D&uuml;sseldorf HBf</a> (IRIS, Gleistafel)</li>
-</ul>
-</div>
-
-<div class="notes">
-<span class="notes">Siehe auch:</span>
-<ul>
-<li><a href="http://reiseauskunft.bahn.de/bin/bhftafel.exe/dn">DeutscheBahn RIS</a></li>
-<li><a href="http://marudor.de/">Handyfreundliches Interface</a> von <a href="https://twitter.com/marudor">@marudor</a></li>
-<li><a href="https://iris.noncd.db.de/wbt/js/index.html?typ=ab&amp;style=qrab&amp;bhf=EE&amp;SecLang=&amp;Zeilen=40&amp;footer=0&amp;disrupt=1"
->DeutscheBahn IRIS</a> und <a href="http://www.db-netz.de/file/2361656/data/betriebsstellen.pdf">betriebsstellen.pdf</a></li>
-<li><a href="http://vrrf.finalrewind.org/">vrr-infoscreen</a></li>
-</ul>
-</div> <!-- notes -->
-
-<div class="about">
-<a href="http://finalrewind.org/projects/db-fakedisplay/">db-infoscreen</a>
-v<%= stash('version') // '???' %><br/>
-Backends:<br/>
-<a href="http://finalrewind.org/projects/Travel-Status-DE-DeutscheBahn/">Travel::Status::DE::HAFAS</a>
-v<%= $Travel::Status::DE::HAFAS::VERSION %><br/>
-<a href="http://finalrewind.org/projects/Travel-Status-DE-IRIS/">Travel::Status::DE::IRIS</a>
-v<%= $Travel::Status::DE::IRIS::VERSION %><br/>
-</div>
-% }
-
-</body>
-</html>
diff --git a/templates/layouts/legacy.html.ep b/templates/layouts/legacy.html.ep
new file mode 100644
index 0000000..e7e59ec
--- /dev/null
+++ b/templates/layouts/legacy.html.ep
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html lang="de">
+<head>
+ <title><%= stash('title') // 'DBF' %></title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="keywords" content="Abfahrtsmonitor, Bahnhofstafel, Abfahrten, Abfahrtstafel, ICE, IC, RE, RB, S-Bahn">
+ <meta name="description" content="Inoffizieller Abfahrtsmonitor für Bahnhöfe der DB">
+ <link rel="icon" type="image/png" href="/static/icons/icon-16x16.png" sizes="16x16">
+ <link rel="icon" type="image/png" href="/static/icons/icon-32x32.png" sizes="32x32">
+ <link rel="icon" type="image/png" href="/static/icons/icon-96x96.png" sizes="96x96">
+ <link rel="apple-touch-icon" href="/static/icons/icon-120x120.png">
+ <link rel="apple-touch-icon" sizes="180x180" href="/static/icons/icon-180x180.png">
+ <link rel="apple-touch-icon" sizes="152x152" href="/static/icons/icon-152x152.png">
+ <link rel="apple-touch-icon" sizes="167x167" href="/static/icons/icon-167x167.png">
+% if ($self->stash('refresh_interval')) {
+ <meta http-equiv="refresh" content="<%= $self->stash('refresh_interval') %>"/>
+% }
+
+ % my $av = 'v110'; # asset version
+ %= stylesheet "/static/${av}/css/legacy.css"
+ %= stylesheet "/static/${av}/css/material-icons.css"
+ %= stylesheet "/static/${av}/css/jquery-ui.min.css"
+% my $force_mobile = param('force_mobile') // stash('force_mobile');
+% if ($force_mobile) {
+ %= stylesheet "/static/${av}/css/legacy-mobile.css"
+% }
+ %if (stash('load_marquee')) {
+ %= javascript '/static/js/jquery-3.4.1.min.js'
+ %= javascript "/static/${av}/js/jquery-ui.min.js"
+ %= javascript "/static/${av}/js/dbf.min.js"
+ % if (not stash('hide_opts')) {
+ %= javascript "/dyn/${av}/autocomplete.js", defer => undef
+ % }
+ %= javascript "/static/${av}/js/marquee.min.js"
+ %= javascript begin
+ $(function () { $('marquee').marquee() });
+ % end
+ % } else {
+ %= javascript '/static/js/jquery-3.4.1.min.js', defer => undef
+ %= javascript "/static/${av}/js/jquery-ui.min.js", defer => undef
+ %= javascript "/static/${av}/js/dbf.min.js", defer => undef
+ % }
+</head>
+<body style="<%= (param('dark') ? 'background-color: #000000; color: #ffffff;' : q{}) %>">
+
+<div class="container">
+% if (my $error = stash 'error') {
+<div class="error"><strong>Backend-Fehler:</strong>
+<pre>
+%= $error
+</pre>
+</div>
+% }
+% elsif (stash('stationlist')) {
+<div class="error"><strong>Mehrdeutige Eingabe.</strong>
+Bitte eine Station aus der Liste auswählen</div>
+% }
+</div>
+
+<div class="content">
+%= content
+</div>
+
+</body>
+</html>