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);
31 my $string = "" . shift;
32 $string =~ s/\"/\\\"/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 $options_str = shift;
99 my %attributes = _hashify(@_);
101 $attributes{id} ||= $self->name_to_id($name);
102 $options_str = $self->options_for_select($options_str) if ref $options_str;
104 return $self->html_tag('select', $options_str, %attributes, name => $name);
108 my ($self, $name, $content, @slurp) = @_;
109 my %attributes = _hashify(@slurp);
111 $attributes{id} ||= $self->name_to_id($name);
112 $attributes{rows} *= 1; # required by standard
113 $attributes{cols} *= 1; # required by standard
114 $content = $content ? _H($content) : '';
116 return $self->html_tag('textarea', $content, %attributes, name => $name);
120 my ($self, $name, @slurp) = @_;
121 my %attributes = _hashify(@slurp);
123 $attributes{id} ||= $self->name_to_id($name);
124 $attributes{value} = 1 unless defined $attributes{value};
125 my $label = delete $attributes{label};
126 my $checkall = delete $attributes{checkall};
128 if ($attributes{checked}) {
129 $attributes{checked} = 'checked';
131 delete $attributes{checked};
134 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'checkbox');
135 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
136 $code .= $self->javascript(qq|\$('#$attributes{id}').checkall('$checkall');|) if $checkall;
141 sub radio_button_tag {
144 my %attributes = _hashify(@_);
146 $attributes{value} = 1 unless defined $attributes{value};
147 $attributes{id} ||= $self->name_to_id($name . "_" . $attributes{value});
148 my $label = delete $attributes{label};
150 if ($attributes{checked}) {
151 $attributes{checked} = 'checked';
153 delete $attributes{checked};
156 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'radio');
157 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
163 my ($self, $name, $value, @slurp) = @_;
164 my %attributes = _hashify(@slurp);
166 $attributes{id} ||= $self->name_to_id($name);
167 $attributes{type} ||= 'text';
169 return $self->html_tag('input', undef, %attributes, name => $name, value => $value);
173 return shift->input_tag(@_, type => 'hidden');
177 my ($self, $content, @slurp) = @_;
178 return $self->html_tag('div', $content, @slurp);
182 my ($self, $content, @slurp) = @_;
183 return $self->html_tag('ul', $content, @slurp);
187 my ($self, $content, @slurp) = @_;
188 return $self->html_tag('li', $content, @slurp);
192 my ($self, $href, $content, @slurp) = @_;
193 my %params = _hashify(@slurp);
197 return $self->html_tag('a', $content, %params, href => $href);
201 my ($self, $name, $value, @slurp) = @_;
202 my %attributes = _hashify(@slurp);
204 $attributes{onclick} = "if (confirm('" . delete($attributes{confirm}) . "')) return true; else return false;" if $attributes{confirm};
206 return $self->input_tag($name, $value, %attributes, type => 'submit', class => 'submit');
210 my ($self, $onclick, $value, @slurp) = @_;
211 my %attributes = _hashify(@slurp);
213 $attributes{id} ||= $self->name_to_id($attributes{name}) if $attributes{name};
214 $attributes{type} ||= 'button';
216 return $self->html_tag('input', undef, %attributes, value => $value, onclick => $onclick);
219 sub options_for_select {
221 my $collection = shift;
222 my %options = _hashify(@_);
224 my $value_key = $options{value} || 'id';
225 my $title_key = $options{title} || $value_key;
227 my $value_sub = $options{value_sub};
228 my $title_sub = $options{title_sub};
230 my $value_title_sub = $options{value_title_sub};
232 my %selected = map { ( $_ => 1 ) } @{ ref($options{default}) eq 'ARRAY' ? $options{default} : defined($options{default}) ? [ $options{default} ] : [] };
235 my ($element, $index, $key, $sub) = @_;
236 my $ref = ref $element;
237 return $sub ? $sub->($element)
239 : $ref eq 'ARRAY' ? $element->[$index]
240 : $ref eq 'HASH' ? $element->{$key}
245 push @elements, [ undef, $options{empty_title} || '' ] if $options{with_empty};
246 push @elements, map [
247 $value_title_sub ? @{ $value_title_sub->($_) } : (
248 $access->($_, 0, $value_key, $value_sub),
249 $access->($_, 1, $title_key, $title_sub),
251 ], @{ $collection } if $collection && ref $collection eq 'ARRAY';
254 foreach my $result (@elements) {
255 my %attributes = ( value => $result->[0] );
256 $attributes{selected} = 'selected' if $selected{ defined($result->[0]) ? $result->[0] : '' };
258 $code .= $self->html_tag('option', _H($result->[1]), %attributes);
265 my ($self, $name, $value) = splice @_, 0, 3;
266 my %attributes = _hashify(@_);
268 my $options = $self->options_for_select([ [ 1, $::locale->text('Yes') ], [ 0, $::locale->text('No') ] ], default => $value ? 1 : 0);
269 return $self->select_tag($name, $options, %attributes);
273 my ($self, $data) = @_;
274 return $self->html_tag('script', $data, type => 'text/javascript');
281 foreach my $file (@_) {
282 $file .= '.css' unless $file =~ m/\.css$/;
283 $file = "css/${file}" unless $file =~ m|/|;
285 $code .= qq|<link rel="stylesheet" href="${file}" type="text/css" media="screen" />|;
292 my ($self, $name, $value, @slurp) = @_;
293 my %params = _hashify(@slurp);
294 my $name_e = _H($name);
296 my $datefmt = apply {
300 } $::myconfig{"dateformat"};
302 my $cal_align = delete $params{cal_align} || 'BR';
303 my $onchange = delete $params{onchange};
304 my $str_value = blessed $value ? $value->to_lxoffice : $value;
306 $self->input_tag($name, $str_value,
309 title => _H($::myconfig{dateformat}),
310 onBlur => 'check_right_date_format(this)',
312 onChange => $onchange,
315 ) . ((!$params{no_cal} && !$params{readonly}) ?
316 $self->html_tag('img', undef,
317 src => 'image/calendar.png',
318 alt => $::locale->text('Calendar'),
320 title => _H($::myconfig{dateformat}),
324 "Calendar.setup({ inputField: '$name_e', ifFormat: '$datefmt', align: '$cal_align', button: 'trigger$seq' });"
328 sub customer_picker {
329 my ($self, $name, $value, %params) = @_;
330 my $name_e = _H($name);
332 $self->hidden_tag($name, (ref $value && $value->can('id')) ? $value->id : '') .
333 $self->input_tag("$name_e\_name", (ref $value && $value->can('name')) ? $value->name : '', %params) .
334 $self->javascript(<<JS);
335 function autocomplete_customer (selector, column) {
336 \$(function(){ \$(selector).autocomplete({
337 source: function(req, rsp) {
339 url: 'controller.pl?action=Customer/ajax_autocomplete',
344 current: function() { \$('#$name_e').val() },
347 success: function (data){ rsp(data) }
352 select: function(event, ui) {
353 \$('#$name_e').val(ui.item.id);
354 \$('#$name_e\_name').val(ui.item.name);
358 autocomplete_customer('#$name_e\_name');
362 # simple version with select_tag
363 sub vendor_selector {
364 my ($self, $name, $value, %params) = @_;
366 my $actual_vendor_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"}) ? $::form->{"$name"}->id : $::form->{"$name"}) :
367 (ref $value && $value->can('id')) ? $value->id : '';
368 my $options_str = $self->options_for_select(SL::DB::Manager::Vendor->get_all(),
369 default => $actual_vendor_id,
370 title_sub => sub { $_[0]->vendornumber . " : " . $_[0]->name },
373 return $self->select_tag($name, $options_str, %params);
377 # simple version with select_tag
379 my ($self, $name, $value, %params) = @_;
381 my $actual_part_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"})? $::form->{"$name"}->id : $::form->{"$name"}) :
382 (ref $value && $value->can('id')) ? $value->id : '';
383 my $options_str = $self->options_for_select(SL::DB::Manager::Part->get_all(),
384 default => $actual_part_id,
385 title_sub => sub { $_[0]->partnumber . " : " . $_[0]->description },
388 return $self->select_tag($name, $options_str, %params);
396 foreach my $file (@_) {
397 $file .= '.js' unless $file =~ m/\.js$/;
398 $file = "js/${file}" unless $file =~ m|/|;
400 $code .= qq|<script type="text/javascript" src="${file}"></script>|;
407 my ($self, $tabs, @slurp) = @_;
408 my %params = _hashify(@slurp);
409 my $id = $params{id} || 'tab_' . _tag_id();
411 $params{selected} *= 1;
413 die 'L.tabbed needs an arrayred of tabs for first argument'
414 unless ref $tabs eq 'ARRAY';
416 my (@header, @blocks);
417 for my $i (0..$#$tabs) {
418 my $tab = $tabs->[$i];
422 my $selected = $params{selected} == $i;
423 my $tab_id = "__tab_id_$i";
424 push @header, $self->li_tag(
425 $self->link('', $tab->{name}, rel => $tab_id),
426 ($selected ? (class => 'selected') : ())
428 push @blocks, $self->div_tag($tab->{data},
429 id => $tab_id, class => 'tabcontent');
432 return '' unless @header;
433 return $self->ul_tag(
434 join('', @header), id => $id, class => 'shadetabs'
437 join('', @blocks), class => 'tabcontentstyle'
440 qq|var $id = new ddtabcontent("$id");$id.setpersist(true);| .
441 qq|$id.setselectedClassTarget("link");$id.init();|
446 my ($self, $name, $src, @slurp) = @_;
447 my %params = _hashify(@slurp);
449 $params{method} ||= 'process';
451 return () if defined $params{if} && !$params{if};
454 if ($params{method} eq 'raw') {
456 } elsif ($params{method} eq 'process') {
457 $data = $self->_context->process($src, %{ $params{args} || {} });
459 die "unknown tag method '$params{method}'";
462 return () unless $data;
464 return +{ name => $name, data => $data };
468 my ($self, $name, $value, @slurp) = @_;
469 my %attributes = _hashify(@slurp);
472 my $min = delete $attributes{min_rows} || 1;
474 if (exists $attributes{cols}) {
475 $cols = delete $attributes{cols};
476 $rows = $::form->numtextrows($value, $cols);
478 $rows = delete $attributes{rows} || 1;
482 ? $self->textarea_tag($name, $value, %attributes, rows => max($rows, $min), ($cols ? (cols => $cols) : ()))
483 : $self->input_tag($name, $value, %attributes, ($cols ? (size => $cols) : ()));
486 sub multiselect2side {
487 my ($self, $id, @slurp) = @_;
488 my %params = _hashify(@slurp);
490 $params{labelsx} = "\"" . _J($params{labelsx} || $::locale->text('Available')) . "\"";
491 $params{labeldx} = "\"" . _J($params{labeldx} || $::locale->text('Selected')) . "\"";
492 $params{moveOptions} = 'false';
494 my $vars = join(', ', map { "${_}: " . $params{$_} } keys %params);
496 <script type="text/javascript">
497 \$().ready(function() {
498 \$('#${id}').multiselect2side({ ${vars} });
506 sub sortable_element {
507 my ($self, $selector, @slurp) = @_;
508 my %params = _hashify(@slurp);
510 my %attributes = ( distance => 5,
511 helper => <<'JAVASCRIPT' );
512 function(event, ui) {
513 ui.children().each(function() {
514 $(this).width($(this).width());
522 if ($params{url} && $params{with}) {
523 my $as = $params{as} || $params{with};
524 my $filter = ".filter(function(idx) { return this.substr(0, " . length($params{with}) . ") == '$params{with}'; })";
525 $filter .= ".map(function(idx, str) { return str.replace('$params{with}_', ''); })";
527 $stop_event = <<JAVASCRIPT;
528 \$.post('$params{url}', { '${as}[]': \$(\$('${selector}').sortable('toArray'))${filter}.toArray() });
532 if (!$params{dont_recolor}) {
533 $stop_event .= <<JAVASCRIPT;
534 \$('${selector}>*:odd').removeClass('listrow1').removeClass('listrow0').addClass('listrow0');
535 \$('${selector}>*:even').removeClass('listrow1').removeClass('listrow0').addClass('listrow1');
540 $attributes{stop} = <<JAVASCRIPT;
541 function(event, ui) {
548 $params{handle} = '.dragdrop' unless exists $params{handle};
549 $attributes{handle} = "'$params{handle}'" if $params{handle};
551 my $attr_str = join(', ', map { "${_}: $attributes{$_}" } keys %attributes);
553 my $code = <<JAVASCRIPT;
554 <script type="text/javascript">
556 \$( "${selector}" ).sortable({ ${attr_str} })
564 sub online_help_tag {
565 my ($self, $tag, @slurp) = @_;
566 my %params = _hashify(@slurp);
567 my $cc = $::myconfig{countrycode};
568 my $file = "doc/online/$cc/$tag.html";
569 my $text = $params{text} || $::locale->text('Help');
571 die 'malformed help tag' unless $tag =~ /^[a-zA-Z0-9_]+$/;
572 return unless -f $file;
573 return $self->html_tag('a', $text, href => $file, class => 'jqModal')
578 require Data::Dumper;
579 return '<pre>' . Data::Dumper::Dumper(@_) . '</pre>';
583 my ($self, $text, @slurp) = @_;
584 my %params = _hashify(@slurp);
587 $params{at} = 3 if 3 > $params{at};
590 return $text if length($text) < $params{at};
591 return substr($text, 0, $params{at}) . '...';
594 sub sortable_table_header {
595 my ($self, $by, @slurp) = @_;
596 my %params = _hashify(@slurp);
598 my $controller = $self->{CONTEXT}->stash->get('SELF');
599 my $sort_spec = $controller->get_sort_spec;
600 my $by_spec = $sort_spec->{$by};
601 my %current_sort_params = $controller->get_current_sort_params;
602 my ($image, $new_dir) = ('', $current_sort_params{dir});
603 my $title = delete($params{title}) || $::locale->text($by_spec->{title});
605 if ($current_sort_params{by} eq $by) {
606 my $current_dir = $current_sort_params{dir} ? 'up' : 'down';
607 $image = '<img border="0" src="image/' . $current_dir . '.png">';
608 $new_dir = 1 - ($current_sort_params{dir} || 0);
611 $params{ $sort_spec->{FORM_PARAMS}->[0] } = $by;
612 $params{ $sort_spec->{FORM_PARAMS}->[1] } = ($new_dir ? '1' : '0');
614 return '<a href="' . $controller->get_callback(%params) . '">' . _H($title) . $image . '</a>';
617 sub paginate_controls {
620 my $controller = $self->{CONTEXT}->stash->get('SELF');
621 my $paginate_spec = $controller->get_paginate_spec;
622 my %paginate_params = $controller->get_current_paginate_params;
624 my %template_params = (
626 cur => $paginate_params{page},
627 max => $paginate_params{num_pages},
628 common => $paginate_params{common_pages},
631 my %url_params = _hashify(@_);
632 $url_params{ $paginate_spec->{FORM_PARAMS}->[0] } = delete $url_params{page};
633 $url_params{ $paginate_spec->{FORM_PARAMS}->[1] } = delete $url_params{per_page} if exists $url_params{per_page};
635 return $controller->get_callback(%url_params);
640 $controller->_template_obj->process('templates/webpages/common/paginate.html', \%template_params, \$output);
650 SL::Templates::Plugin::L -- Layouting / tag generation
654 Usage from a template:
658 [% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right' ] ]) %]
660 [% L.select_tag('direction', L.options_for_select([ { direction => 'left', display => 'To the left' },
661 { direction => 'right', display => 'To the right' } ],
662 value => 'direction', title => 'display', default => 'right')) %]
666 A module modeled a bit after Rails' ActionView helpers. Several small
667 functions that create HTML tags from various kinds of data sources.
671 =head2 LOW-LEVEL FUNCTIONS
675 =item C<name_to_id $name>
677 Converts a name to a HTML id by replacing various characters.
679 =item C<attributes %items>
681 Creates a string from all elements in C<%items> suitable for usage as
682 HTML tag attributes. Keys and values are HTML escaped even though keys
683 must not contain non-ASCII characters for browsers to accept them.
685 =item C<html_tag $tag_name, $content_string, %attributes>
687 Creates an opening and closing HTML tag for C<$tag_name> and puts
688 C<$content_string> between the two. If C<$content_string> is undefined
689 or empty then only a E<lt>tag/E<gt> tag will be created. Attributes
690 are key/value pairs added to the opening tag.
692 C<$content_string> is not HTML escaped.
696 =head2 HIGH-LEVEL FUNCTIONS
700 =item C<select_tag $name, $options_string, %attributes>
702 Creates a HTML 'select' tag named C<$name> with the contents
703 C<$options_string> and with arbitrary HTML attributes from
704 C<%attributes>. The tag's C<id> defaults to C<name_to_id($name)>.
706 The C<$options_string> is usually created by the
707 L</options_for_select> function. If C<$options_string> is an array
708 reference then it will be passed to L</options_for_select>
711 =item C<yes_no_tag $name, $value, %attributes>
713 Creates a HTML 'select' tag with the two entries C<yes> and C<no> by
714 calling L<select_tag> and L<options_for_select>. C<$value> determines
715 which entry is selected. The C<%attributes> are passed through to
718 =item C<input_tag $name, $value, %attributes>
720 Creates a HTML 'input type=text' tag named C<$name> with the value
721 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
722 tag's C<id> defaults to C<name_to_id($name)>.
724 =item C<hidden_tag $name, $value, %attributes>
726 Creates a HTML 'input type=hidden' tag named C<$name> with the value
727 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
728 tag's C<id> defaults to C<name_to_id($name)>.
730 =item C<submit_tag $name, $value, %attributes>
732 Creates a HTML 'input type=submit class=submit' tag named C<$name> with the
733 value C<$value> and with arbitrary HTML attributes from C<%attributes>. The
734 tag's C<id> defaults to C<name_to_id($name)>.
736 If C<$attributes{confirm}> is set then a JavaScript popup dialog will
737 be added via the C<onclick> handler asking the question given with
738 C<$attributes{confirm}>. If request is only submitted if the user
739 clicks the dialog's ok/yes button.
741 =item C<textarea_tag $name, $value, %attributes>
743 Creates a HTML 'textarea' tag named C<$name> with the content
744 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
745 tag's C<id> defaults to C<name_to_id($name)>.
747 =item C<checkbox_tag $name, %attributes>
749 Creates a HTML 'input type=checkbox' tag named C<$name> with arbitrary
750 HTML attributes from C<%attributes>. The tag's C<id> defaults to
751 C<name_to_id($name)>. The tag's C<value> defaults to C<1>.
753 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
754 created with said C<label>. No attribute named C<label> is created in
757 If C<%attributes> contains a key C<checkall> then the value is taken as a
758 JQuery selector and clicking this checkbox will also toggle all checkboxes
759 matching the selector.
761 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
763 Creates a date input field, with an attached javascript that will open a
764 calendar on click. The javascript ist by default anchoered at the bottom right
765 sight. This can be overridden with C<cal_align>, see Calendar documentation for
766 the details, usually you'll want a two letter abbreviation of the alignment.
767 Right + Bottom becomes C<BL>.
769 =item C<radio_button_tag $name, %attributes>
771 Creates a HTML 'input type=radio' tag named C<$name> with arbitrary
772 HTML attributes from C<%attributes>. The tag's C<value> defaults to
773 C<1>. The tag's C<id> defaults to C<name_to_id($name . "_" . $value)>.
775 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
776 created with said C<label>. No attribute named C<label> is created in
779 =item C<javascript_tag $file1, $file2, $file3...>
781 Creates a HTML 'E<lt>script type="text/javascript" src="..."E<gt>'
782 tag for each file name parameter passed. Each file name will be
783 postfixed with '.js' if it isn't already and prefixed with 'js/' if it
784 doesn't contain a slash.
786 =item C<stylesheet_tag $file1, $file2, $file3...>
788 Creates a HTML 'E<lt>link rel="text/stylesheet" href="..."E<gt>' tag
789 for each file name parameter passed. Each file name will be postfixed
790 with '.css' if it isn't already and prefixed with 'css/' if it doesn't
793 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
795 Creates a date input field, with an attached javascript that will open a
796 calendar on click. The javascript ist by default anchoered at the bottom right
797 sight. This can be overridden with C<cal_align>, see Calendar documentation for
798 the details, usually you'll want a two letter abbreviation of the alignment.
799 Right + Bottom becomes C<BL>.
801 =item C<tabbed \@tab, %attributes>
803 Will create a tabbed area. The tabs should be created with the helper function
807 L.tab(LxERP.t8('Basic Data'), 'part/_main_tab.html'),
808 L.tab(LxERP.t8('Custom Variables'), 'part/_cvar_tab.html', if => SELF.display_cvar_tab),
811 An optional attribute is C<selected>, which accepts the ordinal of a tab which
812 should be selected by default.
814 =item C<areainput_tag $name, $content, %PARAMS>
816 Creates a generic input tag or textarea tag, depending on content size. The
817 amount of desired rows must be either given with the C<rows> parameter or can
818 be computed from the value and the C<cols> paramter, Accepted parameters
819 include C<min_rows> for rendering a minimum of rows if a textarea is displayed.
821 You can force input by setting rows to 1, and you can force textarea by setting
824 =item C<multiselect2side $id, %params>
826 Creates a JavaScript snippet calling the jQuery function
827 C<multiselect2side> on the select control with the ID C<$id>. The
828 select itself is not created. C<%params> can contain the following
835 The label of the list of available options. Defaults to the
836 translation of 'Available'.
840 The label of the list of selected options. Defaults to the
841 translation of 'Selected'.
845 =item C<sortable_element $selector, %params>
847 Makes the children of the DOM element C<$selector> (a jQuery selector)
848 sortable with the I<jQuery UI Selectable> library. The children can be
849 dragged & dropped around. After dropping an element an URL can be
850 postet to with the element IDs of the sorted children.
852 If this is used then the JavaScript file C<js/jquery-ui.js> must be
853 included manually as well as it isn't loaded via C<$::form-gt;header>.
855 C<%params> can contain the following entries:
861 The URL to POST an AJAX request to after a dragged element has been
862 dropped. The AJAX request's return value is ignored. If given then
863 C<$params{with}> must be given as well.
867 A string that is interpreted as the prefix of the children's ID. Upon
868 POSTing the result each child whose ID starts with C<$params{with}> is
869 considered. The prefix and the following "_" is removed from the
870 ID. The remaining parts of the IDs of those children are posted as a
871 single array parameter. The array parameter's name is either
872 C<$params{as}> or, missing that, C<$params{with}>.
876 Sets the POST parameter name for AJAX request after dropping an
877 element (see C<$params{with}>).
881 An optional jQuery selector specifying which part of the child element
882 is dragable. If the parameter is not given then it defaults to
883 C<.dragdrop> matching DOM elements with the class C<dragdrop>. If the
884 parameter is set and empty then the whole child element is dragable,
885 and clicks through to underlying elements like inputs or links might
888 =item C<dont_recolor>
890 If trueish then the children will not be recolored. The default is to
891 recolor the children by setting the class C<listrow0> on odd and
892 C<listrow1> on even entries.
898 <script type="text/javascript" src="js/jquery-ui.js"></script>
900 <table id="thing_list">
902 <tr><td>This</td><td>That</td></tr>
905 <tr id="thingy_2"><td>stuff</td><td>more stuff</td></tr>
906 <tr id="thingy_15"><td>stuff</td><td>more stuff</td></tr>
907 <tr id="thingy_6"><td>stuff</td><td>more stuff</td></tr>
911 [% L.sortable_element('#thing_list tbody',
912 url => 'controller.pl?action=SystemThings/reorder',
915 recolor_rows => 1) %]
917 After dropping e.g. the third element at the top of the list a POST
918 request would be made to the C<reorder> action of the C<SystemThings>
919 controller with a single parameter called C<thing_ids> -- an array
920 containing the values C<[ 6, 2, 15 ]>.
924 Dumps the Argument using L<Data::Dumper> into a E<lt>preE<gt> block.
926 =item C<sortable_table_header $by, %params>
928 Create a link and image suitable for placement in a table
929 header. C<$by> must be an index set up by the controller with
930 L<SL::Controller::Helper::make_sorted>.
932 The optional parameter C<$params{title}> can override the column title
933 displayed to the user. Otherwise the column title from the
934 controller's sort spec is used.
936 The other parameters in C<%params> are passed unmodified to the
937 underlying call to L<SL::Controller::Base::url_for>.
939 See the documentation of L<SL::Controller::Helper::Sorted> for an
940 overview and further usage instructions.
942 =item C<paginate_controls>
944 Create a set of links used to paginate a list view.
946 See the documentation of L<SL::Controller::Helper::Paginated> for an
947 overview and further usage instructions.
951 =head2 CONVERSION FUNCTIONS
955 =item C<options_for_select \@collection, %options>
957 Creates a string suitable for a HTML 'select' tag consisting of one
958 'E<lt>optionE<gt>' tag for each element in C<\@collection>. The value
959 to use and the title to display are extracted from the elements in
960 C<\@collection>. Each element can be one of four things:
964 =item 1. An array reference with at least two elements. The first element is
965 the value, the second element is its title.
967 =item 2. A scalar. The scalar is both the value and the title.
969 =item 3. A hash reference. In this case C<%options> must contain
970 I<value> and I<title> keys that name the keys in the element to use
971 for the value and title respectively.
973 =item 4. A blessed reference. In this case C<%options> must contain
974 I<value> and I<title> keys that name functions called on the blessed
975 reference whose return values are used as the value and title
980 For cases 3 and 4 C<$options{value}> defaults to C<id> and
981 C<$options{title}> defaults to C<$options{value}>.
983 In addition to pure keys/method you can also provide coderefs as I<value_sub>
984 and/or I<title_sub>. If present, these take precedence over keys or methods,
985 and are called with the element as first argument. It must return the value or
988 Lastly a joint coderef I<value_title_sub> may be provided, which in turn takes
989 precedence over each individual sub. It will only be called once for each
990 element and must return a list of value and title.
992 If the option C<with_empty> is set then an empty element (value
993 C<undef>) will be used as the first element. The title to display for
994 this element can be set with the option C<empty_title> and defaults to
997 The option C<default> can be either a scalar or an array reference
998 containing the values of the options which should be set to be
1001 =item C<tab, description, target, %PARAMS>
1003 Creates a tab for C<tabbed>. The description will be used as displayed name.
1004 The target should be a block or template that can be processed. C<tab> supports
1005 a C<method> parameter, which can override the process method to apply target.
1006 C<method => 'raw'> will just include the given text as is. I was too lazy to
1007 implement C<include> properly.
1009 Also an C<if> attribute is supported, so that tabs can be suppressed based on
1010 some occasion. In this case the supplied block won't even get processed, and
1011 the resulting tab will get ignored by C<tabbed>:
1013 L.tab('Awesome tab wih much info', '_much_info.html', if => SELF.wants_all)
1015 =item C<truncate $text, %params>
1017 Returns the C<$text> truncated after a certain number of
1020 The number of characters to truncate at is determined by the parameter
1021 C<at> which defaults to 50. If the text is longer than C<$params{at}>
1022 then it will be truncated and postfixed with '...'. Otherwise it will
1023 be returned unmodified.
1027 =head1 MODULE AUTHORS
1029 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
1031 L<http://linet-services.de>