]> iEval git - gruntmaster-page.git/blobdiff - lib/Plack/App/Gruntmaster/HTML.pm
Refactoring, part II (HTML::Seamstress)
[gruntmaster-page.git] / lib / Plack / App / Gruntmaster / HTML.pm
diff --git a/lib/Plack/App/Gruntmaster/HTML.pm b/lib/Plack/App/Gruntmaster/HTML.pm
new file mode 100644 (file)
index 0000000..16ae617
--- /dev/null
@@ -0,0 +1,204 @@
+package Plack::App::Gruntmaster::HTML;
+use v5.14;
+use parent qw/Exporter/;
+our @EXPORT = qw/render render_article/;
+
+use File::Slurp qw/read_file/;
+use HTML::Seamstress;
+use POSIX qw//;
+use Data::Dumper qw/Dumper/;
+
+sub ftime ($)   { POSIX::strftime '%c', localtime shift }
+sub literal ($) { HTML::Element::Library::super_literal shift // '' }
+
+sub HTML::Element::edit_href {
+       my ($self, $sub) = @_;
+       local $_ = $self->attr('href');
+       $sub->();
+       $self->attr(href => $_);
+}
+
+sub HTML::Element::iter3 {
+       my ($self, $data, $code) = @_;
+       my $orig = $self;
+       my $prev = $orig;
+       for my $el (@$data) {
+               my $current = $orig->clone;
+               $code->($el, $current);
+               $prev->postinsert($current);
+               $prev = $current;
+       }
+       $orig->detach;
+}
+
+sub HTML::Element::fid    { shift->look_down(id    => shift) }
+sub HTML::Element::fclass { shift->look_down(class => shift) }
+
+sub HTML::Element::namedlink {
+       my ($self, $id, $name) = @_;
+       $name = $id unless $name =~ /[[:graph:]]/;
+       $self = $self->find('a');
+       $self->edit_href(sub {s/id/$id/});
+       $self->replace_content($name);
+}
+
+sub render {
+       my ($tmpl, $lang, %args) = @_;
+       $lang //= 'en';
+       my $meat = _render($tmpl, $lang, %args);
+       _render('skel', $lang, %args, meat => $meat)
+}
+
+sub render_article {
+       my ($art, $lang) = @_;
+       $lang //= 'en';
+       my $title = read_file "a/$art.$lang.title";
+       my $meat  = read_file "a/$art.$lang";
+       _render('skel', $lang, title => $title , meat => $meat)
+}
+
+sub _render {
+       my ($tmpl, $lang, %args) = @_;
+       my $builder = HTML::Seamstress->new;
+       $builder->ignore_unknown(0);
+       my $tree = $builder->parse_file("tmpl/$tmpl.$lang");
+       $tree = $tree->guts unless $tmpl eq 'skel';
+       $tree->defmap(smap => \%args);
+       my $process = __PACKAGE__->can("process_$tmpl");
+       $process->($tree, %args) if $process;
+       $tree->as_HTML;
+}
+
+sub process_skel {
+       my ($tree, %args) = @_;
+       $tree->content_handler(
+               title   =>         $args{title},
+               content => literal $args{meat});
+}
+
+sub process_us_entry {
+       my ($tree, %args) = @_;
+       $tree->fid($_)->attr('href', "/$_/?owner=$args{id}") for qw/log pb/;
+}
+
+sub process_us {
+       my ($tree, %args) = @_;
+       my $item = $tree->fclass('list-group-item');
+       $item->replace_with(map {
+               my $new = $item->clone;
+               $new->attr(href => $_->{id});
+               $new->replace_content($_->{name} || $_->{id});
+               $new
+       } @{$args{us}});
+}
+
+sub process_ct_entry {
+       my ($tree, %args) = @_;
+       $_->edit_href (sub {s/contest_id/$args{id}/}) for $tree->find('a');
+       $tree->fid('links')->detach unless $args{started};
+       $tree->content_handler(
+               start       => ftime   $args{start},
+               stop        => ftime   $args{stop},
+               description => literal $args{description});
+}
+
+sub process_ct {
+       my ($tree, %args) = @_;
+       my $iter = sub {
+               my ($data, $tr) = @_;
+               $data->{$_} = ftime $data->{$_} for qw/start stop/;
+               $tr->hashmap(class => $data, [qw/name owner/]);
+               $tr->fclass('name')->namedlink($data->{id}, $data->{name});
+               $tr->fclass('owner')->namedlink($data->{owner}, $data->{owner_name});
+       };
+       $args{$_} ? $tree->fid($_)->find('tbody')->find('tr')->iter3($args{$_}, $iter) : $tree->fid($_)->detach for qw/running pending finished/;
+}
+
+sub process_pb_entry {
+       my ($tree, %args) = @_;
+       $tree->fid('owner')->edit_href(sub{s/owner_id/$args{owner}/});
+       $tree->fid('job_log')->edit_href(sub{s/problem_id/$args{id}/});
+       $tree->content_handler(
+               statement => literal $args{statement},
+               author    =>         $args{author},
+               owner     =>         $args{owner_name} || $args{owner});
+       if ($args{cansubmit}) {
+               $tree->look_down(name => 'problem')->attr(value => $args{id});
+               my $contest = $tree->look_down(name => 'contest');
+               $contest->attr(value => $args{contest}) if $args{contest};
+               $contest->detach unless $args{contest}
+       } else {
+               $tree->fid('submit')->detach
+       }
+}
+
+sub process_pb {
+       my ($tree, %args) = @_;
+       my $titer = sub {
+               my ($data, $tr) = @_;
+               $tr->set_child_content(class => 'author', $data->{author});
+               $tr->fclass('name')->namedlink($data->{id}, $data->{name});
+               $tr->fclass('name')->find('a')->edit_href(sub {$_ .= "?contest=$args{contest}"}) if $args{contest};
+               $tr->fclass('owner')->namedlink($data->{owner}, $data->{owner_name});
+       };
+       my $iter = sub {
+               my ($data, $div) = @_;
+               $div->attr(id => $data);
+               $div->find('h2')->replace_content(ucfirst $data);
+               $div->find('tbody')->iter3($args{$data}, $titer);
+       };
+       $tree->fid('beginner')->iter3([grep {$args{$_}} qw/beginner easy medium hard/], $iter);
+}
+
+sub process_log_entry {
+       my ($tree, %args) = @_;
+       $args{errors} ? $tree->fid('errors')->find('pre')->replace_content($args{errors}) : $tree->fid('errors')->detach;
+       my $iter = sub {
+               my ($data, $tr) = @_;
+               $data->{time} = sprintf '%.4fs', $data->{time};
+               $tr->defmap(class => $data);
+               $tr->fclass('result_text')->attr(class => "r$data->{result}")
+       };
+       @{$args{results}} ? $tree->fid('results')->find('tbody')->find('tr')->iter3($args{results}, $iter) : $tree->fid('results')->detach;
+}
+
+sub process_log {
+       my ($tree, %args) = @_;
+       my $iter = sub {
+               my ($data, $tr) = @_;
+               $tr->fclass('id')->namedlink($data->{id});
+               $tr->fclass('problem')->namedlink($data->{problem}, $data->{problem_name});
+               $tr->fclass('problem')->find('a')->edit_href(sub{$_ .= "?contest=$args{contest}"}) if $args{contest};
+               $tr->fclass('date')->replace_content(ftime $data->{date});
+               $tr->fclass('size')->namedlink("$data->{id}.$data->{extension}", sprintf "%.2fKB", $data->{size}/1024);
+               $tr->fclass('size')->attr('data-private', '') if $data->{private};
+               $tr->fclass('owner')->namedlink($data->{owner}, $data->{owner_name});
+               $tr->fclass('result_text')->replace_content($data->{result_text});
+               $tr->fclass('result_text')->attr(class => "r$data->{result}");
+       };
+       $tree->find('table')->find('tbody')->find('tr')->iter3($args{log}, $iter);
+       $args{next_page} ? $tree->fclass('next')->namedlink($args{next_page}, 'Next') : $tree->fclass('next')->attr(class => 'next disabled');
+       $args{previous_page} ? $tree->fclass('previous')->namedlink($args{previous_page}, 'Previous') : $tree->fclass('previous')->attr(class => 'previous disabled');
+       $tree->fclass('current')->replace_content("Page $args{current_page} of $args{last_page}");
+}
+
+sub process_st {
+       my ($tree, %args) = @_;
+       $args{problems} //= [];
+       my $pbiter = sub {
+               my ($data, $th) = @_;
+               $th->attr(class => undef);
+               $th->namedlink($data->id, $data->name);
+       };
+       $tree->fclass('problem')->iter3($args{problems}, $pbiter);
+       my $iter = sub {
+               my ($st, $tr) = @_;
+               $tr->set_child_content(class => 'rank', $st->{rank});
+               $tr->set_child_content(class => 'score', $st->{score});
+               $tr->fclass('user')->namedlink($st->{user}->id, $st->{user}->name);
+               my $pbscore = $tr->fclass('pbscore');
+               $pbscore->detach unless $st->{problems};
+               $pbscore->iter($pbscore => @{$st->{scores}});
+       };
+       $tree->find('tbody')->find('tr')->iter3($args{st}, $iter);
+}
This page took 0.027368 seconds and 4 git commands to generate.