Waren und Lieferanten als benutzerdefinierte Variablen hinzugefĆ¼gt.
[kivitendo-erp.git] / SL / Template / Plugin / L.pm
1 package SL::Template::Plugin::L;
2
3 use base qw( Template::Plugin );
4 use Template::Plugin;
5 use List::MoreUtils qw(apply);
6 use List::Util qw(max);
7 use Scalar::Util qw(blessed);
8
9 use strict;
10
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;
15 sub _tag_id {
16   return "id_" . ( $_id_sequence = ($_id_sequence + 1) % 1e7 );
17 }
18 }
19
20 my %_valueless_attributes = map { $_ => 1 } qw(
21   checked compact declare defer disabled ismap multiple noresize noshade nowrap
22   readonly selected
23 );
24
25 sub _H {
26   my $string = shift;
27   return $::locale->quote_special_chars('HTML', $string);
28 }
29
30 sub _J {
31   my $string =  "" . shift;
32   $string    =~ s/\"/\\\"/g;
33   return $string;
34 }
35
36 sub _hashify {
37   return (@_ && (ref($_[0]) eq 'HASH')) ? %{ $_[0] } : @_;
38 }
39
40 sub new {
41   my ($class, $context, @args) = @_;
42
43   return bless {
44     CONTEXT => $context,
45   }, $class;
46 }
47
48 sub _context {
49   die 'not an accessor' if @_ > 1;
50   return $_[0]->{CONTEXT};
51 }
52
53 sub name_to_id {
54   my $self =  shift;
55   my $name =  shift;
56
57   $name    =~ s/[^\w_]/_/g;
58   $name    =~ s/_+/_/g;
59
60   return $name;
61 }
62
63 sub attributes {
64   my ($self, @slurp)    = @_;
65   my %options = _hashify(@slurp);
66
67   my @result = ();
68   while (my ($name, $value) = each %options) {
69     next unless $name;
70     next if $_valueless_attributes{$name} && !$value;
71     $value = '' if !defined($value);
72     push @result, $_valueless_attributes{$name} ? _H($name) : _H($name) . '="' . _H($value) . '"';
73   }
74
75   return @result ? ' ' . join(' ', @result) : '';
76 }
77
78 sub html_tag {
79   my ($self, $tag, $content, @slurp) = @_;
80   my $attributes = $self->attributes(@slurp);
81
82   return "<${tag}${attributes}>" unless defined($content);
83   return "<${tag}${attributes}>${content}</${tag}>";
84 }
85
86 sub select_tag {
87   my $self            = shift;
88   my $name            = shift;
89   my $options_str     = shift;
90   my %attributes      = _hashify(@_);
91
92   $attributes{id}   ||= $self->name_to_id($name);
93   $options_str        = $self->options_for_select($options_str) if ref $options_str;
94
95   return $self->html_tag('select', $options_str, %attributes, name => $name);
96 }
97
98 sub textarea_tag {
99   my ($self, $name, $content, @slurp) = @_;
100   my %attributes      = _hashify(@slurp);
101
102   $attributes{id}   ||= $self->name_to_id($name);
103   $attributes{rows}  *= 1; # required by standard
104   $attributes{cols}  *= 1; # required by standard
105   $content            = $content ? _H($content) : '';
106
107   return $self->html_tag('textarea', $content, %attributes, name => $name);
108 }
109
110 sub checkbox_tag {
111   my ($self, $name, @slurp) = @_;
112   my %attributes       = _hashify(@slurp);
113
114   $attributes{id}    ||= $self->name_to_id($name);
115   $attributes{value}   = 1 unless defined $attributes{value};
116   my $label            = delete $attributes{label};
117   my $checkall         = delete $attributes{checkall};
118
119   if ($attributes{checked}) {
120     $attributes{checked} = 'checked';
121   } else {
122     delete $attributes{checked};
123   }
124
125   my $code  = $self->html_tag('input', undef,  %attributes, name => $name, type => 'checkbox');
126   $code    .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
127   $code    .= $self->javascript(qq|\$('#$attributes{id}').checkall('$checkall');|) if $checkall;
128
129   return $code;
130 }
131
132 sub radio_button_tag {
133   my $self             = shift;
134   my $name             = shift;
135   my %attributes       = _hashify(@_);
136
137   $attributes{value}   = 1 unless defined $attributes{value};
138   $attributes{id}    ||= $self->name_to_id($name . "_" . $attributes{value});
139   my $label            = delete $attributes{label};
140
141   if ($attributes{checked}) {
142     $attributes{checked} = 'checked';
143   } else {
144     delete $attributes{checked};
145   }
146
147   my $code  = $self->html_tag('input', undef,  %attributes, name => $name, type => 'radio');
148   $code    .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
149
150   return $code;
151 }
152
153 sub input_tag {
154   my ($self, $name, $value, @slurp) = @_;
155   my %attributes      = _hashify(@slurp);
156
157   $attributes{id}   ||= $self->name_to_id($name);
158   $attributes{type} ||= 'text';
159
160   return $self->html_tag('input', undef, %attributes, name => $name, value => $value);
161 }
162
163 sub hidden_tag {
164   return shift->input_tag(@_, type => 'hidden');
165 }
166
167 sub div_tag {
168   my ($self, $content, @slurp) = @_;
169   return $self->html_tag('div', $content, @slurp);
170 }
171
172 sub ul_tag {
173   my ($self, $content, @slurp) = @_;
174   return $self->html_tag('ul', $content, @slurp);
175 }
176
177 sub li_tag {
178   my ($self, $content, @slurp) = @_;
179   return $self->html_tag('li', $content, @slurp);
180 }
181
182 sub link {
183   my ($self, $href, $content, @slurp) = @_;
184   my %params = _hashify(@slurp);
185
186   $href ||= '#';
187
188   return $self->html_tag('a', $content, %params, href => $href);
189 }
190
191 sub submit_tag {
192   my ($self, $name, $value, @slurp) = @_;
193   my %attributes = _hashify(@slurp);
194
195   $attributes{onclick} = "if (confirm('" . delete($attributes{confirm}) . "')) return true; else return false;" if $attributes{confirm};
196
197   return $self->input_tag($name, $value, %attributes, type => 'submit', class => 'submit');
198 }
199
200 sub button_tag {
201   my ($self, $onclick, $value, @slurp) = @_;
202   my %attributes = _hashify(@slurp);
203
204   $attributes{id}   ||= $self->name_to_id($attributes{name}) if $attributes{name};
205   $attributes{type} ||= 'button';
206
207   return $self->html_tag('input', undef, %attributes, value => $value, onclick => $onclick);
208 }
209
210 sub options_for_select {
211   my $self            = shift;
212   my $collection      = shift;
213   my %options         = _hashify(@_);
214
215   my $value_key       = $options{value} || 'id';
216   my $title_key       = $options{title} || $value_key;
217
218   my $value_sub       = $options{value_sub};
219   my $title_sub       = $options{title_sub};
220
221   my $value_title_sub = $options{value_title_sub};
222
223   my %selected        = map { ( $_ => 1 ) } @{ ref($options{default}) eq 'ARRAY' ? $options{default} : defined($options{default}) ? [ $options{default} ] : [] };
224
225   my $access = sub {
226     my ($element, $index, $key, $sub) = @_;
227     my $ref = ref $element;
228     return  $sub            ? $sub->($element)
229          : !$ref            ? $element
230          :  $ref eq 'ARRAY' ? $element->[$index]
231          :  $ref eq 'HASH'  ? $element->{$key}
232          :                    $element->$key;
233   };
234
235   my @elements = ();
236   push @elements, [ undef, $options{empty_title} || '' ] if $options{with_empty};
237   push @elements, map [
238     $value_title_sub ? @{ $value_title_sub->($_) } : (
239       $access->($_, 0, $value_key, $value_sub),
240       $access->($_, 1, $title_key, $title_sub),
241     )
242   ], @{ $collection } if $collection && ref $collection eq 'ARRAY';
243
244   my $code = '';
245   foreach my $result (@elements) {
246     my %attributes = ( value => $result->[0] );
247     $attributes{selected} = 'selected' if $selected{ defined($result->[0]) ? $result->[0] : '' };
248
249     $code .= $self->html_tag('option', _H($result->[1]), %attributes);
250   }
251
252   return $code;
253 }
254
255 sub javascript {
256   my ($self, $data) = @_;
257   return $self->html_tag('script', $data, type => 'text/javascript');
258 }
259
260 sub stylesheet_tag {
261   my $self = shift;
262   my $code = '';
263
264   foreach my $file (@_) {
265     $file .= '.css'        unless $file =~ m/\.css$/;
266     $file  = "css/${file}" unless $file =~ m|/|;
267
268     $code .= qq|<link rel="stylesheet" href="${file}" type="text/css" media="screen" />|;
269   }
270
271   return $code;
272 }
273
274 sub date_tag {
275   my ($self, $name, $value, @slurp) = @_;
276   my %params   = _hashify(@slurp);
277   my $name_e   = _H($name);
278   my $seq      = _tag_id();
279   my $datefmt  = apply {
280     s/d+/\%d/gi;
281     s/m+/\%m/gi;
282     s/y+/\%Y/gi;
283   } $::myconfig{"dateformat"};
284
285   my $cal_align = delete $params{cal_align} || 'BR';
286   my $onchange  = delete $params{onchange};
287   my $str_value = blessed $value ? $value->to_lxoffice : $value;
288
289   $self->input_tag($name, $str_value,
290     id     => $name_e,
291     size   => 11,
292     title  => _H($::myconfig{dateformat}),
293     onBlur => 'check_right_date_format(this)',
294     ($onchange ? (
295     onChange => $onchange,
296     ) : ()),
297     %params,
298   ) . ((!$params{no_cal} && !$params{readonly}) ?
299   $self->html_tag('img', undef,
300     src    => 'image/calendar.png',
301     alt    => $::locale->text('Calendar'),
302     id     => "trigger$seq",
303     title  => _H($::myconfig{dateformat}),
304     %params,
305   ) .
306   $self->javascript(
307     "Calendar.setup({ inputField: '$name_e', ifFormat: '$datefmt', align: '$cal_align', button: 'trigger$seq' });"
308   ) : '');
309 }
310
311 sub customer_picker {
312   my ($self, $name, $value, %params) = @_;
313   my $name_e    = _H($name);
314
315   $self->hidden_tag($name, (ref $value && $value->can('id')) ? $value->id : '') .
316   $self->input_tag("$name_e\_name", (ref $value && $value->can('name')) ? $value->name : '', %params) .
317   $self->javascript(<<JS);
318 function autocomplete_customer (selector, column) {
319   \$(function(){ \$(selector).autocomplete({
320     source: function(req, rsp) {
321       \$.ajax({
322         url: 'controller.pl?action=Customer/ajax_autocomplete',
323         dataType: "json",
324         data: {
325           column: column,
326           term: req.term,
327           current: function() { \$('#$name_e').val() },
328           obsolete: 0,
329         },
330         success: function (data){ rsp(data) }
331       });
332     },
333     limit: 20,
334     delay: 50,
335     select: function(event, ui) {
336       \$('#$name_e').val(ui.item.id);
337       \$('#$name_e\_name').val(ui.item.name);
338     },
339   })});
340 }
341 autocomplete_customer('#$name_e\_name');
342 JS
343 }
344
345 # simple version with select_tag
346 sub vendor_selector {
347   my ($self, $name, $value, %params) = @_;
348
349   my $actual_vendor_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"}) ? $::form->{"$name"}->id : $::form->{"$name"}) :
350                          (ref $value && $value->can('id')) ? $value->id : '';
351   my $options_str = $self->options_for_select(SL::DB::Manager::Vendor->get_all(),
352                                               default      => $actual_vendor_id,
353                                               title_sub    => sub { $_[0]->vendornumber . " : " . $_[0]->name },
354                                               'with_empty' => 1);
355   
356   return $self->select_tag($name, $options_str, %params);
357 }
358
359
360 # simple version with select_tag
361 sub part_selector {
362   my ($self, $name, $value, %params) = @_;
363
364   my $actual_part_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"})? $::form->{"$name"}->id : $::form->{"$name"}) :
365                        (ref $value && $value->can('id')) ? $value->id : '';
366   my $options_str = $self->options_for_select(SL::DB::Manager::Part->get_all(),
367                                               default      => $actual_part_id,
368                                               title_sub    => sub { $_[0]->partnumber . " : " . $_[0]->description },
369                                               'with_empty' => 1);
370   
371   return $self->select_tag($name, $options_str, %params);
372 }
373
374
375 sub javascript_tag {
376   my $self = shift;
377   my $code = '';
378
379   foreach my $file (@_) {
380     $file .= '.js'        unless $file =~ m/\.js$/;
381     $file  = "js/${file}" unless $file =~ m|/|;
382
383     $code .= qq|<script type="text/javascript" src="${file}"></script>|;
384   }
385
386   return $code;
387 }
388
389 sub tabbed {
390   my ($self, $tabs, @slurp) = @_;
391   my %params   = _hashify(@slurp);
392   my $id       = $params{id} || 'tab_' . _tag_id();
393
394   $params{selected} *= 1;
395
396   die 'L.tabbed needs an arrayred of tabs for first argument'
397     unless ref $tabs eq 'ARRAY';
398
399   my (@header, @blocks);
400   for my $i (0..$#$tabs) {
401     my $tab = $tabs->[$i];
402
403     next if $tab eq '';
404
405     my $selected = $params{selected} == $i;
406     my $tab_id   = "__tab_id_$i";
407     push @header, $self->li_tag(
408       $self->link('', $tab->{name}, rel => $tab_id),
409         ($selected ? (class => 'selected') : ())
410     );
411     push @blocks, $self->div_tag($tab->{data},
412       id => $tab_id, class => 'tabcontent');
413   }
414
415   return '' unless @header;
416   return $self->ul_tag(
417     join('', @header), id => $id, class => 'shadetabs'
418   ) .
419   $self->div_tag(
420     join('', @blocks), class => 'tabcontentstyle'
421   ) .
422   $self->javascript(
423     qq|var $id = new ddtabcontent("$id");$id.setpersist(true);| .
424     qq|$id.setselectedClassTarget("link");$id.init();|
425   );
426 }
427
428 sub tab {
429   my ($self, $name, $src, @slurp) = @_;
430   my %params = _hashify(@slurp);
431
432   $params{method} ||= 'process';
433
434   return () if defined $params{if} && !$params{if};
435
436   my $data;
437   if ($params{method} eq 'raw') {
438     $data = $src;
439   } elsif ($params{method} eq 'process') {
440     $data = $self->_context->process($src, %{ $params{args} || {} });
441   } else {
442     die "unknown tag method '$params{method}'";
443   }
444
445   return () unless $data;
446
447   return +{ name => $name, data => $data };
448 }
449
450 sub areainput_tag {
451   my ($self, $name, $value, @slurp) = @_;
452   my %attributes      = _hashify(@slurp);
453
454   my ($rows, $cols);
455   my $min  = delete $attributes{min_rows} || 1;
456
457   if (exists $attributes{cols}) {
458     $cols = delete $attributes{cols};
459     $rows = $::form->numtextrows($value, $cols);
460   } else {
461     $rows = delete $attributes{rows} || 1;
462   }
463
464   return $rows > 1
465     ? $self->textarea_tag($name, $value, %attributes, rows => max($rows, $min), ($cols ? (cols => $cols) : ()))
466     : $self->input_tag($name, $value, %attributes, ($cols ? (size => $cols) : ()));
467 }
468
469 sub multiselect2side {
470   my ($self, $id, @slurp) = @_;
471   my %params              = _hashify(@slurp);
472
473   $params{labelsx}        = "\"" . _J($params{labelsx} || $::locale->text('Available')) . "\"";
474   $params{labeldx}        = "\"" . _J($params{labeldx} || $::locale->text('Selected'))  . "\"";
475   $params{moveOptions}    = 'false';
476
477   my $vars                = join(', ', map { "${_}: " . $params{$_} } keys %params);
478   my $code                = <<EOCODE;
479 <script type="text/javascript">
480   \$().ready(function() {
481     \$('#${id}').multiselect2side({ ${vars} });
482   });
483 </script>
484 EOCODE
485
486   return $code;
487 }
488
489 sub sortable_element {
490   my ($self, $selector, @slurp) = @_;
491   my %params                    = _hashify(@slurp);
492
493   my %attributes = ( distance => 5,
494                      helper   => <<'JAVASCRIPT' );
495     function(event, ui) {
496       ui.children().each(function() {
497         $(this).width($(this).width());
498       });
499       return ui;
500     }
501 JAVASCRIPT
502
503   my $stop_event = '';
504
505   if ($params{url} && $params{with}) {
506     my $as      = $params{as} || $params{with};
507     my $filter  = ".filter(function(idx) { return this.substr(0, " . length($params{with}) . ") == '$params{with}'; })";
508     $filter    .= ".map(function(idx, str) { return str.replace('$params{with}_', ''); })";
509
510     $stop_event = <<JAVASCRIPT;
511         \$.post('$params{url}', { '${as}[]': \$(\$('${selector}').sortable('toArray'))${filter}.toArray() });
512 JAVASCRIPT
513   }
514
515   if (!$params{dont_recolor}) {
516     $stop_event .= <<JAVASCRIPT;
517         \$('${selector}>*:odd').removeClass('listrow1').removeClass('listrow0').addClass('listrow0');
518         \$('${selector}>*:even').removeClass('listrow1').removeClass('listrow0').addClass('listrow1');
519 JAVASCRIPT
520   }
521
522   if ($stop_event) {
523     $attributes{stop} = <<JAVASCRIPT;
524       function(event, ui) {
525         ${stop_event}
526         return ui;
527       }
528 JAVASCRIPT
529   }
530
531   $params{handle}     = '.dragdrop' unless exists $params{handle};
532   $attributes{handle} = "'$params{handle}'" if $params{handle};
533
534   my $attr_str = join(', ', map { "${_}: $attributes{$_}" } keys %attributes);
535
536   my $code = <<JAVASCRIPT;
537 <script type="text/javascript">
538   \$(function() {
539     \$( "${selector}" ).sortable({ ${attr_str} })
540   });
541 </script>
542 JAVASCRIPT
543
544   return $code;
545 }
546
547 sub online_help_tag {
548   my ($self, $tag, @slurp) = @_;
549   my %params               = _hashify(@slurp);
550   my $cc                   = $::myconfig{countrycode};
551   my $file                 = "doc/online/$cc/$tag.html";
552   my $text                 = $params{text} || $::locale->text('Help');
553
554   die 'malformed help tag' unless $tag =~ /^[a-zA-Z0-9_]+$/;
555   return unless -f $file;
556   return $self->html_tag('a', $text, href => $file, class => 'jqModal')
557 }
558
559 sub dump {
560   my $self = shift;
561   require Data::Dumper;
562   return '<pre>' . Data::Dumper::Dumper(@_) . '</pre>';
563 }
564
565 1;
566
567 __END__
568
569 =head1 NAME
570
571 SL::Templates::Plugin::L -- Layouting / tag generation
572
573 =head1 SYNOPSIS
574
575 Usage from a template:
576
577   [% USE L %]
578
579   [% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right' ] ]) %]
580
581   [% L.select_tag('direction', L.options_for_select([ { direction => 'left',  display => 'To the left'  },
582                                                       { direction => 'right', display => 'To the right' } ],
583                                                     value => 'direction', title => 'display', default => 'right')) %]
584
585 =head1 DESCRIPTION
586
587 A module modeled a bit after Rails' ActionView helpers. Several small
588 functions that create HTML tags from various kinds of data sources.
589
590 =head1 FUNCTIONS
591
592 =head2 LOW-LEVEL FUNCTIONS
593
594 =over 4
595
596 =item C<name_to_id $name>
597
598 Converts a name to a HTML id by replacing various characters.
599
600 =item C<attributes %items>
601
602 Creates a string from all elements in C<%items> suitable for usage as
603 HTML tag attributes. Keys and values are HTML escaped even though keys
604 must not contain non-ASCII characters for browsers to accept them.
605
606 =item C<html_tag $tag_name, $content_string, %attributes>
607
608 Creates an opening and closing HTML tag for C<$tag_name> and puts
609 C<$content_string> between the two. If C<$content_string> is undefined
610 or empty then only a E<lt>tag/E<gt> tag will be created. Attributes
611 are key/value pairs added to the opening tag.
612
613 C<$content_string> is not HTML escaped.
614
615 =back
616
617 =head2 HIGH-LEVEL FUNCTIONS
618
619 =over 4
620
621 =item C<select_tag $name, $options_string, %attributes>
622
623 Creates a HTML 'select' tag named C<$name> with the contents
624 C<$options_string> and with arbitrary HTML attributes from
625 C<%attributes>. The tag's C<id> defaults to C<name_to_id($name)>.
626
627 The C<$options_string> is usually created by the
628 L</options_for_select> function. If C<$options_string> is an array
629 reference then it will be passed to L</options_for_select>
630 automatically.
631
632 =item C<input_tag $name, $value, %attributes>
633
634 Creates a HTML 'input type=text' tag named C<$name> with the value
635 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
636 tag's C<id> defaults to C<name_to_id($name)>.
637
638 =item C<hidden_tag $name, $value, %attributes>
639
640 Creates a HTML 'input type=hidden' tag named C<$name> with the value
641 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
642 tag's C<id> defaults to C<name_to_id($name)>.
643
644 =item C<submit_tag $name, $value, %attributes>
645
646 Creates a HTML 'input type=submit class=submit' tag named C<$name> with the
647 value C<$value> and with arbitrary HTML attributes from C<%attributes>. The
648 tag's C<id> defaults to C<name_to_id($name)>.
649
650 If C<$attributes{confirm}> is set then a JavaScript popup dialog will
651 be added via the C<onclick> handler asking the question given with
652 C<$attributes{confirm}>. If request is only submitted if the user
653 clicks the dialog's ok/yes button.
654
655 =item C<textarea_tag $name, $value, %attributes>
656
657 Creates a HTML 'textarea' tag named C<$name> with the content
658 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
659 tag's C<id> defaults to C<name_to_id($name)>.
660
661 =item C<checkbox_tag $name, %attributes>
662
663 Creates a HTML 'input type=checkbox' tag named C<$name> with arbitrary
664 HTML attributes from C<%attributes>. The tag's C<id> defaults to
665 C<name_to_id($name)>. The tag's C<value> defaults to C<1>.
666
667 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
668 created with said C<label>. No attribute named C<label> is created in
669 that case.
670
671 If C<%attributes> contains a key C<checkall> then the value is taken as a
672 JQuery selector and clicking this checkbox will also toggle all checkboxes
673 matching the selector.
674
675 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
676
677 Creates a date input field, with an attached javascript that will open a
678 calendar on click. The javascript ist by default anchoered at the bottom right
679 sight. This can be overridden with C<cal_align>, see Calendar documentation for
680 the details, usually you'll want a two letter abbreviation of the alignment.
681 Right + Bottom becomes C<BL>.
682
683 =item C<radio_button_tag $name, %attributes>
684
685 Creates a HTML 'input type=radio' tag named C<$name> with arbitrary
686 HTML attributes from C<%attributes>. The tag's C<value> defaults to
687 C<1>. The tag's C<id> defaults to C<name_to_id($name . "_" . $value)>.
688
689 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
690 created with said C<label>. No attribute named C<label> is created in
691 that case.
692
693 =item C<javascript_tag $file1, $file2, $file3...>
694
695 Creates a HTML 'E<lt>script type="text/javascript" src="..."E<gt>'
696 tag for each file name parameter passed. Each file name will be
697 postfixed with '.js' if it isn't already and prefixed with 'js/' if it
698 doesn't contain a slash.
699
700 =item C<stylesheet_tag $file1, $file2, $file3...>
701
702 Creates a HTML 'E<lt>link rel="text/stylesheet" href="..."E<gt>' tag
703 for each file name parameter passed. Each file name will be postfixed
704 with '.css' if it isn't already and prefixed with 'css/' if it doesn't
705 contain a slash.
706
707 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
708
709 Creates a date input field, with an attached javascript that will open a
710 calendar on click. The javascript ist by default anchoered at the bottom right
711 sight. This can be overridden with C<cal_align>, see Calendar documentation for
712 the details, usually you'll want a two letter abbreviation of the alignment.
713 Right + Bottom becomes C<BL>.
714
715 =item C<tabbed \@tab, %attributes>
716
717 Will create a tabbed area. The tabs should be created with the helper function
718 C<tab>. Example:
719
720   [% L.tabbed([
721     L.tab(LxERP.t8('Basic Data'),       'part/_main_tab.html'),
722     L.tab(LxERP.t8('Custom Variables'), 'part/_cvar_tab.html', if => SELF.display_cvar_tab),
723   ]) %]
724
725 An optional attribute is C<selected>, which accepts the ordinal of a tab which
726 should be selected by default.
727
728 =item C<areainput_tag $name, $content, %PARAMS>
729
730 Creates a generic input tag or textarea tag, depending on content size. The
731 amount of desired rows must be either given with the C<rows> parameter or can
732 be computed from the value and the C<cols> paramter, Accepted parameters
733 include C<min_rows> for rendering a minimum of rows if a textarea is displayed.
734
735 You can force input by setting rows to 1, and you can force textarea by setting
736 rows to anything >1.
737
738 =item C<multiselect2side $id, %params>
739
740 Creates a JavaScript snippet calling the jQuery function
741 C<multiselect2side> on the select control with the ID C<$id>. The
742 select itself is not created. C<%params> can contain the following
743 entries:
744
745 =over 2
746
747 =item C<labelsx>
748
749 The label of the list of available options. Defaults to the
750 translation of 'Available'.
751
752 =item C<labeldx>
753
754 The label of the list of selected options. Defaults to the
755 translation of 'Selected'.
756
757 =back
758
759 =item C<sortable_element $selector, %params>
760
761 Makes the children of the DOM element C<$selector> (a jQuery selector)
762 sortable with the I<jQuery UI Selectable> library. The children can be
763 dragged & dropped around. After dropping an element an URL can be
764 postet to with the element IDs of the sorted children.
765
766 If this is used then the JavaScript file C<js/jquery-ui.js> must be
767 included manually as well as it isn't loaded via C<$::form-gt;header>.
768
769 C<%params> can contain the following entries:
770
771 =over 2
772
773 =item C<url>
774
775 The URL to POST an AJAX request to after a dragged element has been
776 dropped. The AJAX request's return value is ignored. If given then
777 C<$params{with}> must be given as well.
778
779 =item C<with>
780
781 A string that is interpreted as the prefix of the children's ID. Upon
782 POSTing the result each child whose ID starts with C<$params{with}> is
783 considered. The prefix and the following "_" is removed from the
784 ID. The remaining parts of the IDs of those children are posted as a
785 single array parameter. The array parameter's name is either
786 C<$params{as}> or, missing that, C<$params{with}>.
787
788 =item C<as>
789
790 Sets the POST parameter name for AJAX request after dropping an
791 element (see C<$params{with}>).
792
793 =item C<handle>
794
795 An optional jQuery selector specifying which part of the child element
796 is dragable. If the parameter is not given then it defaults to
797 C<.dragdrop> matching DOM elements with the class C<dragdrop>.  If the
798 parameter is set and empty then the whole child element is dragable,
799 and clicks through to underlying elements like inputs or links might
800 not work.
801
802 =item C<dont_recolor>
803
804 If trueish then the children will not be recolored. The default is to
805 recolor the children by setting the class C<listrow0> on odd and
806 C<listrow1> on even entries.
807
808 =back
809
810 Example:
811
812   <script type="text/javascript" src="js/jquery-ui.js"></script>
813
814   <table id="thing_list">
815     <thead>
816       <tr><td>This</td><td>That</td></tr>
817     </thead>
818     <tbody>
819       <tr id="thingy_2"><td>stuff</td><td>more stuff</td></tr>
820       <tr id="thingy_15"><td>stuff</td><td>more stuff</td></tr>
821       <tr id="thingy_6"><td>stuff</td><td>more stuff</td></tr>
822     </tbody>
823   <table>
824
825   [% L.sortable_element('#thing_list tbody',
826                         url          => 'controller.pl?action=SystemThings/reorder',
827                         with         => 'thingy',
828                         as           => 'thing_ids',
829                         recolor_rows => 1) %]
830
831 After dropping e.g. the third element at the top of the list a POST
832 request would be made to the C<reorder> action of the C<SystemThings>
833 controller with a single parameter called C<thing_ids> -- an array
834 containing the values C<[ 6, 2, 15 ]>.
835
836 =item C<dump REF>
837
838 Dumps the Argument using L<Data::Dumper> into a E<lt>preE<gt> block.
839
840 =back
841
842 =head2 CONVERSION FUNCTIONS
843
844 =over 4
845
846 =item C<options_for_select \@collection, %options>
847
848 Creates a string suitable for a HTML 'select' tag consisting of one
849 'E<lt>optionE<gt>' tag for each element in C<\@collection>. The value
850 to use and the title to display are extracted from the elements in
851 C<\@collection>. Each element can be one of four things:
852
853 =over 12
854
855 =item 1. An array reference with at least two elements. The first element is
856 the value, the second element is its title.
857
858 =item 2. A scalar. The scalar is both the value and the title.
859
860 =item 3. A hash reference. In this case C<%options> must contain
861 I<value> and I<title> keys that name the keys in the element to use
862 for the value and title respectively.
863
864 =item 4. A blessed reference. In this case C<%options> must contain
865 I<value> and I<title> keys that name functions called on the blessed
866 reference whose return values are used as the value and title
867 respectively.
868
869 =back
870
871 For cases 3 and 4 C<$options{value}> defaults to C<id> and
872 C<$options{title}> defaults to C<$options{value}>.
873
874 In addition to pure keys/method you can also provide coderefs as I<value_sub>
875 and/or I<title_sub>. If present, these take precedence over keys or methods,
876 and are called with the element as first argument. It must return the value or
877 title.
878
879 Lastly a joint coderef I<value_title_sub> may be provided, which in turn takes
880 precedence over each individual sub. It will only be called once for each
881 element and must return a list of value and title.
882
883 If the option C<with_empty> is set then an empty element (value
884 C<undef>) will be used as the first element. The title to display for
885 this element can be set with the option C<empty_title> and defaults to
886 an empty string.
887
888 The option C<default> can be either a scalar or an array reference
889 containing the values of the options which should be set to be
890 selected.
891
892 =item C<tab, description, target, %PARAMS>
893
894 Creates a tab for C<tabbed>. The description will be used as displayed name.
895 The target should be a block or template that can be processed. C<tab> supports
896 a C<method> parameter, which can override the process method to apply target.
897 C<method => 'raw'> will just include the given text as is. I was too lazy to
898 implement C<include> properly.
899
900 Also an C<if> attribute is supported, so that tabs can be suppressed based on
901 some occasion. In this case the supplied block won't even get processed, and
902 the resulting tab will get ignored by C<tabbed>:
903
904   L.tab('Awesome tab wih much info', '_much_info.html', if => SELF.wants_all)
905
906 =back
907
908 =head1 MODULE AUTHORS
909
910 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
911
912 L<http://linet-services.de>