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