1 package SL::Template::Plugin::L;
3 use base qw( Template::Plugin );
5 use List::MoreUtils qw(apply);
6 use List::Util qw(max);
7 use Scalar::Util qw(blessed);
13 { # This will give you an id for identifying html tags and such.
14 # It's guaranteed to be unique unless you exceed 10 mio calls per request.
15 # Do not use these id's to store information across requests.
16 my $_id_sequence = int rand 1e7;
18 return "id_" . ( $_id_sequence = ($_id_sequence + 1) % 1e7 );
22 my %_valueless_attributes = map { $_ => 1 } qw(
23 checked compact declare defer disabled ismap multiple noresize noshade nowrap
29 return $::locale->quote_special_chars('HTML', $string);
34 $string =~ s/(\"|\'|\\)/\\$1/g;
39 return (@_ && (ref($_[0]) eq 'HASH')) ? %{ $_[0] } : @_;
43 my ($class, $context, @args) = @_;
51 die 'not an accessor' if @_ > 1;
52 return $_[0]->{CONTEXT};
59 $name =~ s/[^\w_]/_/g;
66 my ($self, @slurp) = @_;
67 my %options = _hashify(@slurp);
70 while (my ($name, $value) = each %options) {
72 next if $_valueless_attributes{$name} && !$value;
73 $value = '' if !defined($value);
74 push @result, $_valueless_attributes{$name} ? _H($name) : _H($name) . '="' . _H($value) . '"';
77 return @result ? ' ' . join(' ', @result) : '';
81 my ($self, $tag, $content, @slurp) = @_;
82 my $attributes = $self->attributes(@slurp);
84 return "<${tag}${attributes}>" unless defined($content);
85 return "<${tag}${attributes}>${content}</${tag}>";
89 my ($self, @slurp) = @_;
90 my %options = _hashify(@slurp);
94 return $self->html_tag('img', undef, %options);
100 my $collection = shift;
101 my %attributes = _hashify(@_);
103 $attributes{id} ||= $self->name_to_id($name);
105 my $value_key = delete($attributes{value_key}) || 'id';
106 my $title_key = delete($attributes{title_key}) || $value_key;
107 my $default_key = delete($attributes{default_key}) || 'selected';
110 my $value_title_sub = delete($attributes{value_title_sub});
112 my $value_sub = delete($attributes{value_sub});
113 my $title_sub = delete($attributes{title_sub});
114 my $default_sub = delete($attributes{default_sub});
116 my $with_empty = delete($attributes{with_empty});
117 my $empty_title = delete($attributes{empty_title});
121 if ( ref($attributes{default}) eq 'ARRAY' ) {
123 foreach my $entry (@{$attributes{default}}) {
124 $selected{$entry} = 1;
126 } elsif ( defined($attributes{default}) ) {
127 $selected{$attributes{default}} = 1;
130 delete($attributes{default});
136 push(@options, [undef, $empty_title || '']);
139 my $normalize_entry = sub {
141 my ($type, $entry, $sub, $key) = @_;
144 return $sub->($entry);
147 my $ref = ref($entry);
151 if ( $type eq 'value' || $type eq 'title' ) {
158 if ( $ref eq 'ARRAY' ) {
160 if ( $type eq 'value' ) {
164 if ( $type eq 'title' ) {
171 if ( $ref eq 'HASH' ) {
172 return $entry->{$key};
175 if ( $type ne 'default' || $entry->can($key) ) {
182 foreach my $entry ( @{ $collection } ) {
186 if ( $value_title_sub ) {
187 ($value, $title) = @{ $value_title_sub->($entry) };
190 $value = $normalize_entry->('value', $entry, $value_sub, $value_key);
191 $title = $normalize_entry->('title', $entry, $title_sub, $title_key);
194 my $default = $normalize_entry->('default', $entry, $default_sub, $default_key);
196 push(@options, [$value, $title, $default]);
199 foreach my $entry (@options) {
200 if ( exists($selected{$entry->[0]}) ) {
207 foreach my $entry (@options) {
208 my %args = (value => $entry->[0]);
210 $args{selected} = $entry->[2];
212 $code .= $self->html_tag('option', _H($entry->[1]), %args);
215 $code = $self->html_tag('select', $code, %attributes, name => $name);
221 my ($self, $name, $content, @slurp) = @_;
222 my %attributes = _hashify(@slurp);
224 $attributes{id} ||= $self->name_to_id($name);
225 $attributes{rows} *= 1; # required by standard
226 $attributes{cols} *= 1; # required by standard
227 $content = $content ? _H($content) : '';
229 return $self->html_tag('textarea', $content, %attributes, name => $name);
233 my ($self, $name, @slurp) = @_;
234 my %attributes = _hashify(@slurp);
236 $attributes{id} ||= $self->name_to_id($name);
237 $attributes{value} = 1 unless defined $attributes{value};
238 my $label = delete $attributes{label};
239 my $checkall = delete $attributes{checkall};
241 if ($attributes{checked}) {
242 $attributes{checked} = 'checked';
244 delete $attributes{checked};
247 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'checkbox');
248 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
249 $code .= $self->javascript(qq|\$('#$attributes{id}').checkall('$checkall');|) if $checkall;
254 sub radio_button_tag {
257 my %attributes = _hashify(@_);
259 $attributes{value} = 1 unless defined $attributes{value};
260 $attributes{id} ||= $self->name_to_id($name . "_" . $attributes{value});
261 my $label = delete $attributes{label};
263 if ($attributes{checked}) {
264 $attributes{checked} = 'checked';
266 delete $attributes{checked};
269 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'radio');
270 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
276 my ($self, $name, $value, @slurp) = @_;
277 my %attributes = _hashify(@slurp);
279 $attributes{id} ||= $self->name_to_id($name);
280 $attributes{type} ||= 'text';
282 return $self->html_tag('input', undef, %attributes, name => $name, value => $value);
286 return shift->input_tag(@_, type => 'hidden');
290 my ($self, $content, @slurp) = @_;
291 return $self->html_tag('div', $content, @slurp);
295 my ($self, $content, @slurp) = @_;
296 return $self->html_tag('ul', $content, @slurp);
300 my ($self, $content, @slurp) = @_;
301 return $self->html_tag('li', $content, @slurp);
305 my ($self, $href, $content, @slurp) = @_;
306 my %params = _hashify(@slurp);
310 return $self->html_tag('a', $content, %params, href => $href);
314 my ($self, $name, $value, @slurp) = @_;
315 my %attributes = _hashify(@slurp);
317 if ( $attributes{confirm} ) {
318 $attributes{onclick} = 'return confirm("'. _J(delete($attributes{confirm})) .'");';
321 return $self->input_tag($name, $value, %attributes, type => 'submit', class => 'submit');
325 my ($self, $onclick, $value, @slurp) = @_;
326 my %attributes = _hashify(@slurp);
328 $attributes{id} ||= $self->name_to_id($attributes{name}) if $attributes{name};
329 $attributes{type} ||= 'button';
331 return $self->html_tag('input', undef, %attributes, value => $value, onclick => $onclick);
335 my ($self, $name, $value) = splice @_, 0, 3;
336 my %attributes = _hashify(@_);
338 return $self->select_tag($name, [ [ 1 => $::locale->text('Yes') ], [ 0 => $::locale->text('No') ] ], default => $value ? 1 : 0, %attributes);
342 my ($self, $data) = @_;
343 return $self->html_tag('script', $data, type => 'text/javascript');
350 foreach my $file (@_) {
351 $file .= '.css' unless $file =~ m/\.css$/;
352 $file = "css/${file}" unless $file =~ m|/|;
354 $code .= qq|<link rel="stylesheet" href="${file}" type="text/css" media="screen" />|;
361 my ($self, $name, $value, @slurp) = @_;
362 my %params = _hashify(@slurp);
363 my $name_e = _H($name);
365 my $datefmt = apply {
369 } $::myconfig{"dateformat"};
371 my $cal_align = delete $params{cal_align} || 'BR';
372 my $onchange = delete $params{onchange};
373 my $str_value = blessed $value ? $value->to_lxoffice : $value;
375 $self->input_tag($name, $str_value,
378 title => _H($::myconfig{dateformat}),
379 onBlur => 'check_right_date_format(this)',
381 onChange => $onchange,
384 ) . ((!$params{no_cal} && !$params{readonly}) ?
385 $self->html_tag('img', undef,
386 src => 'image/calendar.png',
387 alt => $::locale->text('Calendar'),
389 title => _H($::myconfig{dateformat}),
393 "Calendar.setup({ inputField: '$name_e', ifFormat: '$datefmt', align: '$cal_align', button: 'trigger$seq' });"
397 sub customer_picker {
398 my ($self, $name, $value, %params) = @_;
399 my $name_e = _H($name);
401 $::request->{layout}->add_javascripts('autocomplete_customer.js');
403 $self->hidden_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => 'customer_autocomplete') .
404 $self->input_tag("$name_e\_name", (ref $value && $value->can('name')) ? $value->name : '', %params);
407 # simple version with select_tag
408 sub vendor_selector {
409 my ($self, $name, $value, %params) = @_;
411 my $actual_vendor_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"}) ? $::form->{"$name"}->id : $::form->{"$name"}) :
412 (ref $value && $value->can('id')) ? $value->id : '';
414 return $self->select_tag($name, SL::DB::Manager::Vendor->get_all(),
415 default => $actual_vendor_id,
416 title_sub => sub { $_[0]->vendornumber . " : " . $_[0]->name },
422 # simple version with select_tag
424 my ($self, $name, $value, %params) = @_;
426 my $actual_part_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"})? $::form->{"$name"}->id : $::form->{"$name"}) :
427 (ref $value && $value->can('id')) ? $value->id : '';
429 return $self->select_tag($name, SL::DB::Manager::Part->get_all(),
430 default => $actual_part_id,
431 title_sub => sub { $_[0]->partnumber . " : " . $_[0]->description },
441 foreach my $file (@_) {
442 $file .= '.js' unless $file =~ m/\.js$/;
443 $file = "js/${file}" unless $file =~ m|/|;
445 $code .= qq|<script type="text/javascript" src="${file}"></script>|;
452 my ($self, $tabs, @slurp) = @_;
453 my %params = _hashify(@slurp);
454 my $id = $params{id} || 'tab_' . _tag_id();
456 $params{selected} *= 1;
458 die 'L.tabbed needs an arrayred of tabs for first argument'
459 unless ref $tabs eq 'ARRAY';
461 my (@header, @blocks);
462 for my $i (0..$#$tabs) {
463 my $tab = $tabs->[$i];
467 my $tab_id = "__tab_id_$i";
468 push @header, $self->li_tag($self->link('#' . $tab_id, $tab->{name}));
469 push @blocks, $self->div_tag($tab->{data}, id => $tab_id);
472 return '' unless @header;
474 my $ul = $self->ul_tag(join('', @header), id => $id);
475 return $self->div_tag(join('', $ul, @blocks), class => 'tabwidget');
479 my ($self, $name, $src, @slurp) = @_;
480 my %params = _hashify(@slurp);
482 $params{method} ||= 'process';
484 return () if defined $params{if} && !$params{if};
487 if ($params{method} eq 'raw') {
489 } elsif ($params{method} eq 'process') {
490 $data = $self->_context->process($src, %{ $params{args} || {} });
492 die "unknown tag method '$params{method}'";
495 return () unless $data;
497 return +{ name => $name, data => $data };
501 my ($self, $name, $value, @slurp) = @_;
502 my %attributes = _hashify(@slurp);
505 my $min = delete $attributes{min_rows} || 1;
507 if (exists $attributes{cols}) {
508 $cols = delete $attributes{cols};
509 $rows = $::form->numtextrows($value, $cols);
511 $rows = delete $attributes{rows} || 1;
515 ? $self->textarea_tag($name, $value, %attributes, rows => max($rows, $min), ($cols ? (cols => $cols) : ()))
516 : $self->input_tag($name, $value, %attributes, ($cols ? (size => $cols) : ()));
519 sub multiselect2side {
520 my ($self, $id, @slurp) = @_;
521 my %params = _hashify(@slurp);
523 $params{labelsx} = "\"" . _J($params{labelsx} || $::locale->text('Available')) . "\"";
524 $params{labeldx} = "\"" . _J($params{labeldx} || $::locale->text('Selected')) . "\"";
525 $params{moveOptions} = 'false';
527 my $vars = join(', ', map { "${_}: " . $params{$_} } keys %params);
529 <script type="text/javascript">
530 \$().ready(function() {
531 \$('#${id}').multiselect2side({ ${vars} });
539 sub sortable_element {
540 my ($self, $selector, @slurp) = @_;
541 my %params = _hashify(@slurp);
543 my %attributes = ( distance => 5,
544 helper => <<'JAVASCRIPT' );
545 function(event, ui) {
546 ui.children().each(function() {
547 $(this).width($(this).width());
555 if ($params{url} && $params{with}) {
556 my $as = $params{as} || $params{with};
557 my $filter = ".filter(function(idx) { return this.substr(0, " . length($params{with}) . ") == '$params{with}'; })";
558 $filter .= ".map(function(idx, str) { return str.replace('$params{with}_', ''); })";
560 $stop_event = <<JAVASCRIPT;
561 \$.post('$params{url}', { '${as}[]': \$(\$('${selector}').sortable('toArray'))${filter}.toArray() });
565 if (!$params{dont_recolor}) {
566 $stop_event .= <<JAVASCRIPT;
567 \$('${selector}>*:odd').removeClass('listrow1').removeClass('listrow0').addClass('listrow0');
568 \$('${selector}>*:even').removeClass('listrow1').removeClass('listrow0').addClass('listrow1');
573 $attributes{stop} = <<JAVASCRIPT;
574 function(event, ui) {
581 $params{handle} = '.dragdrop' unless exists $params{handle};
582 $attributes{handle} = "'$params{handle}'" if $params{handle};
584 my $attr_str = join(', ', map { "${_}: $attributes{$_}" } keys %attributes);
586 my $code = <<JAVASCRIPT;
587 <script type="text/javascript">
589 \$( "${selector}" ).sortable({ ${attr_str} })
597 sub online_help_tag {
598 my ($self, $tag, @slurp) = @_;
599 my %params = _hashify(@slurp);
600 my $cc = $::myconfig{countrycode};
601 my $file = "doc/online/$cc/$tag.html";
602 my $text = $params{text} || $::locale->text('Help');
604 die 'malformed help tag' unless $tag =~ /^[a-zA-Z0-9_]+$/;
605 return unless -f $file;
606 return $self->html_tag('a', $text, href => $file, class => 'jqModal')
611 require Data::Dumper;
612 return '<pre>' . Data::Dumper::Dumper(@_) . '</pre>';
616 my ($self, $text, @slurp) = @_;
617 my %params = _hashify(@slurp);
620 $params{at} = 3 if 3 > $params{at};
623 return $text if length($text) < $params{at};
624 return substr($text, 0, $params{at}) . '...';
627 sub sortable_table_header {
628 my ($self, $by, @slurp) = @_;
629 my %params = _hashify(@slurp);
631 my $controller = $self->{CONTEXT}->stash->get('SELF');
632 my $sort_spec = $controller->get_sort_spec;
633 my $by_spec = $sort_spec->{$by};
634 my %current_sort_params = $controller->get_current_sort_params;
635 my ($image, $new_dir) = ('', $current_sort_params{dir});
636 my $title = delete($params{title}) || $::locale->text($by_spec->{title});
638 if ($current_sort_params{by} eq $by) {
639 my $current_dir = $current_sort_params{dir} ? 'up' : 'down';
640 $image = '<img border="0" src="image/' . $current_dir . '.png">';
641 $new_dir = 1 - ($current_sort_params{dir} || 0);
644 $params{ $sort_spec->{FORM_PARAMS}->[0] } = $by;
645 $params{ $sort_spec->{FORM_PARAMS}->[1] } = ($new_dir ? '1' : '0');
647 return '<a href="' . $controller->get_callback(%params) . '">' . _H($title) . $image . '</a>';
650 sub paginate_controls {
653 my $controller = $self->{CONTEXT}->stash->get('SELF');
654 my $paginate_spec = $controller->get_paginate_spec;
655 my %paginate_params = $controller->get_current_paginate_params;
657 my %template_params = (
658 pages => \%paginate_params,
660 my %url_params = _hashify(@_);
661 $url_params{ $paginate_spec->{FORM_PARAMS}->[0] } = delete $url_params{page};
662 $url_params{ $paginate_spec->{FORM_PARAMS}->[1] } = delete $url_params{per_page} if exists $url_params{per_page};
664 return $controller->get_callback(%url_params);
668 return SL::Presenter->get->render('common/paginate', %template_params);
677 SL::Templates::Plugin::L -- Layouting / tag generation
681 Usage from a template:
685 [% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right', 1 ] ]) %]
687 [% L.select_tag('direction', [ { direction => 'left', display => 'To the left' },
688 { direction => 'right', display => 'To the right' } ],
689 value_key => 'direction', title_key => 'display', default => 'right')) %]
691 [% L.select_tag('direction', [ { direction => 'left', display => 'To the left' },
692 { direction => 'right', display => 'To the right', selected => 1 } ],
693 value_key => 'direction', title_key => 'display')) %]
697 A module modeled a bit after Rails' ActionView helpers. Several small
698 functions that create HTML tags from various kinds of data sources.
702 =head2 LOW-LEVEL FUNCTIONS
706 =item C<name_to_id $name>
708 Converts a name to a HTML id by replacing various characters.
710 =item C<attributes %items>
712 Creates a string from all elements in C<%items> suitable for usage as
713 HTML tag attributes. Keys and values are HTML escaped even though keys
714 must not contain non-ASCII characters for browsers to accept them.
716 =item C<html_tag $tag_name, $content_string, %attributes>
718 Creates an opening and closing HTML tag for C<$tag_name> and puts
719 C<$content_string> between the two. If C<$content_string> is undefined
720 or empty then only a E<lt>tag/E<gt> tag will be created. Attributes
721 are key/value pairs added to the opening tag.
723 C<$content_string> is not HTML escaped.
727 =head2 HIGH-LEVEL FUNCTIONS
731 =item C<select_tag $name, \@collection, %attributes>
733 Creates a HTML 'select' tag named C<$name> with the contents of one
734 'E<lt>optionE<gt>' tag for each element in C<\@collection> and with arbitrary
735 HTML attributes from C<%attributes>. The value
736 to use and the title to display are extracted from the elements in
737 C<\@collection>. Each element can be one of four things:
741 =item 1. An array reference with at least two elements. The first element is
742 the value, the second element is its title. The third element is optional and and should contain a boolean.
743 If it is true, than the element will be used as default.
745 =item 2. A scalar. The scalar is both the value and the title.
747 =item 3. A hash reference. In this case C<%attributes> must contain
748 I<value_key>, I<title_key> and may contain I<default_key> keys that name the keys in the element to use
749 for the value, title and default respectively.
751 =item 4. A blessed reference. In this case C<%attributes> must contain
752 I<value_key>, I<title_key> and may contain I<default_key> keys that name functions called on the blessed
753 reference whose return values are used as the value, title and default
758 For cases 3 and 4 C<$attributes{value_key}> defaults to C<id>,
759 C<$attributes{title_key}> defaults to C<$attributes{value_key}>
760 and C<$attributes{default_key}> defaults to C<selected>.
762 In addition to pure keys/method you can also provide coderefs as I<value_sub>
763 and/or I<title_sub> and/or I<default_sub>. If present, these take precedence over keys or methods,
764 and are called with the element as first argument. It must return the value, title or default.
766 Lastly a joint coderef I<value_title_sub> may be provided, which in turn takes
767 precedence over the C<value_sub> and C<title_sub> subs. It will only be called once for each
768 element and must return a list of value and title.
770 If the option C<with_empty> is set then an empty element (value
771 C<undef>) will be used as the first element. The title to display for
772 this element can be set with the option C<empty_title> and defaults to
775 The option C<default> can be either a scalar or an array reference
776 containing the values of the options which should be set to be
779 The tag's C<id> defaults to C<name_to_id($name)>.
781 =item C<yes_no_tag $name, $value, %attributes>
783 Creates a HTML 'select' tag with the two entries C<yes> and C<no> by
784 calling L<select_tag>. C<$value> determines
785 which entry is selected. The C<%attributes> are passed through to
788 =item C<input_tag $name, $value, %attributes>
790 Creates a HTML 'input type=text' tag named C<$name> with the value
791 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
792 tag's C<id> defaults to C<name_to_id($name)>.
794 =item C<hidden_tag $name, $value, %attributes>
796 Creates a HTML 'input type=hidden' tag named C<$name> with the value
797 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
798 tag's C<id> defaults to C<name_to_id($name)>.
800 =item C<submit_tag $name, $value, %attributes>
802 Creates a HTML 'input type=submit class=submit' tag named C<$name> with the
803 value C<$value> and with arbitrary HTML attributes from C<%attributes>. The
804 tag's C<id> defaults to C<name_to_id($name)>.
806 If C<$attributes{confirm}> is set then a JavaScript popup dialog will
807 be added via the C<onclick> handler asking the question given with
808 C<$attributes{confirm}>. If request is only submitted if the user
809 clicks the dialog's ok/yes button.
811 =item C<textarea_tag $name, $value, %attributes>
813 Creates a HTML 'textarea' tag named C<$name> with the content
814 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
815 tag's C<id> defaults to C<name_to_id($name)>.
817 =item C<checkbox_tag $name, %attributes>
819 Creates a HTML 'input type=checkbox' tag named C<$name> with arbitrary
820 HTML attributes from C<%attributes>. The tag's C<id> defaults to
821 C<name_to_id($name)>. The tag's C<value> defaults to C<1>.
823 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
824 created with said C<label>. No attribute named C<label> is created in
827 If C<%attributes> contains a key C<checkall> then the value is taken as a
828 JQuery selector and clicking this checkbox will also toggle all checkboxes
829 matching the selector.
831 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
833 Creates a date input field, with an attached javascript that will open a
834 calendar on click. The javascript ist by default anchoered at the bottom right
835 sight. This can be overridden with C<cal_align>, see Calendar documentation for
836 the details, usually you'll want a two letter abbreviation of the alignment.
837 Right + Bottom becomes C<BL>.
839 =item C<radio_button_tag $name, %attributes>
841 Creates a HTML 'input type=radio' tag named C<$name> with arbitrary
842 HTML attributes from C<%attributes>. The tag's C<value> defaults to
843 C<1>. The tag's C<id> defaults to C<name_to_id($name . "_" . $value)>.
845 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
846 created with said C<label>. No attribute named C<label> is created in
849 =item C<javascript_tag $file1, $file2, $file3...>
851 Creates a HTML 'E<lt>script type="text/javascript" src="..."E<gt>'
852 tag for each file name parameter passed. Each file name will be
853 postfixed with '.js' if it isn't already and prefixed with 'js/' if it
854 doesn't contain a slash.
856 =item C<stylesheet_tag $file1, $file2, $file3...>
858 Creates a HTML 'E<lt>link rel="text/stylesheet" href="..."E<gt>' tag
859 for each file name parameter passed. Each file name will be postfixed
860 with '.css' if it isn't already and prefixed with 'css/' if it doesn't
863 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
865 Creates a date input field, with an attached javascript that will open a
866 calendar on click. The javascript ist by default anchoered at the bottom right
867 sight. This can be overridden with C<cal_align>, see Calendar documentation for
868 the details, usually you'll want a two letter abbreviation of the alignment.
869 Right + Bottom becomes C<BL>.
871 =item C<tabbed \@tab, %attributes>
873 Will create a tabbed area. The tabs should be created with the helper function
877 L.tab(LxERP.t8('Basic Data'), 'part/_main_tab.html'),
878 L.tab(LxERP.t8('Custom Variables'), 'part/_cvar_tab.html', if => SELF.display_cvar_tab),
881 =item C<areainput_tag $name, $content, %PARAMS>
883 Creates a generic input tag or textarea tag, depending on content size. The
884 amount of desired rows must be either given with the C<rows> parameter or can
885 be computed from the value and the C<cols> paramter, Accepted parameters
886 include C<min_rows> for rendering a minimum of rows if a textarea is displayed.
888 You can force input by setting rows to 1, and you can force textarea by setting
891 =item C<multiselect2side $id, %params>
893 Creates a JavaScript snippet calling the jQuery function
894 C<multiselect2side> on the select control with the ID C<$id>. The
895 select itself is not created. C<%params> can contain the following
902 The label of the list of available options. Defaults to the
903 translation of 'Available'.
907 The label of the list of selected options. Defaults to the
908 translation of 'Selected'.
912 =item C<sortable_element $selector, %params>
914 Makes the children of the DOM element C<$selector> (a jQuery selector)
915 sortable with the I<jQuery UI Selectable> library. The children can be
916 dragged & dropped around. After dropping an element an URL can be
917 postet to with the element IDs of the sorted children.
919 If this is used then the JavaScript file C<js/jquery-ui.js> must be
920 included manually as well as it isn't loaded via C<$::form-gt;header>.
922 C<%params> can contain the following entries:
928 The URL to POST an AJAX request to after a dragged element has been
929 dropped. The AJAX request's return value is ignored. If given then
930 C<$params{with}> must be given as well.
934 A string that is interpreted as the prefix of the children's ID. Upon
935 POSTing the result each child whose ID starts with C<$params{with}> is
936 considered. The prefix and the following "_" is removed from the
937 ID. The remaining parts of the IDs of those children are posted as a
938 single array parameter. The array parameter's name is either
939 C<$params{as}> or, missing that, C<$params{with}>.
943 Sets the POST parameter name for AJAX request after dropping an
944 element (see C<$params{with}>).
948 An optional jQuery selector specifying which part of the child element
949 is dragable. If the parameter is not given then it defaults to
950 C<.dragdrop> matching DOM elements with the class C<dragdrop>. If the
951 parameter is set and empty then the whole child element is dragable,
952 and clicks through to underlying elements like inputs or links might
955 =item C<dont_recolor>
957 If trueish then the children will not be recolored. The default is to
958 recolor the children by setting the class C<listrow0> on odd and
959 C<listrow1> on even entries.
965 <script type="text/javascript" src="js/jquery-ui.js"></script>
967 <table id="thing_list">
969 <tr><td>This</td><td>That</td></tr>
972 <tr id="thingy_2"><td>stuff</td><td>more stuff</td></tr>
973 <tr id="thingy_15"><td>stuff</td><td>more stuff</td></tr>
974 <tr id="thingy_6"><td>stuff</td><td>more stuff</td></tr>
978 [% L.sortable_element('#thing_list tbody',
979 url => 'controller.pl?action=SystemThings/reorder',
982 recolor_rows => 1) %]
984 After dropping e.g. the third element at the top of the list a POST
985 request would be made to the C<reorder> action of the C<SystemThings>
986 controller with a single parameter called C<thing_ids> -- an array
987 containing the values C<[ 6, 2, 15 ]>.
991 Dumps the Argument using L<Data::Dumper> into a E<lt>preE<gt> block.
993 =item C<sortable_table_header $by, %params>
995 Create a link and image suitable for placement in a table
996 header. C<$by> must be an index set up by the controller with
997 L<SL::Controller::Helper::make_sorted>.
999 The optional parameter C<$params{title}> can override the column title
1000 displayed to the user. Otherwise the column title from the
1001 controller's sort spec is used.
1003 The other parameters in C<%params> are passed unmodified to the
1004 underlying call to L<SL::Controller::Base::url_for>.
1006 See the documentation of L<SL::Controller::Helper::Sorted> for an
1007 overview and further usage instructions.
1009 =item C<paginate_controls>
1011 Create a set of links used to paginate a list view.
1013 See the documentation of L<SL::Controller::Helper::Paginated> for an
1014 overview and further usage instructions.
1018 =head2 CONVERSION FUNCTIONS
1022 =item C<tab, description, target, %PARAMS>
1024 Creates a tab for C<tabbed>. The description will be used as displayed name.
1025 The target should be a block or template that can be processed. C<tab> supports
1026 a C<method> parameter, which can override the process method to apply target.
1027 C<method => 'raw'> will just include the given text as is. I was too lazy to
1028 implement C<include> properly.
1030 Also an C<if> attribute is supported, so that tabs can be suppressed based on
1031 some occasion. In this case the supplied block won't even get processed, and
1032 the resulting tab will get ignored by C<tabbed>:
1034 L.tab('Awesome tab wih much info', '_much_info.html', if => SELF.wants_all)
1036 =item C<truncate $text, %params>
1038 Returns the C<$text> truncated after a certain number of
1041 The number of characters to truncate at is determined by the parameter
1042 C<at> which defaults to 50. If the text is longer than C<$params{at}>
1043 then it will be truncated and postfixed with '...'. Otherwise it will
1044 be returned unmodified.
1048 =head1 MODULE AUTHORS
1050 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
1052 L<http://linet-services.de>