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" />|;
361 my $date_tag_id_idx = 0;
363 my ($self, $name, $value, @slurp) = @_;
365 my %params = _hashify(@slurp);
366 my $id = $self->name_to_id($name) . _tag_id();
367 my @onchange = $params{onchange} ? (onChange => delete $params{onchange}) : ();
368 my @class = $params{no_cal} || $params{readonly} ? () : (class => 'datepicker');
370 return $self->input_tag(
371 $name, blessed($value) ? $value->to_lxoffice : $value,
374 onblur => "check_right_date_format(this);",
380 sub customer_picker {
381 my ($self, $name, $value, %params) = @_;
382 my $name_e = _H($name);
384 $::request->{layout}->add_javascripts('autocomplete_customer.js');
386 $self->hidden_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => 'customer_autocomplete') .
387 $self->input_tag("$name_e\_name", (ref $value && $value->can('name')) ? $value->name : '', %params);
390 # simple version with select_tag
391 sub vendor_selector {
392 my ($self, $name, $value, %params) = @_;
394 my $actual_vendor_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"}) ? $::form->{"$name"}->id : $::form->{"$name"}) :
395 (ref $value && $value->can('id')) ? $value->id : '';
397 return $self->select_tag($name, SL::DB::Manager::Vendor->get_all(),
398 default => $actual_vendor_id,
399 title_sub => sub { $_[0]->vendornumber . " : " . $_[0]->name },
405 # simple version with select_tag
407 my ($self, $name, $value, %params) = @_;
409 my $actual_part_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"})? $::form->{"$name"}->id : $::form->{"$name"}) :
410 (ref $value && $value->can('id')) ? $value->id : '';
412 return $self->select_tag($name, SL::DB::Manager::Part->get_all(),
413 default => $actual_part_id,
414 title_sub => sub { $_[0]->partnumber . " : " . $_[0]->description },
424 foreach my $file (@_) {
425 $file .= '.js' unless $file =~ m/\.js$/;
426 $file = "js/${file}" unless $file =~ m|/|;
428 $code .= qq|<script type="text/javascript" src="${file}"></script>|;
435 my ($self, $tabs, @slurp) = @_;
436 my %params = _hashify(@slurp);
437 my $id = $params{id} || 'tab_' . _tag_id();
439 $params{selected} *= 1;
441 die 'L.tabbed needs an arrayred of tabs for first argument'
442 unless ref $tabs eq 'ARRAY';
444 my (@header, @blocks);
445 for my $i (0..$#$tabs) {
446 my $tab = $tabs->[$i];
450 my $tab_id = "__tab_id_$i";
451 push @header, $self->li_tag($self->link('#' . $tab_id, $tab->{name}));
452 push @blocks, $self->div_tag($tab->{data}, id => $tab_id);
455 return '' unless @header;
457 my $ul = $self->ul_tag(join('', @header), id => $id);
458 return $self->div_tag(join('', $ul, @blocks), class => 'tabwidget');
462 my ($self, $name, $src, @slurp) = @_;
463 my %params = _hashify(@slurp);
465 $params{method} ||= 'process';
467 return () if defined $params{if} && !$params{if};
470 if ($params{method} eq 'raw') {
472 } elsif ($params{method} eq 'process') {
473 $data = $self->_context->process($src, %{ $params{args} || {} });
475 die "unknown tag method '$params{method}'";
478 return () unless $data;
480 return +{ name => $name, data => $data };
484 my ($self, $name, $value, @slurp) = @_;
485 my %attributes = _hashify(@slurp);
488 my $min = delete $attributes{min_rows} || 1;
490 if (exists $attributes{cols}) {
491 $cols = delete $attributes{cols};
492 $rows = $::form->numtextrows($value, $cols);
494 $rows = delete $attributes{rows} || 1;
498 ? $self->textarea_tag($name, $value, %attributes, rows => max($rows, $min), ($cols ? (cols => $cols) : ()))
499 : $self->input_tag($name, $value, %attributes, ($cols ? (size => $cols) : ()));
502 sub multiselect2side {
503 my ($self, $id, @slurp) = @_;
504 my %params = _hashify(@slurp);
506 $params{labelsx} = "\"" . _J($params{labelsx} || $::locale->text('Available')) . "\"";
507 $params{labeldx} = "\"" . _J($params{labeldx} || $::locale->text('Selected')) . "\"";
508 $params{moveOptions} = 'false';
510 my $vars = join(', ', map { "${_}: " . $params{$_} } keys %params);
512 <script type="text/javascript">
513 \$().ready(function() {
514 \$('#${id}').multiselect2side({ ${vars} });
522 sub sortable_element {
523 my ($self, $selector, @slurp) = @_;
524 my %params = _hashify(@slurp);
526 my %attributes = ( distance => 5,
527 helper => <<'JAVASCRIPT' );
528 function(event, ui) {
529 ui.children().each(function() {
530 $(this).width($(this).width());
538 if ($params{url} && $params{with}) {
539 my $as = $params{as} || $params{with};
540 my $filter = ".filter(function(idx) { return this.substr(0, " . length($params{with}) . ") == '$params{with}'; })";
541 $filter .= ".map(function(idx, str) { return str.replace('$params{with}_', ''); })";
543 $stop_event = <<JAVASCRIPT;
544 \$.post('$params{url}', { '${as}[]': \$(\$('${selector}').sortable('toArray'))${filter}.toArray() });
548 if (!$params{dont_recolor}) {
549 $stop_event .= <<JAVASCRIPT;
550 \$('${selector}>*:odd').removeClass('listrow1').removeClass('listrow0').addClass('listrow0');
551 \$('${selector}>*:even').removeClass('listrow1').removeClass('listrow0').addClass('listrow1');
556 $attributes{stop} = <<JAVASCRIPT;
557 function(event, ui) {
564 $params{handle} = '.dragdrop' unless exists $params{handle};
565 $attributes{handle} = "'$params{handle}'" if $params{handle};
567 my $attr_str = join(', ', map { "${_}: $attributes{$_}" } keys %attributes);
569 my $code = <<JAVASCRIPT;
570 <script type="text/javascript">
572 \$( "${selector}" ).sortable({ ${attr_str} })
580 sub online_help_tag {
581 my ($self, $tag, @slurp) = @_;
582 my %params = _hashify(@slurp);
583 my $cc = $::myconfig{countrycode};
584 my $file = "doc/online/$cc/$tag.html";
585 my $text = $params{text} || $::locale->text('Help');
587 die 'malformed help tag' unless $tag =~ /^[a-zA-Z0-9_]+$/;
588 return unless -f $file;
589 return $self->html_tag('a', $text, href => $file, class => 'jqModal')
594 require Data::Dumper;
595 return '<pre>' . Data::Dumper::Dumper(@_) . '</pre>';
599 my ($self, $text, @slurp) = @_;
600 my %params = _hashify(@slurp);
603 $params{at} = 3 if 3 > $params{at};
606 return $text if length($text) < $params{at};
607 return substr($text, 0, $params{at}) . '...';
610 sub sortable_table_header {
611 my ($self, $by, @slurp) = @_;
612 my %params = _hashify(@slurp);
614 my $controller = $self->{CONTEXT}->stash->get('SELF');
615 my $sort_spec = $controller->get_sort_spec;
616 my $by_spec = $sort_spec->{$by};
617 my %current_sort_params = $controller->get_current_sort_params;
618 my ($image, $new_dir) = ('', $current_sort_params{dir});
619 my $title = delete($params{title}) || $::locale->text($by_spec->{title});
621 if ($current_sort_params{by} eq $by) {
622 my $current_dir = $current_sort_params{dir} ? 'up' : 'down';
623 $image = '<img border="0" src="image/' . $current_dir . '.png">';
624 $new_dir = 1 - ($current_sort_params{dir} || 0);
627 $params{ $sort_spec->{FORM_PARAMS}->[0] } = $by;
628 $params{ $sort_spec->{FORM_PARAMS}->[1] } = ($new_dir ? '1' : '0');
630 return '<a href="' . $controller->get_callback(%params) . '">' . _H($title) . $image . '</a>';
633 sub paginate_controls {
636 my $controller = $self->{CONTEXT}->stash->get('SELF');
637 my $paginate_spec = $controller->get_paginate_spec;
638 my %paginate_params = $controller->get_current_paginate_params;
640 my %template_params = (
641 pages => \%paginate_params,
643 my %url_params = _hashify(@_);
644 $url_params{ $paginate_spec->{FORM_PARAMS}->[0] } = delete $url_params{page};
645 $url_params{ $paginate_spec->{FORM_PARAMS}->[1] } = delete $url_params{per_page} if exists $url_params{per_page};
647 return $controller->get_callback(%url_params);
651 return SL::Presenter->get->render('common/paginate', %template_params);
660 SL::Templates::Plugin::L -- Layouting / tag generation
664 Usage from a template:
668 [% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right', 1 ] ]) %]
670 [% L.select_tag('direction', [ { direction => 'left', display => 'To the left' },
671 { direction => 'right', display => 'To the right' } ],
672 value_key => 'direction', title_key => 'display', default => 'right')) %]
674 [% L.select_tag('direction', [ { direction => 'left', display => 'To the left' },
675 { direction => 'right', display => 'To the right', selected => 1 } ],
676 value_key => 'direction', title_key => 'display')) %]
680 A module modeled a bit after Rails' ActionView helpers. Several small
681 functions that create HTML tags from various kinds of data sources.
685 =head2 LOW-LEVEL FUNCTIONS
689 =item C<name_to_id $name>
691 Converts a name to a HTML id by replacing various characters.
693 =item C<attributes %items>
695 Creates a string from all elements in C<%items> suitable for usage as
696 HTML tag attributes. Keys and values are HTML escaped even though keys
697 must not contain non-ASCII characters for browsers to accept them.
699 =item C<html_tag $tag_name, $content_string, %attributes>
701 Creates an opening and closing HTML tag for C<$tag_name> and puts
702 C<$content_string> between the two. If C<$content_string> is undefined
703 or empty then only a E<lt>tag/E<gt> tag will be created. Attributes
704 are key/value pairs added to the opening tag.
706 C<$content_string> is not HTML escaped.
710 =head2 HIGH-LEVEL FUNCTIONS
714 =item C<select_tag $name, \@collection, %attributes>
716 Creates a HTML 'select' tag named C<$name> with the contents of one
717 'E<lt>optionE<gt>' tag for each element in C<\@collection> and with arbitrary
718 HTML attributes from C<%attributes>. The value
719 to use and the title to display are extracted from the elements in
720 C<\@collection>. Each element can be one of four things:
724 =item 1. An array reference with at least two elements. The first element is
725 the value, the second element is its title. The third element is optional and and should contain a boolean.
726 If it is true, than the element will be used as default.
728 =item 2. A scalar. The scalar is both the value and the title.
730 =item 3. A hash reference. In this case C<%attributes> must contain
731 I<value_key>, I<title_key> and may contain I<default_key> keys that name the keys in the element to use
732 for the value, title and default respectively.
734 =item 4. A blessed reference. In this case C<%attributes> must contain
735 I<value_key>, I<title_key> and may contain I<default_key> keys that name functions called on the blessed
736 reference whose return values are used as the value, title and default
741 For cases 3 and 4 C<$attributes{value_key}> defaults to C<id>,
742 C<$attributes{title_key}> defaults to C<$attributes{value_key}>
743 and C<$attributes{default_key}> defaults to C<selected>.
745 In addition to pure keys/method you can also provide coderefs as I<value_sub>
746 and/or I<title_sub> and/or I<default_sub>. If present, these take precedence over keys or methods,
747 and are called with the element as first argument. It must return the value, title or default.
749 Lastly a joint coderef I<value_title_sub> may be provided, which in turn takes
750 precedence over the C<value_sub> and C<title_sub> subs. It will only be called once for each
751 element and must return a list of value and title.
753 If the option C<with_empty> is set then an empty element (value
754 C<undef>) will be used as the first element. The title to display for
755 this element can be set with the option C<empty_title> and defaults to
758 The option C<default> can be either a scalar or an array reference
759 containing the values of the options which should be set to be
762 The tag's C<id> defaults to C<name_to_id($name)>.
764 =item C<yes_no_tag $name, $value, %attributes>
766 Creates a HTML 'select' tag with the two entries C<yes> and C<no> by
767 calling L<select_tag>. C<$value> determines
768 which entry is selected. The C<%attributes> are passed through to
771 =item C<input_tag $name, $value, %attributes>
773 Creates a HTML 'input type=text' tag named C<$name> with the value
774 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
775 tag's C<id> defaults to C<name_to_id($name)>.
777 =item C<hidden_tag $name, $value, %attributes>
779 Creates a HTML 'input type=hidden' tag named C<$name> with the value
780 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
781 tag's C<id> defaults to C<name_to_id($name)>.
783 =item C<submit_tag $name, $value, %attributes>
785 Creates a HTML 'input type=submit class=submit' tag named C<$name> with the
786 value C<$value> and with arbitrary HTML attributes from C<%attributes>. The
787 tag's C<id> defaults to C<name_to_id($name)>.
789 If C<$attributes{confirm}> is set then a JavaScript popup dialog will
790 be added via the C<onclick> handler asking the question given with
791 C<$attributes{confirm}>. If request is only submitted if the user
792 clicks the dialog's ok/yes button.
794 =item C<textarea_tag $name, $value, %attributes>
796 Creates a HTML 'textarea' tag named C<$name> with the content
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<checkbox_tag $name, %attributes>
802 Creates a HTML 'input type=checkbox' tag named C<$name> with arbitrary
803 HTML attributes from C<%attributes>. The tag's C<id> defaults to
804 C<name_to_id($name)>. The tag's C<value> defaults to C<1>.
806 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
807 created with said C<label>. No attribute named C<label> is created in
810 If C<%attributes> contains a key C<checkall> then the value is taken as a
811 JQuery selector and clicking this checkbox will also toggle all checkboxes
812 matching the selector.
814 =item C<date_tag $name, $value, %attributes>
816 Creates a date input field, with an attached javascript that will open a
819 =item C<radio_button_tag $name, %attributes>
821 Creates a HTML 'input type=radio' tag named C<$name> with arbitrary
822 HTML attributes from C<%attributes>. The tag's C<value> defaults to
823 C<1>. The tag's C<id> defaults to C<name_to_id($name . "_" . $value)>.
825 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
826 created with said C<label>. No attribute named C<label> is created in
829 =item C<javascript_tag $file1, $file2, $file3...>
831 Creates a HTML 'E<lt>script type="text/javascript" src="..."E<gt>'
832 tag for each file name parameter passed. Each file name will be
833 postfixed with '.js' if it isn't already and prefixed with 'js/' if it
834 doesn't contain a slash.
836 =item C<stylesheet_tag $file1, $file2, $file3...>
838 Creates a HTML 'E<lt>link rel="text/stylesheet" href="..."E<gt>' tag
839 for each file name parameter passed. Each file name will be postfixed
840 with '.css' if it isn't already and prefixed with 'css/' if it doesn't
843 =item C<tabbed \@tab, %attributes>
845 Will create a tabbed area. The tabs should be created with the helper function
849 L.tab(LxERP.t8('Basic Data'), 'part/_main_tab.html'),
850 L.tab(LxERP.t8('Custom Variables'), 'part/_cvar_tab.html', if => SELF.display_cvar_tab),
853 =item C<areainput_tag $name, $content, %PARAMS>
855 Creates a generic input tag or textarea tag, depending on content size. The
856 amount of desired rows must be either given with the C<rows> parameter or can
857 be computed from the value and the C<cols> paramter, Accepted parameters
858 include C<min_rows> for rendering a minimum of rows if a textarea is displayed.
860 You can force input by setting rows to 1, and you can force textarea by setting
863 =item C<multiselect2side $id, %params>
865 Creates a JavaScript snippet calling the jQuery function
866 C<multiselect2side> on the select control with the ID C<$id>. The
867 select itself is not created. C<%params> can contain the following
874 The label of the list of available options. Defaults to the
875 translation of 'Available'.
879 The label of the list of selected options. Defaults to the
880 translation of 'Selected'.
884 =item C<sortable_element $selector, %params>
886 Makes the children of the DOM element C<$selector> (a jQuery selector)
887 sortable with the I<jQuery UI Selectable> library. The children can be
888 dragged & dropped around. After dropping an element an URL can be
889 postet to with the element IDs of the sorted children.
891 If this is used then the JavaScript file C<js/jquery-ui.js> must be
892 included manually as well as it isn't loaded via C<$::form-gt;header>.
894 C<%params> can contain the following entries:
900 The URL to POST an AJAX request to after a dragged element has been
901 dropped. The AJAX request's return value is ignored. If given then
902 C<$params{with}> must be given as well.
906 A string that is interpreted as the prefix of the children's ID. Upon
907 POSTing the result each child whose ID starts with C<$params{with}> is
908 considered. The prefix and the following "_" is removed from the
909 ID. The remaining parts of the IDs of those children are posted as a
910 single array parameter. The array parameter's name is either
911 C<$params{as}> or, missing that, C<$params{with}>.
915 Sets the POST parameter name for AJAX request after dropping an
916 element (see C<$params{with}>).
920 An optional jQuery selector specifying which part of the child element
921 is dragable. If the parameter is not given then it defaults to
922 C<.dragdrop> matching DOM elements with the class C<dragdrop>. If the
923 parameter is set and empty then the whole child element is dragable,
924 and clicks through to underlying elements like inputs or links might
927 =item C<dont_recolor>
929 If trueish then the children will not be recolored. The default is to
930 recolor the children by setting the class C<listrow0> on odd and
931 C<listrow1> on even entries.
937 <script type="text/javascript" src="js/jquery-ui.js"></script>
939 <table id="thing_list">
941 <tr><td>This</td><td>That</td></tr>
944 <tr id="thingy_2"><td>stuff</td><td>more stuff</td></tr>
945 <tr id="thingy_15"><td>stuff</td><td>more stuff</td></tr>
946 <tr id="thingy_6"><td>stuff</td><td>more stuff</td></tr>
950 [% L.sortable_element('#thing_list tbody',
951 url => 'controller.pl?action=SystemThings/reorder',
954 recolor_rows => 1) %]
956 After dropping e.g. the third element at the top of the list a POST
957 request would be made to the C<reorder> action of the C<SystemThings>
958 controller with a single parameter called C<thing_ids> -- an array
959 containing the values C<[ 6, 2, 15 ]>.
963 Dumps the Argument using L<Data::Dumper> into a E<lt>preE<gt> block.
965 =item C<sortable_table_header $by, %params>
967 Create a link and image suitable for placement in a table
968 header. C<$by> must be an index set up by the controller with
969 L<SL::Controller::Helper::make_sorted>.
971 The optional parameter C<$params{title}> can override the column title
972 displayed to the user. Otherwise the column title from the
973 controller's sort spec is used.
975 The other parameters in C<%params> are passed unmodified to the
976 underlying call to L<SL::Controller::Base::url_for>.
978 See the documentation of L<SL::Controller::Helper::Sorted> for an
979 overview and further usage instructions.
981 =item C<paginate_controls>
983 Create a set of links used to paginate a list view.
985 See the documentation of L<SL::Controller::Helper::Paginated> for an
986 overview and further usage instructions.
990 =head2 CONVERSION FUNCTIONS
994 =item C<tab, description, target, %PARAMS>
996 Creates a tab for C<tabbed>. The description will be used as displayed name.
997 The target should be a block or template that can be processed. C<tab> supports
998 a C<method> parameter, which can override the process method to apply target.
999 C<method => 'raw'> will just include the given text as is. I was too lazy to
1000 implement C<include> properly.
1002 Also an C<if> attribute is supported, so that tabs can be suppressed based on
1003 some occasion. In this case the supplied block won't even get processed, and
1004 the resulting tab will get ignored by C<tabbed>:
1006 L.tab('Awesome tab wih much info', '_much_info.html', if => SELF.wants_all)
1008 =item C<truncate $text, %params>
1010 Returns the C<$text> truncated after a certain number of
1013 The number of characters to truncate at is determined by the parameter
1014 C<at> which defaults to 50. If the text is longer than C<$params{at}>
1015 then it will be truncated and postfixed with '...'. Otherwise it will
1016 be returned unmodified.
1020 =head1 MODULE AUTHORS
1022 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
1024 L<http://linet-services.de>