From d093f01c664ae2afaaed1a5b41e65ebc0ac154f3 Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Mon, 11 Mar 2013 12:06:16 +0100 Subject: [PATCH] L/Presenter: mehrere Funktionen aus L in Presenter verschoben --- SL/Presenter.pm | 1 + SL/Presenter/Tag.pm | 320 ++++++++++++++++++++++++++++++++++++++++ SL/Template/Plugin/L.pm | 283 +++-------------------------------- 3 files changed, 343 insertions(+), 261 deletions(-) create mode 100644 SL/Presenter/Tag.pm diff --git a/SL/Presenter.pm b/SL/Presenter.pm index abfd561fa..61ddab408 100644 --- a/SL/Presenter.pm +++ b/SL/Presenter.pm @@ -15,6 +15,7 @@ use SL::Presenter::Order; use SL::Presenter::Project; use SL::Presenter::Record; use SL::Presenter::Text; +use SL::Presenter::Tag; sub get { return $::request->presenter; diff --git a/SL/Presenter/Tag.pm b/SL/Presenter/Tag.pm new file mode 100644 index 000000000..b42d61dbb --- /dev/null +++ b/SL/Presenter/Tag.pm @@ -0,0 +1,320 @@ +package SL::Presenter::Tag; + +use strict; + +use parent qw(Exporter); + +use Exporter qw(import); +our @EXPORT = qw(html_tag input_tag name_to_id select_tag stringify_attributes); + +use Carp; + +my %_valueless_attributes = map { $_ => 1 } qw( + checked compact declare defer disabled ismap multiple noresize noshade nowrap + readonly selected +); + +sub stringify_attributes { + my ($self, %params) = @_; + + my @result = (); + while (my ($name, $value) = each %params) { + next unless $name; + next if $_valueless_attributes{$name} && !$value; + $value = '' if !defined($value); + push @result, $_valueless_attributes{$name} ? $self->escape($name) : $self->escape($name) . '="' . $self->escape($value) . '"'; + } + + return @result ? ' ' . join(' ', @result) : ''; +} + +sub html_tag { + my ($self, $tag, $content, %params) = @_; + my $attributes = $self->stringify_attributes(%params); + + return "<${tag}${attributes}>" unless defined($content); + return "<${tag}${attributes}>${content}"; +} + +sub input_tag { + my ($self, $name, $value, %attributes) = @_; + + $attributes{id} ||= $self->name_to_id($name); + $attributes{type} ||= 'text'; + + return $self->html_tag('input', undef, %attributes, name => $name, value => $value); +} + +sub name_to_id { + my ($self, $name) = @_; + + $name =~ s/[^\w_]/_/g; + $name =~ s/_+/_/g; + + return $name; +} + +sub select_tag { + my ($self, $name, $collection, %attributes) = @_; + + $attributes{id} ||= $self->name_to_id($name); + + my $value_key = delete($attributes{value_key}) || 'id'; + my $title_key = delete($attributes{title_key}) || $value_key; + my $default_key = delete($attributes{default_key}) || 'selected'; + + + my $value_title_sub = delete($attributes{value_title_sub}); + + my $value_sub = delete($attributes{value_sub}); + my $title_sub = delete($attributes{title_sub}); + my $default_sub = delete($attributes{default_sub}); + + my $with_empty = delete($attributes{with_empty}); + my $empty_title = delete($attributes{empty_title}); + + my $with_optgroups = delete($attributes{with_optgroups}); + + my %selected; + + if ( ref($attributes{default}) eq 'ARRAY' ) { + + foreach my $entry (@{$attributes{default}}) { + $selected{$entry} = 1; + } + } elsif ( defined($attributes{default}) ) { + $selected{$attributes{default}} = 1; + } + + delete($attributes{default}); + + + my @all_options; + push @all_options, [undef, $empty_title || ''] if $with_empty; + + my $normalize_entry = sub { + my ($type, $entry, $sub, $key) = @_; + + return $sub->($entry) if $sub; + + my $ref = ref($entry); + + if ( !$ref ) { + return $entry if $type eq 'value' || $type eq 'title'; + return 0; + } + + if ( $ref eq 'ARRAY' ) { + return $entry->[ $type eq 'value' ? 0 : $type eq 'title' ? 1 : 2 ]; + } + + return $entry->{$key} if $ref eq 'HASH'; + return $entry->$key if $type ne 'default' || $entry->can($key); + return undef; + }; + + my $list_to_code = sub { + my ($sub_collection) = @_; + + my @options; + foreach my $entry ( @{ $sub_collection } ) { + my $value; + my $title; + + if ( $value_title_sub ) { + ($value, $title) = @{ $value_title_sub->($entry) }; + } else { + + $value = $normalize_entry->('value', $entry, $value_sub, $value_key); + $title = $normalize_entry->('title', $entry, $title_sub, $title_key); + } + + my $default = $normalize_entry->('default', $entry, $default_sub, $default_key); + + push(@options, [$value, $title, $default]); + } + + foreach my $entry (@options) { + $entry->[2] = 1 if $selected{$entry->[0]}; + } + + return join '', map { $self->html_tag('option', $self->escape($_->[1]), value => $_->[0], selected => $_->[2]) } @options; + }; + + my $code; + + if (!$with_optgroups) { + $code = $list_to_code->($collection); + + } else { + $code = join '', map { + my ($optgroup_title, $sub_collection) = @{ $_ }; + $self->html_tag('optgroup', $list_to_code->($sub_collection), label => $optgroup_title) + } @{ $collection }; + } + + return $self->html_tag('select', $code, %attributes, name => $name); +} + +1; +__END__ + +=pod + +=encoding utf8 + +=head1 NAME + +SL::Presenter::Tag - Layouting / tag generation + +=head1 SYNOPSIS + +Usage from a template: + + [% USE P %] + + [% P.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right', 1 ] ]) %] + + [% P.select_tag('direction', [ { direction => 'left', display => 'To the left' }, + { direction => 'right', display => 'To the right' } ], + value_key => 'direction', title_key => 'display', default => 'right')) %] + + [% P.select_tag('direction', [ { direction => 'left', display => 'To the left' }, + { direction => 'right', display => 'To the right', selected => 1 } ], + value_key => 'direction', title_key => 'display')) %] + +=head1 DESCRIPTION + +A module modeled a bit after Rails' ActionView helpers. Several small +functions that create HTML tags from various kinds of data sources. + +=head1 FUNCTIONS + +=head2 LOW-LEVEL FUNCTIONS + +=over 4 + +=item C + +Creates an opening and closing HTML tag for C<$tag_name> and puts +C<$content_string> between the two. If C<$content_string> is undefined +or empty then only a Etag/E tag will be created. Attributes +are key/value pairs added to the opening tag. + +C<$content_string> is not HTML escaped. + +=item C + +Converts a name to a HTML id by replacing various characters. + +=item C + +Creates a string from all elements in C<%items> suitable for usage as +HTML tag attributes. Keys and values are HTML escaped even though keys +must not contain non-ASCII characters for browsers to accept them. + +=back + +=head2 HIGH-LEVEL FUNCTIONS + +=over 4 + +=item C + +Creates a HTML 'input type=text' tag named C<$name> with the value +C<$value> and with arbitrary HTML attributes from C<%attributes>. The +tag's C defaults to C. + +=item C + +Creates a HTML 'select' tag named C<$name> with the contents of one +'EoptionE' tag for each element in C<\@collection> and with arbitrary +HTML attributes from C<%attributes>. The value +to use and the title to display are extracted from the elements in +C<\@collection>. Each element can be one of four things: + +=over 12 + +=item 1. An array reference with at least two elements. The first element is +the value, the second element is its title. The third element is optional and and should contain a boolean. +If it is true, than the element will be used as default. + +=item 2. A scalar. The scalar is both the value and the title. + +=item 3. A hash reference. In this case C<%attributes> must contain +I, I and may contain I keys that name the keys in the element to use +for the value, title and default respectively. + +=item 4. A blessed reference. In this case C<%attributes> must contain +I, I and may contain I keys that name functions called on the blessed +reference whose return values are used as the value, title and default +respectively. + +=back + +For cases 3 and 4 C<$attributes{value_key}> defaults to C, +C<$attributes{title_key}> defaults to C<$attributes{value_key}> +and C<$attributes{default_key}> defaults to C. + +In addition to pure keys/method you can also provide coderefs as I +and/or I and/or I. If present, these take precedence over keys or methods, +and are called with the element as first argument. It must return the value, title or default. + +Lastly a joint coderef I may be provided, which in turn takes +precedence over the C and C subs. It will only be called once for each +element and must return a list of value and title. + +If the option C is set then an empty element (value +C) will be used as the first element. The title to display for +this element can be set with the option C and defaults to +an empty string. + +The option C can be either a scalar or an array reference +containing the values of the options which should be set to be +selected. + +The tag's C defaults to C. + +If the option C is set then this function expects +C<\@collection> to be one level deeper. The upper-most level is +translated into a HTML C tag. So the structure becomes: + +=over 4 + +=item 1. Array of array references. Each element in the +C<\@collection> is converted into an optgroup. + +=item 2. The optgroup's C