summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Friesel <derf@finalrewind.org>2019-06-10 16:26:32 +0200
committerDaniel Friesel <derf@finalrewind.org>2019-06-10 16:26:32 +0200
commitb48996cff3bc4e72bf017b2b3fdcffcfd3780a5f (patch)
tree5488c0720fd7002495892dc26780122ed9db73d4
parent5686110134e0fa5afbef038f2f46db7983c4bc4b (diff)
add head nav and prominent geolocation link to app template
-rw-r--r--lib/DBInfoscreen.pm8
-rw-r--r--lib/DBInfoscreen/Controller/Stationboard.pm35
-rw-r--r--public/static/css/app.css736
-rw-r--r--public/static/css/default.css367
-rw-r--r--templates/layouts/app.html.ep258
-rw-r--r--templates/layouts/legacy.html.ep (renamed from templates/layouts/default.html.ep)0
6 files changed, 1015 insertions, 389 deletions
diff --git a/lib/DBInfoscreen.pm b/lib/DBInfoscreen.pm
index d83f58f..44a5520 100644
--- a/lib/DBInfoscreen.pm
+++ b/lib/DBInfoscreen.pm
@@ -261,7 +261,7 @@ sub startup {
$r->get('/_wr/:train/:departure')->to('wagenreihung#wagenreihung');
- $self->defaults( layout => 'default' );
+ $self->defaults( layout => 'app' );
$self->sessions->default_expiration( 3600 * 24 * 28 );
$r->get('/')->to('stationboard#handle_request');
@@ -270,12 +270,12 @@ sub startup {
$self->config(
hypnotoad => {
- accepts => $ENV{DBFAKEDISPLAY_ACCEPTS} // 100,
- clients => $ENV{DBFAKEDISPLAY_CLIENTS} // 10,
+ accepts => $ENV{DBFAKEDISPLAY_ACCEPTS} // 100,
+ clients => $ENV{DBFAKEDISPLAY_CLIENTS} // 10,
listen => [ $ENV{DBFAKEDISPLAY_LISTEN} // 'http://*:8092' ],
pid_file => $ENV{DBFAKEDISPLAY_PID_FILE}
// '/tmp/db-fakedisplay.pid',
- spare => $ENV{DBFAKEDISPLAY_SPARE} // 2,
+ spare => $ENV{DBFAKEDISPLAY_SPARE} // 2,
workers => $ENV{DBFAKEDISPLAY_WORKERS} // 2,
},
);
diff --git a/lib/DBInfoscreen/Controller/Stationboard.pm b/lib/DBInfoscreen/Controller/Stationboard.pm
index 862ba12..e0e778d 100644
--- a/lib/DBInfoscreen/Controller/Stationboard.pm
+++ b/lib/DBInfoscreen/Controller/Stationboard.pm
@@ -373,22 +373,22 @@ sub handle_request {
my $via = $self->param('via');
my @platforms = split( /,/, $self->param('platforms') // q{} );
- my @lines = split( /,/, $self->param('lines') // q{} );
- my $template = $self->param('mode') // 'app';
- my $hide_low_delay = $self->param('hidelowdelay') // 0;
- my $hide_opts = $self->param('hide_opts') // 0;
+ my @lines = split( /,/, $self->param('lines') // q{} );
+ my $template = $self->param('mode') // 'app';
+ my $hide_low_delay = $self->param('hidelowdelay') // 0;
+ my $hide_opts = $self->param('hide_opts') // 0;
my $show_realtime = $self->param('show_realtime') // 0;
- my $show_details = $self->param('detailed') // 0;
- my $backend = $self->param('backend') // 'iris';
- my $admode = $self->param('admode') // 'deparr';
- my $dark_layout = $self->param('dark') // 0;
- my $apiver = $self->param('version') // 0;
+ my $show_details = $self->param('detailed') // 0;
+ my $backend = $self->param('backend') // 'iris';
+ my $admode = $self->param('admode') // 'deparr';
+ my $dark_layout = $self->param('dark') // 0;
+ my $apiver = $self->param('version') // 0;
my $callback = $self->param('callback');
my $with_related = !$self->param('no_related');
my $save_defaults = $self->param('save_defaults') // 0;
- my $limit = $self->param('limit') // 0;
- my @train_types = split( /,/, $self->param('train_types') // q{} );
- my %opt = (
+ my $limit = $self->param('limit') // 0;
+ my @train_types = split( /,/, $self->param('train_types') // q{} );
+ my %opt = (
cache_hafas => $self->app->cache_hafas,
cache_iris_main => $self->app->cache_iris_main,
cache_iris_rt => $self->app->cache_iris_rt,
@@ -423,6 +423,9 @@ sub handle_request {
elsif ( defined $station and $station =~ s{ [.] json $ }{}x ) {
$template = 'json';
}
+ elsif ( $template ne 'app' ) {
+ $self->stash( layout => 'legacy' );
+ }
# Historically, there were two JSON APIs: 'json' (undocumented, raw
# passthrough of serialized Travel::Status::DE::IRIS::Result /
@@ -452,11 +455,7 @@ sub handle_request {
$self->param( $param => $self->session($param) );
}
}
- $self->render(
- 'landingpage',
- hide_opts => 0,
- show_intro => 1
- );
+ $self->render( 'landingpage', show_intro => 1 );
return;
}
@@ -554,7 +553,7 @@ sub handle_request {
for my $result (@results) {
my $platform = ( split( qr{ }, $result->platform // '' ) )[0];
- my $delay = $result->delay;
+ my $delay = $result->delay;
if ( $backend eq 'iris' and $admode eq 'arr' and not $result->arrival )
{
next;
diff --git a/public/static/css/app.css b/public/static/css/app.css
new file mode 100644
index 0000000..9cabc12
--- /dev/null
+++ b/public/static/css/app.css
@@ -0,0 +1,736 @@
+body {
+ margin: 0;
+}
+
+html {
+ font-family: Sans-Serif;
+}
+
+a {
+ color: #000099;
+ text-decoration: none;
+}
+
+p,
+div.about,
+div.input-field,
+div.notes {
+ max-width: 94%;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+p {
+ text-align: justify;
+}
+
+div.content {
+ width: 100%;
+ margin: 0;
+}
+
+div.app {
+ border-width:1px 2px;
+ width:100%;
+ margin-bottom: 5em;
+}
+
+div.app > ul {
+ position:relative;
+ width:100%;
+
+ list-style-type:none;
+ margin:0;
+ padding:0;
+}
+
+div.app > ul > li {
+ min-height:7em;
+ display:block;
+ width:100%;
+ position:relative;
+ cursor: pointer;
+}
+
+div.appdark > ul > li {
+ border-bottom: 1px solid #999999;
+ background-color: #000000;
+}
+
+div.applight > ul > li {
+ border-bottom: 1px solid #999999;
+ background-color: #ffffff;
+}
+
+div.app li .line {
+ font-size: 2.7em;
+ position:absolute;
+ bottom:5px;
+ left:2px;
+ max-width: 6em;
+ max-height: 3ex;
+ overflow: hidden;
+}
+
+div.app li .line .trainno {
+ font-weight: normal;
+}
+
+div.app li .line .trainno_sub {
+ font-weight: normal;
+ font-size: 0.6em;
+ text-align: center;
+ margin-top: -0.2em;
+}
+
+div.app li .sbahn .trainno_sub {
+ font-weight: normal;
+ font-size: 0.5em;
+ text-align: center;
+ margin-top: -0.25em;
+}
+
+div.app li .lineinfo {
+ color:#000000;
+ font-size: 2em;
+ position:absolute;
+ top:0px;
+ left:2px;
+}
+
+div.app .replacement {
+ color: #006600;
+}
+
+div.app .replaced {
+ color: #660000;
+}
+
+div.app .sbahn {
+ font-weight:bold;
+ border-radius: 30px;
+ padding:3px 6px 2px 6px;
+}
+
+div.applight .sbahn {
+ background-color:#95d79f;
+}
+
+div.appdark .sbahn {
+ background-color:#115511;
+}
+
+div.app .bahn,
+div.app .fern,
+div.app .ext {
+ font-weight:bold;
+ border-radius: 5px;
+ padding:3px 5px 2px 5px;
+}
+
+div.applight .bahn {
+ background-color: #eeeeee;
+}
+
+div.appdark .bahn {
+ background-color: #333333;
+}
+
+div.applight .fern {
+ background-color: #ffdddd;
+}
+
+div.appdark .fern {
+ background-color: #551111;
+}
+
+div.applight .ext {
+ background-color: #ffdddd;
+ border: 2px solid #ff6666;
+}
+
+div.appdark .ext {
+ background-color: #551111;
+ border: 2px solid #993333;
+}
+
+div.app li .route {
+ background-color: transparent;
+ font-size:2.1em;
+ position:absolute;
+ top:1px;
+ left:7.7em;
+ height: 1.2em;
+ width: 70%;
+ overflow: hidden;
+}
+
+div.applight li .route {
+ color:#444444;
+}
+
+div.appdark li .route {
+ color:#bbbbbb;
+}
+
+div.app li .info {
+ color:#ff0000;
+ background-color: transparent;
+ font-size:2.1em;
+ position:absolute;
+ top:1px;
+ left:7.7em;
+ height: 1.2em;
+ width: 70%;
+ overflow: hidden;
+}
+
+div.app .moreinfo {
+ font-size:2.1em;
+ position:fixed;
+ top:64px;
+ left:0;
+ right:0;
+ bottom:0em;
+ z-index: 5;
+ overflow: auto;
+ cursor: default;
+}
+
+div.app .moreinfo .mheader,
+div.app .moreinfo .mfooter {
+ max-width: 50em;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+div.applight .moreinfo {
+ background-color: #ffffff;
+}
+
+div.appdark .moreinfo {
+ background-color: #000000;
+}
+
+div.app .collapsed-moreinfo {
+ display: none;
+}
+
+div.app .expanded-moreinfo {
+ display: block;
+}
+
+div.app .moreinfo .mheader {
+ text-align: center;
+ font-size: 120%;
+ padding-top: 0.5em;
+ padding-bottom: 0.5em;
+ padding-left: 1em;
+ padding-right: 1em;
+ border-bottom: 0.1em dashed #cccccc;
+}
+
+div.app .moreinfo .mfooter {
+ padding-top: 1em;
+ padding-left: 1em;
+ padding-right: 1em;
+}
+
+div.app .moreinfo .reason,
+div.app .moreinfo .minfo {
+ color: #ff0000;
+}
+
+div.app .moreinfo .verbose {
+ margin-bottom: 0.6em;
+}
+
+div.app .moreinfo .timeinfo {
+ margin-bottom: 0.6em;
+}
+
+div.applight .moreinfo .mroute .important-stop {
+ color: #000000;
+}
+
+div.appdark .moreinfo .mroute .important-stop {
+ color: #ffffff;
+}
+
+div.applight .moreinfo .mroute .generic-stop {
+ color: #555555;
+}
+
+div.appdark .moreinfo .mroute .generic-stop {
+ color: #999999;
+}
+
+div.applight .moreinfo .mroute .additional-stop {
+ color: #009900;
+}
+
+div.appdark .moreinfo .mroute .additional-stop {
+ color: #009900;
+}
+
+div.applight .moreinfo .mroute .cancelled-stop {
+ color: #cc0000;
+}
+
+div.appdark .moreinfo .mroute .cancelled-stop {
+ color: #cc0000;
+}
+
+div.app li .dest {
+ background-color: transparent;
+ font-size:4em;
+ position:absolute;
+ top:0.62em;
+ left:4em;
+ bottom:0px;
+ width: 70%;
+ overflow: hidden;
+}
+
+div.applight li .dest {
+ color:#000000;
+}
+
+div.appdark li .dest {
+ color:#ffffff;
+}
+
+div.applight li.cancelled {
+ background-color: #ffe7d0;
+}
+
+div.appdark li.cancelled {
+ background-color: #512f00;
+}
+
+div.app li .countdown {
+ background-color: transparent;
+ font-size: 3em;
+ position: absolute;
+ right: 5px;
+ bottom: 2px;
+ padding-left: 0.2em;
+}
+
+div.applight li .countdown {
+ color: #000000;
+}
+
+div.appdark li .countdown {
+ color: #ffffff;
+}
+
+div.app li .header {
+ color:#000000;
+ font-size:2em;
+ font-weight:bold;
+ padding-top:8px;
+ border-width-top:0;
+ display:block;
+ text-align:center;
+}
+
+div.app li .head {
+ border-bottom-width:0;
+}
+
+div.app li .countdown .delay {
+ font-size:1em;
+ color:#FF0000;
+ background-color: transparent;
+ padding-right:7px;
+}
+
+div.app li .countdown .undelay {
+ font-size:1em;
+ color:#006600;
+ padding-right:7px;
+}
+
+div.app li .countdown .delaynorm {
+ font-size:0.9em;
+ color:#BB3333;
+ padding-right:7px;
+}
+
+div.app li .countdown .undelaynorm {
+ font-size:0.9em;
+ color:#338833;
+ padding-right:7px;
+}
+
+div.app li .countdown .platform {
+ font-weight: bold;
+}
+
+div.app li .countdown .changed-platform {
+ color:#ff0000;
+}
+
+div.app li .time {
+ background-color: transparent;
+ font-size:2.3em;
+ position:absolute;
+ right:5px;
+ top:4px;
+ padding-left: 0.2em;
+}
+
+div.applight li .time {
+ color:#000000;
+}
+
+div.appdark li .time {
+ color:#ffffff;
+}
+
+div.app span.delayed {
+ color: #ff0000;
+ background-color: transparent;
+}
+
+ul.ui-autocomplete {
+ max-height: 20em;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+div.geolocation {
+ text-align: center;
+}
+
+div.candidatestatus {
+ text-align: center;
+ color: #999999;
+}
+
+div.candidatelist a {
+ display: block;
+ text-decoration: none;
+ font-size: 1.4em;
+ padding-top: 0.3em;
+ text-align: center;
+ border-bottom: 1px solid #999999;
+}
+
+div.candidatelist a .distance:after {
+ content: " km";
+}
+
+div.candidatelist a .distance {
+ font-size: 0.6em;
+ color: #999999;
+ padding-top: 0.2em;
+ padding-bottom: 0.3em;
+}
+
+div.about {
+ margin-top: 2em;
+ font-family: Sans-Serif;
+ color: #666666;
+}
+
+div.about a {
+ color: #000066;
+ text-decoration: none;
+}
+
+.notice {
+ padding: 15px;
+ margin-bottom: 20px;
+ border: 1px solid #bce8f1;
+ border-radius: 4px;
+ color: #31708f;
+ background-color: #d9edf7;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.warning {
+ padding: 15px;
+ margin-bottom: 20px;
+ border: 1px solid #faebcc;
+ border-radius: 4px;
+ color: #8a6d3b;
+ background-color: #fcf8e3;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.error {
+ padding: 15px;
+ margin-bottom: 20px;
+ border: 1px solid #ebccd1;
+ border-radius: 4px;
+ color: #a94442;
+ background-color: #f2dede;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.error .errcode {
+ font-family: Monospace;
+ margin-top: 2em;
+ font-size: 100%;
+ color: #aaaaaa;
+}
+
+.container {
+ max-width: 60em;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+pre {
+ margin-bottom: 2em;
+}
+
+span.optional,
+span.notes {
+ color: #666666;
+}
+
+.moresettings-header {
+ cursor: pointer;
+}
+
+.moresettings-header-collapsed:before {
+ content: "▹ "
+}
+
+.moresettings-header-expanded:before {
+ content: "▿ "
+}
+
+.moresettings-collapsed {
+ display: none;
+}
+
+.moresettings-expanded {
+ display: block;
+}
+
+.developers-header {
+ cursor: pointer;
+}
+
+.developers-header-collapsed:before {
+ content: "▹ "
+}
+
+.developers-header-expanded:before {
+ content: "▿ "
+}
+
+.developers-collapsed {
+ display: none;
+}
+
+.developers-expanded {
+ display: block;
+}
+
+div.break {
+ height: 1em;
+}
+
+div.field {
+ margin-top: 0.3em;
+ margin-bottom: 0.6em;
+}
+
+input, select, .button {
+ display: inline-block;
+ width: 60em;
+ max-width: 100%;
+ min-height: 1.8em;
+ border-radius: 4px;
+ color: #000;
+ background-color: #fff;
+ border: 1px solid #ccc;
+ box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
+ font-size: 90%;
+ text-align: center;
+ vertical-align: middle;
+}
+
+input[type="text"] {
+ width: 59em;
+ padding-left: 0.5em;
+ padding-right: 0.5em;
+ text-align: left;
+ box-sizing: border-box;
+}
+
+select {
+ min-height: 2em;
+}
+
+input[type="checkbox"] {
+ width: 1.5em;
+ box-shadow: none;
+}
+
+input[type="submit"], .button {
+ color: #fff;
+ background-color: #337ab7;
+ border-color: #2e6da4;
+ cursor: pointer;
+ box-shadow: none;
+ padding-top: 0.9ex;
+ padding-bottom: 0.9ex;
+}
+
+.button {
+ padding-top: 1.1ex;
+ padding-bottom: 0;
+}
+
+input[type="submit"]:active,
+input[type="submit"]:focus,
+input[type="submit"]:hover,
+.button:active,
+.button:focus,
+.button:hover {
+ color: #fff;
+ background-color: #286090;
+ border-color: #204d74;
+}
+
+input[type="submit"]:active,
+.button.active {
+ box-shadow: inset 0 3px 5px rgba(0,0,0,.125);
+}
+
+.button-light {
+ color: #333;
+ background-color: #fff;
+ border-color: #ccc;
+}
+
+.button-light:active,
+.button-light:focus,
+.button-light:hover
+{
+ color: #333;
+ background-color: #e6e6e6;
+ border-color: #adadad;
+}
+
+div.notes {
+ margin-top: 2em;
+}
+
+div.notes ul {
+ margin-top: 1em;
+}
+div.app {
+ max-width: 60em;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.navbar-fixed {
+ position: relative;
+ z-index: 997;
+}
+
+.navbar-fixed nav {
+ position: fixed;
+}
+
+nav {
+ box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.12), 0 1px 5px 0 rgba(0,0,0,.2);
+}
+
+nav {
+ width: 100%;
+}
+
+nav .nav-wrapper {
+ position: relative;
+ height: 100%;
+}
+
+nav i, nav i.material-icons {
+ display: block;
+ font-size: 24px;
+}
+
+nav .brand-logo {
+ position: absolute;
+ display: inline-block;
+ font-size: 2.1rem;
+ padding-left: 0.5rem;
+}
+
+nav ul {
+ margin: 0;
+ padding-left: 0;
+ list-style-type: none;
+}
+
+nav ul li {
+ transition: background-color .3s;
+ float: left;
+ padding: 0;
+ list-style-type: none;
+}
+
+nav ul a {
+ transition: background-color .3s;
+ font-size: 1rem;
+ color: #fff;
+ display: block;
+ padding: 0 15px;
+ cursor: pointer;
+}
+
+@media only screen and (max-width: 600px) {
+ div.app > ul > li {
+ font-size: 35%;
+ }
+ div.navbar-fixed {
+ height: 56px;
+ }
+ .moreinfo {
+ top: 56px;
+ }
+ nav {
+ height: 56px;
+ line-height: 56px;
+ }
+ nav .nav-wrapper i {
+ height: 56px;
+ line-height: 56px;
+ }
+}
+
+@media only screen and (min-width: 600px) {
+ div.app > ul > li {
+ font-size: 40%;
+ }
+ div.navbar-fixed {
+ height: 64px;
+ }
+ nav {
+ height: 64px;
+ line-height: 64px;
+ }
+ nav .nav-wrapper i {
+ height: 64px;
+ line-height: 64px;
+ }
+}
+
+div.app .moreinfo {
+ font-size: 100%;
+}
+
+.content {
+}
diff --git a/public/static/css/default.css b/public/static/css/default.css
index aca2dba..f5cb255 100644
--- a/public/static/css/default.css
+++ b/public/static/css/default.css
@@ -29,373 +29,6 @@ div.content {
margin: 0;
}
-div.app {
- border-width:1px 2px;
- width:100%;
- margin-bottom: 5em;
-}
-
-div.app > ul {
- position:relative;
- width:100%;
-
- list-style-type:none;
- margin:0;
- padding:0;
-}
-
-div.app > ul > li {
- min-height:7em;
- display:block;
- width:100%;
- position:relative;
- cursor: pointer;
-}
-
-div.appdark > ul > li {
- border-bottom: 1px solid #999999;
- background-color: #000000;
-}
-
-div.applight > ul > li {
- border-bottom: 1px solid #999999;
- background-color: #ffffff;
-}
-
-div.app li .line {
- font-size: 2.7em;
- position:absolute;
- bottom:5px;
- left:2px;
- max-width: 6em;
- max-height: 3ex;
- overflow: hidden;
-}
-
-div.app li .line .trainno {
- font-weight: normal;
-}
-
-div.app li .line .trainno_sub {
- font-weight: normal;
- font-size: 0.6em;
- text-align: center;
- margin-top: -0.2em;
-}
-
-div.app li .sbahn .trainno_sub {
- font-weight: normal;
- font-size: 0.5em;
- text-align: center;
- margin-top: -0.25em;
-}
-
-div.app li .lineinfo {
- color:#000000;
- font-size: 2em;
- position:absolute;
- top:0px;
- left:2px;
-}
-
-div.app .replacement {
- color: #006600;
-}
-
-div.app .replaced {
- color: #660000;
-}
-
-div.app .sbahn {
- font-weight:bold;
- border-radius: 30px;
- padding:3px 6px 2px 6px;
-}
-
-div.applight .sbahn {
- background-color:#95d79f;
-}
-
-div.appdark .sbahn {
- background-color:#115511;
-}
-
-div.app .bahn,
-div.app .fern,
-div.app .ext {
- font-weight:bold;
- border-radius: 5px;
- padding:3px 5px 2px 5px;
-}
-
-div.applight .bahn {
- background-color: #eeeeee;
-}
-
-div.appdark .bahn {
- background-color: #333333;
-}
-
-div.applight .fern {
- background-color: #ffdddd;
-}
-
-div.appdark .fern {
- background-color: #551111;
-}
-
-div.applight .ext {
- background-color: #ffdddd;
- border: 2px solid #ff6666;
-}
-
-div.appdark .ext {
- background-color: #551111;
- border: 2px solid #993333;
-}
-
-div.app li .route {
- background-color: transparent;
- font-size:2.1em;
- position:absolute;
- top:1px;
- left:7.7em;
- height: 1.2em;
- width: 70%;
- overflow: hidden;
-}
-
-div.applight li .route {
- color:#444444;
-}
-
-div.appdark li .route {
- color:#bbbbbb;
-}
-
-div.app li .info {
- color:#ff0000;
- background-color: transparent;
- font-size:2.1em;
- position:absolute;
- top:1px;
- left:7.7em;
- height: 1.2em;
- width: 70%;
- overflow: hidden;
-}
-
-div.app .moreinfo {
- font-size:2.1em;
- position:fixed;
- top:0em;
- left:0;
- right:0;
- bottom:0em;
- z-index: 5;
- overflow: auto;
- cursor: default;
-}
-
-div.app .moreinfo .mheader,
-div.app .moreinfo .mfooter {
- max-width: 50em;
- margin-left: auto;
- margin-right: auto;
-}
-
-div.applight .moreinfo {
- background-color: #ffffff;
-}
-
-div.appdark .moreinfo {
- background-color: #000000;
-}
-
-div.app .collapsed-moreinfo {
- display: none;
-}
-
-div.app .expanded-moreinfo {
- display: block;
-}
-
-div.app .moreinfo .mheader {
- text-align: center;
- font-size: 120%;
- padding-top: 0.5em;
- padding-bottom: 0.5em;
- padding-left: 1em;
- padding-right: 1em;
- border-bottom: 0.1em dashed #cccccc;
-}
-
-div.app .moreinfo .mfooter {
- padding-top: 1em;
- padding-left: 1em;
- padding-right: 1em;
-}
-
-div.app .moreinfo .reason,
-div.app .moreinfo .minfo {
- color: #ff0000;
-}
-
-div.app .moreinfo .verbose {
- margin-bottom: 0.6em;
-}
-
-div.app .moreinfo .timeinfo {
- margin-bottom: 0.6em;
-}
-
-div.applight .moreinfo .mroute .important-stop {
- color: #000000;
-}
-
-div.appdark .moreinfo .mroute .important-stop {
- color: #ffffff;
-}
-
-div.applight .moreinfo .mroute .generic-stop {
- color: #555555;
-}
-
-div.appdark .moreinfo .mroute .generic-stop {
- color: #999999;
-}
-
-div.applight .moreinfo .mroute .additional-stop {
- color: #009900;
-}
-
-div.appdark .moreinfo .mroute .additional-stop {
- color: #009900;
-}
-
-div.applight .moreinfo .mroute .cancelled-stop {
- color: #cc0000;
-}
-
-div.appdark .moreinfo .mroute .cancelled-stop {
- color: #cc0000;
-}
-
-div.app li .dest {
- background-color: transparent;
- font-size:4em;
- position:absolute;
- top:0.62em;
- left:4em;
- bottom:0px;
- width: 70%;
- overflow: hidden;
-}
-
-div.applight li .dest {
- color:#000000;
-}
-
-div.appdark li .dest {
- color:#ffffff;
-}
-
-div.applight li.cancelled {
- background-color: #ffe7d0;
-}
-
-div.appdark li.cancelled {
- background-color: #512f00;
-}
-
-div.app li .countdown {
- background-color: transparent;
- font-size: 3em;
- position: absolute;
- right: 5px;
- bottom: 2px;
- padding-left: 0.2em;
-}
-
-div.applight li .countdown {
- color: #000000;
-}
-
-div.appdark li .countdown {
- color: #ffffff;
-}
-
-div.app li .header {
- color:#000000;
- font-size:2em;
- font-weight:bold;
- padding-top:8px;
- border-width-top:0;
- display:block;
- text-align:center;
-}
-
-div.app li .head {
- border-bottom-width:0;
-}
-
-div.app li .countdown .delay {
- font-size:1em;
- color:#FF0000;
- background-color: transparent;
- padding-right:7px;
-}
-
-div.app li .countdown .undelay {
- font-size:1em;
- color:#006600;
- padding-right:7px;
-}
-
-div.app li .countdown .delaynorm {
- font-size:0.9em;
- color:#BB3333;
- padding-right:7px;
-}
-
-div.app li .countdown .undelaynorm {
- font-size:0.9em;
- color:#338833;
- padding-right:7px;
-}
-
-div.app li .countdown .platform {
- font-weight: bold;
-}
-
-div.app li .countdown .changed-platform {
- color:#ff0000;
-}
-
-div.app li .time {
- background-color: transparent;
- font-size:2.3em;
- position:absolute;
- right:5px;
- top:4px;
- padding-left: 0.2em;
-}
-
-div.applight li .time {
- color:#000000;
-}
-
-div.appdark li .time {
- color:#ffffff;
-}
-
-div.app span.delayed {
- color: #ff0000;
- background-color: transparent;
-}
-
-/* ... */
-
div.infoscreen {
border-width:1px 2px;
width:100%;
diff --git a/templates/layouts/app.html.ep b/templates/layouts/app.html.ep
new file mode 100644
index 0000000..6189c9c
--- /dev/null
+++ b/templates/layouts/app.html.ep
@@ -0,0 +1,258 @@
+<!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">
+ <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 = 'v15'; # asset version
+ %= stylesheet "/static/${av}/css/app.css"
+ %= 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
+ % }
+</head>
+<body>
+
+<div class="navbar-fixed">
+ <nav style="color: #ffffff; background-color: #00838f;">
+ <div class="nav-wrapper container">
+ <span class="brand-logo">
+ %= stash('title') || 'DBF'
+ </span>
+ <ul id="nav-mobile" style="float: right;">
+ <li><a href="#stationinput"><i class="material-icons">edit</i></a></li>
+ <li><a href="/_auto"><i class="material-icons">my_location</i></a></li>
+ </ul>
+ </div>
+ </nav>
+</div>
+
+<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>
+
+% 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">
+ Backend
+ </div>
+ <div>
+ %= select_field backend => [ ['IRIS' => 'iris'], ['HAFAS' => 'ris'] ]
+ </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">
+ Nur Verspätungen &gt;5 Min. anzeigen
+ </label>
+ </div>
+ </div>
+ <div class="field">
+ <div class="desc">
+ %= check_box 'dark' => 1, id => 'id_dark'
+ <label for="id_dark">
+ Dunkles Layout (experimentell)
+ </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="break"></div>
+ <span class="optional">Nur für IRIS-Backend:</span>
+ <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">
+ <div class="desc">
+ %= check_box 'save_defaults' => 1, id => 'id_save_defaults'
+ <label for="id_save_defaults">
+ Ausgewählte Optionen als Default speichern
+ </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>Diese Seite kann gerne als iframe in eigene Infoscreens o.ä. eingebunden werden.
+ Für eine kleine Ansicht (z.B. iframe in einer normalen Website) bitte das
+ "App"-Frontend verwenden. Für eine große Ansicht
+ (z.B. als alleinstehender Infoscreen) gibt es das "Infoscreen"-Frontend.</li>
+ <li>Die Parameter <span style="font-family: monospace;">mode=json&amp;version=3</span>
+ (alternativ auch <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 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 (Links siehe unten) und kann auch
+ auf eigenen Servern installiert werden. Automatisierte Crawler, die mehrere
+ Dutzend Stationen pro Minute abfragen, bitte nur auf eigenen Instanzen
+ betreiben.</li>
+ </ul>
+ </div> <!-- developers -->
+</div> <!-- notes -->
+
+<div class="notes">
+<span class="notes">Siehe auch:</span>
+<ul>
+<li><a href="https://reiseauskunft.bahn.de/bin/bhftafel.exe/dn">DB Abfahrtsmonitor</a>
+ (<a href="https://mobile.bahn.de/bin/mobil/bhftafel.exe/dox">mobil</a>)</li>
+<li>Für Nahverkehr: <a href="https://vrrf.finalrewind.org/">vrr-infoscreen</a></li>
+</ul>
+</div> <!-- notes -->
+
+</div> <!-- container -->
+
+<div class="container">
+<div class="about">
+<a href="_about">db-infoscreen</a>
+v<%= stash('version') // '???' %>
+<br/>
+<a href="_datenschutz" rel="nofollow">Datenschutzerklärung</a>
+<a href="_impressum" rel="nofollow">Impressum</a><br/>
+</div> <!-- about -->
+</div> <!-- container -->
+% }
+
+</body>
+</html>
diff --git a/templates/layouts/default.html.ep b/templates/layouts/legacy.html.ep
index 46ce984..46ce984 100644
--- a/templates/layouts/default.html.ep
+++ b/templates/layouts/legacy.html.ep