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 my $string = "" . shift;
26 $string =~ s/\"/\\\"/g;
31 return (@_ && (ref($_[0]) eq 'HASH')) ? %{ $_[0] } : @_;
35 my ($class, $context, @args) = @_;
43 die 'not an accessor' if @_ > 1;
44 return $_[0]->{CONTEXT};
51 $name =~ s/[^\w_]/_/g;
58 my ($self, @slurp) = @_;
59 my %options = _hashify(@slurp);
62 while (my ($name, $value) = each %options) {
64 next if $name eq 'disabled' && !$value;
65 $value = '' if !defined($value);
66 push @result, _H($name) . '="' . _H($value) . '"';
69 return @result ? ' ' . join(' ', @result) : '';
73 my ($self, $tag, $content, @slurp) = @_;
74 my $attributes = $self->attributes(@slurp);
76 return "<${tag}${attributes}/>" unless defined($content);
77 return "<${tag}${attributes}>${content}</${tag}>";
83 my $options_str = shift;
84 my %attributes = _hashify(@_);
86 $attributes{id} ||= $self->name_to_id($name);
87 $options_str = $self->options_for_select($options_str) if ref $options_str;
89 return $self->html_tag('select', $options_str, %attributes, name => $name);
93 my ($self, $name, $content, @slurp) = @_;
94 my %attributes = _hashify(@slurp);
96 $attributes{id} ||= $self->name_to_id($name);
97 $content = $content ? _H($content) : '';
99 return $self->html_tag('textarea', $content, %attributes, name => $name);
103 my ($self, $name, @slurp) = @_;
104 my %attributes = _hashify(@slurp);
106 $attributes{id} ||= $self->name_to_id($name);
107 $attributes{value} = 1 unless defined $attributes{value};
108 my $label = delete $attributes{label};
110 if ($attributes{checked}) {
111 $attributes{checked} = 'checked';
113 delete $attributes{checked};
116 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'checkbox');
117 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
122 sub radio_button_tag {
125 my %attributes = _hashify(@_);
127 $attributes{value} = 1 unless defined $attributes{value};
128 $attributes{id} ||= $self->name_to_id($name . "_" . $attributes{value});
129 my $label = delete $attributes{label};
131 if ($attributes{checked}) {
132 $attributes{checked} = 'checked';
134 delete $attributes{checked};
137 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'radio');
138 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
144 my ($self, $name, $value, @slurp) = @_;
145 my %attributes = _hashify(@slurp);
147 $attributes{id} ||= $self->name_to_id($name);
148 $attributes{type} ||= 'text';
150 return $self->html_tag('input', undef, %attributes, name => $name, value => $value);
154 return shift->input_tag(@_, type => 'hidden');
158 my ($self, $content, @slurp) = @_;
159 return $self->html_tag('div', $content, @slurp);
163 my ($self, $content, @slurp) = @_;
164 return $self->html_tag('ul', $content, @slurp);
168 my ($self, $content, @slurp) = @_;
169 return $self->html_tag('li', $content, @slurp);
173 my ($self, $href, $content, @slurp) = @_;
174 my %params = _hashify(@slurp);
178 return $self->html_tag('a', $content, %params, href => $href);
182 my ($self, $name, $value, @slurp) = @_;
183 my %attributes = _hashify(@slurp);
185 $attributes{onclick} = "if (confirm('" . delete($attributes{confirm}) . "')) return true; else return false;" if $attributes{confirm};
187 return $self->input_tag($name, $value, %attributes, type => 'submit', class => 'submit');
191 my ($self, $onclick, $value, @slurp) = @_;
192 my %attributes = _hashify(@slurp);
194 return $self->input_tag(undef, $value, %attributes, type => 'button', onclick => $onclick);
197 sub options_for_select {
199 my $collection = shift;
200 my %options = _hashify(@_);
202 my $value_key = $options{value} || 'id';
203 my $title_key = $options{title} || $value_key;
205 my $value_sub = $options{value_sub};
206 my $title_sub = $options{title_sub};
208 my $value_title_sub = $options{value_title_sub};
210 my %selected = map { ( $_ => 1 ) } @{ ref($options{default}) eq 'ARRAY' ? $options{default} : $options{default} ? [ $options{default} ] : [] };
213 my ($element, $index, $key, $sub) = @_;
214 my $ref = ref $element;
215 return $sub ? $sub->($element)
217 : $ref eq 'ARRAY' ? $element->[$index]
218 : $ref eq 'HASH' ? $element->{$key}
223 push @elements, [ undef, $options{empty_title} || '' ] if $options{with_empty};
224 push @elements, map [
225 $value_title_sub ? $value_title_sub->($_) : (
226 $access->($_, 0, $value_key, $value_sub),
227 $access->($_, 1, $title_key, $title_sub),
229 ], @{ $collection } if $collection && ref $collection eq 'ARRAY';
232 foreach my $result (@elements) {
233 my %attributes = ( value => $result->[0] );
234 $attributes{selected} = 'selected' if $selected{ $result->[0] || '' };
236 $code .= $self->html_tag('option', _H($result->[1]), %attributes);
243 my ($self, $data) = @_;
244 return $self->html_tag('script', $data, type => 'text/javascript');
251 foreach my $file (@_) {
252 $file .= '.css' unless $file =~ m/\.css$/;
253 $file = "css/${file}" unless $file =~ m|/|;
255 $code .= qq|<link rel="stylesheet" href="${file}" type="text/css" media="screen" />|;
262 my ($self, $name, $value, @slurp) = @_;
263 my %params = _hashify(@slurp);
264 my $name_e = _H($name);
266 my $datefmt = apply {
270 } $::myconfig{"dateformat"};
272 $params{cal_align} ||= 'BR';
274 $self->input_tag($name, $value,
277 title => _H($::myconfig{dateformat}),
278 onBlur => 'check_right_date_format(this)',
280 ) . ((!$params{no_cal}) ?
281 $self->html_tag('img', undef,
282 src => 'image/calendar.png',
284 title => _H($::myconfig{dateformat}),
288 "Calendar.setup({ inputField: '$name_e', ifFormat: '$datefmt', align: '$params{cal_align}', button: 'trigger$seq' });"
295 foreach my $file (@_) {
296 $file .= '.js' unless $file =~ m/\.js$/;
297 $file = "js/${file}" unless $file =~ m|/|;
299 $code .= qq|<script type="text/javascript" src="${file}"></script>|;
306 my ($self, $tabs, @slurp) = @_;
307 my %params = _hashify(@slurp);
308 my $id = $params{id} || 'tab_' . _tag_id();
310 $params{selected} *= 1;
312 die 'L.tabbed needs an arrayred of tabs for first argument'
313 unless ref $tabs eq 'ARRAY';
315 my (@header, @blocks);
316 for my $i (0..$#$tabs) {
317 my $tab = $tabs->[$i];
321 my $selected = $params{selected} == $i;
322 my $tab_id = "__tab_id_$i";
323 push @header, $self->li_tag(
324 $self->link('', $tab->{name}, rel => $tab_id),
325 ($selected ? (class => 'selected') : ())
327 push @blocks, $self->div_tag($tab->{data},
328 id => $tab_id, class => 'tabcontent');
331 return '' unless @header;
332 return $self->ul_tag(
333 join('', @header), id => $id, class => 'shadetabs'
336 join('', @blocks), class => 'tabcontentstyle'
339 qq|var $id = new ddtabcontent("$id");$id.setpersist(true);| .
340 qq|$id.setselectedClassTarget("link");$id.init();|
345 my ($self, $name, $src, @slurp) = @_;
346 my %params = _hashify(@slurp);
348 $params{method} ||= 'process';
350 return () if defined $params{if} && !$params{if};
353 if ($params{method} eq 'raw') {
355 } elsif ($params{method} eq 'process') {
356 $data = $self->_context->process($src, %{ $params{args} || {} });
358 die "unknown tag method '$params{method}'";
361 return () unless $data;
363 return +{ name => $name, data => $data };
367 my ($self, $name, $value, @slurp) = @_;
368 my %attributes = _hashify(@slurp);
370 my $rows = delete $attributes{rows} || 1;
371 my $min = delete $attributes{min_rows} || 1;
374 ? $self->textarea_tag($name, $value, %attributes, rows => max $rows, $min)
375 : $self->input_tag($name, $value, %attributes);
378 sub multiselect2side {
379 my ($self, $id, @slurp) = @_;
380 my %params = _hashify(@slurp);
382 $params{labelsx} = "\"" . _J($params{labelsx} || $::locale->text('Available')) . "\"";
383 $params{labeldx} = "\"" . _J($params{labeldx} || $::locale->text('Selected')) . "\"";
384 $params{moveOptions} = 'false';
386 my $vars = join(', ', map { "${_}: " . $params{$_} } keys %params);
388 <script type="text/javascript">
389 \$().ready(function() {
390 \$('#${id}').multiselect2side({ ${vars} });
400 require Data::Dumper;
401 return '<pre>' . Data::Dumper::Dumper(@_) . '</pre>';
410 SL::Templates::Plugin::L -- Layouting / tag generation
414 Usage from a template:
418 [% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right' ] ]) %]
420 [% L.select_tag('direction', L.options_for_select([ { direction => 'left', display => 'To the left' },
421 { direction => 'right', display => 'To the right' } ],
422 value => 'direction', title => 'display', default => 'right')) %]
426 A module modeled a bit after Rails' ActionView helpers. Several small
427 functions that create HTML tags from various kinds of data sources.
431 =head2 LOW-LEVEL FUNCTIONS
435 =item C<name_to_id $name>
437 Converts a name to a HTML id by replacing various characters.
439 =item C<attributes %items>
441 Creates a string from all elements in C<%items> suitable for usage as
442 HTML tag attributes. Keys and values are HTML escaped even though keys
443 must not contain non-ASCII characters for browsers to accept them.
445 =item C<html_tag $tag_name, $content_string, %attributes>
447 Creates an opening and closing HTML tag for C<$tag_name> and puts
448 C<$content_string> between the two. If C<$content_string> is undefined
449 or empty then only a E<lt>tag/E<gt> tag will be created. Attributes
450 are key/value pairs added to the opening tag.
452 C<$content_string> is not HTML escaped.
456 =head2 HIGH-LEVEL FUNCTIONS
460 =item C<select_tag $name, $options_string, %attributes>
462 Creates a HTML 'select' tag named C<$name> with the contents
463 C<$options_string> and with arbitrary HTML attributes from
464 C<%attributes>. The tag's C<id> defaults to C<name_to_id($name)>.
466 The C<$options_string> is usually created by the
467 L</options_for_select> function. If C<$options_string> is an array
468 reference then it will be passed to L</options_for_select>
471 =item C<input_tag $name, $value, %attributes>
473 Creates a HTML 'input type=text' tag named C<$name> with the value
474 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
475 tag's C<id> defaults to C<name_to_id($name)>.
477 =item C<hidden_tag $name, $value, %attributes>
479 Creates a HTML 'input type=hidden' tag named C<$name> with the value
480 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
481 tag's C<id> defaults to C<name_to_id($name)>.
483 =item C<submit_tag $name, $value, %attributes>
485 Creates a HTML 'input type=submit class=submit' tag named C<$name> with the
486 value C<$value> and with arbitrary HTML attributes from C<%attributes>. The
487 tag's C<id> defaults to C<name_to_id($name)>.
489 If C<$attributes{confirm}> is set then a JavaScript popup dialog will
490 be added via the C<onclick> handler asking the question given with
491 C<$attributes{confirm}>. If request is only submitted if the user
492 clicks the dialog's ok/yes button.
494 =item C<textarea_tag $name, $value, %attributes>
496 Creates a HTML 'textarea' tag named C<$name> with the content
497 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
498 tag's C<id> defaults to C<name_to_id($name)>.
500 =item C<checkbox_tag $name, %attributes>
502 Creates a HTML 'input type=checkbox' tag named C<$name> with arbitrary
503 HTML attributes from C<%attributes>. The tag's C<id> defaults to
504 C<name_to_id($name)>. The tag's C<value> defaults to C<1>.
506 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
507 created with said C<label>. No attribute named C<label> is created in
510 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
512 Creates a date input field, with an attached javascript that will open a
513 calendar on click. The javascript ist by default anchoered at the bottom right
514 sight. This can be overridden with C<cal_align>, see Calendar documentation for
515 the details, usually you'll want a two letter abbreviation of the alignment.
516 Right + Bottom becomes C<BL>.
518 =item C<radio_button_tag $name, %attributes>
520 Creates a HTML 'input type=radio' tag named C<$name> with arbitrary
521 HTML attributes from C<%attributes>. The tag's C<value> defaults to
522 C<1>. The tag's C<id> defaults to C<name_to_id($name . "_" . $value)>.
524 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
525 created with said C<label>. No attribute named C<label> is created in
528 =item C<javascript_tag $file1, $file2, $file3...>
530 Creates a HTML 'E<lt>script type="text/javascript" src="..."E<gt>'
531 tag for each file name parameter passed. Each file name will be
532 postfixed with '.js' if it isn't already and prefixed with 'js/' if it
533 doesn't contain a slash.
535 =item C<stylesheet_tag $file1, $file2, $file3...>
537 Creates a HTML 'E<lt>link rel="text/stylesheet" href="..."E<gt>' tag
538 for each file name parameter passed. Each file name will be postfixed
539 with '.css' if it isn't already and prefixed with 'css/' if it doesn't
542 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
544 Creates a date input field, with an attached javascript that will open a
545 calendar on click. The javascript ist by default anchoered at the bottom right
546 sight. This can be overridden with C<cal_align>, see Calendar documentation for
547 the details, usually you'll want a two letter abbreviation of the alignment.
548 Right + Bottom becomes C<BL>.
550 =item C<tabbed \@tab, %attributes>
552 Will create a tabbed area. The tabs should be created with the helper function
556 L.tab(LxERP.t8('Basic Data'), 'part/_main_tab.html'),
557 L.tab(LxERP.t8('Custom Variables'), 'part/_cvar_tab.html', if => SELF.display_cvar_tab),
560 An optional attribute is C<selected>, which accepts the ordinal of a tab which
561 should be selected by default.
563 =item C<areainput_tag $name, $content, %PARAMS>
565 Creates a generic input tag or textarea tag, depending on content size. The
566 mount of desired rows must be given with C<rows> parameter, Accpeted parameters
567 include C<min_rows> for rendering a minimum of rows if a textarea is displayed.
569 You can force input by setting rows to 1, and you can force textarea by setting
572 =item C<multiselect2side $id, %params>
574 Creates a JavaScript snippet calling the jQuery function
575 C<multiselect2side> on the select control with the ID C<$id>. The
576 select itself is not created. C<%params> can contain the following
583 The label of the list of available options. Defaults to the
584 translation of 'Available'.
588 The label of the list of selected options. Defaults to the
589 translation of 'Selected'.
595 Dumps the Argument using L<Data::Dumper> into a E<lt>preE<gt> block.
599 =head2 CONVERSION FUNCTIONS
603 =item C<options_for_select \@collection, %options>
605 Creates a string suitable for a HTML 'select' tag consisting of one
606 'E<lt>optionE<gt>' tag for each element in C<\@collection>. The value
607 to use and the title to display are extracted from the elements in
608 C<\@collection>. Each element can be one of four things:
612 =item 1. An array reference with at least two elements. The first element is
613 the value, the second element is its title.
615 =item 2. A scalar. The scalar is both the value and the title.
617 =item 3. A hash reference. In this case C<%options> must contain
618 I<value> and I<title> keys that name the keys in the element to use
619 for the value and title respectively.
621 =item 4. A blessed reference. In this case C<%options> must contain
622 I<value> and I<title> keys that name functions called on the blessed
623 reference whose return values are used as the value and title
628 For cases 3 and 4 C<$options{value}> defaults to C<id> and
629 C<$options{title}> defaults to C<$options{value}>.
631 In addition to pure keys/method you can also provide coderefs as I<value_sub>
632 and/or I<title_sub>. If present, these take precedence over keys or methods,
633 and are called with the element as first argument. It must return the value or
636 Lastly a joint coderef I<value_title_sub> may be provided, which in turn takes
637 precedence over each individual sub. It will only be called once for each
638 element and must return a list of value and title.
640 If the option C<with_empty> is set then an empty element (value
641 C<undef>) will be used as the first element. The title to display for
642 this element can be set with the option C<empty_title> and defaults to
645 The option C<default> can be either a scalar or an array reference
646 containing the values of the options which should be set to be
649 =item C<tab, description, target, %PARAMS>
651 Creates a tab for C<tabbed>. The description will be used as displayed name.
652 The target should be a block or template that can be processed. C<tab> supports
653 a C<method> parameter, which can override the process method to apply target.
654 C<method => 'raw'> will just include the given text as is. I was too lazy to
655 implement C<include> properly.
657 Also an C<if> attribute is supported, so that tabs can be suppressed based on
658 some occasion. In this case the supplied block won't even get processed, and
659 the resulting tab will get ignored by C<tabbed>:
661 L.tab('Awesome tab wih much info', '_much_info.html', if => SELF.wants_all)
665 =head1 MODULE AUTHORS
667 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
669 L<http://linet-services.de>