1 package SL::Template::Plugin::L;
3 use base qw( Template::Plugin );
5 use List::MoreUtils qw(apply);
9 { # This will give you an id for identifying html tags and such.
10 # It's guaranteed to be unique unless you exceed 10 mio calls per request.
11 # Do not use these id's to store information across requests.
12 my $_id_sequence = int rand 1e7;
14 return $_id_sequence = ($_id_sequence + 1) % 1e7;
20 return $::locale->quote_special_chars('HTML', $string);
24 return (@_ && (ref($_[0]) eq 'HASH')) ? %{ $_[0] } : @_;
28 my ($class, $context, @args) = @_;
36 die 'not an accessor' if @_ > 1;
37 return $_[0]->{CONTEXT};
44 $name =~ s/[^\w_]/_/g;
52 my %options = _hashify(@_);
55 while (my ($name, $value) = each %options) {
57 $value = '' if !defined($value);
58 push @result, _H($name) . '="' . _H($value) . '"';
61 return @result ? ' ' . join(' ', @result) : '';
68 my $attributes = $self->attributes(@_);
70 return "<${tag}${attributes}/>" unless defined($content);
71 return "<${tag}${attributes}>${content}</${tag}>";
77 my $options_str = shift;
78 my %attributes = _hashify(@_);
80 $attributes{id} ||= $self->name_to_id($name);
81 $options_str = $self->options_for_select($options_str) if ref $options_str;
83 return $self->html_tag('select', $options_str, %attributes, name => $name);
90 my %attributes = _hashify(@_);
92 $attributes{id} ||= $self->name_to_id($name);
93 $content = $content ? _H($content) : '';
95 return $self->html_tag('textarea', $content, %attributes, name => $name);
101 my %attributes = _hashify(@_);
103 $attributes{id} ||= $self->name_to_id($name);
104 $attributes{value} = 1 unless defined $attributes{value};
105 my $label = delete $attributes{label};
107 if ($attributes{checked}) {
108 $attributes{checked} = 'checked';
110 delete $attributes{checked};
113 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'checkbox');
114 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
119 sub radio_button_tag {
122 my %attributes = _hashify(@_);
124 $attributes{value} = 1 unless defined $attributes{value};
125 $attributes{id} ||= $self->name_to_id($name . "_" . $attributes{value});
126 my $label = delete $attributes{label};
128 if ($attributes{checked}) {
129 $attributes{checked} = 'checked';
131 delete $attributes{checked};
134 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'radio');
135 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
141 my ($self, $name, $value, @slurp) = @_;
142 my %attributes = _hashify(@slurp);
144 $attributes{id} ||= $self->name_to_id($name);
145 $attributes{type} ||= 'text';
147 return $self->html_tag('input', undef, %attributes, name => $name, value => $value);
151 return shift->input_tag(@_, type => 'hidden');
155 my ($self, $content, @slurp) = @_;
156 return $self->html_tag('div', $content, @slurp);
160 my ($self, $content, @slurp) = @_;
161 return $self->html_tag('ul', $content, @slurp);
165 my ($self, $content, @slurp) = @_;
166 return $self->html_tag('li', $content, @slurp);
170 my ($self, $href, $content, @slurp) = @_;
171 my %params = _hashify(@slurp);
175 return $self->html_tag('a', $content, %params, href => $href);
182 my %attributes = _hashify(@_);
184 $attributes{onclick} = "if (confirm('" . delete($attributes{confirm}) . "')) return true; else return false;" if $attributes{confirm};
186 return $self->input_tag($name, $value, %attributes, type => 'submit', class => 'submit');
189 sub options_for_select {
191 my $collection = shift;
192 my %options = _hashify(@_);
194 my $value_key = $options{value} || 'id';
195 my $title_key = $options{title} || $value_key;
197 my $value_sub = $options{value_sub};
198 my $title_sub = $options{title_sub};
200 my $value_title_sub = $options{value_title_sub};
203 my ($element, $index, $key, $sub) = @_;
204 my $ref = ref $element;
205 return $sub ? $sub->($element)
207 : $ref eq 'ARRAY' ? $element->[$index]
208 : $ref eq 'HASH' ? $element->{$key}
213 push @elements, [ undef, $options{empty_title} || '' ] if $options{with_empty};
214 push @elements, map [
215 $value_title_sub ? $value_title_sub->($_) : (
216 $access->($_, 0, $value_key, $value_sub),
217 $access->($_, 1, $title_key, $title_sub),
219 ], @{ $collection } if $collection && ref $collection eq 'ARRAY';
222 foreach my $result (@elements) {
223 my %attributes = ( value => $result->[0] );
224 $attributes{selected} = 'selected' if $options{default} && ($options{default} eq ($result->[0] || ''));
226 $code .= $self->html_tag('option', _H($result->[1]), %attributes);
233 my ($self, $data) = @_;
234 return $self->html_tag('script', $data, type => 'text/javascript');
238 my ($self, $name, $value, @slurp) = @_;
239 my %params = _hashify(@slurp);
240 my $name_e = _H($name);
242 my $datefmt = apply {
246 } $::myconfig{"dateformat"};
248 $params{cal_align} ||= 'BR';
250 $self->input_tag($name, $value,
253 title => _H($::myconfig{dateformat}),
254 onBlur => 'check_right_date_format(this)',
256 ) . ((!$params{no_cal}) ?
257 $self->html_tag('img', undef,
258 src => 'image/calendar.png',
260 title => _H($::myconfig{dateformat}),
264 "Calendar.setup({ inputField: '$name_e', ifFormat: '$datefmt', align: '$params{cal_align}', button: 'trigger$seq' });"
271 foreach my $file (@_) {
272 $file .= '.js' unless $file =~ m/\.js$/;
273 $file = "js/${file}" unless $file =~ m|/|;
275 $code .= qq|<script type="text/javascript" src="${file}"></script>|;
282 my ($self, $tabs, @slurp) = @_;
283 my %params = _hashify(@slurp);
284 my $id = 'tab_' . _tag_id();
286 $params{selected} *= 1;
288 die 'L.tabbed needs an arrayred of tabs for first argument'
289 unless ref $tabs eq 'ARRAY';
291 my (@header, @blocks);
292 for my $i (0..$#$tabs) {
293 my $tab = $tabs->[$i];
297 my $selected = $params{selected} == $i;
298 my $tab_id = _tag_id();
299 push @header, $self->li_tag(
300 $self->link('', $tab->{name}, rel => $tab_id),
301 ($selected ? (class => 'selected') : ())
303 push @blocks, $self->div_tag($tab->{data},
304 id => $tab_id, class => 'tabcontent');
307 return '' unless @header;
308 return $self->ul_tag(
309 join('', @header), id => $id, class => 'shadetabs'
312 join('', @blocks), class => 'tabcontentstyle'
315 qq|var $id = new ddtabcontent("$id");$id.setpersist(true);| .
316 qq|$id.setselectedClassTarget("link");$id.init();|
321 my ($self, $name, $src, @slurp) = @_;
322 my %params = _hashify(@slurp);
324 $params{method} ||= 'process';
326 return () if defined $params{if} && !$params{if};
329 if ($params{method} eq 'raw') {
331 } elsif ($params{method} eq 'process') {
332 $data = $self->_context->process($src, %{ $params{args} || {} });
334 die "unknown tag method '$params{method}'";
337 return () unless $data;
339 return +{ name => $name, data => $data };
348 SL::Templates::Plugin::L -- Layouting / tag generation
352 Usage from a template:
356 [% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right' ] ]) %]
358 [% L.select_tag('direction', L.options_for_select([ { direction => 'left', display => 'To the left' },
359 { direction => 'right', display => 'To the right' } ],
360 value => 'direction', title => 'display', default => 'right')) %]
364 A module modeled a bit after Rails' ActionView helpers. Several small
365 functions that create HTML tags from various kinds of data sources.
369 =head2 LOW-LEVEL FUNCTIONS
373 =item C<name_to_id $name>
375 Converts a name to a HTML id by replacing various characters.
377 =item C<attributes %items>
379 Creates a string from all elements in C<%items> suitable for usage as
380 HTML tag attributes. Keys and values are HTML escaped even though keys
381 must not contain non-ASCII characters for browsers to accept them.
383 =item C<html_tag $tag_name, $content_string, %attributes>
385 Creates an opening and closing HTML tag for C<$tag_name> and puts
386 C<$content_string> between the two. If C<$content_string> is undefined
387 or empty then only a E<lt>tag/E<gt> tag will be created. Attributes
388 are key/value pairs added to the opening tag.
390 C<$content_string> is not HTML escaped.
394 =head2 HIGH-LEVEL FUNCTIONS
398 =item C<select_tag $name, $options_string, %attributes>
400 Creates a HTML 'select' tag named C<$name> with the contents
401 C<$options_string> and with arbitrary HTML attributes from
402 C<%attributes>. The tag's C<id> defaults to C<name_to_id($name)>.
404 The C<$options_string> is usually created by the
405 L</options_for_select> function. If C<$options_string> is an array
406 reference then it will be passed to L</options_for_select>
409 =item C<input_tag $name, $value, %attributes>
411 Creates a HTML 'input type=text' tag named C<$name> with the value
412 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
413 tag's C<id> defaults to C<name_to_id($name)>.
415 =item C<hidden_tag $name, $value, %attributes>
417 Creates a HTML 'input type=hidden' tag named C<$name> with the value
418 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
419 tag's C<id> defaults to C<name_to_id($name)>.
421 =item C<submit_tag $name, $value, %attributes>
423 Creates a HTML 'input type=submit class=submit' tag named C<$name> with the
424 value C<$value> and with arbitrary HTML attributes from C<%attributes>. The
425 tag's C<id> defaults to C<name_to_id($name)>.
427 If C<$attributes{confirm}> is set then a JavaScript popup dialog will
428 be added via the C<onclick> handler asking the question given with
429 C<$attributes{confirm}>. If request is only submitted if the user
430 clicks the dialog's ok/yes button.
432 =item C<textarea_tag $name, $value, %attributes>
434 Creates a HTML 'textarea' tag named C<$name> with the content
435 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
436 tag's C<id> defaults to C<name_to_id($name)>.
438 =item C<checkbox_tag $name, %attributes>
440 Creates a HTML 'input type=checkbox' tag named C<$name> with arbitrary
441 HTML attributes from C<%attributes>. The tag's C<id> defaults to
442 C<name_to_id($name)>. The tag's C<value> defaults to C<1>.
444 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
445 created with said C<label>. No attribute named C<label> is created in
448 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
450 Creates a date input field, with an attached javascript that will open a
451 calendar on click. The javascript ist by default anchoered at the bottom right
452 sight. This can be overridden with C<cal_align>, see Calendar documentation for
453 the details, usually you'll want a two letter abbreviation of the alignment.
454 Right + Bottom becomes C<BL>.
456 =item C<radio_button_tag $name, %attributes>
458 Creates a HTML 'input type=radio' tag named C<$name> with arbitrary
459 HTML attributes from C<%attributes>. The tag's C<value> defaults to
460 C<1>. The tag's C<id> defaults to C<name_to_id($name . "_" . $value)>.
462 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
463 created with said C<label>. No attribute named C<label> is created in
466 =item C<javascript_tag $file1, $file2, $file3...>
468 Creates a HTML 'E<lt>script type="text/javascript" src="..."E<gt>'
469 tag for each file name parameter passed. Each file name will be
470 postfixed with '.js' if it isn't already and prefixed with 'js/' if it
471 doesn't contain a slash.
473 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
475 Creates a date input field, with an attached javascript that will open a
476 calendar on click. The javascript ist by default anchoered at the bottom right
477 sight. This can be overridden with C<cal_align>, see Calendar documentation for
478 the details, usually you'll want a two letter abbreviation of the alignment.
479 Right + Bottom becomes C<BL>.
481 =item C<tabbed \@tab, %attributes>
483 Will create a tabbed area. The tabs should be created with the helper function
487 L.tab(LxERP.t8('Basic Data'), 'part/_main_tab.html'),
488 L.tab(LxERP.t8('Custom Variables'), 'part/_cvar_tab.html', if => SELF.display_cvar_tab),
491 An optional attribute is C<selected>, which accepts the ordinal of a tab which
492 should be selected by default.
496 =head2 CONVERSION FUNCTIONS
500 =item C<options_for_select \@collection, %options>
502 Creates a string suitable for a HTML 'select' tag consisting of one
503 'E<lt>optionE<gt>' tag for each element in C<\@collection>. The value
504 to use and the title to display are extracted from the elements in
505 C<\@collection>. Each element can be one of four things:
509 =item 1. An array reference with at least two elements. The first element is
510 the value, the second element is its title.
512 =item 2. A scalar. The scalar is both the value and the title.
514 =item 3. A hash reference. In this case C<%options> must contain
515 I<value> and I<title> keys that name the keys in the element to use
516 for the value and title respectively.
518 =item 4. A blessed reference. In this case C<%options> must contain
519 I<value> and I<title> keys that name functions called on the blessed
520 reference whose return values are used as the value and title
525 For cases 3 and 4 C<$options{value}> defaults to C<id> and
526 C<$options{title}> defaults to C<$options{value}>.
528 In addition to pure keys/method you can also provide coderefs as I<value_sub>
529 and/or I<title_sub>. If present, these take precedence over keys or methods,
530 and are called with the element as first argument. It must return the value or
533 Lastly a joint coderef I<value_title_sub> may be provided, which in turn takes
534 precedence over each individual sub. It will only be called once for each
535 element and must return a list of value and title.
537 If the option C<with_empty> is set then an empty element (value
538 C<undef>) will be used as the first element. The title to display for
539 this element can be set with the option C<empty_title> and defaults to
542 =item C<tab, description, target, %PARAMS>
544 Creates a tab for C<tabbed>. The description will be used as displayed name.
545 The target should be a block or template that can be processed. C<tab> supports
546 a C<method> parameter, which can override the process method to apply target.
547 C<method => 'raw'> will just include the given text as is. I was too lazy to
548 implement C<include> properly.
550 Also an C<if> attribute is supported, so that tabs can be suppressed based on
551 some occasion. In this case the supplied block won't even get processed, and
552 the resulting tab will get ignored by C<tabbed>:
554 L.tab('Awesome tab wih much info', '_much_info.html', if => SELF.wants_all)
558 =head1 MODULE AUTHORS
560 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
562 L<http://linet-services.de>