1 package SL::Template::Plugin::L;
 
   3 use base qw( Template::Plugin );
 
   5 use List::MoreUtils qw(apply);
 
   6 use List::Util qw(max);
 
   7 use Scalar::Util qw(blessed);
 
  13 { # This will give you an id for identifying html tags and such.
 
  14   # It's guaranteed to be unique unless you exceed 10 mio calls per request.
 
  15   # Do not use these id's to store information across requests.
 
  16 my $_id_sequence = int rand 1e7;
 
  18   return "id_" . ( $_id_sequence = ($_id_sequence + 1) % 1e7 );
 
  22 my %_valueless_attributes = map { $_ => 1 } qw(
 
  23   checked compact declare defer disabled ismap multiple noresize noshade nowrap
 
  29   return $::locale->quote_special_chars('HTML', $string);
 
  34   $string    =~ s/(\"|\'|\\)/\\$1/g;
 
  39   return (@_ && (ref($_[0]) eq 'HASH')) ? %{ $_[0] } : @_;
 
  43   my ($class, $context, @args) = @_;
 
  51   die 'not an accessor' if @_ > 1;
 
  52   return $_[0]->{CONTEXT};
 
  59   $name    =~ s/[^\w_]/_/g;
 
  66   my ($self, @slurp)    = @_;
 
  67   my %options = _hashify(@slurp);
 
  70   while (my ($name, $value) = each %options) {
 
  72     next if $_valueless_attributes{$name} && !$value;
 
  73     $value = '' if !defined($value);
 
  74     push @result, $_valueless_attributes{$name} ? _H($name) : _H($name) . '="' . _H($value) . '"';
 
  77   return @result ? ' ' . join(' ', @result) : '';
 
  81   my ($self, $tag, $content, @slurp) = @_;
 
  82   my $attributes = $self->attributes(@slurp);
 
  84   return "<${tag}${attributes}>" unless defined($content);
 
  85   return "<${tag}${attributes}>${content}</${tag}>";
 
  89   my ($self, @slurp) = @_;
 
  90   my %options = _hashify(@slurp);
 
  94   return $self->html_tag('img', undef, %options);
 
 100   my $collection      = shift;
 
 101   my %attributes      = _hashify(@_);
 
 103   $attributes{id}   ||= $self->name_to_id($name);
 
 105   my $value_key       = delete($attributes{value_key}) || 'id';
 
 106   my $title_key       = delete($attributes{title_key}) || $value_key;
 
 107   my $default_key     = delete($attributes{default_key}) || 'selected';
 
 110   my $value_title_sub = delete($attributes{value_title_sub});
 
 112   my $value_sub       = delete($attributes{value_sub});
 
 113   my $title_sub       = delete($attributes{title_sub});
 
 114   my $default_sub     = delete($attributes{default_sub});
 
 116   my $with_empty      = delete($attributes{with_empty});
 
 117   my $empty_title     = delete($attributes{empty_title});
 
 121   if ( ref($attributes{default}) eq 'ARRAY' ) {
 
 123     foreach my $entry (@{$attributes{default}}) {
 
 124       $selected{$entry} = 1;
 
 126   } elsif ( defined($attributes{default}) ) {
 
 127     $selected{$attributes{default}} = 1;
 
 130   delete($attributes{default});
 
 136     push(@options, [undef, $empty_title || '']);
 
 139   my $normalize_entry = sub {
 
 141     my ($type, $entry, $sub, $key) = @_;
 
 144       return $sub->($entry);
 
 147     my $ref = ref($entry);
 
 151       if ( $type eq 'value' || $type eq 'title' ) {
 
 158     if ( $ref eq 'ARRAY' ) {
 
 160       if ( $type eq 'value' ) {
 
 164       if ( $type eq 'title' ) {
 
 171     if ( $ref eq 'HASH' ) {
 
 172       return $entry->{$key};
 
 175     if ( $type ne 'default' || $entry->can($key) ) {
 
 182   foreach my $entry ( @{ $collection } ) {
 
 186     if ( $value_title_sub ) {
 
 187       ($value, $title) = @{ $value_title_sub->($entry) };
 
 190       $value = $normalize_entry->('value', $entry, $value_sub, $value_key);
 
 191       $title = $normalize_entry->('title', $entry, $title_sub, $title_key);
 
 194     my $default = $normalize_entry->('default', $entry, $default_sub, $default_key);
 
 196     push(@options, [$value, $title, $default]);
 
 199   foreach my $entry (@options) {
 
 200     if ( exists($selected{$entry->[0]}) ) {
 
 207   foreach my $entry (@options) {
 
 208     my %args = (value => $entry->[0]);
 
 210     $args{selected} = $entry->[2];
 
 212     $code .= $self->html_tag('option', _H($entry->[1]), %args);
 
 215   $code = $self->html_tag('select', $code, %attributes, name => $name);
 
 221   my ($self, $name, $content, @slurp) = @_;
 
 222   my %attributes      = _hashify(@slurp);
 
 224   $attributes{id}   ||= $self->name_to_id($name);
 
 225   $attributes{rows}  *= 1; # required by standard
 
 226   $attributes{cols}  *= 1; # required by standard
 
 227   $content            = $content ? _H($content) : '';
 
 229   return $self->html_tag('textarea', $content, %attributes, name => $name);
 
 233   my ($self, $name, @slurp) = @_;
 
 234   my %attributes       = _hashify(@slurp);
 
 236   $attributes{id}    ||= $self->name_to_id($name);
 
 237   $attributes{value}   = 1 unless defined $attributes{value};
 
 238   my $label            = delete $attributes{label};
 
 239   my $checkall         = delete $attributes{checkall};
 
 241   if ($attributes{checked}) {
 
 242     $attributes{checked} = 'checked';
 
 244     delete $attributes{checked};
 
 247   my $code  = $self->html_tag('input', undef,  %attributes, name => $name, type => 'checkbox');
 
 248   $code    .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
 
 249   $code    .= $self->javascript(qq|\$('#$attributes{id}').checkall('$checkall');|) if $checkall;
 
 254 sub radio_button_tag {
 
 257   my %attributes       = _hashify(@_);
 
 259   $attributes{value}   = 1 unless defined $attributes{value};
 
 260   $attributes{id}    ||= $self->name_to_id($name . "_" . $attributes{value});
 
 261   my $label            = delete $attributes{label};
 
 263   if ($attributes{checked}) {
 
 264     $attributes{checked} = 'checked';
 
 266     delete $attributes{checked};
 
 269   my $code  = $self->html_tag('input', undef,  %attributes, name => $name, type => 'radio');
 
 270   $code    .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
 
 276   my ($self, $name, $value, @slurp) = @_;
 
 277   my %attributes      = _hashify(@slurp);
 
 279   $attributes{id}   ||= $self->name_to_id($name);
 
 280   $attributes{type} ||= 'text';
 
 282   return $self->html_tag('input', undef, %attributes, name => $name, value => $value);
 
 286   my ($self, $name, $value, @slurp) = @_;
 
 287   return $self->input_tag($name, $value, _hashify(@slurp), type => 'hidden');
 
 291   my ($self, $content, @slurp) = @_;
 
 292   return $self->html_tag('div', $content, @slurp);
 
 296   my ($self, $content, @slurp) = @_;
 
 297   return $self->html_tag('ul', $content, @slurp);
 
 301   my ($self, $content, @slurp) = @_;
 
 302   return $self->html_tag('li', $content, @slurp);
 
 306   my ($self, $href, $content, @slurp) = @_;
 
 307   my %params = _hashify(@slurp);
 
 311   return $self->html_tag('a', $content, %params, href => $href);
 
 315   my ($self, $name, $value, @slurp) = @_;
 
 316   my %attributes = _hashify(@slurp);
 
 318   if ( $attributes{confirm} ) {
 
 319     $attributes{onclick} = 'return confirm("'. _J(delete($attributes{confirm})) .'");';
 
 322   return $self->input_tag($name, $value, %attributes, type => 'submit', class => 'submit');
 
 326   my ($self, $onclick, $value, @slurp) = @_;
 
 327   my %attributes = _hashify(@slurp);
 
 329   $attributes{id}   ||= $self->name_to_id($attributes{name}) if $attributes{name};
 
 330   $attributes{type} ||= 'button';
 
 332   return $self->html_tag('input', undef, %attributes, value => $value, onclick => $onclick);
 
 336   my ($self, $name, $value) = splice @_, 0, 3;
 
 337   my %attributes            = _hashify(@_);
 
 339   return $self->select_tag($name, [ [ 1 => $::locale->text('Yes') ], [ 0 => $::locale->text('No') ] ], default => $value ? 1 : 0, %attributes);
 
 343   my ($self, $data) = @_;
 
 344   return $self->html_tag('script', $data, type => 'text/javascript');
 
 351   foreach my $file (@_) {
 
 352     $file .= '.css'        unless $file =~ m/\.css$/;
 
 353     $file  = "css/${file}" unless $file =~ m|/|;
 
 355     $code .= qq|<link rel="stylesheet" href="${file}" type="text/css" media="screen" />|;
 
 362   my ($self, $name, $value, @slurp) = @_;
 
 363   my %params   = _hashify(@slurp);
 
 364   my $name_e   = _H($name);
 
 366   my $datefmt  = apply {
 
 370   } $::myconfig{"dateformat"};
 
 372   my $cal_align = delete $params{cal_align} || 'BR';
 
 373   my $onchange  = delete $params{onchange};
 
 374   my $str_value = blessed $value ? $value->to_lxoffice : $value;
 
 376   $self->input_tag($name, $str_value,
 
 379     title  => _H($::myconfig{dateformat}),
 
 380     onBlur => 'check_right_date_format(this)',
 
 382     onChange => $onchange,
 
 385   ) . ((!$params{no_cal} && !$params{readonly}) ?
 
 386   $self->html_tag('img', undef,
 
 387     src    => 'image/calendar.png',
 
 388     alt    => $::locale->text('Calendar'),
 
 390     title  => _H($::myconfig{dateformat}),
 
 394     "Calendar.setup({ inputField: '$name_e', ifFormat: '$datefmt', align: '$cal_align', button: 'trigger$seq' });"
 
 398 sub customer_picker {
 
 399   my ($self, $name, $value, %params) = @_;
 
 400   my $name_e    = _H($name);
 
 402   $::request->{layout}->add_javascripts('autocomplete_customer.js');
 
 404   $self->hidden_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => 'customer_autocomplete') .
 
 405   $self->input_tag("$name_e\_name", (ref $value && $value->can('name')) ? $value->name : '', %params);
 
 408 # simple version with select_tag
 
 409 sub vendor_selector {
 
 410   my ($self, $name, $value, %params) = @_;
 
 412   my $actual_vendor_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"}) ? $::form->{"$name"}->id : $::form->{"$name"}) :
 
 413                          (ref $value && $value->can('id')) ? $value->id : '';
 
 415   return $self->select_tag($name, SL::DB::Manager::Vendor->get_all(),
 
 416                                   default      => $actual_vendor_id,
 
 417                                   title_sub    => sub { $_[0]->vendornumber . " : " . $_[0]->name },
 
 423 # simple version with select_tag
 
 425   my ($self, $name, $value, %params) = @_;
 
 427   my $actual_part_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"})? $::form->{"$name"}->id : $::form->{"$name"}) :
 
 428                        (ref $value && $value->can('id')) ? $value->id : '';
 
 430   return $self->select_tag($name, SL::DB::Manager::Part->get_all(),
 
 431                            default      => $actual_part_id,
 
 432                            title_sub    => sub { $_[0]->partnumber . " : " . $_[0]->description },
 
 442   foreach my $file (@_) {
 
 443     $file .= '.js'        unless $file =~ m/\.js$/;
 
 444     $file  = "js/${file}" unless $file =~ m|/|;
 
 446     $code .= qq|<script type="text/javascript" src="${file}"></script>|;
 
 453   my ($self, $tabs, @slurp) = @_;
 
 454   my %params   = _hashify(@slurp);
 
 455   my $id       = $params{id} || 'tab_' . _tag_id();
 
 457   $params{selected} *= 1;
 
 459   die 'L.tabbed needs an arrayred of tabs for first argument'
 
 460     unless ref $tabs eq 'ARRAY';
 
 462   my (@header, @blocks);
 
 463   for my $i (0..$#$tabs) {
 
 464     my $tab = $tabs->[$i];
 
 468     my $tab_id = "__tab_id_$i";
 
 469     push @header, $self->li_tag($self->link('#' . $tab_id, $tab->{name}));
 
 470     push @blocks, $self->div_tag($tab->{data}, id => $tab_id);
 
 473   return '' unless @header;
 
 475   my $ul = $self->ul_tag(join('', @header), id => $id);
 
 476   return $self->div_tag(join('', $ul, @blocks), class => 'tabwidget');
 
 480   my ($self, $name, $src, @slurp) = @_;
 
 481   my %params = _hashify(@slurp);
 
 483   $params{method} ||= 'process';
 
 485   return () if defined $params{if} && !$params{if};
 
 488   if ($params{method} eq 'raw') {
 
 490   } elsif ($params{method} eq 'process') {
 
 491     $data = $self->_context->process($src, %{ $params{args} || {} });
 
 493     die "unknown tag method '$params{method}'";
 
 496   return () unless $data;
 
 498   return +{ name => $name, data => $data };
 
 502   my ($self, $name, $value, @slurp) = @_;
 
 503   my %attributes      = _hashify(@slurp);
 
 506   my $min  = delete $attributes{min_rows} || 1;
 
 508   if (exists $attributes{cols}) {
 
 509     $cols = delete $attributes{cols};
 
 510     $rows = $::form->numtextrows($value, $cols);
 
 512     $rows = delete $attributes{rows} || 1;
 
 516     ? $self->textarea_tag($name, $value, %attributes, rows => max($rows, $min), ($cols ? (cols => $cols) : ()))
 
 517     : $self->input_tag($name, $value, %attributes, ($cols ? (size => $cols) : ()));
 
 520 sub multiselect2side {
 
 521   my ($self, $id, @slurp) = @_;
 
 522   my %params              = _hashify(@slurp);
 
 524   $params{labelsx}        = "\"" . _J($params{labelsx} || $::locale->text('Available')) . "\"";
 
 525   $params{labeldx}        = "\"" . _J($params{labeldx} || $::locale->text('Selected'))  . "\"";
 
 526   $params{moveOptions}    = 'false';
 
 528   my $vars                = join(', ', map { "${_}: " . $params{$_} } keys %params);
 
 530 <script type="text/javascript">
 
 531   \$().ready(function() {
 
 532     \$('#${id}').multiselect2side({ ${vars} });
 
 540 sub sortable_element {
 
 541   my ($self, $selector, @slurp) = @_;
 
 542   my %params                    = _hashify(@slurp);
 
 544   my %attributes = ( distance => 5,
 
 545                      helper   => <<'JAVASCRIPT' );
 
 546     function(event, ui) {
 
 547       ui.children().each(function() {
 
 548         $(this).width($(this).width());
 
 556   if ($params{url} && $params{with}) {
 
 557     my $as      = $params{as} || $params{with};
 
 558     my $filter  = ".filter(function(idx) { return this.substr(0, " . length($params{with}) . ") == '$params{with}'; })";
 
 559     $filter    .= ".map(function(idx, str) { return str.replace('$params{with}_', ''); })";
 
 561     $stop_event = <<JAVASCRIPT;
 
 562         \$.post('$params{url}', { '${as}[]': \$(\$('${selector}').sortable('toArray'))${filter}.toArray() });
 
 566   if (!$params{dont_recolor}) {
 
 567     $stop_event .= <<JAVASCRIPT;
 
 568         \$('${selector}>*:odd').removeClass('listrow1').removeClass('listrow0').addClass('listrow0');
 
 569         \$('${selector}>*:even').removeClass('listrow1').removeClass('listrow0').addClass('listrow1');
 
 574     $attributes{stop} = <<JAVASCRIPT;
 
 575       function(event, ui) {
 
 582   $params{handle}     = '.dragdrop' unless exists $params{handle};
 
 583   $attributes{handle} = "'$params{handle}'" if $params{handle};
 
 585   my $attr_str = join(', ', map { "${_}: $attributes{$_}" } keys %attributes);
 
 587   my $code = <<JAVASCRIPT;
 
 588 <script type="text/javascript">
 
 590     \$( "${selector}" ).sortable({ ${attr_str} })
 
 598 sub online_help_tag {
 
 599   my ($self, $tag, @slurp) = @_;
 
 600   my %params               = _hashify(@slurp);
 
 601   my $cc                   = $::myconfig{countrycode};
 
 602   my $file                 = "doc/online/$cc/$tag.html";
 
 603   my $text                 = $params{text} || $::locale->text('Help');
 
 605   die 'malformed help tag' unless $tag =~ /^[a-zA-Z0-9_]+$/;
 
 606   return unless -f $file;
 
 607   return $self->html_tag('a', $text, href => $file, class => 'jqModal')
 
 612   require Data::Dumper;
 
 613   return '<pre>' . Data::Dumper::Dumper(@_) . '</pre>';
 
 617   my ($self, $text, @slurp) = @_;
 
 618   my %params                = _hashify(@slurp);
 
 621   $params{at}               =  3 if 3 > $params{at};
 
 624   return $text if length($text) < $params{at};
 
 625   return substr($text, 0, $params{at}) . '...';
 
 628 sub sortable_table_header {
 
 629   my ($self, $by, @slurp) = @_;
 
 630   my %params              = _hashify(@slurp);
 
 632   my $controller          = $self->{CONTEXT}->stash->get('SELF');
 
 633   my $sort_spec           = $controller->get_sort_spec;
 
 634   my $by_spec             = $sort_spec->{$by};
 
 635   my %current_sort_params = $controller->get_current_sort_params;
 
 636   my ($image, $new_dir)   = ('', $current_sort_params{dir});
 
 637   my $title               = delete($params{title}) || $::locale->text($by_spec->{title});
 
 639   if ($current_sort_params{by} eq $by) {
 
 640     my $current_dir = $current_sort_params{dir} ? 'up' : 'down';
 
 641     $image          = '<img border="0" src="image/' . $current_dir . '.png">';
 
 642     $new_dir        = 1 - ($current_sort_params{dir} || 0);
 
 645   $params{ $sort_spec->{FORM_PARAMS}->[0] } = $by;
 
 646   $params{ $sort_spec->{FORM_PARAMS}->[1] } = ($new_dir ? '1' : '0');
 
 648   return '<a href="' . $controller->get_callback(%params) . '">' . _H($title) . $image . '</a>';
 
 651 sub paginate_controls {
 
 654   my $controller      = $self->{CONTEXT}->stash->get('SELF');
 
 655   my $paginate_spec   = $controller->get_paginate_spec;
 
 656   my %paginate_params = $controller->get_current_paginate_params;
 
 658   my %template_params = (
 
 659     pages             => \%paginate_params,
 
 661       my %url_params                                    = _hashify(@_);
 
 662       $url_params{ $paginate_spec->{FORM_PARAMS}->[0] } = delete $url_params{page};
 
 663       $url_params{ $paginate_spec->{FORM_PARAMS}->[1] } = delete $url_params{per_page} if exists $url_params{per_page};
 
 665       return $controller->get_callback(%url_params);
 
 669   return SL::Presenter->get->render('common/paginate', %template_params);
 
 678 SL::Templates::Plugin::L -- Layouting / tag generation
 
 682 Usage from a template:
 
 686   [% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right', 1 ] ]) %]
 
 688   [% L.select_tag('direction', [ { direction => 'left',  display => 'To the left'  },
 
 689                                  { direction => 'right', display => 'To the right' } ],
 
 690                                value_key => 'direction', title_key => 'display', default => 'right')) %]
 
 692   [% L.select_tag('direction', [ { direction => 'left',  display => 'To the left'  },
 
 693                                  { direction => 'right', display => 'To the right', selected => 1 } ],
 
 694                                value_key => 'direction', title_key => 'display')) %]
 
 698 A module modeled a bit after Rails' ActionView helpers. Several small
 
 699 functions that create HTML tags from various kinds of data sources.
 
 703 =head2 LOW-LEVEL FUNCTIONS
 
 707 =item C<name_to_id $name>
 
 709 Converts a name to a HTML id by replacing various characters.
 
 711 =item C<attributes %items>
 
 713 Creates a string from all elements in C<%items> suitable for usage as
 
 714 HTML tag attributes. Keys and values are HTML escaped even though keys
 
 715 must not contain non-ASCII characters for browsers to accept them.
 
 717 =item C<html_tag $tag_name, $content_string, %attributes>
 
 719 Creates an opening and closing HTML tag for C<$tag_name> and puts
 
 720 C<$content_string> between the two. If C<$content_string> is undefined
 
 721 or empty then only a E<lt>tag/E<gt> tag will be created. Attributes
 
 722 are key/value pairs added to the opening tag.
 
 724 C<$content_string> is not HTML escaped.
 
 728 =head2 HIGH-LEVEL FUNCTIONS
 
 732 =item C<select_tag $name, \@collection, %attributes>
 
 734 Creates a HTML 'select' tag named C<$name> with the contents of one
 
 735 'E<lt>optionE<gt>' tag for each element in C<\@collection> and with arbitrary
 
 736 HTML attributes from C<%attributes>. The value
 
 737 to use and the title to display are extracted from the elements in
 
 738 C<\@collection>. Each element can be one of four things:
 
 742 =item 1. An array reference with at least two elements. The first element is
 
 743 the value, the second element is its title. The third element is optional and and should contain a boolean.
 
 744 If it is true, than the element will be used as default.
 
 746 =item 2. A scalar. The scalar is both the value and the title.
 
 748 =item 3. A hash reference. In this case C<%attributes> must contain
 
 749 I<value_key>, I<title_key> and may contain I<default_key> keys that name the keys in the element to use
 
 750 for the value, title and default respectively.
 
 752 =item 4. A blessed reference. In this case C<%attributes> must contain
 
 753 I<value_key>, I<title_key> and may contain I<default_key> keys that name functions called on the blessed
 
 754 reference whose return values are used as the value, title and default
 
 759 For cases 3 and 4 C<$attributes{value_key}> defaults to C<id>,
 
 760 C<$attributes{title_key}> defaults to C<$attributes{value_key}>
 
 761 and C<$attributes{default_key}> defaults to C<selected>.
 
 763 In addition to pure keys/method you can also provide coderefs as I<value_sub>
 
 764 and/or I<title_sub> and/or I<default_sub>. If present, these take precedence over keys or methods,
 
 765 and are called with the element as first argument. It must return the value, title or default.
 
 767 Lastly a joint coderef I<value_title_sub> may be provided, which in turn takes
 
 768 precedence over the C<value_sub> and C<title_sub> subs. It will only be called once for each
 
 769 element and must return a list of value and title.
 
 771 If the option C<with_empty> is set then an empty element (value
 
 772 C<undef>) will be used as the first element. The title to display for
 
 773 this element can be set with the option C<empty_title> and defaults to
 
 776 The option C<default> can be either a scalar or an array reference
 
 777 containing the values of the options which should be set to be
 
 780 The tag's C<id> defaults to C<name_to_id($name)>.
 
 782 =item C<yes_no_tag $name, $value, %attributes>
 
 784 Creates a HTML 'select' tag with the two entries C<yes> and C<no> by
 
 785 calling L<select_tag>. C<$value> determines
 
 786 which entry is selected. The C<%attributes> are passed through to
 
 789 =item C<input_tag $name, $value, %attributes>
 
 791 Creates a HTML 'input type=text' tag named C<$name> with the value
 
 792 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
 
 793 tag's C<id> defaults to C<name_to_id($name)>.
 
 795 =item C<hidden_tag $name, $value, %attributes>
 
 797 Creates a HTML 'input type=hidden' tag named C<$name> with the value
 
 798 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
 
 799 tag's C<id> defaults to C<name_to_id($name)>.
 
 801 =item C<submit_tag $name, $value, %attributes>
 
 803 Creates a HTML 'input type=submit class=submit' tag named C<$name> with the
 
 804 value C<$value> and with arbitrary HTML attributes from C<%attributes>. The
 
 805 tag's C<id> defaults to C<name_to_id($name)>.
 
 807 If C<$attributes{confirm}> is set then a JavaScript popup dialog will
 
 808 be added via the C<onclick> handler asking the question given with
 
 809 C<$attributes{confirm}>. If request is only submitted if the user
 
 810 clicks the dialog's ok/yes button.
 
 812 =item C<textarea_tag $name, $value, %attributes>
 
 814 Creates a HTML 'textarea' tag named C<$name> with the content
 
 815 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
 
 816 tag's C<id> defaults to C<name_to_id($name)>.
 
 818 =item C<checkbox_tag $name, %attributes>
 
 820 Creates a HTML 'input type=checkbox' tag named C<$name> with arbitrary
 
 821 HTML attributes from C<%attributes>. The tag's C<id> defaults to
 
 822 C<name_to_id($name)>. The tag's C<value> defaults to C<1>.
 
 824 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
 
 825 created with said C<label>. No attribute named C<label> is created in
 
 828 If C<%attributes> contains a key C<checkall> then the value is taken as a
 
 829 JQuery selector and clicking this checkbox will also toggle all checkboxes
 
 830 matching the selector.
 
 832 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
 
 834 Creates a date input field, with an attached javascript that will open a
 
 835 calendar on click. The javascript ist by default anchoered at the bottom right
 
 836 sight. This can be overridden with C<cal_align>, see Calendar documentation for
 
 837 the details, usually you'll want a two letter abbreviation of the alignment.
 
 838 Right + Bottom becomes C<BL>.
 
 840 =item C<radio_button_tag $name, %attributes>
 
 842 Creates a HTML 'input type=radio' tag named C<$name> with arbitrary
 
 843 HTML attributes from C<%attributes>. The tag's C<value> defaults to
 
 844 C<1>. The tag's C<id> defaults to C<name_to_id($name . "_" . $value)>.
 
 846 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
 
 847 created with said C<label>. No attribute named C<label> is created in
 
 850 =item C<javascript_tag $file1, $file2, $file3...>
 
 852 Creates a HTML 'E<lt>script type="text/javascript" src="..."E<gt>'
 
 853 tag for each file name parameter passed. Each file name will be
 
 854 postfixed with '.js' if it isn't already and prefixed with 'js/' if it
 
 855 doesn't contain a slash.
 
 857 =item C<stylesheet_tag $file1, $file2, $file3...>
 
 859 Creates a HTML 'E<lt>link rel="text/stylesheet" href="..."E<gt>' tag
 
 860 for each file name parameter passed. Each file name will be postfixed
 
 861 with '.css' if it isn't already and prefixed with 'css/' if it doesn't
 
 864 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
 
 866 Creates a date input field, with an attached javascript that will open a
 
 867 calendar on click. The javascript ist by default anchoered at the bottom right
 
 868 sight. This can be overridden with C<cal_align>, see Calendar documentation for
 
 869 the details, usually you'll want a two letter abbreviation of the alignment.
 
 870 Right + Bottom becomes C<BL>.
 
 872 =item C<tabbed \@tab, %attributes>
 
 874 Will create a tabbed area. The tabs should be created with the helper function
 
 878     L.tab(LxERP.t8('Basic Data'),       'part/_main_tab.html'),
 
 879     L.tab(LxERP.t8('Custom Variables'), 'part/_cvar_tab.html', if => SELF.display_cvar_tab),
 
 882 =item C<areainput_tag $name, $content, %PARAMS>
 
 884 Creates a generic input tag or textarea tag, depending on content size. The
 
 885 amount of desired rows must be either given with the C<rows> parameter or can
 
 886 be computed from the value and the C<cols> paramter, Accepted parameters
 
 887 include C<min_rows> for rendering a minimum of rows if a textarea is displayed.
 
 889 You can force input by setting rows to 1, and you can force textarea by setting
 
 892 =item C<multiselect2side $id, %params>
 
 894 Creates a JavaScript snippet calling the jQuery function
 
 895 C<multiselect2side> on the select control with the ID C<$id>. The
 
 896 select itself is not created. C<%params> can contain the following
 
 903 The label of the list of available options. Defaults to the
 
 904 translation of 'Available'.
 
 908 The label of the list of selected options. Defaults to the
 
 909 translation of 'Selected'.
 
 913 =item C<sortable_element $selector, %params>
 
 915 Makes the children of the DOM element C<$selector> (a jQuery selector)
 
 916 sortable with the I<jQuery UI Selectable> library. The children can be
 
 917 dragged & dropped around. After dropping an element an URL can be
 
 918 postet to with the element IDs of the sorted children.
 
 920 If this is used then the JavaScript file C<js/jquery-ui.js> must be
 
 921 included manually as well as it isn't loaded via C<$::form-gt;header>.
 
 923 C<%params> can contain the following entries:
 
 929 The URL to POST an AJAX request to after a dragged element has been
 
 930 dropped. The AJAX request's return value is ignored. If given then
 
 931 C<$params{with}> must be given as well.
 
 935 A string that is interpreted as the prefix of the children's ID. Upon
 
 936 POSTing the result each child whose ID starts with C<$params{with}> is
 
 937 considered. The prefix and the following "_" is removed from the
 
 938 ID. The remaining parts of the IDs of those children are posted as a
 
 939 single array parameter. The array parameter's name is either
 
 940 C<$params{as}> or, missing that, C<$params{with}>.
 
 944 Sets the POST parameter name for AJAX request after dropping an
 
 945 element (see C<$params{with}>).
 
 949 An optional jQuery selector specifying which part of the child element
 
 950 is dragable. If the parameter is not given then it defaults to
 
 951 C<.dragdrop> matching DOM elements with the class C<dragdrop>.  If the
 
 952 parameter is set and empty then the whole child element is dragable,
 
 953 and clicks through to underlying elements like inputs or links might
 
 956 =item C<dont_recolor>
 
 958 If trueish then the children will not be recolored. The default is to
 
 959 recolor the children by setting the class C<listrow0> on odd and
 
 960 C<listrow1> on even entries.
 
 966   <script type="text/javascript" src="js/jquery-ui.js"></script>
 
 968   <table id="thing_list">
 
 970       <tr><td>This</td><td>That</td></tr>
 
 973       <tr id="thingy_2"><td>stuff</td><td>more stuff</td></tr>
 
 974       <tr id="thingy_15"><td>stuff</td><td>more stuff</td></tr>
 
 975       <tr id="thingy_6"><td>stuff</td><td>more stuff</td></tr>
 
 979   [% L.sortable_element('#thing_list tbody',
 
 980                         url          => 'controller.pl?action=SystemThings/reorder',
 
 983                         recolor_rows => 1) %]
 
 985 After dropping e.g. the third element at the top of the list a POST
 
 986 request would be made to the C<reorder> action of the C<SystemThings>
 
 987 controller with a single parameter called C<thing_ids> -- an array
 
 988 containing the values C<[ 6, 2, 15 ]>.
 
 992 Dumps the Argument using L<Data::Dumper> into a E<lt>preE<gt> block.
 
 994 =item C<sortable_table_header $by, %params>
 
 996 Create a link and image suitable for placement in a table
 
 997 header. C<$by> must be an index set up by the controller with
 
 998 L<SL::Controller::Helper::make_sorted>.
 
1000 The optional parameter C<$params{title}> can override the column title
 
1001 displayed to the user. Otherwise the column title from the
 
1002 controller's sort spec is used.
 
1004 The other parameters in C<%params> are passed unmodified to the
 
1005 underlying call to L<SL::Controller::Base::url_for>.
 
1007 See the documentation of L<SL::Controller::Helper::Sorted> for an
 
1008 overview and further usage instructions.
 
1010 =item C<paginate_controls>
 
1012 Create a set of links used to paginate a list view.
 
1014 See the documentation of L<SL::Controller::Helper::Paginated> for an
 
1015 overview and further usage instructions.
 
1019 =head2 CONVERSION FUNCTIONS
 
1023 =item C<tab, description, target, %PARAMS>
 
1025 Creates a tab for C<tabbed>. The description will be used as displayed name.
 
1026 The target should be a block or template that can be processed. C<tab> supports
 
1027 a C<method> parameter, which can override the process method to apply target.
 
1028 C<method => 'raw'> will just include the given text as is. I was too lazy to
 
1029 implement C<include> properly.
 
1031 Also an C<if> attribute is supported, so that tabs can be suppressed based on
 
1032 some occasion. In this case the supplied block won't even get processed, and
 
1033 the resulting tab will get ignored by C<tabbed>:
 
1035   L.tab('Awesome tab wih much info', '_much_info.html', if => SELF.wants_all)
 
1037 =item C<truncate $text, %params>
 
1039 Returns the C<$text> truncated after a certain number of
 
1042 The number of characters to truncate at is determined by the parameter
 
1043 C<at> which defaults to 50. If the text is longer than C<$params{at}>
 
1044 then it will be truncated and postfixed with '...'. Otherwise it will
 
1045 be returned unmodified.
 
1049 =head1 MODULE AUTHORS
 
1051 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
 
1053 L<http://linet-services.de>