1 package SL::Template::Plugin::L;
3 use base qw( Template::Plugin );
5 use List::MoreUtils qw(apply);
6 use List::Util qw(max);
10 { # This will give you an id for identifying html tags and such.
11 # It's guaranteed to be unique unless you exceed 10 mio calls per request.
12 # Do not use these id's to store information across requests.
13 my $_id_sequence = int rand 1e7;
15 return $_id_sequence = ($_id_sequence + 1) % 1e7;
21 return $::locale->quote_special_chars('HTML', $string);
25 return (@_ && (ref($_[0]) eq 'HASH')) ? %{ $_[0] } : @_;
29 my ($class, $context, @args) = @_;
37 die 'not an accessor' if @_ > 1;
38 return $_[0]->{CONTEXT};
45 $name =~ s/[^\w_]/_/g;
53 my %options = _hashify(@_);
56 while (my ($name, $value) = each %options) {
58 $value = '' if !defined($value);
59 push @result, _H($name) . '="' . _H($value) . '"';
62 return @result ? ' ' . join(' ', @result) : '';
69 my $attributes = $self->attributes(@_);
71 return "<${tag}${attributes}/>" unless defined($content);
72 return "<${tag}${attributes}>${content}</${tag}>";
78 my $options_str = shift;
79 my %attributes = _hashify(@_);
81 $attributes{id} ||= $self->name_to_id($name);
82 $options_str = $self->options_for_select($options_str) if ref $options_str;
84 return $self->html_tag('select', $options_str, %attributes, name => $name);
88 my ($self, $name, $content, @slurp) = @_;
89 my %attributes = _hashify(@slurp);
91 $attributes{id} ||= $self->name_to_id($name);
92 $content = $content ? _H($content) : '';
94 return $self->html_tag('textarea', $content, %attributes, name => $name);
100 my %attributes = _hashify(@_);
102 $attributes{id} ||= $self->name_to_id($name);
103 $attributes{value} = 1 unless defined $attributes{value};
104 my $label = delete $attributes{label};
106 if ($attributes{checked}) {
107 $attributes{checked} = 'checked';
109 delete $attributes{checked};
112 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'checkbox');
113 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
118 sub radio_button_tag {
121 my %attributes = _hashify(@_);
123 $attributes{value} = 1 unless defined $attributes{value};
124 $attributes{id} ||= $self->name_to_id($name . "_" . $attributes{value});
125 my $label = delete $attributes{label};
127 if ($attributes{checked}) {
128 $attributes{checked} = 'checked';
130 delete $attributes{checked};
133 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'radio');
134 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
140 my ($self, $name, $value, @slurp) = @_;
141 my %attributes = _hashify(@slurp);
143 $attributes{id} ||= $self->name_to_id($name);
144 $attributes{type} ||= 'text';
146 return $self->html_tag('input', undef, %attributes, name => $name, value => $value);
150 return shift->input_tag(@_, type => 'hidden');
154 my ($self, $content, @slurp) = @_;
155 return $self->html_tag('div', $content, @slurp);
159 my ($self, $content, @slurp) = @_;
160 return $self->html_tag('ul', $content, @slurp);
164 my ($self, $content, @slurp) = @_;
165 return $self->html_tag('li', $content, @slurp);
169 my ($self, $href, $content, @slurp) = @_;
170 my %params = _hashify(@slurp);
174 return $self->html_tag('a', $content, %params, href => $href);
178 my ($self, $name, $value, @slurp) = @_;
179 my %attributes = _hashify(@slurp);
181 $attributes{onclick} = "if (confirm('" . delete($attributes{confirm}) . "')) return true; else return false;" if $attributes{confirm};
183 return $self->input_tag($name, $value, %attributes, type => 'submit', class => 'submit');
187 my ($self, $onclick, $value, @slurp) = @_;
188 my %attributes = _hashify(@slurp);
190 return $self->input_tag(undef, $value, %attributes, type => 'button', onclick => $onclick);
193 sub options_for_select {
195 my $collection = shift;
196 my %options = _hashify(@_);
198 my $value_key = $options{value} || 'id';
199 my $title_key = $options{title} || $value_key;
201 my $value_sub = $options{value_sub};
202 my $title_sub = $options{title_sub};
204 my $value_title_sub = $options{value_title_sub};
207 my ($element, $index, $key, $sub) = @_;
208 my $ref = ref $element;
209 return $sub ? $sub->($element)
211 : $ref eq 'ARRAY' ? $element->[$index]
212 : $ref eq 'HASH' ? $element->{$key}
217 push @elements, [ undef, $options{empty_title} || '' ] if $options{with_empty};
218 push @elements, map [
219 $value_title_sub ? $value_title_sub->($_) : (
220 $access->($_, 0, $value_key, $value_sub),
221 $access->($_, 1, $title_key, $title_sub),
223 ], @{ $collection } if $collection && ref $collection eq 'ARRAY';
226 foreach my $result (@elements) {
227 my %attributes = ( value => $result->[0] );
228 $attributes{selected} = 'selected' if $options{default} && ($options{default} eq ($result->[0] || ''));
230 $code .= $self->html_tag('option', _H($result->[1]), %attributes);
237 my ($self, $data) = @_;
238 return $self->html_tag('script', $data, type => 'text/javascript');
242 my ($self, $name, $value, @slurp) = @_;
243 my %params = _hashify(@slurp);
244 my $name_e = _H($name);
246 my $datefmt = apply {
250 } $::myconfig{"dateformat"};
252 $params{cal_align} ||= 'BR';
254 $self->input_tag($name, $value,
257 title => _H($::myconfig{dateformat}),
258 onBlur => 'check_right_date_format(this)',
260 ) . ((!$params{no_cal}) ?
261 $self->html_tag('img', undef,
262 src => 'image/calendar.png',
264 title => _H($::myconfig{dateformat}),
268 "Calendar.setup({ inputField: '$name_e', ifFormat: '$datefmt', align: '$params{cal_align}', button: 'trigger$seq' });"
275 foreach my $file (@_) {
276 $file .= '.js' unless $file =~ m/\.js$/;
277 $file = "js/${file}" unless $file =~ m|/|;
279 $code .= qq|<script type="text/javascript" src="${file}"></script>|;
286 my ($self, $tabs, @slurp) = @_;
287 my %params = _hashify(@slurp);
288 my $id = 'tab_' . _tag_id();
290 $params{selected} *= 1;
292 die 'L.tabbed needs an arrayred of tabs for first argument'
293 unless ref $tabs eq 'ARRAY';
295 my (@header, @blocks);
296 for my $i (0..$#$tabs) {
297 my $tab = $tabs->[$i];
301 my $selected = $params{selected} == $i;
302 my $tab_id = _tag_id();
303 push @header, $self->li_tag(
304 $self->link('', $tab->{name}, rel => $tab_id),
305 ($selected ? (class => 'selected') : ())
307 push @blocks, $self->div_tag($tab->{data},
308 id => $tab_id, class => 'tabcontent');
311 return '' unless @header;
312 return $self->ul_tag(
313 join('', @header), id => $id, class => 'shadetabs'
316 join('', @blocks), class => 'tabcontentstyle'
319 qq|var $id = new ddtabcontent("$id");$id.setpersist(true);| .
320 qq|$id.setselectedClassTarget("link");$id.init();|
325 my ($self, $name, $src, @slurp) = @_;
326 my %params = _hashify(@slurp);
328 $params{method} ||= 'process';
330 return () if defined $params{if} && !$params{if};
333 if ($params{method} eq 'raw') {
335 } elsif ($params{method} eq 'process') {
336 $data = $self->_context->process($src, %{ $params{args} || {} });
338 die "unknown tag method '$params{method}'";
341 return () unless $data;
343 return +{ name => $name, data => $data };
347 my ($self, $name, $value, @slurp) = @_;
348 my %attributes = _hashify(@slurp);
350 my $rows = delete $attributes{rows} || 1;
351 my $min = delete $attributes{min_rows} || 1;
354 ? $self->textarea_tag($name, $value, %attributes, rows => max $rows, $min)
355 : $self->input_tag($name, $value, %attributes);
364 SL::Templates::Plugin::L -- Layouting / tag generation
368 Usage from a template:
372 [% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right' ] ]) %]
374 [% L.select_tag('direction', L.options_for_select([ { direction => 'left', display => 'To the left' },
375 { direction => 'right', display => 'To the right' } ],
376 value => 'direction', title => 'display', default => 'right')) %]
380 A module modeled a bit after Rails' ActionView helpers. Several small
381 functions that create HTML tags from various kinds of data sources.
385 =head2 LOW-LEVEL FUNCTIONS
389 =item C<name_to_id $name>
391 Converts a name to a HTML id by replacing various characters.
393 =item C<attributes %items>
395 Creates a string from all elements in C<%items> suitable for usage as
396 HTML tag attributes. Keys and values are HTML escaped even though keys
397 must not contain non-ASCII characters for browsers to accept them.
399 =item C<html_tag $tag_name, $content_string, %attributes>
401 Creates an opening and closing HTML tag for C<$tag_name> and puts
402 C<$content_string> between the two. If C<$content_string> is undefined
403 or empty then only a E<lt>tag/E<gt> tag will be created. Attributes
404 are key/value pairs added to the opening tag.
406 C<$content_string> is not HTML escaped.
410 =head2 HIGH-LEVEL FUNCTIONS
414 =item C<select_tag $name, $options_string, %attributes>
416 Creates a HTML 'select' tag named C<$name> with the contents
417 C<$options_string> and with arbitrary HTML attributes from
418 C<%attributes>. The tag's C<id> defaults to C<name_to_id($name)>.
420 The C<$options_string> is usually created by the
421 L</options_for_select> function. If C<$options_string> is an array
422 reference then it will be passed to L</options_for_select>
425 =item C<input_tag $name, $value, %attributes>
427 Creates a HTML 'input type=text' tag named C<$name> with the value
428 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
429 tag's C<id> defaults to C<name_to_id($name)>.
431 =item C<hidden_tag $name, $value, %attributes>
433 Creates a HTML 'input type=hidden' tag named C<$name> with the value
434 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
435 tag's C<id> defaults to C<name_to_id($name)>.
437 =item C<submit_tag $name, $value, %attributes>
439 Creates a HTML 'input type=submit class=submit' tag named C<$name> with the
440 value C<$value> and with arbitrary HTML attributes from C<%attributes>. The
441 tag's C<id> defaults to C<name_to_id($name)>.
443 If C<$attributes{confirm}> is set then a JavaScript popup dialog will
444 be added via the C<onclick> handler asking the question given with
445 C<$attributes{confirm}>. If request is only submitted if the user
446 clicks the dialog's ok/yes button.
448 =item C<textarea_tag $name, $value, %attributes>
450 Creates a HTML 'textarea' tag named C<$name> with the content
451 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
452 tag's C<id> defaults to C<name_to_id($name)>.
454 =item C<checkbox_tag $name, %attributes>
456 Creates a HTML 'input type=checkbox' tag named C<$name> with arbitrary
457 HTML attributes from C<%attributes>. The tag's C<id> defaults to
458 C<name_to_id($name)>. The tag's C<value> defaults to C<1>.
460 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
461 created with said C<label>. No attribute named C<label> is created in
464 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
466 Creates a date input field, with an attached javascript that will open a
467 calendar on click. The javascript ist by default anchoered at the bottom right
468 sight. This can be overridden with C<cal_align>, see Calendar documentation for
469 the details, usually you'll want a two letter abbreviation of the alignment.
470 Right + Bottom becomes C<BL>.
472 =item C<radio_button_tag $name, %attributes>
474 Creates a HTML 'input type=radio' tag named C<$name> with arbitrary
475 HTML attributes from C<%attributes>. The tag's C<value> defaults to
476 C<1>. The tag's C<id> defaults to C<name_to_id($name . "_" . $value)>.
478 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
479 created with said C<label>. No attribute named C<label> is created in
482 =item C<javascript_tag $file1, $file2, $file3...>
484 Creates a HTML 'E<lt>script type="text/javascript" src="..."E<gt>'
485 tag for each file name parameter passed. Each file name will be
486 postfixed with '.js' if it isn't already and prefixed with 'js/' if it
487 doesn't contain a slash.
489 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
491 Creates a date input field, with an attached javascript that will open a
492 calendar on click. The javascript ist by default anchoered at the bottom right
493 sight. This can be overridden with C<cal_align>, see Calendar documentation for
494 the details, usually you'll want a two letter abbreviation of the alignment.
495 Right + Bottom becomes C<BL>.
497 =item C<tabbed \@tab, %attributes>
499 Will create a tabbed area. The tabs should be created with the helper function
503 L.tab(LxERP.t8('Basic Data'), 'part/_main_tab.html'),
504 L.tab(LxERP.t8('Custom Variables'), 'part/_cvar_tab.html', if => SELF.display_cvar_tab),
507 An optional attribute is C<selected>, which accepts the ordinal of a tab which
508 should be selected by default.
510 =item C<areainput_tag $name, $content, %PARAMS>
512 Creates a generic input tag or textarea tag, depending on content size. The
513 mount of desired rows must be given with C<rows> parameter, Accpeted parameters
514 include C<min_rows> for rendering a minimum of rows if a textarea is displayed.
516 You can force input by setting rows to 1, and you can force textarea by setting
521 =head2 CONVERSION FUNCTIONS
525 =item C<options_for_select \@collection, %options>
527 Creates a string suitable for a HTML 'select' tag consisting of one
528 'E<lt>optionE<gt>' tag for each element in C<\@collection>. The value
529 to use and the title to display are extracted from the elements in
530 C<\@collection>. Each element can be one of four things:
534 =item 1. An array reference with at least two elements. The first element is
535 the value, the second element is its title.
537 =item 2. A scalar. The scalar is both the value and the title.
539 =item 3. A hash reference. In this case C<%options> must contain
540 I<value> and I<title> keys that name the keys in the element to use
541 for the value and title respectively.
543 =item 4. A blessed reference. In this case C<%options> must contain
544 I<value> and I<title> keys that name functions called on the blessed
545 reference whose return values are used as the value and title
550 For cases 3 and 4 C<$options{value}> defaults to C<id> and
551 C<$options{title}> defaults to C<$options{value}>.
553 In addition to pure keys/method you can also provide coderefs as I<value_sub>
554 and/or I<title_sub>. If present, these take precedence over keys or methods,
555 and are called with the element as first argument. It must return the value or
558 Lastly a joint coderef I<value_title_sub> may be provided, which in turn takes
559 precedence over each individual sub. It will only be called once for each
560 element and must return a list of value and title.
562 If the option C<with_empty> is set then an empty element (value
563 C<undef>) will be used as the first element. The title to display for
564 this element can be set with the option C<empty_title> and defaults to
567 =item C<tab, description, target, %PARAMS>
569 Creates a tab for C<tabbed>. The description will be used as displayed name.
570 The target should be a block or template that can be processed. C<tab> supports
571 a C<method> parameter, which can override the process method to apply target.
572 C<method => 'raw'> will just include the given text as is. I was too lazy to
573 implement C<include> properly.
575 Also an C<if> attribute is supported, so that tabs can be suppressed based on
576 some occasion. In this case the supplied block won't even get processed, and
577 the resulting tab will get ignored by C<tabbed>:
579 L.tab('Awesome tab wih much info', '_much_info.html', if => SELF.wants_all)
583 =head1 MODULE AUTHORS
585 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
587 L<http://linet-services.de>