<!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 innerdeutsche Zugfahrten"> <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 = 'v40'; # 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) { var path = '/static/<%=$av%>/css/' + name + '.min.css'; var old = document.getElementById(id); if (old && (old.href != path)) { old.href = path; document.cookie = 'theme=' + name; } } var 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'); function toggleTheme() { currentTheme = otherTheme[currentTheme] || 'light'; localStorage.setItem('theme', currentTheme); 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 (stash('with_geolocation')) { %= javascript "/static/${av}/js/geolocation.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;"> <li class="waves-effect waves-light"> <a onClick="javascript:toggleTheme()"><i class="material-icons" aria-label="Farbschema invertieren">invert_colors</i></a> </li> % if (stash('hide_opts')) { <li><a href="/"><i class="material-icons" aria-label="Hauptseite">edit</i></a></li> % } % else { <li><a href="#stationinput"><i class="material-icons" aria-label="Menü">edit</i></a></li> % } <li><a href="/_auto"><i class="material-icons" aria-label="Stationen in der Umgebung suchen">my_location</i></a></li> </ul> </div> </nav> </div> <div class="container"> % if (my $error = stash 'error') { <div class="error"><strong>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> % if (not stash('hide_opts')) { <div class="container"> <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') % } % elsif (stash('station')) { %= text_field 'station', class => 'station', placeholder => 'Name oder DS100-Kürzel', id => 'stationinput' % } % else { %= text_field 'station', class => 'station', placeholder => 'Name oder DS100-Kürzel', id => 'stationinput', autofocus => 'autofocus' % } </div> </div> <div class="field"> %= submit_button 'Abfahrtsmonitor' </div> % if (not stash('show_intro')) { <div class="break"></div> <div class="field"> <a class="button" href="<%= url_for('_auto')->to_abs->scheme('https') %>">Bahnhöfe im Umfeld suchen</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"> Frontend </div> <div> %= select_field mode => [ ['App' => 'app'], ['Infoscreen' => 'infoscreen'], ['Bahnhofstafel' => 'multi'], ['Gleisanzeiger' => 'single'] ] </div> </div> <div class="field"> <div class="desc"> Nur Züge ü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"> %= 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 'hide_opts' => 1, id => 'id_hide_opts' <label for="id_hide_opts"> Formular verstecken (für Infoscreens) </label> </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"> %= check_box 'detailed' => 1, id => 'id_detailed' <label for="id_detailed"> Mehr Details (Zugnummern und Ankunftszeiten) anzeigen </label> </div> </div> <div class="field"> <div class="desc"> %= check_box 'show_realtime' => 1, id => 'id_show_realtime' <label for="id_show_realtime"> Echtzeitangaben statt Fahrplandaten anzeigen </label> </div> </div> <div class="field"> <div class="desc"> %= check_box 'no_related' => 1, id => 'id_no_related' <label for="id_no_related"> Betriebliche Bahnhofstrennungen berücksichtigen (z.B. "Hbf (Fern+Regio)" vs. "Hbf (S)") </label> </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- und Entwickler-Hinweise</div> <div class="developers developers-collapsed"> <ul> <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 Parameter <span style="font-family: monospace;">mode=json&version=3</span> (alternativ <span style="font-family: monospace;">https://dbf.finalrewind.org/Bahnhofsname.json?version=3</span>) bieten ein JSON-IRIS-Interface. Die route-Elemente können zusätzlich die Felder "isAdditional" oder "isCancelled" enthalten, der Rest sollte selbsterklärend sein. Im Fehlerfall fehlt das "departures"-Element, stattdessen wird ein "error"-Element mit Fehlermeldung zurückgegeben. 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>Ein JSON-Interface für Zugdetails ist in Arbeit.</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="about"> <a href="_about">Über DBF</a> · <a href="_datenschutz" rel="nofollow">Datenschutz</a> · <a href="_impressum" rel="nofollow">Impressum</a><br/> Version <%= stash('version') // '???' %> </div> <!-- about --> </div> <!-- container --> % } </body> </html>