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);
 
  11 { # This will give you an id for identifying html tags and such.
 
  12   # It's guaranteed to be unique unless you exceed 10 mio calls per request.
 
  13   # Do not use these id's to store information across requests.
 
  14 my $_id_sequence = int rand 1e7;
 
  16   return "id_" . ( $_id_sequence = ($_id_sequence + 1) % 1e7 );
 
  20 my %_valueless_attributes = map { $_ => 1 } qw(
 
  21   checked compact declare defer disabled ismap multiple noresize noshade nowrap
 
  27   return $::locale->quote_special_chars('HTML', $string);
 
  32   $string    =~ s/(\"|\'|\\)/\\$1/g;
 
  37   return (@_ && (ref($_[0]) eq 'HASH')) ? %{ $_[0] } : @_;
 
  41   my ($class, $context, @args) = @_;
 
  49   die 'not an accessor' if @_ > 1;
 
  50   return $_[0]->{CONTEXT};
 
  57   $name    =~ s/[^\w_]/_/g;
 
  64   my ($self, @slurp)    = @_;
 
  65   my %options = _hashify(@slurp);
 
  68   while (my ($name, $value) = each %options) {
 
  70     next if $_valueless_attributes{$name} && !$value;
 
  71     $value = '' if !defined($value);
 
  72     push @result, $_valueless_attributes{$name} ? _H($name) : _H($name) . '="' . _H($value) . '"';
 
  75   return @result ? ' ' . join(' ', @result) : '';
 
  79   my ($self, $tag, $content, @slurp) = @_;
 
  80   my $attributes = $self->attributes(@slurp);
 
  82   return "<${tag}${attributes}>" unless defined($content);
 
  83   return "<${tag}${attributes}>${content}</${tag}>";
 
  87   my ($self, @slurp) = @_;
 
  88   my %options = _hashify(@slurp);
 
  92   return $self->html_tag('img', undef, %options);
 
  98   my $collection      = shift;
 
  99   my %attributes      = _hashify(@_);
 
 101   $attributes{id}   ||= $self->name_to_id($name);
 
 103   my $value_key       = delete($attributes{value_key}) || 'id';
 
 104   my $title_key       = delete($attributes{title_key}) || $value_key;
 
 105   my $default_key     = delete($attributes{default_key}) || 'selected';
 
 108   my $value_title_sub = delete($attributes{value_title_sub});
 
 110   my $value_sub       = delete($attributes{value_sub});
 
 111   my $title_sub       = delete($attributes{title_sub});
 
 112   my $default_sub     = delete($attributes{default_sub});
 
 117   if ( ref($attributes{default}) eq 'ARRAY' ) {
 
 119     foreach my $entry (@{$attributes{default}}) {
 
 120       $selected{$entry} = 1;
 
 122   } elsif ( defined($attributes{default}) ) {
 
 123     $selected{$attributes{default}} = 1;
 
 126   delete($attributes{default});
 
 131   if ( delete($attributes{with_empty}) ) {
 
 132     push(@options, [undef, $attributes{empty_title} || '']);
 
 135   my $normalize_entry = sub {
 
 137     my ($type, $entry, $sub, $key) = @_;
 
 140       return $sub->($entry);
 
 143     my $ref = ref($entry);
 
 147       if ( $type eq 'value' || $type eq 'title' ) {
 
 154     if ( $ref eq 'ARRAY' ) {
 
 156       if ( $type eq 'value' ) {
 
 160       if ( $type eq 'title' ) {
 
 167     if ( $ref eq 'HASH' ) {
 
 168       return $entry->{$key};
 
 171     if ( $type ne 'default' || $entry->can($key) ) {
 
 178   foreach my $entry ( @{ $collection } ) {
 
 182     if ( $value_title_sub ) {
 
 183       ($value, $title) = $value_title_sub->($entry);
 
 186       $value = $normalize_entry->('value', $entry, $value_sub, $value_key);
 
 187       $title = $normalize_entry->('title', $entry, $title_sub, $title_key);
 
 190     my $default = $normalize_entry->('default', $entry, $default_sub, $default_key);
 
 192     push(@options, [$value, $title, $default]);
 
 195   foreach my $entry (@options) {
 
 196     if ( exists($selected{$entry->[0]}) ) {
 
 203   foreach my $entry (@options) {
 
 204     my %args = (value => $entry->[0]);
 
 206     $args{selected} = $entry->[2];
 
 208     $code .= $self->html_tag('option', _H($entry->[1]), %args);
 
 211   $code = $self->html_tag('select', $code, %attributes, name => $name);
 
 217   my ($self, $name, $content, @slurp) = @_;
 
 218   my %attributes      = _hashify(@slurp);
 
 220   $attributes{id}   ||= $self->name_to_id($name);
 
 221   $attributes{rows}  *= 1; # required by standard
 
 222   $attributes{cols}  *= 1; # required by standard
 
 223   $content            = $content ? _H($content) : '';
 
 225   return $self->html_tag('textarea', $content, %attributes, name => $name);
 
 229   my ($self, $name, @slurp) = @_;
 
 230   my %attributes       = _hashify(@slurp);
 
 232   $attributes{id}    ||= $self->name_to_id($name);
 
 233   $attributes{value}   = 1 unless defined $attributes{value};
 
 234   my $label            = delete $attributes{label};
 
 235   my $checkall         = delete $attributes{checkall};
 
 237   if ($attributes{checked}) {
 
 238     $attributes{checked} = 'checked';
 
 240     delete $attributes{checked};
 
 243   my $code  = $self->html_tag('input', undef,  %attributes, name => $name, type => 'checkbox');
 
 244   $code    .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
 
 245   $code    .= $self->javascript(qq|\$('#$attributes{id}').checkall('$checkall');|) if $checkall;
 
 250 sub radio_button_tag {
 
 253   my %attributes       = _hashify(@_);
 
 255   $attributes{value}   = 1 unless defined $attributes{value};
 
 256   $attributes{id}    ||= $self->name_to_id($name . "_" . $attributes{value});
 
 257   my $label            = delete $attributes{label};
 
 259   if ($attributes{checked}) {
 
 260     $attributes{checked} = 'checked';
 
 262     delete $attributes{checked};
 
 265   my $code  = $self->html_tag('input', undef,  %attributes, name => $name, type => 'radio');
 
 266   $code    .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
 
 272   my ($self, $name, $value, @slurp) = @_;
 
 273   my %attributes      = _hashify(@slurp);
 
 275   $attributes{id}   ||= $self->name_to_id($name);
 
 276   $attributes{type} ||= 'text';
 
 278   return $self->html_tag('input', undef, %attributes, name => $name, value => $value);
 
 282   return shift->input_tag(@_, type => 'hidden');
 
 286   my ($self, $content, @slurp) = @_;
 
 287   return $self->html_tag('div', $content, @slurp);
 
 291   my ($self, $content, @slurp) = @_;
 
 292   return $self->html_tag('ul', $content, @slurp);
 
 296   my ($self, $content, @slurp) = @_;
 
 297   return $self->html_tag('li', $content, @slurp);
 
 301   my ($self, $href, $content, @slurp) = @_;
 
 302   my %params = _hashify(@slurp);
 
 306   return $self->html_tag('a', $content, %params, href => $href);
 
 310   my ($self, $name, $value, @slurp) = @_;
 
 311   my %attributes = _hashify(@slurp);
 
 313   if ( $attributes{confirm} ) {
 
 314     $attributes{onclick} = 'return confirm("'. _J(delete($attributes{confirm})) .'");';
 
 317   return $self->input_tag($name, $value, %attributes, type => 'submit', class => 'submit');
 
 321   my ($self, $onclick, $value, @slurp) = @_;
 
 322   my %attributes = _hashify(@slurp);
 
 324   $attributes{id}   ||= $self->name_to_id($attributes{name}) if $attributes{name};
 
 325   $attributes{type} ||= 'button';
 
 327   return $self->html_tag('input', undef, %attributes, value => $value, onclick => $onclick);
 
 331   my ($self, $name, $value) = splice @_, 0, 3;
 
 332   my %attributes            = _hashify(@_);
 
 334   return $self->select_tag($name, [ [ 1 => $::locale->text('Yes') ], [ 0 => $::locale->text('No') ] ], default => $value ? 1 : 0, %attributes);
 
 338   my ($self, $data) = @_;
 
 339   return $self->html_tag('script', $data, type => 'text/javascript');
 
 346   foreach my $file (@_) {
 
 347     $file .= '.css'        unless $file =~ m/\.css$/;
 
 348     $file  = "css/${file}" unless $file =~ m|/|;
 
 350     $code .= qq|<link rel="stylesheet" href="${file}" type="text/css" media="screen" />|;
 
 357   my ($self, $name, $value, @slurp) = @_;
 
 358   my %params   = _hashify(@slurp);
 
 359   my $name_e   = _H($name);
 
 361   my $datefmt  = apply {
 
 365   } $::myconfig{"dateformat"};
 
 367   my $cal_align = delete $params{cal_align} || 'BR';
 
 368   my $onchange  = delete $params{onchange};
 
 369   my $str_value = blessed $value ? $value->to_lxoffice : $value;
 
 371   $self->input_tag($name, $str_value,
 
 374     title  => _H($::myconfig{dateformat}),
 
 375     onBlur => 'check_right_date_format(this)',
 
 377     onChange => $onchange,
 
 380   ) . ((!$params{no_cal} && !$params{readonly}) ?
 
 381   $self->html_tag('img', undef,
 
 382     src    => 'image/calendar.png',
 
 383     alt    => $::locale->text('Calendar'),
 
 385     title  => _H($::myconfig{dateformat}),
 
 389     "Calendar.setup({ inputField: '$name_e', ifFormat: '$datefmt', align: '$cal_align', button: 'trigger$seq' });"
 
 393 sub customer_picker {
 
 394   my ($self, $name, $value, %params) = @_;
 
 395   my $name_e    = _H($name);
 
 397   $self->hidden_tag($name, (ref $value && $value->can('id')) ? $value->id : '') .
 
 398   $self->input_tag("$name_e\_name", (ref $value && $value->can('name')) ? $value->name : '', %params) .
 
 399   $self->javascript(<<JS);
 
 400 function autocomplete_customer (selector, column) {
 
 401   \$(function(){ \$(selector).autocomplete({
 
 402     source: function(req, rsp) {
 
 404         url: 'controller.pl?action=Customer/ajax_autocomplete',
 
 409           current: function() { \$('#$name_e').val() },
 
 412         success: function (data){ rsp(data) }
 
 417     select: function(event, ui) {
 
 418       \$('#$name_e').val(ui.item.id);
 
 419       \$('#$name_e\_name').val(ui.item.name);
 
 423 autocomplete_customer('#$name_e\_name');
 
 427 # simple version with select_tag
 
 428 sub vendor_selector {
 
 429   my ($self, $name, $value, %params) = @_;
 
 431   my $actual_vendor_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"}) ? $::form->{"$name"}->id : $::form->{"$name"}) :
 
 432                          (ref $value && $value->can('id')) ? $value->id : '';
 
 434   return $self->select_tag($name, SL::DB::Manager::Vendor->get_all(),
 
 435                                   default      => $actual_vendor_id,
 
 436                                   title_sub    => sub { $_[0]->vendornumber . " : " . $_[0]->name },
 
 442 # simple version with select_tag
 
 444   my ($self, $name, $value, %params) = @_;
 
 446   my $actual_part_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"})? $::form->{"$name"}->id : $::form->{"$name"}) :
 
 447                        (ref $value && $value->can('id')) ? $value->id : '';
 
 449   return $self->select_tag($name, SL::DB::Manager::Part->get_all(),
 
 450                            default      => $actual_part_id,
 
 451                            title_sub    => sub { $_[0]->partnumber . " : " . $_[0]->description },
 
 461   foreach my $file (@_) {
 
 462     $file .= '.js'        unless $file =~ m/\.js$/;
 
 463     $file  = "js/${file}" unless $file =~ m|/|;
 
 465     $code .= qq|<script type="text/javascript" src="${file}"></script>|;
 
 472   my ($self, $tabs, @slurp) = @_;
 
 473   my %params   = _hashify(@slurp);
 
 474   my $id       = $params{id} || 'tab_' . _tag_id();
 
 476   $params{selected} *= 1;
 
 478   die 'L.tabbed needs an arrayred of tabs for first argument'
 
 479     unless ref $tabs eq 'ARRAY';
 
 481   my (@header, @blocks);
 
 482   for my $i (0..$#$tabs) {
 
 483     my $tab = $tabs->[$i];
 
 487     my $selected = $params{selected} == $i;
 
 488     my $tab_id   = "__tab_id_$i";
 
 489     push @header, $self->li_tag(
 
 490       $self->link('', $tab->{name}, rel => $tab_id),
 
 491         ($selected ? (class => 'selected') : ())
 
 493     push @blocks, $self->div_tag($tab->{data},
 
 494       id => $tab_id, class => 'tabcontent');
 
 497   return '' unless @header;
 
 498   return $self->ul_tag(
 
 499     join('', @header), id => $id, class => 'shadetabs'
 
 502     join('', @blocks), class => 'tabcontentstyle'
 
 505     qq|var $id = new ddtabcontent("$id");$id.setpersist(true);| .
 
 506     qq|$id.setselectedClassTarget("link");$id.init();|
 
 511   my ($self, $name, $src, @slurp) = @_;
 
 512   my %params = _hashify(@slurp);
 
 514   $params{method} ||= 'process';
 
 516   return () if defined $params{if} && !$params{if};
 
 519   if ($params{method} eq 'raw') {
 
 521   } elsif ($params{method} eq 'process') {
 
 522     $data = $self->_context->process($src, %{ $params{args} || {} });
 
 524     die "unknown tag method '$params{method}'";
 
 527   return () unless $data;
 
 529   return +{ name => $name, data => $data };
 
 533   my ($self, $name, $value, @slurp) = @_;
 
 534   my %attributes      = _hashify(@slurp);
 
 537   my $min  = delete $attributes{min_rows} || 1;
 
 539   if (exists $attributes{cols}) {
 
 540     $cols = delete $attributes{cols};
 
 541     $rows = $::form->numtextrows($value, $cols);
 
 543     $rows = delete $attributes{rows} || 1;
 
 547     ? $self->textarea_tag($name, $value, %attributes, rows => max($rows, $min), ($cols ? (cols => $cols) : ()))
 
 548     : $self->input_tag($name, $value, %attributes, ($cols ? (size => $cols) : ()));
 
 551 sub multiselect2side {
 
 552   my ($self, $id, @slurp) = @_;
 
 553   my %params              = _hashify(@slurp);
 
 555   $params{labelsx}        = "\"" . _J($params{labelsx} || $::locale->text('Available')) . "\"";
 
 556   $params{labeldx}        = "\"" . _J($params{labeldx} || $::locale->text('Selected'))  . "\"";
 
 557   $params{moveOptions}    = 'false';
 
 559   my $vars                = join(', ', map { "${_}: " . $params{$_} } keys %params);
 
 561 <script type="text/javascript">
 
 562   \$().ready(function() {
 
 563     \$('#${id}').multiselect2side({ ${vars} });
 
 571 sub sortable_element {
 
 572   my ($self, $selector, @slurp) = @_;
 
 573   my %params                    = _hashify(@slurp);
 
 575   my %attributes = ( distance => 5,
 
 576                      helper   => <<'JAVASCRIPT' );
 
 577     function(event, ui) {
 
 578       ui.children().each(function() {
 
 579         $(this).width($(this).width());
 
 587   if ($params{url} && $params{with}) {
 
 588     my $as      = $params{as} || $params{with};
 
 589     my $filter  = ".filter(function(idx) { return this.substr(0, " . length($params{with}) . ") == '$params{with}'; })";
 
 590     $filter    .= ".map(function(idx, str) { return str.replace('$params{with}_', ''); })";
 
 592     $stop_event = <<JAVASCRIPT;
 
 593         \$.post('$params{url}', { '${as}[]': \$(\$('${selector}').sortable('toArray'))${filter}.toArray() });
 
 597   if (!$params{dont_recolor}) {
 
 598     $stop_event .= <<JAVASCRIPT;
 
 599         \$('${selector}>*:odd').removeClass('listrow1').removeClass('listrow0').addClass('listrow0');
 
 600         \$('${selector}>*:even').removeClass('listrow1').removeClass('listrow0').addClass('listrow1');
 
 605     $attributes{stop} = <<JAVASCRIPT;
 
 606       function(event, ui) {
 
 613   $params{handle}     = '.dragdrop' unless exists $params{handle};
 
 614   $attributes{handle} = "'$params{handle}'" if $params{handle};
 
 616   my $attr_str = join(', ', map { "${_}: $attributes{$_}" } keys %attributes);
 
 618   my $code = <<JAVASCRIPT;
 
 619 <script type="text/javascript">
 
 621     \$( "${selector}" ).sortable({ ${attr_str} })
 
 629 sub online_help_tag {
 
 630   my ($self, $tag, @slurp) = @_;
 
 631   my %params               = _hashify(@slurp);
 
 632   my $cc                   = $::myconfig{countrycode};
 
 633   my $file                 = "doc/online/$cc/$tag.html";
 
 634   my $text                 = $params{text} || $::locale->text('Help');
 
 636   die 'malformed help tag' unless $tag =~ /^[a-zA-Z0-9_]+$/;
 
 637   return unless -f $file;
 
 638   return $self->html_tag('a', $text, href => $file, class => 'jqModal')
 
 643   require Data::Dumper;
 
 644   return '<pre>' . Data::Dumper::Dumper(@_) . '</pre>';
 
 648   my ($self, $text, @slurp) = @_;
 
 649   my %params                = _hashify(@slurp);
 
 652   $params{at}               =  3 if 3 > $params{at};
 
 655   return $text if length($text) < $params{at};
 
 656   return substr($text, 0, $params{at}) . '...';
 
 659 sub sortable_table_header {
 
 660   my ($self, $by, @slurp) = @_;
 
 661   my %params              = _hashify(@slurp);
 
 663   my $controller          = $self->{CONTEXT}->stash->get('SELF');
 
 664   my $sort_spec           = $controller->get_sort_spec;
 
 665   my $by_spec             = $sort_spec->{$by};
 
 666   my %current_sort_params = $controller->get_current_sort_params;
 
 667   my ($image, $new_dir)   = ('', $current_sort_params{dir});
 
 668   my $title               = delete($params{title}) || $::locale->text($by_spec->{title});
 
 670   if ($current_sort_params{by} eq $by) {
 
 671     my $current_dir = $current_sort_params{dir} ? 'up' : 'down';
 
 672     $image          = '<img border="0" src="image/' . $current_dir . '.png">';
 
 673     $new_dir        = 1 - ($current_sort_params{dir} || 0);
 
 676   $params{ $sort_spec->{FORM_PARAMS}->[0] } = $by;
 
 677   $params{ $sort_spec->{FORM_PARAMS}->[1] } = ($new_dir ? '1' : '0');
 
 679   return '<a href="' . $controller->get_callback(%params) . '">' . _H($title) . $image . '</a>';
 
 682 sub paginate_controls {
 
 685   my $controller      = $self->{CONTEXT}->stash->get('SELF');
 
 686   my $paginate_spec   = $controller->get_paginate_spec;
 
 687   my %paginate_params = $controller->get_current_paginate_params;
 
 689   my %template_params = (
 
 691       cur             => $paginate_params{page},
 
 692       max             => $paginate_params{num_pages},
 
 693       common          => $paginate_params{common_pages},
 
 696       my %url_params                                    = _hashify(@_);
 
 697       $url_params{ $paginate_spec->{FORM_PARAMS}->[0] } = delete $url_params{page};
 
 698       $url_params{ $paginate_spec->{FORM_PARAMS}->[1] } = delete $url_params{per_page} if exists $url_params{per_page};
 
 700       return $controller->get_callback(%url_params);
 
 705   $controller->_template_obj->process('templates/webpages/common/paginate.html', \%template_params, \$output);
 
 715 SL::Templates::Plugin::L -- Layouting / tag generation
 
 719 Usage from a template:
 
 723   [% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right', 1 ] ]) %]
 
 725   [% L.select_tag('direction', [ { direction => 'left',  display => 'To the left'  },
 
 726                                  { direction => 'right', display => 'To the right' } ],
 
 727                                value_key => 'direction', title_key => 'display', default => 'right')) %]
 
 729   [% L.select_tag('direction', [ { direction => 'left',  display => 'To the left'  },
 
 730                                  { direction => 'right', display => 'To the right', selected => 1 } ],
 
 731                                value_key => 'direction', title_key => 'display')) %]
 
 735 A module modeled a bit after Rails' ActionView helpers. Several small
 
 736 functions that create HTML tags from various kinds of data sources.
 
 740 =head2 LOW-LEVEL FUNCTIONS
 
 744 =item C<name_to_id $name>
 
 746 Converts a name to a HTML id by replacing various characters.
 
 748 =item C<attributes %items>
 
 750 Creates a string from all elements in C<%items> suitable for usage as
 
 751 HTML tag attributes. Keys and values are HTML escaped even though keys
 
 752 must not contain non-ASCII characters for browsers to accept them.
 
 754 =item C<html_tag $tag_name, $content_string, %attributes>
 
 756 Creates an opening and closing HTML tag for C<$tag_name> and puts
 
 757 C<$content_string> between the two. If C<$content_string> is undefined
 
 758 or empty then only a E<lt>tag/E<gt> tag will be created. Attributes
 
 759 are key/value pairs added to the opening tag.
 
 761 C<$content_string> is not HTML escaped.
 
 765 =head2 HIGH-LEVEL FUNCTIONS
 
 769 =item C<select_tag $name, \@collection, %attributes>
 
 771 Creates a HTML 'select' tag named C<$name> with the contents of one
 
 772 'E<lt>optionE<gt>' tag for each element in C<\@collection> and with arbitrary
 
 773 HTML attributes from C<%attributes>. The value
 
 774 to use and the title to display are extracted from the elements in
 
 775 C<\@collection>. Each element can be one of four things:
 
 779 =item 1. An array reference with at least two elements. The first element is
 
 780 the value, the second element is its title. The third element is optional and and should contain a boolean.
 
 781 If it is true, than the element will be used as default.
 
 783 =item 2. A scalar. The scalar is both the value and the title.
 
 785 =item 3. A hash reference. In this case C<%attributes> must contain
 
 786 I<value_key>, I<title_key> and may contain I<default_key> keys that name the keys in the element to use
 
 787 for the value, title and default respectively.
 
 789 =item 4. A blessed reference. In this case C<%attributes> must contain
 
 790 I<value_key>, I<title_key> and may contain I<default_key> keys that name functions called on the blessed
 
 791 reference whose return values are used as the value, title and default
 
 796 For cases 3 and 4 C<$attributes{value_key}> defaults to C<id>,
 
 797 C<$attributes{title_key}> defaults to C<$attributes{value_key}>
 
 798 and C<$attributes{default_key}> defaults to C<selected>.
 
 800 In addition to pure keys/method you can also provide coderefs as I<value_sub>
 
 801 and/or I<title_sub> and/or I<default_sub>. If present, these take precedence over keys or methods,
 
 802 and are called with the element as first argument. It must return the value, title or default.
 
 804 Lastly a joint coderef I<value_title_sub> may be provided, which in turn takes
 
 805 precedence over the C<value_sub> and C<title_sub> subs. It will only be called once for each
 
 806 element and must return a list of value and title.
 
 808 If the option C<with_empty> is set then an empty element (value
 
 809 C<undef>) will be used as the first element. The title to display for
 
 810 this element can be set with the option C<empty_title> and defaults to
 
 813 The option C<default> can be either a scalar or an array reference
 
 814 containing the values of the options which should be set to be
 
 817 The tag's C<id> defaults to C<name_to_id($name)>.
 
 819 =item C<yes_no_tag $name, $value, %attributes>
 
 821 Creates a HTML 'select' tag with the two entries C<yes> and C<no> by
 
 822 calling L<select_tag>. C<$value> determines
 
 823 which entry is selected. The C<%attributes> are passed through to
 
 826 =item C<input_tag $name, $value, %attributes>
 
 828 Creates a HTML 'input type=text' tag named C<$name> with the value
 
 829 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
 
 830 tag's C<id> defaults to C<name_to_id($name)>.
 
 832 =item C<hidden_tag $name, $value, %attributes>
 
 834 Creates a HTML 'input type=hidden' tag named C<$name> with the value
 
 835 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
 
 836 tag's C<id> defaults to C<name_to_id($name)>.
 
 838 =item C<submit_tag $name, $value, %attributes>
 
 840 Creates a HTML 'input type=submit class=submit' tag named C<$name> with the
 
 841 value C<$value> and with arbitrary HTML attributes from C<%attributes>. The
 
 842 tag's C<id> defaults to C<name_to_id($name)>.
 
 844 If C<$attributes{confirm}> is set then a JavaScript popup dialog will
 
 845 be added via the C<onclick> handler asking the question given with
 
 846 C<$attributes{confirm}>. If request is only submitted if the user
 
 847 clicks the dialog's ok/yes button.
 
 849 =item C<textarea_tag $name, $value, %attributes>
 
 851 Creates a HTML 'textarea' tag named C<$name> with the content
 
 852 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
 
 853 tag's C<id> defaults to C<name_to_id($name)>.
 
 855 =item C<checkbox_tag $name, %attributes>
 
 857 Creates a HTML 'input type=checkbox' tag named C<$name> with arbitrary
 
 858 HTML attributes from C<%attributes>. The tag's C<id> defaults to
 
 859 C<name_to_id($name)>. The tag's C<value> defaults to C<1>.
 
 861 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
 
 862 created with said C<label>. No attribute named C<label> is created in
 
 865 If C<%attributes> contains a key C<checkall> then the value is taken as a
 
 866 JQuery selector and clicking this checkbox will also toggle all checkboxes
 
 867 matching the selector.
 
 869 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
 
 871 Creates a date input field, with an attached javascript that will open a
 
 872 calendar on click. The javascript ist by default anchoered at the bottom right
 
 873 sight. This can be overridden with C<cal_align>, see Calendar documentation for
 
 874 the details, usually you'll want a two letter abbreviation of the alignment.
 
 875 Right + Bottom becomes C<BL>.
 
 877 =item C<radio_button_tag $name, %attributes>
 
 879 Creates a HTML 'input type=radio' tag named C<$name> with arbitrary
 
 880 HTML attributes from C<%attributes>. The tag's C<value> defaults to
 
 881 C<1>. The tag's C<id> defaults to C<name_to_id($name . "_" . $value)>.
 
 883 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
 
 884 created with said C<label>. No attribute named C<label> is created in
 
 887 =item C<javascript_tag $file1, $file2, $file3...>
 
 889 Creates a HTML 'E<lt>script type="text/javascript" src="..."E<gt>'
 
 890 tag for each file name parameter passed. Each file name will be
 
 891 postfixed with '.js' if it isn't already and prefixed with 'js/' if it
 
 892 doesn't contain a slash.
 
 894 =item C<stylesheet_tag $file1, $file2, $file3...>
 
 896 Creates a HTML 'E<lt>link rel="text/stylesheet" href="..."E<gt>' tag
 
 897 for each file name parameter passed. Each file name will be postfixed
 
 898 with '.css' if it isn't already and prefixed with 'css/' if it doesn't
 
 901 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
 
 903 Creates a date input field, with an attached javascript that will open a
 
 904 calendar on click. The javascript ist by default anchoered at the bottom right
 
 905 sight. This can be overridden with C<cal_align>, see Calendar documentation for
 
 906 the details, usually you'll want a two letter abbreviation of the alignment.
 
 907 Right + Bottom becomes C<BL>.
 
 909 =item C<tabbed \@tab, %attributes>
 
 911 Will create a tabbed area. The tabs should be created with the helper function
 
 915     L.tab(LxERP.t8('Basic Data'),       'part/_main_tab.html'),
 
 916     L.tab(LxERP.t8('Custom Variables'), 'part/_cvar_tab.html', if => SELF.display_cvar_tab),
 
 919 An optional attribute is C<selected>, which accepts the ordinal of a tab which
 
 920 should be selected by default.
 
 922 =item C<areainput_tag $name, $content, %PARAMS>
 
 924 Creates a generic input tag or textarea tag, depending on content size. The
 
 925 amount of desired rows must be either given with the C<rows> parameter or can
 
 926 be computed from the value and the C<cols> paramter, Accepted parameters
 
 927 include C<min_rows> for rendering a minimum of rows if a textarea is displayed.
 
 929 You can force input by setting rows to 1, and you can force textarea by setting
 
 932 =item C<multiselect2side $id, %params>
 
 934 Creates a JavaScript snippet calling the jQuery function
 
 935 C<multiselect2side> on the select control with the ID C<$id>. The
 
 936 select itself is not created. C<%params> can contain the following
 
 943 The label of the list of available options. Defaults to the
 
 944 translation of 'Available'.
 
 948 The label of the list of selected options. Defaults to the
 
 949 translation of 'Selected'.
 
 953 =item C<sortable_element $selector, %params>
 
 955 Makes the children of the DOM element C<$selector> (a jQuery selector)
 
 956 sortable with the I<jQuery UI Selectable> library. The children can be
 
 957 dragged & dropped around. After dropping an element an URL can be
 
 958 postet to with the element IDs of the sorted children.
 
 960 If this is used then the JavaScript file C<js/jquery-ui.js> must be
 
 961 included manually as well as it isn't loaded via C<$::form-gt;header>.
 
 963 C<%params> can contain the following entries:
 
 969 The URL to POST an AJAX request to after a dragged element has been
 
 970 dropped. The AJAX request's return value is ignored. If given then
 
 971 C<$params{with}> must be given as well.
 
 975 A string that is interpreted as the prefix of the children's ID. Upon
 
 976 POSTing the result each child whose ID starts with C<$params{with}> is
 
 977 considered. The prefix and the following "_" is removed from the
 
 978 ID. The remaining parts of the IDs of those children are posted as a
 
 979 single array parameter. The array parameter's name is either
 
 980 C<$params{as}> or, missing that, C<$params{with}>.
 
 984 Sets the POST parameter name for AJAX request after dropping an
 
 985 element (see C<$params{with}>).
 
 989 An optional jQuery selector specifying which part of the child element
 
 990 is dragable. If the parameter is not given then it defaults to
 
 991 C<.dragdrop> matching DOM elements with the class C<dragdrop>.  If the
 
 992 parameter is set and empty then the whole child element is dragable,
 
 993 and clicks through to underlying elements like inputs or links might
 
 996 =item C<dont_recolor>
 
 998 If trueish then the children will not be recolored. The default is to
 
 999 recolor the children by setting the class C<listrow0> on odd and
 
1000 C<listrow1> on even entries.
 
1006   <script type="text/javascript" src="js/jquery-ui.js"></script>
 
1008   <table id="thing_list">
 
1010       <tr><td>This</td><td>That</td></tr>
 
1013       <tr id="thingy_2"><td>stuff</td><td>more stuff</td></tr>
 
1014       <tr id="thingy_15"><td>stuff</td><td>more stuff</td></tr>
 
1015       <tr id="thingy_6"><td>stuff</td><td>more stuff</td></tr>
 
1019   [% L.sortable_element('#thing_list tbody',
 
1020                         url          => 'controller.pl?action=SystemThings/reorder',
 
1023                         recolor_rows => 1) %]
 
1025 After dropping e.g. the third element at the top of the list a POST
 
1026 request would be made to the C<reorder> action of the C<SystemThings>
 
1027 controller with a single parameter called C<thing_ids> -- an array
 
1028 containing the values C<[ 6, 2, 15 ]>.
 
1032 Dumps the Argument using L<Data::Dumper> into a E<lt>preE<gt> block.
 
1034 =item C<sortable_table_header $by, %params>
 
1036 Create a link and image suitable for placement in a table
 
1037 header. C<$by> must be an index set up by the controller with
 
1038 L<SL::Controller::Helper::make_sorted>.
 
1040 The optional parameter C<$params{title}> can override the column title
 
1041 displayed to the user. Otherwise the column title from the
 
1042 controller's sort spec is used.
 
1044 The other parameters in C<%params> are passed unmodified to the
 
1045 underlying call to L<SL::Controller::Base::url_for>.
 
1047 See the documentation of L<SL::Controller::Helper::Sorted> for an
 
1048 overview and further usage instructions.
 
1050 =item C<paginate_controls>
 
1052 Create a set of links used to paginate a list view.
 
1054 See the documentation of L<SL::Controller::Helper::Paginated> for an
 
1055 overview and further usage instructions.
 
1059 =head2 CONVERSION FUNCTIONS
 
1063 =item C<tab, description, target, %PARAMS>
 
1065 Creates a tab for C<tabbed>. The description will be used as displayed name.
 
1066 The target should be a block or template that can be processed. C<tab> supports
 
1067 a C<method> parameter, which can override the process method to apply target.
 
1068 C<method => 'raw'> will just include the given text as is. I was too lazy to
 
1069 implement C<include> properly.
 
1071 Also an C<if> attribute is supported, so that tabs can be suppressed based on
 
1072 some occasion. In this case the supplied block won't even get processed, and
 
1073 the resulting tab will get ignored by C<tabbed>:
 
1075   L.tab('Awesome tab wih much info', '_much_info.html', if => SELF.wants_all)
 
1077 =item C<truncate $text, %params>
 
1079 Returns the C<$text> truncated after a certain number of
 
1082 The number of characters to truncate at is determined by the parameter
 
1083 C<at> which defaults to 50. If the text is longer than C<$params{at}>
 
1084 then it will be truncated and postfixed with '...'. Otherwise it will
 
1085 be returned unmodified.
 
1089 =head1 MODULE AUTHORS
 
1091 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
 
1093 L<http://linet-services.de>