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   return shift->input_tag(@_, type => 'hidden');
 
 290   my ($self, $content, @slurp) = @_;
 
 291   return $self->html_tag('div', $content, @slurp);
 
 295   my ($self, $content, @slurp) = @_;
 
 296   return $self->html_tag('ul', $content, @slurp);
 
 300   my ($self, $content, @slurp) = @_;
 
 301   return $self->html_tag('li', $content, @slurp);
 
 305   my ($self, $href, $content, @slurp) = @_;
 
 306   my %params = _hashify(@slurp);
 
 310   return $self->html_tag('a', $content, %params, href => $href);
 
 314   my ($self, $name, $value, @slurp) = @_;
 
 315   my %attributes = _hashify(@slurp);
 
 317   if ( $attributes{confirm} ) {
 
 318     $attributes{onclick} = 'return confirm("'. _J(delete($attributes{confirm})) .'");';
 
 321   return $self->input_tag($name, $value, %attributes, type => 'submit', class => 'submit');
 
 325   my ($self, $onclick, $value, @slurp) = @_;
 
 326   my %attributes = _hashify(@slurp);
 
 328   $attributes{id}   ||= $self->name_to_id($attributes{name}) if $attributes{name};
 
 329   $attributes{type} ||= 'button';
 
 331   return $self->html_tag('input', undef, %attributes, value => $value, onclick => $onclick);
 
 335   my ($self, $name, $value) = splice @_, 0, 3;
 
 336   my %attributes            = _hashify(@_);
 
 338   return $self->select_tag($name, [ [ 1 => $::locale->text('Yes') ], [ 0 => $::locale->text('No') ] ], default => $value ? 1 : 0, %attributes);
 
 342   my ($self, $data) = @_;
 
 343   return $self->html_tag('script', $data, type => 'text/javascript');
 
 350   foreach my $file (@_) {
 
 351     $file .= '.css'        unless $file =~ m/\.css$/;
 
 352     $file  = "css/${file}" unless $file =~ m|/|;
 
 354     $code .= qq|<link rel="stylesheet" href="${file}" type="text/css" media="screen" />|;
 
 361   my ($self, $name, $value, @slurp) = @_;
 
 362   my %params   = _hashify(@slurp);
 
 363   my $name_e   = _H($name);
 
 365   my $datefmt  = apply {
 
 369   } $::myconfig{"dateformat"};
 
 371   my $cal_align = delete $params{cal_align} || 'BR';
 
 372   my $onchange  = delete $params{onchange};
 
 373   my $str_value = blessed $value ? $value->to_lxoffice : $value;
 
 375   $self->input_tag($name, $str_value,
 
 378     title  => _H($::myconfig{dateformat}),
 
 379     onBlur => 'check_right_date_format(this)',
 
 381     onChange => $onchange,
 
 384   ) . ((!$params{no_cal} && !$params{readonly}) ?
 
 385   $self->html_tag('img', undef,
 
 386     src    => 'image/calendar.png',
 
 387     alt    => $::locale->text('Calendar'),
 
 389     title  => _H($::myconfig{dateformat}),
 
 393     "Calendar.setup({ inputField: '$name_e', ifFormat: '$datefmt', align: '$cal_align', button: 'trigger$seq' });"
 
 397 sub customer_picker {
 
 398   my ($self, $name, $value, %params) = @_;
 
 399   my $name_e    = _H($name);
 
 401   $::request->{layout}->add_javascripts('autocomplete_customer.js');
 
 403   $self->hidden_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => 'customer_autocomplete') .
 
 404   $self->input_tag("$name_e\_name", (ref $value && $value->can('name')) ? $value->name : '', %params);
 
 407 # simple version with select_tag
 
 408 sub vendor_selector {
 
 409   my ($self, $name, $value, %params) = @_;
 
 411   my $actual_vendor_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"}) ? $::form->{"$name"}->id : $::form->{"$name"}) :
 
 412                          (ref $value && $value->can('id')) ? $value->id : '';
 
 414   return $self->select_tag($name, SL::DB::Manager::Vendor->get_all(),
 
 415                                   default      => $actual_vendor_id,
 
 416                                   title_sub    => sub { $_[0]->vendornumber . " : " . $_[0]->name },
 
 422 # simple version with select_tag
 
 424   my ($self, $name, $value, %params) = @_;
 
 426   my $actual_part_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"})? $::form->{"$name"}->id : $::form->{"$name"}) :
 
 427                        (ref $value && $value->can('id')) ? $value->id : '';
 
 429   return $self->select_tag($name, SL::DB::Manager::Part->get_all(),
 
 430                            default      => $actual_part_id,
 
 431                            title_sub    => sub { $_[0]->partnumber . " : " . $_[0]->description },
 
 441   foreach my $file (@_) {
 
 442     $file .= '.js'        unless $file =~ m/\.js$/;
 
 443     $file  = "js/${file}" unless $file =~ m|/|;
 
 445     $code .= qq|<script type="text/javascript" src="${file}"></script>|;
 
 452   my ($self, $tabs, @slurp) = @_;
 
 453   my %params   = _hashify(@slurp);
 
 454   my $id       = $params{id} || 'tab_' . _tag_id();
 
 456   $params{selected} *= 1;
 
 458   die 'L.tabbed needs an arrayred of tabs for first argument'
 
 459     unless ref $tabs eq 'ARRAY';
 
 461   my (@header, @blocks);
 
 462   for my $i (0..$#$tabs) {
 
 463     my $tab = $tabs->[$i];
 
 467     my $selected = $params{selected} == $i;
 
 468     my $tab_id   = "__tab_id_$i";
 
 469     push @header, $self->li_tag(
 
 470       $self->link('', $tab->{name}, rel => $tab_id),
 
 471         ($selected ? (class => 'selected') : ())
 
 473     push @blocks, $self->div_tag($tab->{data},
 
 474       id => $tab_id, class => 'tabcontent');
 
 477   return '' unless @header;
 
 478   return $self->ul_tag(
 
 479     join('', @header), id => $id, class => 'shadetabs'
 
 482     join('', @blocks), class => 'tabcontentstyle'
 
 485     qq|var $id = new ddtabcontent("$id");$id.setpersist(true);| .
 
 486     qq|$id.setselectedClassTarget("link");$id.init();|
 
 491   my ($self, $name, $src, @slurp) = @_;
 
 492   my %params = _hashify(@slurp);
 
 494   $params{method} ||= 'process';
 
 496   return () if defined $params{if} && !$params{if};
 
 499   if ($params{method} eq 'raw') {
 
 501   } elsif ($params{method} eq 'process') {
 
 502     $data = $self->_context->process($src, %{ $params{args} || {} });
 
 504     die "unknown tag method '$params{method}'";
 
 507   return () unless $data;
 
 509   return +{ name => $name, data => $data };
 
 513   my ($self, $name, $value, @slurp) = @_;
 
 514   my %attributes      = _hashify(@slurp);
 
 517   my $min  = delete $attributes{min_rows} || 1;
 
 519   if (exists $attributes{cols}) {
 
 520     $cols = delete $attributes{cols};
 
 521     $rows = $::form->numtextrows($value, $cols);
 
 523     $rows = delete $attributes{rows} || 1;
 
 527     ? $self->textarea_tag($name, $value, %attributes, rows => max($rows, $min), ($cols ? (cols => $cols) : ()))
 
 528     : $self->input_tag($name, $value, %attributes, ($cols ? (size => $cols) : ()));
 
 531 sub multiselect2side {
 
 532   my ($self, $id, @slurp) = @_;
 
 533   my %params              = _hashify(@slurp);
 
 535   $params{labelsx}        = "\"" . _J($params{labelsx} || $::locale->text('Available')) . "\"";
 
 536   $params{labeldx}        = "\"" . _J($params{labeldx} || $::locale->text('Selected'))  . "\"";
 
 537   $params{moveOptions}    = 'false';
 
 539   my $vars                = join(', ', map { "${_}: " . $params{$_} } keys %params);
 
 541 <script type="text/javascript">
 
 542   \$().ready(function() {
 
 543     \$('#${id}').multiselect2side({ ${vars} });
 
 551 sub sortable_element {
 
 552   my ($self, $selector, @slurp) = @_;
 
 553   my %params                    = _hashify(@slurp);
 
 555   my %attributes = ( distance => 5,
 
 556                      helper   => <<'JAVASCRIPT' );
 
 557     function(event, ui) {
 
 558       ui.children().each(function() {
 
 559         $(this).width($(this).width());
 
 567   if ($params{url} && $params{with}) {
 
 568     my $as      = $params{as} || $params{with};
 
 569     my $filter  = ".filter(function(idx) { return this.substr(0, " . length($params{with}) . ") == '$params{with}'; })";
 
 570     $filter    .= ".map(function(idx, str) { return str.replace('$params{with}_', ''); })";
 
 572     $stop_event = <<JAVASCRIPT;
 
 573         \$.post('$params{url}', { '${as}[]': \$(\$('${selector}').sortable('toArray'))${filter}.toArray() });
 
 577   if (!$params{dont_recolor}) {
 
 578     $stop_event .= <<JAVASCRIPT;
 
 579         \$('${selector}>*:odd').removeClass('listrow1').removeClass('listrow0').addClass('listrow0');
 
 580         \$('${selector}>*:even').removeClass('listrow1').removeClass('listrow0').addClass('listrow1');
 
 585     $attributes{stop} = <<JAVASCRIPT;
 
 586       function(event, ui) {
 
 593   $params{handle}     = '.dragdrop' unless exists $params{handle};
 
 594   $attributes{handle} = "'$params{handle}'" if $params{handle};
 
 596   my $attr_str = join(', ', map { "${_}: $attributes{$_}" } keys %attributes);
 
 598   my $code = <<JAVASCRIPT;
 
 599 <script type="text/javascript">
 
 601     \$( "${selector}" ).sortable({ ${attr_str} })
 
 609 sub online_help_tag {
 
 610   my ($self, $tag, @slurp) = @_;
 
 611   my %params               = _hashify(@slurp);
 
 612   my $cc                   = $::myconfig{countrycode};
 
 613   my $file                 = "doc/online/$cc/$tag.html";
 
 614   my $text                 = $params{text} || $::locale->text('Help');
 
 616   die 'malformed help tag' unless $tag =~ /^[a-zA-Z0-9_]+$/;
 
 617   return unless -f $file;
 
 618   return $self->html_tag('a', $text, href => $file, class => 'jqModal')
 
 623   require Data::Dumper;
 
 624   return '<pre>' . Data::Dumper::Dumper(@_) . '</pre>';
 
 628   my ($self, $text, @slurp) = @_;
 
 629   my %params                = _hashify(@slurp);
 
 632   $params{at}               =  3 if 3 > $params{at};
 
 635   return $text if length($text) < $params{at};
 
 636   return substr($text, 0, $params{at}) . '...';
 
 639 sub sortable_table_header {
 
 640   my ($self, $by, @slurp) = @_;
 
 641   my %params              = _hashify(@slurp);
 
 643   my $controller          = $self->{CONTEXT}->stash->get('SELF');
 
 644   my $sort_spec           = $controller->get_sort_spec;
 
 645   my $by_spec             = $sort_spec->{$by};
 
 646   my %current_sort_params = $controller->get_current_sort_params;
 
 647   my ($image, $new_dir)   = ('', $current_sort_params{dir});
 
 648   my $title               = delete($params{title}) || $::locale->text($by_spec->{title});
 
 650   if ($current_sort_params{by} eq $by) {
 
 651     my $current_dir = $current_sort_params{dir} ? 'up' : 'down';
 
 652     $image          = '<img border="0" src="image/' . $current_dir . '.png">';
 
 653     $new_dir        = 1 - ($current_sort_params{dir} || 0);
 
 656   $params{ $sort_spec->{FORM_PARAMS}->[0] } = $by;
 
 657   $params{ $sort_spec->{FORM_PARAMS}->[1] } = ($new_dir ? '1' : '0');
 
 659   return '<a href="' . $controller->get_callback(%params) . '">' . _H($title) . $image . '</a>';
 
 662 sub paginate_controls {
 
 665   my $controller      = $self->{CONTEXT}->stash->get('SELF');
 
 666   my $paginate_spec   = $controller->get_paginate_spec;
 
 667   my %paginate_params = $controller->get_current_paginate_params;
 
 669   my %template_params = (
 
 670     pages             => \%paginate_params,
 
 672       my %url_params                                    = _hashify(@_);
 
 673       $url_params{ $paginate_spec->{FORM_PARAMS}->[0] } = delete $url_params{page};
 
 674       $url_params{ $paginate_spec->{FORM_PARAMS}->[1] } = delete $url_params{per_page} if exists $url_params{per_page};
 
 676       return $controller->get_callback(%url_params);
 
 680   return SL::Presenter->get->render('common/paginate', %template_params);
 
 689 SL::Templates::Plugin::L -- Layouting / tag generation
 
 693 Usage from a template:
 
 697   [% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right', 1 ] ]) %]
 
 699   [% L.select_tag('direction', [ { direction => 'left',  display => 'To the left'  },
 
 700                                  { direction => 'right', display => 'To the right' } ],
 
 701                                value_key => 'direction', title_key => 'display', default => 'right')) %]
 
 703   [% L.select_tag('direction', [ { direction => 'left',  display => 'To the left'  },
 
 704                                  { direction => 'right', display => 'To the right', selected => 1 } ],
 
 705                                value_key => 'direction', title_key => 'display')) %]
 
 709 A module modeled a bit after Rails' ActionView helpers. Several small
 
 710 functions that create HTML tags from various kinds of data sources.
 
 714 =head2 LOW-LEVEL FUNCTIONS
 
 718 =item C<name_to_id $name>
 
 720 Converts a name to a HTML id by replacing various characters.
 
 722 =item C<attributes %items>
 
 724 Creates a string from all elements in C<%items> suitable for usage as
 
 725 HTML tag attributes. Keys and values are HTML escaped even though keys
 
 726 must not contain non-ASCII characters for browsers to accept them.
 
 728 =item C<html_tag $tag_name, $content_string, %attributes>
 
 730 Creates an opening and closing HTML tag for C<$tag_name> and puts
 
 731 C<$content_string> between the two. If C<$content_string> is undefined
 
 732 or empty then only a E<lt>tag/E<gt> tag will be created. Attributes
 
 733 are key/value pairs added to the opening tag.
 
 735 C<$content_string> is not HTML escaped.
 
 739 =head2 HIGH-LEVEL FUNCTIONS
 
 743 =item C<select_tag $name, \@collection, %attributes>
 
 745 Creates a HTML 'select' tag named C<$name> with the contents of one
 
 746 'E<lt>optionE<gt>' tag for each element in C<\@collection> and with arbitrary
 
 747 HTML attributes from C<%attributes>. The value
 
 748 to use and the title to display are extracted from the elements in
 
 749 C<\@collection>. Each element can be one of four things:
 
 753 =item 1. An array reference with at least two elements. The first element is
 
 754 the value, the second element is its title. The third element is optional and and should contain a boolean.
 
 755 If it is true, than the element will be used as default.
 
 757 =item 2. A scalar. The scalar is both the value and the title.
 
 759 =item 3. A hash reference. In this case C<%attributes> must contain
 
 760 I<value_key>, I<title_key> and may contain I<default_key> keys that name the keys in the element to use
 
 761 for the value, title and default respectively.
 
 763 =item 4. A blessed reference. In this case C<%attributes> must contain
 
 764 I<value_key>, I<title_key> and may contain I<default_key> keys that name functions called on the blessed
 
 765 reference whose return values are used as the value, title and default
 
 770 For cases 3 and 4 C<$attributes{value_key}> defaults to C<id>,
 
 771 C<$attributes{title_key}> defaults to C<$attributes{value_key}>
 
 772 and C<$attributes{default_key}> defaults to C<selected>.
 
 774 In addition to pure keys/method you can also provide coderefs as I<value_sub>
 
 775 and/or I<title_sub> and/or I<default_sub>. If present, these take precedence over keys or methods,
 
 776 and are called with the element as first argument. It must return the value, title or default.
 
 778 Lastly a joint coderef I<value_title_sub> may be provided, which in turn takes
 
 779 precedence over the C<value_sub> and C<title_sub> subs. It will only be called once for each
 
 780 element and must return a list of value and title.
 
 782 If the option C<with_empty> is set then an empty element (value
 
 783 C<undef>) will be used as the first element. The title to display for
 
 784 this element can be set with the option C<empty_title> and defaults to
 
 787 The option C<default> can be either a scalar or an array reference
 
 788 containing the values of the options which should be set to be
 
 791 The tag's C<id> defaults to C<name_to_id($name)>.
 
 793 =item C<yes_no_tag $name, $value, %attributes>
 
 795 Creates a HTML 'select' tag with the two entries C<yes> and C<no> by
 
 796 calling L<select_tag>. C<$value> determines
 
 797 which entry is selected. The C<%attributes> are passed through to
 
 800 =item C<input_tag $name, $value, %attributes>
 
 802 Creates a HTML 'input type=text' tag named C<$name> with the value
 
 803 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
 
 804 tag's C<id> defaults to C<name_to_id($name)>.
 
 806 =item C<hidden_tag $name, $value, %attributes>
 
 808 Creates a HTML 'input type=hidden' tag named C<$name> with the value
 
 809 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
 
 810 tag's C<id> defaults to C<name_to_id($name)>.
 
 812 =item C<submit_tag $name, $value, %attributes>
 
 814 Creates a HTML 'input type=submit class=submit' tag named C<$name> with the
 
 815 value C<$value> and with arbitrary HTML attributes from C<%attributes>. The
 
 816 tag's C<id> defaults to C<name_to_id($name)>.
 
 818 If C<$attributes{confirm}> is set then a JavaScript popup dialog will
 
 819 be added via the C<onclick> handler asking the question given with
 
 820 C<$attributes{confirm}>. If request is only submitted if the user
 
 821 clicks the dialog's ok/yes button.
 
 823 =item C<textarea_tag $name, $value, %attributes>
 
 825 Creates a HTML 'textarea' tag named C<$name> with the content
 
 826 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
 
 827 tag's C<id> defaults to C<name_to_id($name)>.
 
 829 =item C<checkbox_tag $name, %attributes>
 
 831 Creates a HTML 'input type=checkbox' tag named C<$name> with arbitrary
 
 832 HTML attributes from C<%attributes>. The tag's C<id> defaults to
 
 833 C<name_to_id($name)>. The tag's C<value> defaults to C<1>.
 
 835 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
 
 836 created with said C<label>. No attribute named C<label> is created in
 
 839 If C<%attributes> contains a key C<checkall> then the value is taken as a
 
 840 JQuery selector and clicking this checkbox will also toggle all checkboxes
 
 841 matching the selector.
 
 843 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
 
 845 Creates a date input field, with an attached javascript that will open a
 
 846 calendar on click. The javascript ist by default anchoered at the bottom right
 
 847 sight. This can be overridden with C<cal_align>, see Calendar documentation for
 
 848 the details, usually you'll want a two letter abbreviation of the alignment.
 
 849 Right + Bottom becomes C<BL>.
 
 851 =item C<radio_button_tag $name, %attributes>
 
 853 Creates a HTML 'input type=radio' tag named C<$name> with arbitrary
 
 854 HTML attributes from C<%attributes>. The tag's C<value> defaults to
 
 855 C<1>. The tag's C<id> defaults to C<name_to_id($name . "_" . $value)>.
 
 857 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
 
 858 created with said C<label>. No attribute named C<label> is created in
 
 861 =item C<javascript_tag $file1, $file2, $file3...>
 
 863 Creates a HTML 'E<lt>script type="text/javascript" src="..."E<gt>'
 
 864 tag for each file name parameter passed. Each file name will be
 
 865 postfixed with '.js' if it isn't already and prefixed with 'js/' if it
 
 866 doesn't contain a slash.
 
 868 =item C<stylesheet_tag $file1, $file2, $file3...>
 
 870 Creates a HTML 'E<lt>link rel="text/stylesheet" href="..."E<gt>' tag
 
 871 for each file name parameter passed. Each file name will be postfixed
 
 872 with '.css' if it isn't already and prefixed with 'css/' if it doesn't
 
 875 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
 
 877 Creates a date input field, with an attached javascript that will open a
 
 878 calendar on click. The javascript ist by default anchoered at the bottom right
 
 879 sight. This can be overridden with C<cal_align>, see Calendar documentation for
 
 880 the details, usually you'll want a two letter abbreviation of the alignment.
 
 881 Right + Bottom becomes C<BL>.
 
 883 =item C<tabbed \@tab, %attributes>
 
 885 Will create a tabbed area. The tabs should be created with the helper function
 
 889     L.tab(LxERP.t8('Basic Data'),       'part/_main_tab.html'),
 
 890     L.tab(LxERP.t8('Custom Variables'), 'part/_cvar_tab.html', if => SELF.display_cvar_tab),
 
 893 An optional attribute is C<selected>, which accepts the ordinal of a tab which
 
 894 should be selected by default.
 
 896 =item C<areainput_tag $name, $content, %PARAMS>
 
 898 Creates a generic input tag or textarea tag, depending on content size. The
 
 899 amount of desired rows must be either given with the C<rows> parameter or can
 
 900 be computed from the value and the C<cols> paramter, Accepted parameters
 
 901 include C<min_rows> for rendering a minimum of rows if a textarea is displayed.
 
 903 You can force input by setting rows to 1, and you can force textarea by setting
 
 906 =item C<multiselect2side $id, %params>
 
 908 Creates a JavaScript snippet calling the jQuery function
 
 909 C<multiselect2side> on the select control with the ID C<$id>. The
 
 910 select itself is not created. C<%params> can contain the following
 
 917 The label of the list of available options. Defaults to the
 
 918 translation of 'Available'.
 
 922 The label of the list of selected options. Defaults to the
 
 923 translation of 'Selected'.
 
 927 =item C<sortable_element $selector, %params>
 
 929 Makes the children of the DOM element C<$selector> (a jQuery selector)
 
 930 sortable with the I<jQuery UI Selectable> library. The children can be
 
 931 dragged & dropped around. After dropping an element an URL can be
 
 932 postet to with the element IDs of the sorted children.
 
 934 If this is used then the JavaScript file C<js/jquery-ui.js> must be
 
 935 included manually as well as it isn't loaded via C<$::form-gt;header>.
 
 937 C<%params> can contain the following entries:
 
 943 The URL to POST an AJAX request to after a dragged element has been
 
 944 dropped. The AJAX request's return value is ignored. If given then
 
 945 C<$params{with}> must be given as well.
 
 949 A string that is interpreted as the prefix of the children's ID. Upon
 
 950 POSTing the result each child whose ID starts with C<$params{with}> is
 
 951 considered. The prefix and the following "_" is removed from the
 
 952 ID. The remaining parts of the IDs of those children are posted as a
 
 953 single array parameter. The array parameter's name is either
 
 954 C<$params{as}> or, missing that, C<$params{with}>.
 
 958 Sets the POST parameter name for AJAX request after dropping an
 
 959 element (see C<$params{with}>).
 
 963 An optional jQuery selector specifying which part of the child element
 
 964 is dragable. If the parameter is not given then it defaults to
 
 965 C<.dragdrop> matching DOM elements with the class C<dragdrop>.  If the
 
 966 parameter is set and empty then the whole child element is dragable,
 
 967 and clicks through to underlying elements like inputs or links might
 
 970 =item C<dont_recolor>
 
 972 If trueish then the children will not be recolored. The default is to
 
 973 recolor the children by setting the class C<listrow0> on odd and
 
 974 C<listrow1> on even entries.
 
 980   <script type="text/javascript" src="js/jquery-ui.js"></script>
 
 982   <table id="thing_list">
 
 984       <tr><td>This</td><td>That</td></tr>
 
 987       <tr id="thingy_2"><td>stuff</td><td>more stuff</td></tr>
 
 988       <tr id="thingy_15"><td>stuff</td><td>more stuff</td></tr>
 
 989       <tr id="thingy_6"><td>stuff</td><td>more stuff</td></tr>
 
 993   [% L.sortable_element('#thing_list tbody',
 
 994                         url          => 'controller.pl?action=SystemThings/reorder',
 
 997                         recolor_rows => 1) %]
 
 999 After dropping e.g. the third element at the top of the list a POST
 
1000 request would be made to the C<reorder> action of the C<SystemThings>
 
1001 controller with a single parameter called C<thing_ids> -- an array
 
1002 containing the values C<[ 6, 2, 15 ]>.
 
1006 Dumps the Argument using L<Data::Dumper> into a E<lt>preE<gt> block.
 
1008 =item C<sortable_table_header $by, %params>
 
1010 Create a link and image suitable for placement in a table
 
1011 header. C<$by> must be an index set up by the controller with
 
1012 L<SL::Controller::Helper::make_sorted>.
 
1014 The optional parameter C<$params{title}> can override the column title
 
1015 displayed to the user. Otherwise the column title from the
 
1016 controller's sort spec is used.
 
1018 The other parameters in C<%params> are passed unmodified to the
 
1019 underlying call to L<SL::Controller::Base::url_for>.
 
1021 See the documentation of L<SL::Controller::Helper::Sorted> for an
 
1022 overview and further usage instructions.
 
1024 =item C<paginate_controls>
 
1026 Create a set of links used to paginate a list view.
 
1028 See the documentation of L<SL::Controller::Helper::Paginated> for an
 
1029 overview and further usage instructions.
 
1033 =head2 CONVERSION FUNCTIONS
 
1037 =item C<tab, description, target, %PARAMS>
 
1039 Creates a tab for C<tabbed>. The description will be used as displayed name.
 
1040 The target should be a block or template that can be processed. C<tab> supports
 
1041 a C<method> parameter, which can override the process method to apply target.
 
1042 C<method => 'raw'> will just include the given text as is. I was too lazy to
 
1043 implement C<include> properly.
 
1045 Also an C<if> attribute is supported, so that tabs can be suppressed based on
 
1046 some occasion. In this case the supplied block won't even get processed, and
 
1047 the resulting tab will get ignored by C<tabbed>:
 
1049   L.tab('Awesome tab wih much info', '_much_info.html', if => SELF.wants_all)
 
1051 =item C<truncate $text, %params>
 
1053 Returns the C<$text> truncated after a certain number of
 
1056 The number of characters to truncate at is determined by the parameter
 
1057 C<at> which defaults to 50. If the text is longer than C<$params{at}>
 
1058 then it will be truncated and postfixed with '...'. Otherwise it will
 
1059 be returned unmodified.
 
1063 =head1 MODULE AUTHORS
 
1065 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
 
1067 L<http://linet-services.de>