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>