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}>";
89 my $options_str = shift;
90 my %attributes = _hashify(@_);
92 $attributes{id} ||= $self->name_to_id($name);
93 $options_str = $self->options_for_select($options_str) if ref $options_str;
95 return $self->html_tag('select', $options_str, %attributes, name => $name);
99 my ($self, $name, $content, @slurp) = @_;
100 my %attributes = _hashify(@slurp);
102 $attributes{id} ||= $self->name_to_id($name);
103 $attributes{rows} *= 1; # required by standard
104 $attributes{cols} *= 1; # required by standard
105 $content = $content ? _H($content) : '';
107 return $self->html_tag('textarea', $content, %attributes, name => $name);
111 my ($self, $name, @slurp) = @_;
112 my %attributes = _hashify(@slurp);
114 $attributes{id} ||= $self->name_to_id($name);
115 $attributes{value} = 1 unless defined $attributes{value};
116 my $label = delete $attributes{label};
117 my $checkall = delete $attributes{checkall};
119 if ($attributes{checked}) {
120 $attributes{checked} = 'checked';
122 delete $attributes{checked};
125 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'checkbox');
126 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
127 $code .= $self->javascript(qq|\$('#$attributes{id}').checkall('$checkall');|) if $checkall;
132 sub radio_button_tag {
135 my %attributes = _hashify(@_);
137 $attributes{value} = 1 unless defined $attributes{value};
138 $attributes{id} ||= $self->name_to_id($name . "_" . $attributes{value});
139 my $label = delete $attributes{label};
141 if ($attributes{checked}) {
142 $attributes{checked} = 'checked';
144 delete $attributes{checked};
147 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'radio');
148 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
154 my ($self, $name, $value, @slurp) = @_;
155 my %attributes = _hashify(@slurp);
157 $attributes{id} ||= $self->name_to_id($name);
158 $attributes{type} ||= 'text';
160 return $self->html_tag('input', undef, %attributes, name => $name, value => $value);
164 return shift->input_tag(@_, type => 'hidden');
168 my ($self, $content, @slurp) = @_;
169 return $self->html_tag('div', $content, @slurp);
173 my ($self, $content, @slurp) = @_;
174 return $self->html_tag('ul', $content, @slurp);
178 my ($self, $content, @slurp) = @_;
179 return $self->html_tag('li', $content, @slurp);
183 my ($self, $href, $content, @slurp) = @_;
184 my %params = _hashify(@slurp);
188 return $self->html_tag('a', $content, %params, href => $href);
192 my ($self, $name, $value, @slurp) = @_;
193 my %attributes = _hashify(@slurp);
195 $attributes{onclick} = "if (confirm('" . delete($attributes{confirm}) . "')) return true; else return false;" if $attributes{confirm};
197 return $self->input_tag($name, $value, %attributes, type => 'submit', class => 'submit');
201 my ($self, $onclick, $value, @slurp) = @_;
202 my %attributes = _hashify(@slurp);
204 $attributes{id} ||= $self->name_to_id($attributes{name}) if $attributes{name};
205 $attributes{type} ||= 'button';
207 return $self->html_tag('input', undef, %attributes, value => $value, onclick => $onclick);
210 sub options_for_select {
212 my $collection = shift;
213 my %options = _hashify(@_);
215 my $value_key = $options{value} || 'id';
216 my $title_key = $options{title} || $value_key;
218 my $value_sub = $options{value_sub};
219 my $title_sub = $options{title_sub};
221 my $value_title_sub = $options{value_title_sub};
223 my %selected = map { ( $_ => 1 ) } @{ ref($options{default}) eq 'ARRAY' ? $options{default} : defined($options{default}) ? [ $options{default} ] : [] };
226 my ($element, $index, $key, $sub) = @_;
227 my $ref = ref $element;
228 return $sub ? $sub->($element)
230 : $ref eq 'ARRAY' ? $element->[$index]
231 : $ref eq 'HASH' ? $element->{$key}
236 push @elements, [ undef, $options{empty_title} || '' ] if $options{with_empty};
237 push @elements, map [
238 $value_title_sub ? @{ $value_title_sub->($_) } : (
239 $access->($_, 0, $value_key, $value_sub),
240 $access->($_, 1, $title_key, $title_sub),
242 ], @{ $collection } if $collection && ref $collection eq 'ARRAY';
245 foreach my $result (@elements) {
246 my %attributes = ( value => $result->[0] );
247 $attributes{selected} = 'selected' if $selected{ defined($result->[0]) ? $result->[0] : '' };
249 $code .= $self->html_tag('option', _H($result->[1]), %attributes);
256 my ($self, $name, $value) = splice @_, 0, 3;
257 my %attributes = _hashify(@_);
259 my $options = $self->options_for_select([ [ 1, $::locale->text('Yes') ], [ 0, $::locale->text('No') ] ], default => $value ? 1 : 0);
260 return $self->select_tag($name, $options, %attributes);
264 my ($self, $data) = @_;
265 return $self->html_tag('script', $data, type => 'text/javascript');
272 foreach my $file (@_) {
273 $file .= '.css' unless $file =~ m/\.css$/;
274 $file = "css/${file}" unless $file =~ m|/|;
276 $code .= qq|<link rel="stylesheet" href="${file}" type="text/css" media="screen" />|;
283 my ($self, $name, $value, @slurp) = @_;
284 my %params = _hashify(@slurp);
285 my $name_e = _H($name);
287 my $datefmt = apply {
291 } $::myconfig{"dateformat"};
293 my $cal_align = delete $params{cal_align} || 'BR';
294 my $onchange = delete $params{onchange};
295 my $str_value = blessed $value ? $value->to_lxoffice : $value;
297 $self->input_tag($name, $str_value,
300 title => _H($::myconfig{dateformat}),
301 onBlur => 'check_right_date_format(this)',
303 onChange => $onchange,
306 ) . ((!$params{no_cal} && !$params{readonly}) ?
307 $self->html_tag('img', undef,
308 src => 'image/calendar.png',
309 alt => $::locale->text('Calendar'),
311 title => _H($::myconfig{dateformat}),
315 "Calendar.setup({ inputField: '$name_e', ifFormat: '$datefmt', align: '$cal_align', button: 'trigger$seq' });"
319 sub customer_picker {
320 my ($self, $name, $value, %params) = @_;
321 my $name_e = _H($name);
323 $self->hidden_tag($name, (ref $value && $value->can('id')) ? $value->id : '') .
324 $self->input_tag("$name_e\_name", (ref $value && $value->can('name')) ? $value->name : '', %params) .
325 $self->javascript(<<JS);
326 function autocomplete_customer (selector, column) {
327 \$(function(){ \$(selector).autocomplete({
328 source: function(req, rsp) {
330 url: 'controller.pl?action=Customer/ajax_autocomplete',
335 current: function() { \$('#$name_e').val() },
338 success: function (data){ rsp(data) }
343 select: function(event, ui) {
344 \$('#$name_e').val(ui.item.id);
345 \$('#$name_e\_name').val(ui.item.name);
349 autocomplete_customer('#$name_e\_name');
353 # simple version with select_tag
354 sub vendor_selector {
355 my ($self, $name, $value, %params) = @_;
357 my $actual_vendor_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"}) ? $::form->{"$name"}->id : $::form->{"$name"}) :
358 (ref $value && $value->can('id')) ? $value->id : '';
359 my $options_str = $self->options_for_select(SL::DB::Manager::Vendor->get_all(),
360 default => $actual_vendor_id,
361 title_sub => sub { $_[0]->vendornumber . " : " . $_[0]->name },
364 return $self->select_tag($name, $options_str, %params);
368 # simple version with select_tag
370 my ($self, $name, $value, %params) = @_;
372 my $actual_part_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"})? $::form->{"$name"}->id : $::form->{"$name"}) :
373 (ref $value && $value->can('id')) ? $value->id : '';
374 my $options_str = $self->options_for_select(SL::DB::Manager::Part->get_all(),
375 default => $actual_part_id,
376 title_sub => sub { $_[0]->partnumber . " : " . $_[0]->description },
379 return $self->select_tag($name, $options_str, %params);
387 foreach my $file (@_) {
388 $file .= '.js' unless $file =~ m/\.js$/;
389 $file = "js/${file}" unless $file =~ m|/|;
391 $code .= qq|<script type="text/javascript" src="${file}"></script>|;
398 my ($self, $tabs, @slurp) = @_;
399 my %params = _hashify(@slurp);
400 my $id = $params{id} || 'tab_' . _tag_id();
402 $params{selected} *= 1;
404 die 'L.tabbed needs an arrayred of tabs for first argument'
405 unless ref $tabs eq 'ARRAY';
407 my (@header, @blocks);
408 for my $i (0..$#$tabs) {
409 my $tab = $tabs->[$i];
413 my $selected = $params{selected} == $i;
414 my $tab_id = "__tab_id_$i";
415 push @header, $self->li_tag(
416 $self->link('', $tab->{name}, rel => $tab_id),
417 ($selected ? (class => 'selected') : ())
419 push @blocks, $self->div_tag($tab->{data},
420 id => $tab_id, class => 'tabcontent');
423 return '' unless @header;
424 return $self->ul_tag(
425 join('', @header), id => $id, class => 'shadetabs'
428 join('', @blocks), class => 'tabcontentstyle'
431 qq|var $id = new ddtabcontent("$id");$id.setpersist(true);| .
432 qq|$id.setselectedClassTarget("link");$id.init();|
437 my ($self, $name, $src, @slurp) = @_;
438 my %params = _hashify(@slurp);
440 $params{method} ||= 'process';
442 return () if defined $params{if} && !$params{if};
445 if ($params{method} eq 'raw') {
447 } elsif ($params{method} eq 'process') {
448 $data = $self->_context->process($src, %{ $params{args} || {} });
450 die "unknown tag method '$params{method}'";
453 return () unless $data;
455 return +{ name => $name, data => $data };
459 my ($self, $name, $value, @slurp) = @_;
460 my %attributes = _hashify(@slurp);
463 my $min = delete $attributes{min_rows} || 1;
465 if (exists $attributes{cols}) {
466 $cols = delete $attributes{cols};
467 $rows = $::form->numtextrows($value, $cols);
469 $rows = delete $attributes{rows} || 1;
473 ? $self->textarea_tag($name, $value, %attributes, rows => max($rows, $min), ($cols ? (cols => $cols) : ()))
474 : $self->input_tag($name, $value, %attributes, ($cols ? (size => $cols) : ()));
477 sub multiselect2side {
478 my ($self, $id, @slurp) = @_;
479 my %params = _hashify(@slurp);
481 $params{labelsx} = "\"" . _J($params{labelsx} || $::locale->text('Available')) . "\"";
482 $params{labeldx} = "\"" . _J($params{labeldx} || $::locale->text('Selected')) . "\"";
483 $params{moveOptions} = 'false';
485 my $vars = join(', ', map { "${_}: " . $params{$_} } keys %params);
487 <script type="text/javascript">
488 \$().ready(function() {
489 \$('#${id}').multiselect2side({ ${vars} });
497 sub sortable_element {
498 my ($self, $selector, @slurp) = @_;
499 my %params = _hashify(@slurp);
501 my %attributes = ( distance => 5,
502 helper => <<'JAVASCRIPT' );
503 function(event, ui) {
504 ui.children().each(function() {
505 $(this).width($(this).width());
513 if ($params{url} && $params{with}) {
514 my $as = $params{as} || $params{with};
515 my $filter = ".filter(function(idx) { return this.substr(0, " . length($params{with}) . ") == '$params{with}'; })";
516 $filter .= ".map(function(idx, str) { return str.replace('$params{with}_', ''); })";
518 $stop_event = <<JAVASCRIPT;
519 \$.post('$params{url}', { '${as}[]': \$(\$('${selector}').sortable('toArray'))${filter}.toArray() });
523 if (!$params{dont_recolor}) {
524 $stop_event .= <<JAVASCRIPT;
525 \$('${selector}>*:odd').removeClass('listrow1').removeClass('listrow0').addClass('listrow0');
526 \$('${selector}>*:even').removeClass('listrow1').removeClass('listrow0').addClass('listrow1');
531 $attributes{stop} = <<JAVASCRIPT;
532 function(event, ui) {
539 $params{handle} = '.dragdrop' unless exists $params{handle};
540 $attributes{handle} = "'$params{handle}'" if $params{handle};
542 my $attr_str = join(', ', map { "${_}: $attributes{$_}" } keys %attributes);
544 my $code = <<JAVASCRIPT;
545 <script type="text/javascript">
547 \$( "${selector}" ).sortable({ ${attr_str} })
555 sub online_help_tag {
556 my ($self, $tag, @slurp) = @_;
557 my %params = _hashify(@slurp);
558 my $cc = $::myconfig{countrycode};
559 my $file = "doc/online/$cc/$tag.html";
560 my $text = $params{text} || $::locale->text('Help');
562 die 'malformed help tag' unless $tag =~ /^[a-zA-Z0-9_]+$/;
563 return unless -f $file;
564 return $self->html_tag('a', $text, href => $file, class => 'jqModal')
569 require Data::Dumper;
570 return '<pre>' . Data::Dumper::Dumper(@_) . '</pre>';
574 my ($self, $text, @slurp) = @_;
575 my %params = _hashify(@slurp);
578 $params{at} = 3 if 3 > $params{at};
581 return $text if length($text) < $params{at};
582 return substr($text, 0, $params{at}) . '...';
585 sub sortable_table_header {
586 my ($self, $by, @slurp) = @_;
587 my %params = _hashify(@slurp);
589 my $controller = $self->{CONTEXT}->stash->get('SELF');
590 my $sort_spec = $controller->get_sort_spec;
591 my $by_spec = $sort_spec->{$by};
592 my %current_sort_params = $controller->get_current_sort_params;
593 my ($image, $new_dir) = ('', $current_sort_params{dir});
594 my $title = delete($params{title}) || $::locale->text($by_spec->{title});
596 if ($current_sort_params{by} eq $by) {
597 my $current_dir = $current_sort_params{dir} ? 'up' : 'down';
598 $image = '<img border="0" src="image/' . $current_dir . '.png">';
599 $new_dir = 1 - ($current_sort_params{dir} || 0);
602 $params{ $sort_spec->{FORM_PARAMS}->[0] } = $by;
603 $params{ $sort_spec->{FORM_PARAMS}->[1] } = ($new_dir ? '1' : '0');
605 return '<a href="' . $controller->get_callback(%params) . '">' . _H($title) . $image . '</a>';
608 sub paginate_controls {
611 my $controller = $self->{CONTEXT}->stash->get('SELF');
612 my $paginate_spec = $controller->get_paginate_spec;
613 my %paginate_params = $controller->get_current_paginate_params;
615 my %template_params = (
617 cur => $paginate_params{page},
618 max => $paginate_params{num_pages},
619 common => $paginate_params{common_pages},
622 my %url_params = _hashify(@_);
623 $url_params{ $paginate_spec->{FORM_PARAMS}->[0] } = delete $url_params{page};
624 $url_params{ $paginate_spec->{FORM_PARAMS}->[1] } = delete $url_params{per_page} if exists $url_params{per_page};
626 return $controller->get_callback(%url_params);
631 $controller->_template_obj->process('templates/webpages/common/paginate.html', \%template_params, \$output);
641 SL::Templates::Plugin::L -- Layouting / tag generation
645 Usage from a template:
649 [% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right' ] ]) %]
651 [% L.select_tag('direction', L.options_for_select([ { direction => 'left', display => 'To the left' },
652 { direction => 'right', display => 'To the right' } ],
653 value => 'direction', title => 'display', default => 'right')) %]
657 A module modeled a bit after Rails' ActionView helpers. Several small
658 functions that create HTML tags from various kinds of data sources.
662 =head2 LOW-LEVEL FUNCTIONS
666 =item C<name_to_id $name>
668 Converts a name to a HTML id by replacing various characters.
670 =item C<attributes %items>
672 Creates a string from all elements in C<%items> suitable for usage as
673 HTML tag attributes. Keys and values are HTML escaped even though keys
674 must not contain non-ASCII characters for browsers to accept them.
676 =item C<html_tag $tag_name, $content_string, %attributes>
678 Creates an opening and closing HTML tag for C<$tag_name> and puts
679 C<$content_string> between the two. If C<$content_string> is undefined
680 or empty then only a E<lt>tag/E<gt> tag will be created. Attributes
681 are key/value pairs added to the opening tag.
683 C<$content_string> is not HTML escaped.
687 =head2 HIGH-LEVEL FUNCTIONS
691 =item C<select_tag $name, $options_string, %attributes>
693 Creates a HTML 'select' tag named C<$name> with the contents
694 C<$options_string> and with arbitrary HTML attributes from
695 C<%attributes>. The tag's C<id> defaults to C<name_to_id($name)>.
697 The C<$options_string> is usually created by the
698 L</options_for_select> function. If C<$options_string> is an array
699 reference then it will be passed to L</options_for_select>
702 =item C<yes_no_tag $name, $value, %attributes>
704 Creates a HTML 'select' tag with the two entries C<yes> and C<no> by
705 calling L<select_tag> and L<options_for_select>. C<$value> determines
706 which entry is selected. The C<%attributes> are passed through to
709 =item C<input_tag $name, $value, %attributes>
711 Creates a HTML 'input type=text' tag named C<$name> with the value
712 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
713 tag's C<id> defaults to C<name_to_id($name)>.
715 =item C<hidden_tag $name, $value, %attributes>
717 Creates a HTML 'input type=hidden' tag named C<$name> with the value
718 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
719 tag's C<id> defaults to C<name_to_id($name)>.
721 =item C<submit_tag $name, $value, %attributes>
723 Creates a HTML 'input type=submit class=submit' tag named C<$name> with the
724 value C<$value> and with arbitrary HTML attributes from C<%attributes>. The
725 tag's C<id> defaults to C<name_to_id($name)>.
727 If C<$attributes{confirm}> is set then a JavaScript popup dialog will
728 be added via the C<onclick> handler asking the question given with
729 C<$attributes{confirm}>. If request is only submitted if the user
730 clicks the dialog's ok/yes button.
732 =item C<textarea_tag $name, $value, %attributes>
734 Creates a HTML 'textarea' tag named C<$name> with the content
735 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
736 tag's C<id> defaults to C<name_to_id($name)>.
738 =item C<checkbox_tag $name, %attributes>
740 Creates a HTML 'input type=checkbox' tag named C<$name> with arbitrary
741 HTML attributes from C<%attributes>. The tag's C<id> defaults to
742 C<name_to_id($name)>. The tag's C<value> defaults to C<1>.
744 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
745 created with said C<label>. No attribute named C<label> is created in
748 If C<%attributes> contains a key C<checkall> then the value is taken as a
749 JQuery selector and clicking this checkbox will also toggle all checkboxes
750 matching the selector.
752 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
754 Creates a date input field, with an attached javascript that will open a
755 calendar on click. The javascript ist by default anchoered at the bottom right
756 sight. This can be overridden with C<cal_align>, see Calendar documentation for
757 the details, usually you'll want a two letter abbreviation of the alignment.
758 Right + Bottom becomes C<BL>.
760 =item C<radio_button_tag $name, %attributes>
762 Creates a HTML 'input type=radio' tag named C<$name> with arbitrary
763 HTML attributes from C<%attributes>. The tag's C<value> defaults to
764 C<1>. The tag's C<id> defaults to C<name_to_id($name . "_" . $value)>.
766 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
767 created with said C<label>. No attribute named C<label> is created in
770 =item C<javascript_tag $file1, $file2, $file3...>
772 Creates a HTML 'E<lt>script type="text/javascript" src="..."E<gt>'
773 tag for each file name parameter passed. Each file name will be
774 postfixed with '.js' if it isn't already and prefixed with 'js/' if it
775 doesn't contain a slash.
777 =item C<stylesheet_tag $file1, $file2, $file3...>
779 Creates a HTML 'E<lt>link rel="text/stylesheet" href="..."E<gt>' tag
780 for each file name parameter passed. Each file name will be postfixed
781 with '.css' if it isn't already and prefixed with 'css/' if it doesn't
784 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
786 Creates a date input field, with an attached javascript that will open a
787 calendar on click. The javascript ist by default anchoered at the bottom right
788 sight. This can be overridden with C<cal_align>, see Calendar documentation for
789 the details, usually you'll want a two letter abbreviation of the alignment.
790 Right + Bottom becomes C<BL>.
792 =item C<tabbed \@tab, %attributes>
794 Will create a tabbed area. The tabs should be created with the helper function
798 L.tab(LxERP.t8('Basic Data'), 'part/_main_tab.html'),
799 L.tab(LxERP.t8('Custom Variables'), 'part/_cvar_tab.html', if => SELF.display_cvar_tab),
802 An optional attribute is C<selected>, which accepts the ordinal of a tab which
803 should be selected by default.
805 =item C<areainput_tag $name, $content, %PARAMS>
807 Creates a generic input tag or textarea tag, depending on content size. The
808 amount of desired rows must be either given with the C<rows> parameter or can
809 be computed from the value and the C<cols> paramter, Accepted parameters
810 include C<min_rows> for rendering a minimum of rows if a textarea is displayed.
812 You can force input by setting rows to 1, and you can force textarea by setting
815 =item C<multiselect2side $id, %params>
817 Creates a JavaScript snippet calling the jQuery function
818 C<multiselect2side> on the select control with the ID C<$id>. The
819 select itself is not created. C<%params> can contain the following
826 The label of the list of available options. Defaults to the
827 translation of 'Available'.
831 The label of the list of selected options. Defaults to the
832 translation of 'Selected'.
836 =item C<sortable_element $selector, %params>
838 Makes the children of the DOM element C<$selector> (a jQuery selector)
839 sortable with the I<jQuery UI Selectable> library. The children can be
840 dragged & dropped around. After dropping an element an URL can be
841 postet to with the element IDs of the sorted children.
843 If this is used then the JavaScript file C<js/jquery-ui.js> must be
844 included manually as well as it isn't loaded via C<$::form-gt;header>.
846 C<%params> can contain the following entries:
852 The URL to POST an AJAX request to after a dragged element has been
853 dropped. The AJAX request's return value is ignored. If given then
854 C<$params{with}> must be given as well.
858 A string that is interpreted as the prefix of the children's ID. Upon
859 POSTing the result each child whose ID starts with C<$params{with}> is
860 considered. The prefix and the following "_" is removed from the
861 ID. The remaining parts of the IDs of those children are posted as a
862 single array parameter. The array parameter's name is either
863 C<$params{as}> or, missing that, C<$params{with}>.
867 Sets the POST parameter name for AJAX request after dropping an
868 element (see C<$params{with}>).
872 An optional jQuery selector specifying which part of the child element
873 is dragable. If the parameter is not given then it defaults to
874 C<.dragdrop> matching DOM elements with the class C<dragdrop>. If the
875 parameter is set and empty then the whole child element is dragable,
876 and clicks through to underlying elements like inputs or links might
879 =item C<dont_recolor>
881 If trueish then the children will not be recolored. The default is to
882 recolor the children by setting the class C<listrow0> on odd and
883 C<listrow1> on even entries.
889 <script type="text/javascript" src="js/jquery-ui.js"></script>
891 <table id="thing_list">
893 <tr><td>This</td><td>That</td></tr>
896 <tr id="thingy_2"><td>stuff</td><td>more stuff</td></tr>
897 <tr id="thingy_15"><td>stuff</td><td>more stuff</td></tr>
898 <tr id="thingy_6"><td>stuff</td><td>more stuff</td></tr>
902 [% L.sortable_element('#thing_list tbody',
903 url => 'controller.pl?action=SystemThings/reorder',
906 recolor_rows => 1) %]
908 After dropping e.g. the third element at the top of the list a POST
909 request would be made to the C<reorder> action of the C<SystemThings>
910 controller with a single parameter called C<thing_ids> -- an array
911 containing the values C<[ 6, 2, 15 ]>.
915 Dumps the Argument using L<Data::Dumper> into a E<lt>preE<gt> block.
917 =item C<sortable_table_header $by, %params>
919 Create a link and image suitable for placement in a table
920 header. C<$by> must be an index set up by the controller with
921 L<SL::Controller::Helper::make_sorted>.
923 The optional parameter C<$params{title}> can override the column title
924 displayed to the user. Otherwise the column title from the
925 controller's sort spec is used.
927 The other parameters in C<%params> are passed unmodified to the
928 underlying call to L<SL::Controller::Base::url_for>.
930 See the documentation of L<SL::Controller::Helper::Sorted> for an
931 overview and further usage instructions.
933 =item C<paginate_controls>
935 Create a set of links used to paginate a list view.
937 See the documentation of L<SL::Controller::Helper::Paginated> for an
938 overview and further usage instructions.
942 =head2 CONVERSION FUNCTIONS
946 =item C<options_for_select \@collection, %options>
948 Creates a string suitable for a HTML 'select' tag consisting of one
949 'E<lt>optionE<gt>' tag for each element in C<\@collection>. The value
950 to use and the title to display are extracted from the elements in
951 C<\@collection>. Each element can be one of four things:
955 =item 1. An array reference with at least two elements. The first element is
956 the value, the second element is its title.
958 =item 2. A scalar. The scalar is both the value and the title.
960 =item 3. A hash reference. In this case C<%options> must contain
961 I<value> and I<title> keys that name the keys in the element to use
962 for the value and title respectively.
964 =item 4. A blessed reference. In this case C<%options> must contain
965 I<value> and I<title> keys that name functions called on the blessed
966 reference whose return values are used as the value and title
971 For cases 3 and 4 C<$options{value}> defaults to C<id> and
972 C<$options{title}> defaults to C<$options{value}>.
974 In addition to pure keys/method you can also provide coderefs as I<value_sub>
975 and/or I<title_sub>. If present, these take precedence over keys or methods,
976 and are called with the element as first argument. It must return the value or
979 Lastly a joint coderef I<value_title_sub> may be provided, which in turn takes
980 precedence over each individual sub. It will only be called once for each
981 element and must return a list of value and title.
983 If the option C<with_empty> is set then an empty element (value
984 C<undef>) will be used as the first element. The title to display for
985 this element can be set with the option C<empty_title> and defaults to
988 The option C<default> can be either a scalar or an array reference
989 containing the values of the options which should be set to be
992 =item C<tab, description, target, %PARAMS>
994 Creates a tab for C<tabbed>. The description will be used as displayed name.
995 The target should be a block or template that can be processed. C<tab> supports
996 a C<method> parameter, which can override the process method to apply target.
997 C<method => 'raw'> will just include the given text as is. I was too lazy to
998 implement C<include> properly.
1000 Also an C<if> attribute is supported, so that tabs can be suppressed based on
1001 some occasion. In this case the supplied block won't even get processed, and
1002 the resulting tab will get ignored by C<tabbed>:
1004 L.tab('Awesome tab wih much info', '_much_info.html', if => SELF.wants_all)
1006 =item C<truncate $text, %params>
1008 Returns the C<$text> truncated after a certain number of
1011 The number of characters to truncate at is determined by the parameter
1012 C<at> which defaults to 50. If the text is longer than C<$params{at}>
1013 then it will be truncated and postfixed with '...'. Otherwise it will
1014 be returned unmodified.
1018 =head1 MODULE AUTHORS
1020 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
1022 L<http://linet-services.de>