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);
32 $string =~ s/(\"|\'|\\)/\\$1/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 $collection = shift;
90 my %attributes = _hashify(@_);
92 $attributes{id} ||= $self->name_to_id($name);
94 my $value_key = delete($attributes{value_key}) || 'id';
95 my $title_key = delete($attributes{title_key}) || $value_key;
96 my $default_key = delete($attributes{default_key}) || 'selected';
99 my $value_title_sub = delete($attributes{value_title_sub});
101 my $value_sub = delete($attributes{value_sub});
102 my $title_sub = delete($attributes{title_sub});
103 my $default_sub = delete($attributes{default_sub});
108 if ( ref($attributes{default}) eq 'ARRAY' ) {
110 foreach my $entry (@{$attributes{default}}) {
111 $selected{$entry} = 1;
113 } elsif ( defined($attributes{default}) ) {
114 $selected{$attributes{default}} = 1;
117 delete($attributes{default});
122 if ( delete($attributes{with_empty}) ) {
123 push(@options, [undef, $attributes{empty_title} || '']);
126 my $normalize_entry = sub {
128 my ($type, $entry, $sub, $key) = @_;
131 return $sub->($entry);
134 my $ref = ref($entry);
138 if ( $type eq 'value' || $type eq 'title' ) {
145 if ( $ref eq 'ARRAY' ) {
147 if ( $type eq 'value' ) {
151 if ( $type eq 'title' ) {
158 if ( $ref eq 'HASH' ) {
159 return $entry->{$key};
162 if ( $type ne 'default' || $entry->can($key) ) {
169 foreach my $entry ( @{ $collection } ) {
173 if ( $value_title_sub ) {
174 ($value, $title) = $value_title_sub->($entry);
177 $value = $normalize_entry->('value', $entry, $value_sub, $value_key);
178 $title = $normalize_entry->('title', $entry, $title_sub, $title_key);
181 my $default = $normalize_entry->('default', $entry, $default_sub, $default_key);
183 push(@options, [$value, $title, $default]);
186 foreach my $entry (@options) {
187 if ( exists($selected{$entry->[0]}) ) {
194 foreach my $entry (@options) {
195 my %args = (value => $entry->[0]);
197 $args{selected} = $entry->[2];
199 $code .= $self->html_tag('option', _H($entry->[1]), %args);
202 $code = $self->html_tag('select', $code, %attributes, name => $name);
208 my ($self, $name, $content, @slurp) = @_;
209 my %attributes = _hashify(@slurp);
211 $attributes{id} ||= $self->name_to_id($name);
212 $attributes{rows} *= 1; # required by standard
213 $attributes{cols} *= 1; # required by standard
214 $content = $content ? _H($content) : '';
216 return $self->html_tag('textarea', $content, %attributes, name => $name);
220 my ($self, $name, @slurp) = @_;
221 my %attributes = _hashify(@slurp);
223 $attributes{id} ||= $self->name_to_id($name);
224 $attributes{value} = 1 unless defined $attributes{value};
225 my $label = delete $attributes{label};
226 my $checkall = delete $attributes{checkall};
228 if ($attributes{checked}) {
229 $attributes{checked} = 'checked';
231 delete $attributes{checked};
234 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'checkbox');
235 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
236 $code .= $self->javascript(qq|\$('#$attributes{id}').checkall('$checkall');|) if $checkall;
241 sub radio_button_tag {
244 my %attributes = _hashify(@_);
246 $attributes{value} = 1 unless defined $attributes{value};
247 $attributes{id} ||= $self->name_to_id($name . "_" . $attributes{value});
248 my $label = delete $attributes{label};
250 if ($attributes{checked}) {
251 $attributes{checked} = 'checked';
253 delete $attributes{checked};
256 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'radio');
257 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
263 my ($self, $name, $value, @slurp) = @_;
264 my %attributes = _hashify(@slurp);
266 $attributes{id} ||= $self->name_to_id($name);
267 $attributes{type} ||= 'text';
269 return $self->html_tag('input', undef, %attributes, name => $name, value => $value);
273 return shift->input_tag(@_, type => 'hidden');
277 my ($self, $content, @slurp) = @_;
278 return $self->html_tag('div', $content, @slurp);
282 my ($self, $content, @slurp) = @_;
283 return $self->html_tag('ul', $content, @slurp);
287 my ($self, $content, @slurp) = @_;
288 return $self->html_tag('li', $content, @slurp);
292 my ($self, $href, $content, @slurp) = @_;
293 my %params = _hashify(@slurp);
297 return $self->html_tag('a', $content, %params, href => $href);
301 my ($self, $name, $value, @slurp) = @_;
302 my %attributes = _hashify(@slurp);
304 if ( $attributes{confirm} ) {
305 $attributes{onclick} = 'return confirm("'. _J(delete($attributes{confirm})) .'");';
308 return $self->input_tag($name, $value, %attributes, type => 'submit', class => 'submit');
312 my ($self, $onclick, $value, @slurp) = @_;
313 my %attributes = _hashify(@slurp);
315 $attributes{id} ||= $self->name_to_id($attributes{name}) if $attributes{name};
316 $attributes{type} ||= 'button';
318 return $self->html_tag('input', undef, %attributes, value => $value, onclick => $onclick);
322 my ($self, $name, $value) = splice @_, 0, 3;
323 my %attributes = _hashify(@_);
325 return $self->select_tag($name, [ [ 1 => $::locale->text('Yes') ], [ 0 => $::locale->text('No') ] ], default => $value ? 1 : 0, %attributes);
329 my ($self, $data) = @_;
330 return $self->html_tag('script', $data, type => 'text/javascript');
337 foreach my $file (@_) {
338 $file .= '.css' unless $file =~ m/\.css$/;
339 $file = "css/${file}" unless $file =~ m|/|;
341 $code .= qq|<link rel="stylesheet" href="${file}" type="text/css" media="screen" />|;
348 my ($self, $name, $value, @slurp) = @_;
349 my %params = _hashify(@slurp);
350 my $name_e = _H($name);
352 my $datefmt = apply {
356 } $::myconfig{"dateformat"};
358 my $cal_align = delete $params{cal_align} || 'BR';
359 my $onchange = delete $params{onchange};
360 my $str_value = blessed $value ? $value->to_lxoffice : $value;
362 $self->input_tag($name, $str_value,
365 title => _H($::myconfig{dateformat}),
366 onBlur => 'check_right_date_format(this)',
368 onChange => $onchange,
371 ) . ((!$params{no_cal} && !$params{readonly}) ?
372 $self->html_tag('img', undef,
373 src => 'image/calendar.png',
374 alt => $::locale->text('Calendar'),
376 title => _H($::myconfig{dateformat}),
380 "Calendar.setup({ inputField: '$name_e', ifFormat: '$datefmt', align: '$cal_align', button: 'trigger$seq' });"
384 sub customer_picker {
385 my ($self, $name, $value, %params) = @_;
386 my $name_e = _H($name);
388 $self->hidden_tag($name, (ref $value && $value->can('id')) ? $value->id : '') .
389 $self->input_tag("$name_e\_name", (ref $value && $value->can('name')) ? $value->name : '', %params) .
390 $self->javascript(<<JS);
391 function autocomplete_customer (selector, column) {
392 \$(function(){ \$(selector).autocomplete({
393 source: function(req, rsp) {
395 url: 'controller.pl?action=Customer/ajax_autocomplete',
400 current: function() { \$('#$name_e').val() },
403 success: function (data){ rsp(data) }
408 select: function(event, ui) {
409 \$('#$name_e').val(ui.item.id);
410 \$('#$name_e\_name').val(ui.item.name);
414 autocomplete_customer('#$name_e\_name');
418 # simple version with select_tag
419 sub vendor_selector {
420 my ($self, $name, $value, %params) = @_;
422 my $actual_vendor_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"}) ? $::form->{"$name"}->id : $::form->{"$name"}) :
423 (ref $value && $value->can('id')) ? $value->id : '';
425 return $self->select_tag($name, SL::DB::Manager::Vendor->get_all(),
426 default => $actual_vendor_id,
427 title_sub => sub { $_[0]->vendornumber . " : " . $_[0]->name },
433 # simple version with select_tag
435 my ($self, $name, $value, %params) = @_;
437 my $actual_part_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"})? $::form->{"$name"}->id : $::form->{"$name"}) :
438 (ref $value && $value->can('id')) ? $value->id : '';
440 return $self->select_tag($name, SL::DB::Manager::Part->get_all(),
441 default => $actual_part_id,
442 title_sub => sub { $_[0]->partnumber . " : " . $_[0]->description },
452 foreach my $file (@_) {
453 $file .= '.js' unless $file =~ m/\.js$/;
454 $file = "js/${file}" unless $file =~ m|/|;
456 $code .= qq|<script type="text/javascript" src="${file}"></script>|;
463 my ($self, $tabs, @slurp) = @_;
464 my %params = _hashify(@slurp);
465 my $id = $params{id} || 'tab_' . _tag_id();
467 $params{selected} *= 1;
469 die 'L.tabbed needs an arrayred of tabs for first argument'
470 unless ref $tabs eq 'ARRAY';
472 my (@header, @blocks);
473 for my $i (0..$#$tabs) {
474 my $tab = $tabs->[$i];
478 my $selected = $params{selected} == $i;
479 my $tab_id = "__tab_id_$i";
480 push @header, $self->li_tag(
481 $self->link('', $tab->{name}, rel => $tab_id),
482 ($selected ? (class => 'selected') : ())
484 push @blocks, $self->div_tag($tab->{data},
485 id => $tab_id, class => 'tabcontent');
488 return '' unless @header;
489 return $self->ul_tag(
490 join('', @header), id => $id, class => 'shadetabs'
493 join('', @blocks), class => 'tabcontentstyle'
496 qq|var $id = new ddtabcontent("$id");$id.setpersist(true);| .
497 qq|$id.setselectedClassTarget("link");$id.init();|
502 my ($self, $name, $src, @slurp) = @_;
503 my %params = _hashify(@slurp);
505 $params{method} ||= 'process';
507 return () if defined $params{if} && !$params{if};
510 if ($params{method} eq 'raw') {
512 } elsif ($params{method} eq 'process') {
513 $data = $self->_context->process($src, %{ $params{args} || {} });
515 die "unknown tag method '$params{method}'";
518 return () unless $data;
520 return +{ name => $name, data => $data };
524 my ($self, $name, $value, @slurp) = @_;
525 my %attributes = _hashify(@slurp);
528 my $min = delete $attributes{min_rows} || 1;
530 if (exists $attributes{cols}) {
531 $cols = delete $attributes{cols};
532 $rows = $::form->numtextrows($value, $cols);
534 $rows = delete $attributes{rows} || 1;
538 ? $self->textarea_tag($name, $value, %attributes, rows => max($rows, $min), ($cols ? (cols => $cols) : ()))
539 : $self->input_tag($name, $value, %attributes, ($cols ? (size => $cols) : ()));
542 sub multiselect2side {
543 my ($self, $id, @slurp) = @_;
544 my %params = _hashify(@slurp);
546 $params{labelsx} = "\"" . _J($params{labelsx} || $::locale->text('Available')) . "\"";
547 $params{labeldx} = "\"" . _J($params{labeldx} || $::locale->text('Selected')) . "\"";
548 $params{moveOptions} = 'false';
550 my $vars = join(', ', map { "${_}: " . $params{$_} } keys %params);
552 <script type="text/javascript">
553 \$().ready(function() {
554 \$('#${id}').multiselect2side({ ${vars} });
562 sub sortable_element {
563 my ($self, $selector, @slurp) = @_;
564 my %params = _hashify(@slurp);
566 my %attributes = ( distance => 5,
567 helper => <<'JAVASCRIPT' );
568 function(event, ui) {
569 ui.children().each(function() {
570 $(this).width($(this).width());
578 if ($params{url} && $params{with}) {
579 my $as = $params{as} || $params{with};
580 my $filter = ".filter(function(idx) { return this.substr(0, " . length($params{with}) . ") == '$params{with}'; })";
581 $filter .= ".map(function(idx, str) { return str.replace('$params{with}_', ''); })";
583 $stop_event = <<JAVASCRIPT;
584 \$.post('$params{url}', { '${as}[]': \$(\$('${selector}').sortable('toArray'))${filter}.toArray() });
588 if (!$params{dont_recolor}) {
589 $stop_event .= <<JAVASCRIPT;
590 \$('${selector}>*:odd').removeClass('listrow1').removeClass('listrow0').addClass('listrow0');
591 \$('${selector}>*:even').removeClass('listrow1').removeClass('listrow0').addClass('listrow1');
596 $attributes{stop} = <<JAVASCRIPT;
597 function(event, ui) {
604 $params{handle} = '.dragdrop' unless exists $params{handle};
605 $attributes{handle} = "'$params{handle}'" if $params{handle};
607 my $attr_str = join(', ', map { "${_}: $attributes{$_}" } keys %attributes);
609 my $code = <<JAVASCRIPT;
610 <script type="text/javascript">
612 \$( "${selector}" ).sortable({ ${attr_str} })
620 sub online_help_tag {
621 my ($self, $tag, @slurp) = @_;
622 my %params = _hashify(@slurp);
623 my $cc = $::myconfig{countrycode};
624 my $file = "doc/online/$cc/$tag.html";
625 my $text = $params{text} || $::locale->text('Help');
627 die 'malformed help tag' unless $tag =~ /^[a-zA-Z0-9_]+$/;
628 return unless -f $file;
629 return $self->html_tag('a', $text, href => $file, class => 'jqModal')
634 require Data::Dumper;
635 return '<pre>' . Data::Dumper::Dumper(@_) . '</pre>';
639 my ($self, $text, @slurp) = @_;
640 my %params = _hashify(@slurp);
643 $params{at} = 3 if 3 > $params{at};
646 return $text if length($text) < $params{at};
647 return substr($text, 0, $params{at}) . '...';
650 sub sortable_table_header {
651 my ($self, $by, @slurp) = @_;
652 my %params = _hashify(@slurp);
654 my $controller = $self->{CONTEXT}->stash->get('SELF');
655 my $sort_spec = $controller->get_sort_spec;
656 my $by_spec = $sort_spec->{$by};
657 my %current_sort_params = $controller->get_current_sort_params;
658 my ($image, $new_dir) = ('', $current_sort_params{dir});
659 my $title = delete($params{title}) || $by_spec->{title};
661 if ($current_sort_params{by} eq $by) {
662 my $current_dir = $current_sort_params{dir} ? 'up' : 'down';
663 $image = '<img border="0" src="image/' . $current_dir . '.png">';
664 $new_dir = 1 - ($current_sort_params{dir} || 0);
667 $params{ $sort_spec->{FORM_PARAMS}->[0] } = $by;
668 $params{ $sort_spec->{FORM_PARAMS}->[1] } = ($new_dir ? '1' : '0');
670 return '<a href="' . $controller->get_callback(%params) . '">' . _H($title) . $image . '</a>';
679 SL::Templates::Plugin::L -- Layouting / tag generation
683 Usage from a template:
687 [% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right', 1 ] ]) %]
689 [% L.select_tag('direction', [ { direction => 'left', display => 'To the left' },
690 { direction => 'right', display => 'To the right' } ],
691 value_key => 'direction', title_key => 'display', default => 'right')) %]
693 [% L.select_tag('direction', [ { direction => 'left', display => 'To the left' },
694 { direction => 'right', display => 'To the right', selected => 1 } ],
695 value_key => 'direction', title_key => 'display')) %]
699 A module modeled a bit after Rails' ActionView helpers. Several small
700 functions that create HTML tags from various kinds of data sources.
704 =head2 LOW-LEVEL FUNCTIONS
708 =item C<name_to_id $name>
710 Converts a name to a HTML id by replacing various characters.
712 =item C<attributes %items>
714 Creates a string from all elements in C<%items> suitable for usage as
715 HTML tag attributes. Keys and values are HTML escaped even though keys
716 must not contain non-ASCII characters for browsers to accept them.
718 =item C<html_tag $tag_name, $content_string, %attributes>
720 Creates an opening and closing HTML tag for C<$tag_name> and puts
721 C<$content_string> between the two. If C<$content_string> is undefined
722 or empty then only a E<lt>tag/E<gt> tag will be created. Attributes
723 are key/value pairs added to the opening tag.
725 C<$content_string> is not HTML escaped.
729 =head2 HIGH-LEVEL FUNCTIONS
733 =item C<select_tag $name, \@collection, %attributes>
735 Creates a HTML 'select' tag named C<$name> with the contents of one
736 'E<lt>optionE<gt>' tag for each element in C<\@collection> and with arbitrary
737 HTML attributes from C<%attributes>. The value
738 to use and the title to display are extracted from the elements in
739 C<\@collection>. Each element can be one of four things:
743 =item 1. An array reference with at least two elements. The first element is
744 the value, the second element is its title. The third element is optional and and should contain a boolean.
745 If it is true, than the element will be used as default.
747 =item 2. A scalar. The scalar is both the value and the title.
749 =item 3. A hash reference. In this case C<%attributes> must contain
750 I<value_key>, I<title_key> and may contain I<default_key> keys that name the keys in the element to use
751 for the value, title and default respectively.
753 =item 4. A blessed reference. In this case C<%attributes> must contain
754 I<value_key>, I<title_key> and may contain I<default_key> keys that name functions called on the blessed
755 reference whose return values are used as the value, title and default
760 For cases 3 and 4 C<$attributes{value_key}> defaults to C<id>,
761 C<$attributes{title_key}> defaults to C<$attributes{value_key}>
762 and C<$attributes{default_key}> defaults to C<selected>.
764 In addition to pure keys/method you can also provide coderefs as I<value_sub>
765 and/or I<title_sub> and/or I<default_sub>. If present, these take precedence over keys or methods,
766 and are called with the element as first argument. It must return the value, title or default.
768 Lastly a joint coderef I<value_title_sub> may be provided, which in turn takes
769 precedence over the C<value_sub> and C<title_sub> subs. It will only be called once for each
770 element and must return a list of value and title.
772 If the option C<with_empty> is set then an empty element (value
773 C<undef>) will be used as the first element. The title to display for
774 this element can be set with the option C<empty_title> and defaults to
777 The option C<default> can be either a scalar or an array reference
778 containing the values of the options which should be set to be
781 The tag's C<id> defaults to C<name_to_id($name)>.
783 =item C<yes_no_tag $name, $value, %attributes>
785 Creates a HTML 'select' tag with the two entries C<yes> and C<no> by
786 calling L<select_tag>. C<$value> determines
787 which entry is selected. The C<%attributes> are passed through to
790 =item C<input_tag $name, $value, %attributes>
792 Creates a HTML 'input type=text' tag named C<$name> with the value
793 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
794 tag's C<id> defaults to C<name_to_id($name)>.
796 =item C<hidden_tag $name, $value, %attributes>
798 Creates a HTML 'input type=hidden' tag named C<$name> with the value
799 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
800 tag's C<id> defaults to C<name_to_id($name)>.
802 =item C<submit_tag $name, $value, %attributes>
804 Creates a HTML 'input type=submit class=submit' tag named C<$name> with the
805 value C<$value> and with arbitrary HTML attributes from C<%attributes>. The
806 tag's C<id> defaults to C<name_to_id($name)>.
808 If C<$attributes{confirm}> is set then a JavaScript popup dialog will
809 be added via the C<onclick> handler asking the question given with
810 C<$attributes{confirm}>. If request is only submitted if the user
811 clicks the dialog's ok/yes button.
813 =item C<textarea_tag $name, $value, %attributes>
815 Creates a HTML 'textarea' tag named C<$name> with the content
816 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
817 tag's C<id> defaults to C<name_to_id($name)>.
819 =item C<checkbox_tag $name, %attributes>
821 Creates a HTML 'input type=checkbox' tag named C<$name> with arbitrary
822 HTML attributes from C<%attributes>. The tag's C<id> defaults to
823 C<name_to_id($name)>. The tag's C<value> defaults to C<1>.
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 If C<%attributes> contains a key C<checkall> then the value is taken as a
830 JQuery selector and clicking this checkbox will also toggle all checkboxes
831 matching the selector.
833 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
835 Creates a date input field, with an attached javascript that will open a
836 calendar on click. The javascript ist by default anchoered at the bottom right
837 sight. This can be overridden with C<cal_align>, see Calendar documentation for
838 the details, usually you'll want a two letter abbreviation of the alignment.
839 Right + Bottom becomes C<BL>.
841 =item C<radio_button_tag $name, %attributes>
843 Creates a HTML 'input type=radio' tag named C<$name> with arbitrary
844 HTML attributes from C<%attributes>. The tag's C<value> defaults to
845 C<1>. The tag's C<id> defaults to C<name_to_id($name . "_" . $value)>.
847 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
848 created with said C<label>. No attribute named C<label> is created in
851 =item C<javascript_tag $file1, $file2, $file3...>
853 Creates a HTML 'E<lt>script type="text/javascript" src="..."E<gt>'
854 tag for each file name parameter passed. Each file name will be
855 postfixed with '.js' if it isn't already and prefixed with 'js/' if it
856 doesn't contain a slash.
858 =item C<stylesheet_tag $file1, $file2, $file3...>
860 Creates a HTML 'E<lt>link rel="text/stylesheet" href="..."E<gt>' tag
861 for each file name parameter passed. Each file name will be postfixed
862 with '.css' if it isn't already and prefixed with 'css/' if it doesn't
865 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
867 Creates a date input field, with an attached javascript that will open a
868 calendar on click. The javascript ist by default anchoered at the bottom right
869 sight. This can be overridden with C<cal_align>, see Calendar documentation for
870 the details, usually you'll want a two letter abbreviation of the alignment.
871 Right + Bottom becomes C<BL>.
873 =item C<tabbed \@tab, %attributes>
875 Will create a tabbed area. The tabs should be created with the helper function
879 L.tab(LxERP.t8('Basic Data'), 'part/_main_tab.html'),
880 L.tab(LxERP.t8('Custom Variables'), 'part/_cvar_tab.html', if => SELF.display_cvar_tab),
883 An optional attribute is C<selected>, which accepts the ordinal of a tab which
884 should be selected by default.
886 =item C<areainput_tag $name, $content, %PARAMS>
888 Creates a generic input tag or textarea tag, depending on content size. The
889 amount of desired rows must be either given with the C<rows> parameter or can
890 be computed from the value and the C<cols> paramter, Accepted parameters
891 include C<min_rows> for rendering a minimum of rows if a textarea is displayed.
893 You can force input by setting rows to 1, and you can force textarea by setting
896 =item C<multiselect2side $id, %params>
898 Creates a JavaScript snippet calling the jQuery function
899 C<multiselect2side> on the select control with the ID C<$id>. The
900 select itself is not created. C<%params> can contain the following
907 The label of the list of available options. Defaults to the
908 translation of 'Available'.
912 The label of the list of selected options. Defaults to the
913 translation of 'Selected'.
917 =item C<sortable_element $selector, %params>
919 Makes the children of the DOM element C<$selector> (a jQuery selector)
920 sortable with the I<jQuery UI Selectable> library. The children can be
921 dragged & dropped around. After dropping an element an URL can be
922 postet to with the element IDs of the sorted children.
924 If this is used then the JavaScript file C<js/jquery-ui.js> must be
925 included manually as well as it isn't loaded via C<$::form-gt;header>.
927 C<%params> can contain the following entries:
933 The URL to POST an AJAX request to after a dragged element has been
934 dropped. The AJAX request's return value is ignored. If given then
935 C<$params{with}> must be given as well.
939 A string that is interpreted as the prefix of the children's ID. Upon
940 POSTing the result each child whose ID starts with C<$params{with}> is
941 considered. The prefix and the following "_" is removed from the
942 ID. The remaining parts of the IDs of those children are posted as a
943 single array parameter. The array parameter's name is either
944 C<$params{as}> or, missing that, C<$params{with}>.
948 Sets the POST parameter name for AJAX request after dropping an
949 element (see C<$params{with}>).
953 An optional jQuery selector specifying which part of the child element
954 is dragable. If the parameter is not given then it defaults to
955 C<.dragdrop> matching DOM elements with the class C<dragdrop>. If the
956 parameter is set and empty then the whole child element is dragable,
957 and clicks through to underlying elements like inputs or links might
960 =item C<dont_recolor>
962 If trueish then the children will not be recolored. The default is to
963 recolor the children by setting the class C<listrow0> on odd and
964 C<listrow1> on even entries.
970 <script type="text/javascript" src="js/jquery-ui.js"></script>
972 <table id="thing_list">
974 <tr><td>This</td><td>That</td></tr>
977 <tr id="thingy_2"><td>stuff</td><td>more stuff</td></tr>
978 <tr id="thingy_15"><td>stuff</td><td>more stuff</td></tr>
979 <tr id="thingy_6"><td>stuff</td><td>more stuff</td></tr>
983 [% L.sortable_element('#thing_list tbody',
984 url => 'controller.pl?action=SystemThings/reorder',
987 recolor_rows => 1) %]
989 After dropping e.g. the third element at the top of the list a POST
990 request would be made to the C<reorder> action of the C<SystemThings>
991 controller with a single parameter called C<thing_ids> -- an array
992 containing the values C<[ 6, 2, 15 ]>.
996 Dumps the Argument using L<Data::Dumper> into a E<lt>preE<gt> block.
998 =item C<sortable_table_header $by, %params>
1000 Create a link and image suitable for placement in a table
1001 header. C<$by> must be an index set up by the controller with
1002 L<SL::Controller::Helper::make_sorted>.
1004 The optional parameter C<$params{title}> can override the column title
1005 displayed to the user. Otherwise the column title from the
1006 controller's sort spec is used.
1008 The other parameters in C<%params> are passed unmodified to the
1009 underlying call to L<SL::Controller::Base::url_for>.
1011 See the documentation of L<SL::Controller::Helper::Sorted> for an
1012 overview and further usage instructions.
1016 =head2 CONVERSION FUNCTIONS
1020 =item C<tab, description, target, %PARAMS>
1022 Creates a tab for C<tabbed>. The description will be used as displayed name.
1023 The target should be a block or template that can be processed. C<tab> supports
1024 a C<method> parameter, which can override the process method to apply target.
1025 C<method => 'raw'> will just include the given text as is. I was too lazy to
1026 implement C<include> properly.
1028 Also an C<if> attribute is supported, so that tabs can be suppressed based on
1029 some occasion. In this case the supplied block won't even get processed, and
1030 the resulting tab will get ignored by C<tabbed>:
1032 L.tab('Awesome tab wih much info', '_much_info.html', if => SELF.wants_all)
1034 =item C<truncate $text, %params>
1036 Returns the C<$text> truncated after a certain number of
1039 The number of characters to truncate at is determined by the parameter
1040 C<at> which defaults to 50. If the text is longer than C<$params{at}>
1041 then it will be truncated and postfixed with '...'. Otherwise it will
1042 be returned unmodified.
1046 =head1 MODULE AUTHORS
1048 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
1050 L<http://linet-services.de>