diff options
author | Daniel Friesel <derf@finalrewind.org> | 2023-01-29 17:40:27 +0100 |
---|---|---|
committer | Daniel Friesel <derf@finalrewind.org> | 2023-01-29 17:40:27 +0100 |
commit | 6526ae6a64c14f9d64a791b778b157f502a5aa0a (patch) | |
tree | 1d31b11010b2e3ae8d6f0fdc24315888cefbf692 | |
parent | a32109701fc567defbd6673e9cdf6bdf10b83ec7 (diff) |
add hafas (bus, ubahn, tram) support
-rw-r--r-- | lib/DBInfoscreen/Controller/Stationboard.pm | 395 | ||||
-rw-r--r-- | lib/DBInfoscreen/Helper/HAFAS.pm | 20 | ||||
-rw-r--r-- | public/static/js/collapse.js | 13 | ||||
-rw-r--r-- | templates/_train_details.html.ep | 8 | ||||
-rw-r--r-- | templates/app.html.ep | 10 | ||||
-rw-r--r-- | templates/layouts/app.html.ep | 8 |
6 files changed, 335 insertions, 119 deletions
diff --git a/lib/DBInfoscreen/Controller/Stationboard.pm b/lib/DBInfoscreen/Controller/Stationboard.pm index f871fde..1ec39f3 100644 --- a/lib/DBInfoscreen/Controller/Stationboard.pm +++ b/lib/DBInfoscreen/Controller/Stationboard.pm @@ -15,6 +15,7 @@ use List::MoreUtils qw(); use Mojo::JSON qw(decode_json); use Mojo::Promise; use Mojo::UserAgent; +use Travel::Status::DE::HAFAS; use Travel::Status::DE::IRIS; use Travel::Status::DE::IRIS::Stations; use XML::LibXML; @@ -186,11 +187,15 @@ sub result_has_via { } sub log_api_access { + my ($suffix) = @_; + $suffix //= q{}; + + my $file = "$ENV{DBFAKEDISPLAY_STATS}${suffix}"; my $counter = 1; - if ( -r $ENV{DBFAKEDISPLAY_STATS} ) { - $counter = read_file( $ENV{DBFAKEDISPLAY_STATS} ) + 1; + if ( -r $file ) { + $counter = read_file($file) + 1; } - write_file( $ENV{DBFAKEDISPLAY_STATS}, $counter ); + write_file( $file, $counter ); return; } @@ -261,6 +266,19 @@ sub get_results_p { my ( $station, %opt ) = @_; my $data; + if ( $opt{hafas} ) { + return Travel::Status::DE::HAFAS->new_p( + station => $station, + cache => $opt{cache_iris_rt}, + lwp_options => { + timeout => 10, + agent => 'dbf.finalrewind.org/2' + }, + promise => 'Mojo::Promise', + user_agent => Mojo::UserAgent->new, + ); + } + if ( $ENV{DBFAKEDISPLAY_STATS} ) { log_api_access(); } @@ -309,11 +327,13 @@ sub handle_request { my $station = $self->stash('station'); my $template = $self->param('mode') // 'app'; + my $hafas = !!$self->param('hafas'); my $with_related = !$self->param('no_related'); my %opt = ( cache_iris_main => $self->app->cache_iris_main, cache_iris_rt => $self->app->cache_iris_rt, - lookahead => $self->config->{lookahead} + lookahead => $self->config->{lookahead}, + hafas => $hafas, ); if ( $self->param('past') ) { @@ -530,19 +550,21 @@ sub format_iris_result_info { sub render_train { my ( $self, $result, $departure, $station_name, $template ) = @_; - $departure->{links} = []; - $departure->{route_pre_diff} = [ - $self->json_route_diff( - [ $result->route_pre ], - [ $result->sched_route_pre ] - ) - ]; - $departure->{route_post_diff} = [ - $self->json_route_diff( - [ $result->route_post ], - [ $result->sched_route_post ] - ) - ]; + $departure->{links} = []; + if ( $result->can('route_pre') ) { + $departure->{route_pre_diff} = [ + $self->json_route_diff( + [ $result->route_pre ], + [ $result->sched_route_pre ] + ) + ]; + $departure->{route_post_diff} = [ + $self->json_route_diff( + [ $result->route_post ], + [ $result->sched_route_post ] + ) + ]; + } if ( not $result->has_realtime ) { my $now = DateTime->now( time_zone => 'Europe/Berlin' ); @@ -555,15 +577,35 @@ sub render_train { } my $linetype = 'bahn'; - my @classes = $result->classes; - if ( @classes == 0 ) { - $linetype = 'ext'; - } - elsif ( grep { $_ eq 'S' } @classes ) { - $linetype = 'sbahn'; + + if ( $result->can('classes') ) { + my @classes = $result->classes; + if ( @classes == 0 ) { + $linetype = 'ext'; + } + elsif ( grep { $_ eq 'S' } @classes ) { + $linetype = 'sbahn'; + } + elsif ( grep { $_ eq 'F' } @classes ) { + $linetype = 'fern'; + } } - elsif ( grep { $_ eq 'F' } @classes ) { - $linetype = 'fern'; + elsif ( $result->can('class') ) { + if ( $result->class <= 2 ) { + $linetype = 'fern'; + } + elsif ( $result->class == 16 ) { + $linetype = 'sbahn'; + } + elsif ( $result->class == 32 ) { + $linetype = 'bus'; + } + elsif ( $result->class == 128 ) { + $linetype = 'ubahn'; + } + elsif ( $result->class == 256 ) { + $linetype = 'tram'; + } } $self->render_later; @@ -965,8 +1007,6 @@ sub train_details { my ($self) = @_; my $train = $self->stash('train'); - my ( $train_type, $train_no ) = ( $train =~ m{ ^ (\S+) \s+ (.*) $ }x ); - # TODO error handling if ( $self->param('ajax') ) { @@ -980,9 +1020,9 @@ sub train_details { $self->stash( version => $self->config->{version} ); my $res = { - train_type => $train_type, + train_type => undef, train_line => undef, - train_no => $train_no, + train_no => undef, route_pre_diff => [], route_post_diff => [], moreinfo => [], @@ -990,22 +1030,38 @@ sub train_details { replacement_for => [], }; - $self->stash( title => "${train_type} ${train_no}" ); - $self->stash( hide_opts => 1 ); + my %opt; + if ( $train =~ m{[|]} ) { + $opt{trip_id} = $train; + } + else { + my ( $train_type, $train_no ) = ( $train =~ m{ ^ (\S+) \s+ (.*) $ }x ); + $res->{train_type} = $train_type; + $res->{train_no} = $train_no; + $self->stash( title => "${train_type} ${train_no}" ); + $opt{train_type} = $train_type; + $opt{train_no} = $train_no; + } + + $self->stash( hide_opts => 1 ); $self->render_later; my $linetype = 'bahn'; - $self->hafas->get_route_timestamps_p( - train_type => $train_type, - train_no => $train_no - )->then( + $self->hafas->get_route_timestamps_p(%opt)->then( sub { my ( $route_ts, $journey ) = @_; $res->{trip_id} = $journey->id; + if ( not $res->{train_type} ) { + my $train_type = $res->{train_type} = $journey->type // q{}; + my $train_no = $res->{train_no} = $journey->number // q{}; + $res->{train_line} = $journey->line_no // q{}; + $self->stash( title => "${train_type} ${train_no}" ); + } + if ( not defined $journey->class ) { $linetype = 'ext'; } @@ -1018,6 +1074,15 @@ sub train_details { elsif ( $journey->class <= 16 ) { $linetype = 'sbahn'; } + elsif ( $journey->class == 32 ) { + $linetype = 'bus'; + } + elsif ( $journey->class == 128 ) { + $linetype = 'ubahn'; + } + elsif ( $journey->class == 256 ) { + $linetype = 'tram'; + } $res->{origin} = $journey->route_start; $res->{destination} = $journey->route_end; @@ -1031,6 +1096,45 @@ sub train_details { } } + if ( my $req_name = $self->param('highlight') ) { + my $split; + for my $i ( 0 .. $#{ $res->{route_post_diff} } ) { + if ( $res->{route_post_diff}[$i]{name} eq $req_name ) { + $split = $i; + } + } + if ( defined $split ) { + $self->stash( station_name => $req_name ); + for my $i ( 0 .. $split - 1 ) { + push( + @{ $res->{route_pre_diff} }, + shift( @{ $res->{route_post_diff} } ) + ); + } + my $station_info = shift( @{ $res->{route_post_diff} } ); + if ( $station_info->{sched_arr} ) { + $res->{sched_arrival} + = $station_info->{sched_arr}->strftime('%H:%M'); + } + if ( $station_info->{rt_arr} ) { + $res->{arrival} + = $station_info->{rt_arr}->strftime('%H:%M'); + } + if ( $station_info->{sched_dep} ) { + $res->{sched_departure} + = $station_info->{sched_dep}->strftime('%H:%M'); + } + if ( $station_info->{rt_dep} ) { + $res->{departure} + = $station_info->{rt_dep}->strftime('%H:%M'); + } + $res->{arrival_is_cancelled} + = $station_info->{arr_cancelled}; + $res->{departure_is_cancelled} + = $station_info->{dep_cancelled}; + } + } + my @him_messages; my @him_details; for my $message ( $journey->messages ) { @@ -1184,21 +1288,46 @@ sub handle_result { { next; } - my ( $info, $moreinfo ) - = $self->format_iris_result_info( $template, $result ); + my ( $info, $moreinfo ); + if ( $result->can('replacement_for') ) { + ( $info, $moreinfo ) + = $self->format_iris_result_info( $template, $result ); + } - my $time = $result->time; + my $time + = $result->can('time') + ? $result->time + : $result->sched_datetime->strftime('%H:%M'); my $linetype = 'bahn'; - my @classes = $result->classes; - if ( @classes == 0 ) { - $linetype = 'ext'; - } - elsif ( grep { $_ eq 'S' } @classes ) { - $linetype = 'sbahn'; + if ( $result->can('classes') ) { + my @classes = $result->classes; + if ( @classes == 0 ) { + $linetype = 'ext'; + } + elsif ( grep { $_ eq 'S' } @classes ) { + $linetype = 'sbahn'; + } + elsif ( grep { $_ eq 'F' } @classes ) { + $linetype = 'fern'; + } } - elsif ( grep { $_ eq 'F' } @classes ) { - $linetype = 'fern'; + elsif ( $result->can('class') ) { + if ( $result->class <= 2 ) { + $linetype = 'fern'; + } + elsif ( $result->class == 16 ) { + $linetype = 'sbahn'; + } + elsif ( $result->class == 32 ) { + $linetype = 'bus'; + } + elsif ( $result->class == 128 ) { + $linetype = 'ubahn'; + } + elsif ( $result->class == 256 ) { + $linetype = 'tram'; + } } # ->time defaults to dep, so we only need to overwrite $time @@ -1319,70 +1448,120 @@ sub handle_result { ); } else { - push( - @departures, - { - time => $time, - sched_arrival => $result->sched_arrival - ? $result->sched_arrival->strftime('%H:%M') - : undef, - sched_departure => $result->sched_departure - ? $result->sched_departure->strftime('%H:%M') - : undef, - arrival => $result->arrival - ? $result->arrival->strftime('%H:%M') - : undef, - departure => $result->departure - ? $result->departure->strftime('%H:%M') - : undef, - train => $result->train, - train_type => $result->type // '', - train_line => $result->line_no, - train_no => $result->train_no, - via => [ $result->route_interesting(3) ], - destination => $result->destination, - origin => $result->origin, - platform => $result->platform, - scheduled_platform => $result->sched_platform, - info => $info, - is_cancelled => $result->is_cancelled, - departure_is_cancelled => $result->departure_is_cancelled, - arrival_is_cancelled => $result->arrival_is_cancelled, - linetype => $linetype, - messages => { - delay => [ - map { { timestamp => $_->[0], text => $_->[1] } } - $result->delay_messages + if ( $result->can('replacement_for') ) { + push( + @departures, + { + time => $time, + sched_arrival => $result->sched_arrival + ? $result->sched_arrival->strftime('%H:%M') + : undef, + sched_departure => $result->sched_departure + ? $result->sched_departure->strftime('%H:%M') + : undef, + arrival => $result->arrival + ? $result->arrival->strftime('%H:%M') + : undef, + departure => $result->departure + ? $result->departure->strftime('%H:%M') + : undef, + train => $result->train, + train_type => $result->type // '', + train_line => $result->line_no, + train_no => $result->train_no, + via => [ $result->route_interesting(3) ], + destination => $result->destination, + origin => $result->origin, + platform => $result->platform, + scheduled_platform => $result->sched_platform, + info => $info, + is_cancelled => $result->is_cancelled, + departure_is_cancelled => + $result->departure_is_cancelled, + arrival_is_cancelled => $result->arrival_is_cancelled, + linetype => $linetype, + messages => { + delay => [ + map { + { + timestamp => $_->[0], + text => $_->[1] + } + } $result->delay_messages + ], + qos => [ + map { + { + timestamp => $_->[0], + text => $_->[1] + } + } $result->qos_messages + ], + }, + station => $result->station, + moreinfo => $moreinfo, + delay => $delay, + missing_realtime => ( + not $result->has_realtime + and $result->start < $now ? 1 : 0 + ), + route_pre => [ $result->route_pre ], + route_post => [ $result->route_post ], + additional_stops => [ $result->additional_stops ], + canceled_stops => [ $result->canceled_stops ], + replaced_by => [ + map { $_->type . q{ } . $_->train_no } + $result->replaced_by ], - qos => [ - map { { timestamp => $_->[0], text => $_->[1] } } - $result->qos_messages + replacement_for => [ + map { $_->type . q{ } . $_->train_no } + $result->replacement_for ], - }, - station => $result->station, - moreinfo => $moreinfo, - delay => $delay, - missing_realtime => ( - not $result->has_realtime - and $result->start < $now ? 1 : 0 - ), - route_pre => [ $result->route_pre ], - route_post => [ $result->route_post ], - additional_stops => [ $result->additional_stops ], - canceled_stops => [ $result->canceled_stops ], - replaced_by => [ - map { $_->type . q{ } . $_->train_no } - $result->replaced_by - ], - replacement_for => [ - map { $_->type . q{ } . $_->train_no } - $result->replacement_for - ], - wr_link => $result->sched_departure - ? $result->sched_departure->strftime('%Y%m%d%H%M') - : undef, - } - ); + wr_link => $result->sched_departure + ? $result->sched_departure->strftime('%Y%m%d%H%M') + : undef, + } + ); + } + else { + push( + @departures, + { + time => $time, + sched_departure => $result->sched_datetime + ? $result->sched_datetime->strftime('%H:%M') + : undef, + departure => $result->rt_datetime + ? $result->rt_datetime->strftime('%H:%M') + : undef, + train => $result->name, + train_type => q{}, + train_line => $result->line, + train_no => $result->number, + journey_id => $result->id, + via => + [ map { $_->{name} } $result->route_interesting(3) ], + destination => $result->destination, + origin => $result->origin, + platform => $result->platform, + scheduled_platform => $result->sched_platform, + info => $info, + is_cancelled => $result->is_cancelled + || $result->is_partially_cancelled, + linetype => $linetype, + station => $result->station, + moreinfo => $moreinfo, + delay => $delay, + replaced_by => [], + replacement_for => [], + route_pre => [], + route_post => [ map { $_->{name} } $result->route ], + wr_link => $result->sched_datetime + ? $result->sched_datetime->strftime('%Y%m%d%H%M') + : undef, + } + ); + } if ( $self->param('train') ) { $self->render_train( $result, $departures[-1], $data->{station_name} // $self->stash('station') ); diff --git a/lib/DBInfoscreen/Helper/HAFAS.pm b/lib/DBInfoscreen/Helper/HAFAS.pm index a6769aa..6111a5b 100644 --- a/lib/DBInfoscreen/Helper/HAFAS.pm +++ b/lib/DBInfoscreen/Helper/HAFAS.pm @@ -181,7 +181,19 @@ sub get_route_timestamps_p { my $promise = Mojo::Promise->new; my $now = DateTime->now( time_zone => 'Europe/Berlin' ); - if ( $opt{train} ) { + my $hafas_promise; + + if ( $opt{trip_id} ) { + $hafas_promise = Travel::Status::DE::HAFAS->new_p( + journey => { + id => $opt{trip_id}, + }, + cache => $self->{realtime_cache}, + promise => 'Mojo::Promise', + user_agent => $self->{user_agent}->request_timeout(10) + ); + } + elsif ( $opt{train} ) { $opt{date_yy} = $opt{train}->start->strftime('%d.%m.%y'); $opt{date_yyyy} = $opt{train}->start->strftime('%d.%m.%Y'); $opt{train_req} = $opt{train}->type . ' ' . $opt{train}->train_no; @@ -193,7 +205,7 @@ sub get_route_timestamps_p { $opt{date_yyyy} = $now->strftime('%d.%m.%Y'); } - $self->trainsearch_p(%opt)->then( + $hafas_promise //= $self->trainsearch_p(%opt)->then( sub { my ($trainsearch_result) = @_; my $trip_id = $trainsearch_result->{trip_id}; @@ -208,7 +220,9 @@ sub get_route_timestamps_p { user_agent => $self->{user_agent}->request_timeout(10) ); } - )->then( + ); + + $hafas_promise->then( sub { my ($hafas) = @_; my $journey = $hafas->result; diff --git a/public/static/js/collapse.js b/public/static/js/collapse.js index 82420c7..0c4178f 100644 --- a/public/static/js/collapse.js +++ b/public/static/js/collapse.js @@ -29,7 +29,7 @@ function dbf_show_moreinfo(trainElem, keep_old) { $('.moreinfo').each(function() { const infoElem = $(this); if (!keep_old) { - $('.moreinfo .train-line').removeClass('bahn sbahn fern ext').addClass(trainElem.data('linetype')); + $('.moreinfo .train-line').removeClass('sbahn fern ext ubahn bus tram').addClass(trainElem.data('linetype')); $('.moreinfo .train-line').text(trainElem.data('line')); $('.moreinfo .train-no').text(trainElem.data('no')); $('.moreinfo .train-origin').text(trainElem.data('from')); @@ -80,7 +80,7 @@ function dbf_show_moreinfo(trainElem, keep_old) { } $('.moreinfo .mfooter').append('Fahrtverlauf: <ul class="mroute">' + routebuf + '</ul>'); } - $.get(window.location.href, {train: trainElem.data('train'), ajax: 1}, function(data) { + $.get(window.location.href, {train: trainElem.data('train'), jid: trainElem.data('jid'), ajax: 1}, function(data) { $('.moreinfo').html(data); }).fail(function() { $('.moreinfo .mfooter').append('Der Zug ist abgefahren (Zug nicht gefunden)'); @@ -99,13 +99,20 @@ function dbf_reg_handlers() { if (window.location.href.includes('detailed=1')) { suffix += '&detailed=1'; } + if (window.location.href.includes('hafas=1')) { + suffix += '&hafas=1&highlight=' + trainElem.data('station'); + } if (window.location.href.includes('past=1')) { suffix += '&past=1'; } if (window.location.href.includes('rt=1') || window.location.href.includes('show_realtime=1')) { suffix += '&rt=1'; } - history.pushState({'page':'traindetail','station':station,'train':trainElem.data('no')}, 'test', '/z/' + trainElem.data('train') + '/' + trainElem.data('station') + suffix); + if (window.location.href.includes('hafas=1')) { + history.pushState({'page':'traindetail','jid':trainElem.data('jid')}, 'test', '/z/' + trainElem.data('jid') + suffix); + } else { + history.pushState({'page':'traindetail','station':station,'train':trainElem.data('no')}, 'test', '/z/' + trainElem.data('train') + '/' + trainElem.data('station') + suffix); + } dbf_show_moreinfo(trainElem, false); }); const trainid = $(location).attr('hash').substr(1); diff --git a/templates/_train_details.html.ep b/templates/_train_details.html.ep index ab6ea45..0240a18 100644 --- a/templates/_train_details.html.ep +++ b/templates/_train_details.html.ep @@ -210,7 +210,7 @@ <ul class="mroute"> % for my $stop (@{$departure->{route_pre_diff}}) { <li class="<%= $stop->{isPast} ? 'past-stop' : 'future-stop' %>"> - <a href="<%= url_for('station', station => $stop->{eva} // $stop->{name})->query({detailed => param('detailed'), past => param('past'), rt => param('rt')}) %>#<%= ($departure->{train_type} // q{x}) . ($departure->{train_no} // q{x}) %>" class=" + <a href="<%= url_for('station', station => $stop->{eva} // $stop->{name})->query({detailed => param('detailed'), past => param('past'), rt => param('rt'), hafas => param('hafas')}) %>#<%= ($departure->{train_type} // q{x}) . ($departure->{train_no} // q{x}) %>" class=" % if ($stop->{isAdditional}) { additional-stop % } @@ -265,7 +265,7 @@ % } % for my $stop (@{$departure->{route_post_diff}}) { <li class="<%= $stop->{isPast} ? 'past-stop' : 'future-stop' %>"> - <a href="<%= url_for('station', station => $stop->{eva} // $stop->{name})->query({detailed => param('detailed'), past => param('past'), rt => param('rt')}) %>#<%= ($departure->{train_type} // q{x}) . ($departure->{train_no} // q{x}) %>" class=" + <a href="<%= url_for('station', station => $stop->{eva} // $stop->{name})->query({detailed => param('detailed'), past => param('past'), rt => param('rt'), hafas => param('hafas')}) %>#<%= ($departure->{train_type} // q{x}) . ($departure->{train_no} // q{x}) %>" class=" % if ($stop->{isAdditional}) { additional-stop % } @@ -352,7 +352,7 @@ % my $tt = $train->{type} // $train->{rawType} // 'Zug'; % $tt =~ s{ .*|[0-9]}{}; % if ($tt ne 'Zug') { - <li><a href="<%= url_for('train', train => "$tt $train_no")->query({detailed => param('detailed'), past => param('past'), rt => param('rt')}) %>"><%= $tt %> <%= $train_no %></a> + <li><a href="<%= url_for('train', train => "$tt $train_no")->query({detailed => param('detailed'), past => param('past'), rt => param('rt'), hafas => param('hafas')}) %>"><%= $tt %> <%= $train_no %></a> % } % else { <li><%= $tt %> <%= $train_no %> @@ -377,7 +377,7 @@ % my $tt = $train->{type} // $train->{rawType} // 'Zug'; % $tt =~ s{ .*|[0-9]}{}; % if ($tt ne 'Zug') { - <li><a href="<%= url_for('train', train => "$tt $train_no")->query({detailed => param('detailed'), past => param('past'), rt => param('rt')}) %>"><%= $tt %> <%= $train_no %></a> + <li><a href="<%= url_for('train', train => "$tt $train_no")->query({detailed => param('detailed'), past => param('past'), rt => param('rt'), hafas => param('hafas')}) %>"><%= $tt %> <%= $train_no %></a> % } % else { <li><%= $tt %> <%= $train_no %> diff --git a/templates/app.html.ep b/templates/app.html.ep index 6062905..360a2f0 100644 --- a/templates/app.html.ep +++ b/templates/app.html.ep @@ -30,6 +30,9 @@ % $route_str .= $stop . ($via_cur < $via_max ? ' - ' : q{}); % } <li +% if (param('hafas')) { + data-jid="<%= $departure->{journey_id} %>" +% } data-train="<%= ($departure->{train_type} // q{}) %> <%= ($departure->{train_no} // $departure->{train} // q{}) %>" data-line="<%= $departure->{train_type} %> <%= $departure->{train_line} // $departure->{train_no} %>" data-no="<%= $departure->{train_line} ? $departure->{train_no} : q{} %>" @@ -49,7 +52,12 @@ % else { > % } - <a href="/z/<%= Mojo::Util::url_escape(($departure->{train_type} // q{}) . ' ' . ($departure->{train_no} // $departure->{train} // q{}) . '/' . ($departure->{station} // $station)) %>"> +% if (param('hafas')) { + <a href="/z/<%= Mojo::Util::url_escape($departure->{journey_id}) . '?hafas=1&highlight=' . Mojo::Util::url_escape($departure->{station} // $station) %>"> +% } +% else { + <a href="/z/<%= Mojo::Util::url_escape(($departure->{train_type} // q{}) . ' ' . ($departure->{train_no} // $departure->{train} // q{})) . '/' . Mojo::Util::url_escape($departure->{station} // $station) %>"> +% } <div class="anchor" id="<%= ($departure->{train_type} // q{x}) . ($departure->{train_no} // q{x}) %>"></div> <div class="line <%= $departure->{linetype} %>"> % if ($departure->{train_type} and $departure->{train_no}) { diff --git a/templates/layouts/app.html.ep b/templates/layouts/app.html.ep index 8d4b2df..4ed395c 100644 --- a/templates/layouts/app.html.ep +++ b/templates/layouts/app.html.ep @@ -193,6 +193,14 @@ Bitte eine Station aus der Liste auswählen</div> </div> <div class="field"> <div class="desc"> + %= check_box 'hafas' => 1, id => 'id_hafas' + <label for="id_hafas"> + Bus, Stadtbahn und weiteren Nahverkehr anzeigen (via DB HAFAS mgate) + </label> + </div> + </div> + <div class="field"> + <div class="desc"> Nur Züge über </div> <div> |