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};
56 my ($method, @args) = @_;
58 my $presenter = $::request->presenter;
60 return '' unless $presenter->can($method);
62 splice @args, -1, 1, %{ $args[-1] } if @args && (ref($args[-1]) eq 'HASH');
64 $presenter->$method(@args);
71 $name =~ s/[^\w_]/_/g;
78 my ($self, @slurp) = @_;
79 my %options = _hashify(@slurp);
82 while (my ($name, $value) = each %options) {
84 next if $_valueless_attributes{$name} && !$value;
85 $value = '' if !defined($value);
86 push @result, $_valueless_attributes{$name} ? _H($name) : _H($name) . '="' . _H($value) . '"';
89 return @result ? ' ' . join(' ', @result) : '';
93 my ($self, $tag, $content, @slurp) = @_;
94 my $attributes = $self->attributes(@slurp);
96 return "<${tag}${attributes}>" unless defined($content);
97 return "<${tag}${attributes}>${content}</${tag}>";
101 my ($self, @slurp) = @_;
102 my %options = _hashify(@slurp);
104 $options{alt} ||= '';
106 return $self->html_tag('img', undef, %options);
112 my $collection = shift;
113 my %attributes = _hashify(@_);
115 $attributes{id} ||= $self->name_to_id($name);
117 my $value_key = delete($attributes{value_key}) || 'id';
118 my $title_key = delete($attributes{title_key}) || $value_key;
119 my $default_key = delete($attributes{default_key}) || 'selected';
122 my $value_title_sub = delete($attributes{value_title_sub});
124 my $value_sub = delete($attributes{value_sub});
125 my $title_sub = delete($attributes{title_sub});
126 my $default_sub = delete($attributes{default_sub});
128 my $with_empty = delete($attributes{with_empty});
129 my $empty_title = delete($attributes{empty_title});
133 if ( ref($attributes{default}) eq 'ARRAY' ) {
135 foreach my $entry (@{$attributes{default}}) {
136 $selected{$entry} = 1;
138 } elsif ( defined($attributes{default}) ) {
139 $selected{$attributes{default}} = 1;
142 delete($attributes{default});
148 push(@options, [undef, $empty_title || '']);
151 my $normalize_entry = sub {
153 my ($type, $entry, $sub, $key) = @_;
156 return $sub->($entry);
159 my $ref = ref($entry);
163 if ( $type eq 'value' || $type eq 'title' ) {
170 if ( $ref eq 'ARRAY' ) {
172 if ( $type eq 'value' ) {
176 if ( $type eq 'title' ) {
183 if ( $ref eq 'HASH' ) {
184 return $entry->{$key};
187 if ( $type ne 'default' || $entry->can($key) ) {
194 foreach my $entry ( @{ $collection } ) {
198 if ( $value_title_sub ) {
199 ($value, $title) = @{ $value_title_sub->($entry) };
202 $value = $normalize_entry->('value', $entry, $value_sub, $value_key);
203 $title = $normalize_entry->('title', $entry, $title_sub, $title_key);
206 my $default = $normalize_entry->('default', $entry, $default_sub, $default_key);
208 push(@options, [$value, $title, $default]);
211 foreach my $entry (@options) {
212 if ( exists($selected{$entry->[0]}) ) {
219 foreach my $entry (@options) {
220 my %args = (value => $entry->[0]);
222 $args{selected} = $entry->[2];
224 $code .= $self->html_tag('option', _H($entry->[1]), %args);
227 $code = $self->html_tag('select', $code, %attributes, name => $name);
233 my ($self, $name, $content, @slurp) = @_;
234 my %attributes = _hashify(@slurp);
236 $attributes{id} ||= $self->name_to_id($name);
237 $attributes{rows} *= 1; # required by standard
238 $attributes{cols} *= 1; # required by standard
239 $content = $content ? _H($content) : '';
241 return $self->html_tag('textarea', $content, %attributes, name => $name);
245 my ($self, $name, @slurp) = @_;
246 my %attributes = _hashify(@slurp);
248 $attributes{id} ||= $self->name_to_id($name);
249 $attributes{value} = 1 unless defined $attributes{value};
250 my $label = delete $attributes{label};
251 my $checkall = delete $attributes{checkall};
253 if ($attributes{checked}) {
254 $attributes{checked} = 'checked';
256 delete $attributes{checked};
259 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'checkbox');
260 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
261 $code .= $self->javascript(qq|\$('#$attributes{id}').checkall('$checkall');|) if $checkall;
266 sub radio_button_tag {
269 my %attributes = _hashify(@_);
271 $attributes{value} = 1 unless defined $attributes{value};
272 $attributes{id} ||= $self->name_to_id($name . "_" . $attributes{value});
273 my $label = delete $attributes{label};
275 if ($attributes{checked}) {
276 $attributes{checked} = 'checked';
278 delete $attributes{checked};
281 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'radio');
282 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
288 my ($self, $name, $value, @slurp) = @_;
289 my %attributes = _hashify(@slurp);
291 $attributes{id} ||= $self->name_to_id($name);
292 $attributes{type} ||= 'text';
294 return $self->html_tag('input', undef, %attributes, name => $name, value => $value);
298 my ($self, $name, $value, @slurp) = @_;
299 return $self->input_tag($name, $value, _hashify(@slurp), type => 'hidden');
303 my ($self, $content, @slurp) = @_;
304 return $self->html_tag('div', $content, @slurp);
308 my ($self, $content, @slurp) = @_;
309 return $self->html_tag('ul', $content, @slurp);
313 my ($self, $content, @slurp) = @_;
314 return $self->html_tag('li', $content, @slurp);
318 my ($self, $href, $content, @slurp) = @_;
319 my %params = _hashify(@slurp);
323 return $self->html_tag('a', $content, %params, href => $href);
327 my ($self, $name, $value, @slurp) = @_;
328 my %attributes = _hashify(@slurp);
330 if ( $attributes{confirm} ) {
331 $attributes{onclick} = 'return confirm("'. _J(delete($attributes{confirm})) .'");';
334 return $self->input_tag($name, $value, %attributes, type => 'submit', class => 'submit');
338 my ($self, $onclick, $value, @slurp) = @_;
339 my %attributes = _hashify(@slurp);
341 $attributes{id} ||= $self->name_to_id($attributes{name}) if $attributes{name};
342 $attributes{type} ||= 'button';
344 return $self->html_tag('input', undef, %attributes, value => $value, onclick => $onclick);
348 my ($self, $name, $value) = splice @_, 0, 3;
349 my %attributes = _hashify(@_);
351 return $self->select_tag($name, [ [ 1 => $::locale->text('Yes') ], [ 0 => $::locale->text('No') ] ], default => $value ? 1 : 0, %attributes);
355 my ($self, $data) = @_;
356 return $self->html_tag('script', $data, type => 'text/javascript');
363 foreach my $file (@_) {
364 $file .= '.css' unless $file =~ m/\.css$/;
365 $file = "css/${file}" unless $file =~ m|/|;
367 $code .= qq|<link rel="stylesheet" href="${file}" type="text/css" media="screen" />|;
373 my $date_tag_id_idx = 0;
375 my ($self, $name, $value, @slurp) = @_;
377 my %params = _hashify(@slurp);
378 my $id = $self->name_to_id($name) . _tag_id();
379 my @onchange = $params{onchange} ? (onChange => delete $params{onchange}) : ();
380 my @class = $params{no_cal} || $params{readonly} ? () : (class => 'datepicker');
382 return $self->input_tag(
383 $name, blessed($value) ? $value->to_lxoffice : $value,
386 onblur => "check_right_date_format(this);",
392 sub customer_picker {
393 my ($self, $name, $value, %params) = @_;
394 my $name_e = _H($name);
396 $::request->{layout}->add_javascripts('autocomplete_customer.js');
398 $self->hidden_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => 'customer_autocomplete') .
399 $self->input_tag("$name_e\_name", (ref $value && $value->can('name')) ? $value->name : '', %params);
402 # simple version with select_tag
403 sub vendor_selector {
404 my ($self, $name, $value, %params) = @_;
406 my $actual_vendor_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"}) ? $::form->{"$name"}->id : $::form->{"$name"}) :
407 (ref $value && $value->can('id')) ? $value->id : '';
409 return $self->select_tag($name, SL::DB::Manager::Vendor->get_all(),
410 default => $actual_vendor_id,
411 title_sub => sub { $_[0]->vendornumber . " : " . $_[0]->name },
417 # simple version with select_tag
419 my ($self, $name, $value, %params) = @_;
421 my $actual_part_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"})? $::form->{"$name"}->id : $::form->{"$name"}) :
422 (ref $value && $value->can('id')) ? $value->id : '';
424 return $self->select_tag($name, SL::DB::Manager::Part->get_all(),
425 default => $actual_part_id,
426 title_sub => sub { $_[0]->partnumber . " : " . $_[0]->description },
436 foreach my $file (@_) {
437 $file .= '.js' unless $file =~ m/\.js$/;
438 $file = "js/${file}" unless $file =~ m|/|;
440 $code .= qq|<script type="text/javascript" src="${file}"></script>|;
447 my ($self, $tabs, @slurp) = @_;
448 my %params = _hashify(@slurp);
449 my $id = $params{id} || 'tab_' . _tag_id();
451 $params{selected} *= 1;
453 die 'L.tabbed needs an arrayred of tabs for first argument'
454 unless ref $tabs eq 'ARRAY';
456 my (@header, @blocks);
457 for my $i (0..$#$tabs) {
458 my $tab = $tabs->[$i];
462 my $tab_id = "__tab_id_$i";
463 push @header, $self->li_tag($self->link('#' . $tab_id, $tab->{name}));
464 push @blocks, $self->div_tag($tab->{data}, id => $tab_id);
467 return '' unless @header;
469 my $ul = $self->ul_tag(join('', @header), id => $id);
470 return $self->div_tag(join('', $ul, @blocks), class => 'tabwidget');
474 my ($self, $name, $src, @slurp) = @_;
475 my %params = _hashify(@slurp);
477 $params{method} ||= 'process';
479 return () if defined $params{if} && !$params{if};
482 if ($params{method} eq 'raw') {
484 } elsif ($params{method} eq 'process') {
485 $data = $self->_context->process($src, %{ $params{args} || {} });
487 die "unknown tag method '$params{method}'";
490 return () unless $data;
492 return +{ name => $name, data => $data };
496 my ($self, $name, $value, @slurp) = @_;
497 my %attributes = _hashify(@slurp);
500 my $min = delete $attributes{min_rows} || 1;
502 if (exists $attributes{cols}) {
503 $cols = delete $attributes{cols};
504 $rows = $::form->numtextrows($value, $cols);
506 $rows = delete $attributes{rows} || 1;
510 ? $self->textarea_tag($name, $value, %attributes, rows => max($rows, $min), ($cols ? (cols => $cols) : ()))
511 : $self->input_tag($name, $value, %attributes, ($cols ? (size => $cols) : ()));
514 sub multiselect2side {
515 my ($self, $id, @slurp) = @_;
516 my %params = _hashify(@slurp);
518 $params{labelsx} = "\"" . _J($params{labelsx} || $::locale->text('Available')) . "\"";
519 $params{labeldx} = "\"" . _J($params{labeldx} || $::locale->text('Selected')) . "\"";
520 $params{moveOptions} = 'false';
522 my $vars = join(', ', map { "${_}: " . $params{$_} } keys %params);
524 <script type="text/javascript">
525 \$().ready(function() {
526 \$('#${id}').multiselect2side({ ${vars} });
534 sub sortable_element {
535 my ($self, $selector, @slurp) = @_;
536 my %params = _hashify(@slurp);
538 my %attributes = ( distance => 5,
539 helper => <<'JAVASCRIPT' );
540 function(event, ui) {
541 ui.children().each(function() {
542 $(this).width($(this).width());
550 if ($params{url} && $params{with}) {
551 my $as = $params{as} || $params{with};
552 my $filter = ".filter(function(idx) { return this.substr(0, " . length($params{with}) . ") == '$params{with}'; })";
553 $filter .= ".map(function(idx, str) { return str.replace('$params{with}_', ''); })";
555 $stop_event = <<JAVASCRIPT;
556 \$.post('$params{url}', { '${as}[]': \$(\$('${selector}').sortable('toArray'))${filter}.toArray() });
560 if (!$params{dont_recolor}) {
561 $stop_event .= <<JAVASCRIPT;
562 \$('${selector}>*:odd').removeClass('listrow1').removeClass('listrow0').addClass('listrow0');
563 \$('${selector}>*:even').removeClass('listrow1').removeClass('listrow0').addClass('listrow1');
568 $attributes{stop} = <<JAVASCRIPT;
569 function(event, ui) {
576 $params{handle} = '.dragdrop' unless exists $params{handle};
577 $attributes{handle} = "'$params{handle}'" if $params{handle};
579 my $attr_str = join(', ', map { "${_}: $attributes{$_}" } keys %attributes);
581 my $code = <<JAVASCRIPT;
582 <script type="text/javascript">
584 \$( "${selector}" ).sortable({ ${attr_str} })
592 sub online_help_tag {
593 my ($self, $tag, @slurp) = @_;
594 my %params = _hashify(@slurp);
595 my $cc = $::myconfig{countrycode};
596 my $file = "doc/online/$cc/$tag.html";
597 my $text = $params{text} || $::locale->text('Help');
599 die 'malformed help tag' unless $tag =~ /^[a-zA-Z0-9_]+$/;
600 return unless -f $file;
601 return $self->html_tag('a', $text, href => $file, class => 'jqModal')
606 require Data::Dumper;
607 return '<pre>' . Data::Dumper::Dumper(@_) . '</pre>';
612 return _call_presenter('truncate', @_);
615 sub sortable_table_header {
616 my ($self, $by, @slurp) = @_;
617 my %params = _hashify(@slurp);
619 my $controller = $self->{CONTEXT}->stash->get('SELF');
620 my $sort_spec = $controller->get_sort_spec;
621 my $by_spec = $sort_spec->{$by};
622 my %current_sort_params = $controller->get_current_sort_params;
623 my ($image, $new_dir) = ('', $current_sort_params{dir});
624 my $title = delete($params{title}) || $::locale->text($by_spec->{title});
626 if ($current_sort_params{by} eq $by) {
627 my $current_dir = $current_sort_params{dir} ? 'up' : 'down';
628 $image = '<img border="0" src="image/' . $current_dir . '.png">';
629 $new_dir = 1 - ($current_sort_params{dir} || 0);
632 $params{ $sort_spec->{FORM_PARAMS}->[0] } = $by;
633 $params{ $sort_spec->{FORM_PARAMS}->[1] } = ($new_dir ? '1' : '0');
635 return '<a href="' . $controller->get_callback(%params) . '">' . _H($title) . $image . '</a>';
638 sub paginate_controls {
641 my $controller = $self->{CONTEXT}->stash->get('SELF');
642 my $paginate_spec = $controller->get_paginate_spec;
643 my %paginate_params = $controller->get_current_paginate_params;
645 my %template_params = (
646 pages => \%paginate_params,
648 my %url_params = _hashify(@_);
649 $url_params{ $paginate_spec->{FORM_PARAMS}->[0] } = delete $url_params{page};
650 $url_params{ $paginate_spec->{FORM_PARAMS}->[1] } = delete $url_params{per_page} if exists $url_params{per_page};
652 return $controller->get_callback(%url_params);
656 return SL::Presenter->get->render('common/paginate', %template_params);
661 return _call_presenter('simple_format', @_);
670 SL::Templates::Plugin::L -- Layouting / tag generation
674 Usage from a template:
678 [% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right', 1 ] ]) %]
680 [% L.select_tag('direction', [ { direction => 'left', display => 'To the left' },
681 { direction => 'right', display => 'To the right' } ],
682 value_key => 'direction', title_key => 'display', default => 'right')) %]
684 [% L.select_tag('direction', [ { direction => 'left', display => 'To the left' },
685 { direction => 'right', display => 'To the right', selected => 1 } ],
686 value_key => 'direction', title_key => 'display')) %]
690 A module modeled a bit after Rails' ActionView helpers. Several small
691 functions that create HTML tags from various kinds of data sources.
695 =head2 LOW-LEVEL FUNCTIONS
699 =item C<name_to_id $name>
701 Converts a name to a HTML id by replacing various characters.
703 =item C<attributes %items>
705 Creates a string from all elements in C<%items> suitable for usage as
706 HTML tag attributes. Keys and values are HTML escaped even though keys
707 must not contain non-ASCII characters for browsers to accept them.
709 =item C<html_tag $tag_name, $content_string, %attributes>
711 Creates an opening and closing HTML tag for C<$tag_name> and puts
712 C<$content_string> between the two. If C<$content_string> is undefined
713 or empty then only a E<lt>tag/E<gt> tag will be created. Attributes
714 are key/value pairs added to the opening tag.
716 C<$content_string> is not HTML escaped.
720 =head2 HIGH-LEVEL FUNCTIONS
724 =item C<select_tag $name, \@collection, %attributes>
726 Creates a HTML 'select' tag named C<$name> with the contents of one
727 'E<lt>optionE<gt>' tag for each element in C<\@collection> and with arbitrary
728 HTML attributes from C<%attributes>. The value
729 to use and the title to display are extracted from the elements in
730 C<\@collection>. Each element can be one of four things:
734 =item 1. An array reference with at least two elements. The first element is
735 the value, the second element is its title. The third element is optional and and should contain a boolean.
736 If it is true, than the element will be used as default.
738 =item 2. A scalar. The scalar is both the value and the title.
740 =item 3. A hash reference. In this case C<%attributes> must contain
741 I<value_key>, I<title_key> and may contain I<default_key> keys that name the keys in the element to use
742 for the value, title and default respectively.
744 =item 4. A blessed reference. In this case C<%attributes> must contain
745 I<value_key>, I<title_key> and may contain I<default_key> keys that name functions called on the blessed
746 reference whose return values are used as the value, title and default
751 For cases 3 and 4 C<$attributes{value_key}> defaults to C<id>,
752 C<$attributes{title_key}> defaults to C<$attributes{value_key}>
753 and C<$attributes{default_key}> defaults to C<selected>.
755 In addition to pure keys/method you can also provide coderefs as I<value_sub>
756 and/or I<title_sub> and/or I<default_sub>. If present, these take precedence over keys or methods,
757 and are called with the element as first argument. It must return the value, title or default.
759 Lastly a joint coderef I<value_title_sub> may be provided, which in turn takes
760 precedence over the C<value_sub> and C<title_sub> subs. It will only be called once for each
761 element and must return a list of value and title.
763 If the option C<with_empty> is set then an empty element (value
764 C<undef>) will be used as the first element. The title to display for
765 this element can be set with the option C<empty_title> and defaults to
768 The option C<default> can be either a scalar or an array reference
769 containing the values of the options which should be set to be
772 The tag's C<id> defaults to C<name_to_id($name)>.
774 =item C<yes_no_tag $name, $value, %attributes>
776 Creates a HTML 'select' tag with the two entries C<yes> and C<no> by
777 calling L<select_tag>. C<$value> determines
778 which entry is selected. The C<%attributes> are passed through to
781 =item C<input_tag $name, $value, %attributes>
783 Creates a HTML 'input type=text' tag named C<$name> with the value
784 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
785 tag's C<id> defaults to C<name_to_id($name)>.
787 =item C<hidden_tag $name, $value, %attributes>
789 Creates a HTML 'input type=hidden' tag named C<$name> with the value
790 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
791 tag's C<id> defaults to C<name_to_id($name)>.
793 =item C<submit_tag $name, $value, %attributes>
795 Creates a HTML 'input type=submit class=submit' tag named C<$name> with the
796 value C<$value> and with arbitrary HTML attributes from C<%attributes>. The
797 tag's C<id> defaults to C<name_to_id($name)>.
799 If C<$attributes{confirm}> is set then a JavaScript popup dialog will
800 be added via the C<onclick> handler asking the question given with
801 C<$attributes{confirm}>. If request is only submitted if the user
802 clicks the dialog's ok/yes button.
804 =item C<textarea_tag $name, $value, %attributes>
806 Creates a HTML 'textarea' tag named C<$name> with the content
807 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
808 tag's C<id> defaults to C<name_to_id($name)>.
810 =item C<checkbox_tag $name, %attributes>
812 Creates a HTML 'input type=checkbox' tag named C<$name> with arbitrary
813 HTML attributes from C<%attributes>. The tag's C<id> defaults to
814 C<name_to_id($name)>. The tag's C<value> defaults to C<1>.
816 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
817 created with said C<label>. No attribute named C<label> is created in
820 If C<%attributes> contains a key C<checkall> then the value is taken as a
821 JQuery selector and clicking this checkbox will also toggle all checkboxes
822 matching the selector.
824 =item C<date_tag $name, $value, %attributes>
826 Creates a date input field, with an attached javascript that will open a
829 =item C<radio_button_tag $name, %attributes>
831 Creates a HTML 'input type=radio' tag named C<$name> with arbitrary
832 HTML attributes from C<%attributes>. The tag's C<value> defaults to
833 C<1>. The tag's C<id> defaults to C<name_to_id($name . "_" . $value)>.
835 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
836 created with said C<label>. No attribute named C<label> is created in
839 =item C<javascript_tag $file1, $file2, $file3...>
841 Creates a HTML 'E<lt>script type="text/javascript" src="..."E<gt>'
842 tag for each file name parameter passed. Each file name will be
843 postfixed with '.js' if it isn't already and prefixed with 'js/' if it
844 doesn't contain a slash.
846 =item C<stylesheet_tag $file1, $file2, $file3...>
848 Creates a HTML 'E<lt>link rel="text/stylesheet" href="..."E<gt>' tag
849 for each file name parameter passed. Each file name will be postfixed
850 with '.css' if it isn't already and prefixed with 'css/' if it doesn't
853 =item C<tabbed \@tab, %attributes>
855 Will create a tabbed area. The tabs should be created with the helper function
859 L.tab(LxERP.t8('Basic Data'), 'part/_main_tab.html'),
860 L.tab(LxERP.t8('Custom Variables'), 'part/_cvar_tab.html', if => SELF.display_cvar_tab),
863 =item C<areainput_tag $name, $content, %PARAMS>
865 Creates a generic input tag or textarea tag, depending on content size. The
866 amount of desired rows must be either given with the C<rows> parameter or can
867 be computed from the value and the C<cols> paramter, Accepted parameters
868 include C<min_rows> for rendering a minimum of rows if a textarea is displayed.
870 You can force input by setting rows to 1, and you can force textarea by setting
873 =item C<multiselect2side $id, %params>
875 Creates a JavaScript snippet calling the jQuery function
876 C<multiselect2side> on the select control with the ID C<$id>. The
877 select itself is not created. C<%params> can contain the following
884 The label of the list of available options. Defaults to the
885 translation of 'Available'.
889 The label of the list of selected options. Defaults to the
890 translation of 'Selected'.
894 =item C<sortable_element $selector, %params>
896 Makes the children of the DOM element C<$selector> (a jQuery selector)
897 sortable with the I<jQuery UI Selectable> library. The children can be
898 dragged & dropped around. After dropping an element an URL can be
899 postet to with the element IDs of the sorted children.
901 If this is used then the JavaScript file C<js/jquery-ui.js> must be
902 included manually as well as it isn't loaded via C<$::form-gt;header>.
904 C<%params> can contain the following entries:
910 The URL to POST an AJAX request to after a dragged element has been
911 dropped. The AJAX request's return value is ignored. If given then
912 C<$params{with}> must be given as well.
916 A string that is interpreted as the prefix of the children's ID. Upon
917 POSTing the result each child whose ID starts with C<$params{with}> is
918 considered. The prefix and the following "_" is removed from the
919 ID. The remaining parts of the IDs of those children are posted as a
920 single array parameter. The array parameter's name is either
921 C<$params{as}> or, missing that, C<$params{with}>.
925 Sets the POST parameter name for AJAX request after dropping an
926 element (see C<$params{with}>).
930 An optional jQuery selector specifying which part of the child element
931 is dragable. If the parameter is not given then it defaults to
932 C<.dragdrop> matching DOM elements with the class C<dragdrop>. If the
933 parameter is set and empty then the whole child element is dragable,
934 and clicks through to underlying elements like inputs or links might
937 =item C<dont_recolor>
939 If trueish then the children will not be recolored. The default is to
940 recolor the children by setting the class C<listrow0> on odd and
941 C<listrow1> on even entries.
947 <script type="text/javascript" src="js/jquery-ui.js"></script>
949 <table id="thing_list">
951 <tr><td>This</td><td>That</td></tr>
954 <tr id="thingy_2"><td>stuff</td><td>more stuff</td></tr>
955 <tr id="thingy_15"><td>stuff</td><td>more stuff</td></tr>
956 <tr id="thingy_6"><td>stuff</td><td>more stuff</td></tr>
960 [% L.sortable_element('#thing_list tbody',
961 url => 'controller.pl?action=SystemThings/reorder',
964 recolor_rows => 1) %]
966 After dropping e.g. the third element at the top of the list a POST
967 request would be made to the C<reorder> action of the C<SystemThings>
968 controller with a single parameter called C<thing_ids> -- an array
969 containing the values C<[ 6, 2, 15 ]>.
973 Dumps the Argument using L<Data::Dumper> into a E<lt>preE<gt> block.
975 =item C<sortable_table_header $by, %params>
977 Create a link and image suitable for placement in a table
978 header. C<$by> must be an index set up by the controller with
979 L<SL::Controller::Helper::make_sorted>.
981 The optional parameter C<$params{title}> can override the column title
982 displayed to the user. Otherwise the column title from the
983 controller's sort spec is used.
985 The other parameters in C<%params> are passed unmodified to the
986 underlying call to L<SL::Controller::Base::url_for>.
988 See the documentation of L<SL::Controller::Helper::Sorted> for an
989 overview and further usage instructions.
991 =item C<paginate_controls>
993 Create a set of links used to paginate a list view.
995 See the documentation of L<SL::Controller::Helper::Paginated> for an
996 overview and further usage instructions.
1000 =head2 CONVERSION FUNCTIONS
1004 =item C<tab, description, target, %PARAMS>
1006 Creates a tab for C<tabbed>. The description will be used as displayed name.
1007 The target should be a block or template that can be processed. C<tab> supports
1008 a C<method> parameter, which can override the process method to apply target.
1009 C<method => 'raw'> will just include the given text as is. I was too lazy to
1010 implement C<include> properly.
1012 Also an C<if> attribute is supported, so that tabs can be suppressed based on
1013 some occasion. In this case the supplied block won't even get processed, and
1014 the resulting tab will get ignored by C<tabbed>:
1016 L.tab('Awesome tab wih much info', '_much_info.html', if => SELF.wants_all)
1018 =item C<truncate $text, [%params]>
1020 See L<SL::Presenter::Text/truncate>.
1022 =item C<simple_format $text>
1024 See L<SL::Presenter::Text/simple_format>.
1028 =head1 MODULE AUTHORS
1030 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
1032 L<http://linet-services.de>