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}>";
87 my ($self, @slurp) = @_;
88 my %options = _hashify(@slurp);
92 return $self->html_tag('img', undef, %options);
98 my $collection = shift;
99 my %attributes = _hashify(@_);
101 $attributes{id} ||= $self->name_to_id($name);
103 my $value_key = delete($attributes{value_key}) || 'id';
104 my $title_key = delete($attributes{title_key}) || $value_key;
105 my $default_key = delete($attributes{default_key}) || 'selected';
108 my $value_title_sub = delete($attributes{value_title_sub});
110 my $value_sub = delete($attributes{value_sub});
111 my $title_sub = delete($attributes{title_sub});
112 my $default_sub = delete($attributes{default_sub});
114 my $with_empty = delete($attributes{with_empty});
115 my $empty_title = delete($attributes{empty_title});
119 if ( ref($attributes{default}) eq 'ARRAY' ) {
121 foreach my $entry (@{$attributes{default}}) {
122 $selected{$entry} = 1;
124 } elsif ( defined($attributes{default}) ) {
125 $selected{$attributes{default}} = 1;
128 delete($attributes{default});
134 push(@options, [undef, $empty_title || '']);
137 my $normalize_entry = sub {
139 my ($type, $entry, $sub, $key) = @_;
142 return $sub->($entry);
145 my $ref = ref($entry);
149 if ( $type eq 'value' || $type eq 'title' ) {
156 if ( $ref eq 'ARRAY' ) {
158 if ( $type eq 'value' ) {
162 if ( $type eq 'title' ) {
169 if ( $ref eq 'HASH' ) {
170 return $entry->{$key};
173 if ( $type ne 'default' || $entry->can($key) ) {
180 foreach my $entry ( @{ $collection } ) {
184 if ( $value_title_sub ) {
185 ($value, $title) = @{ $value_title_sub->($entry) };
188 $value = $normalize_entry->('value', $entry, $value_sub, $value_key);
189 $title = $normalize_entry->('title', $entry, $title_sub, $title_key);
192 my $default = $normalize_entry->('default', $entry, $default_sub, $default_key);
194 push(@options, [$value, $title, $default]);
197 foreach my $entry (@options) {
198 if ( exists($selected{$entry->[0]}) ) {
205 foreach my $entry (@options) {
206 my %args = (value => $entry->[0]);
208 $args{selected} = $entry->[2];
210 $code .= $self->html_tag('option', _H($entry->[1]), %args);
213 $code = $self->html_tag('select', $code, %attributes, name => $name);
219 my ($self, $name, $content, @slurp) = @_;
220 my %attributes = _hashify(@slurp);
222 $attributes{id} ||= $self->name_to_id($name);
223 $attributes{rows} *= 1; # required by standard
224 $attributes{cols} *= 1; # required by standard
225 $content = $content ? _H($content) : '';
227 return $self->html_tag('textarea', $content, %attributes, name => $name);
231 my ($self, $name, @slurp) = @_;
232 my %attributes = _hashify(@slurp);
234 $attributes{id} ||= $self->name_to_id($name);
235 $attributes{value} = 1 unless defined $attributes{value};
236 my $label = delete $attributes{label};
237 my $checkall = delete $attributes{checkall};
239 if ($attributes{checked}) {
240 $attributes{checked} = 'checked';
242 delete $attributes{checked};
245 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'checkbox');
246 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
247 $code .= $self->javascript(qq|\$('#$attributes{id}').checkall('$checkall');|) if $checkall;
252 sub radio_button_tag {
255 my %attributes = _hashify(@_);
257 $attributes{value} = 1 unless defined $attributes{value};
258 $attributes{id} ||= $self->name_to_id($name . "_" . $attributes{value});
259 my $label = delete $attributes{label};
261 if ($attributes{checked}) {
262 $attributes{checked} = 'checked';
264 delete $attributes{checked};
267 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'radio');
268 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
274 my ($self, $name, $value, @slurp) = @_;
275 my %attributes = _hashify(@slurp);
277 $attributes{id} ||= $self->name_to_id($name);
278 $attributes{type} ||= 'text';
280 return $self->html_tag('input', undef, %attributes, name => $name, value => $value);
284 return shift->input_tag(@_, type => 'hidden');
288 my ($self, $content, @slurp) = @_;
289 return $self->html_tag('div', $content, @slurp);
293 my ($self, $content, @slurp) = @_;
294 return $self->html_tag('ul', $content, @slurp);
298 my ($self, $content, @slurp) = @_;
299 return $self->html_tag('li', $content, @slurp);
303 my ($self, $href, $content, @slurp) = @_;
304 my %params = _hashify(@slurp);
308 return $self->html_tag('a', $content, %params, href => $href);
312 my ($self, $name, $value, @slurp) = @_;
313 my %attributes = _hashify(@slurp);
315 if ( $attributes{confirm} ) {
316 $attributes{onclick} = 'return confirm("'. _J(delete($attributes{confirm})) .'");';
319 return $self->input_tag($name, $value, %attributes, type => 'submit', class => 'submit');
323 my ($self, $onclick, $value, @slurp) = @_;
324 my %attributes = _hashify(@slurp);
326 $attributes{id} ||= $self->name_to_id($attributes{name}) if $attributes{name};
327 $attributes{type} ||= 'button';
329 return $self->html_tag('input', undef, %attributes, value => $value, onclick => $onclick);
333 my ($self, $name, $value) = splice @_, 0, 3;
334 my %attributes = _hashify(@_);
336 return $self->select_tag($name, [ [ 1 => $::locale->text('Yes') ], [ 0 => $::locale->text('No') ] ], default => $value ? 1 : 0, %attributes);
340 my ($self, $data) = @_;
341 return $self->html_tag('script', $data, type => 'text/javascript');
348 foreach my $file (@_) {
349 $file .= '.css' unless $file =~ m/\.css$/;
350 $file = "css/${file}" unless $file =~ m|/|;
352 $code .= qq|<link rel="stylesheet" href="${file}" type="text/css" media="screen" />|;
359 my ($self, $name, $value, @slurp) = @_;
360 my %params = _hashify(@slurp);
361 my $name_e = _H($name);
363 my $datefmt = apply {
367 } $::myconfig{"dateformat"};
369 my $cal_align = delete $params{cal_align} || 'BR';
370 my $onchange = delete $params{onchange};
371 my $str_value = blessed $value ? $value->to_lxoffice : $value;
373 $self->input_tag($name, $str_value,
376 title => _H($::myconfig{dateformat}),
377 onBlur => 'check_right_date_format(this)',
379 onChange => $onchange,
382 ) . ((!$params{no_cal} && !$params{readonly}) ?
383 $self->html_tag('img', undef,
384 src => 'image/calendar.png',
385 alt => $::locale->text('Calendar'),
387 title => _H($::myconfig{dateformat}),
391 "Calendar.setup({ inputField: '$name_e', ifFormat: '$datefmt', align: '$cal_align', button: 'trigger$seq' });"
395 sub customer_picker {
396 my ($self, $name, $value, %params) = @_;
397 my $name_e = _H($name);
399 $::request->{layout}->add_javascripts('autocomplete_customer.js');
401 $self->hidden_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => 'customer_autocomplete') .
402 $self->input_tag("$name_e\_name", (ref $value && $value->can('name')) ? $value->name : '', %params);
405 # simple version with select_tag
406 sub vendor_selector {
407 my ($self, $name, $value, %params) = @_;
409 my $actual_vendor_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::Vendor->get_all(),
413 default => $actual_vendor_id,
414 title_sub => sub { $_[0]->vendornumber . " : " . $_[0]->name },
420 # simple version with select_tag
422 my ($self, $name, $value, %params) = @_;
424 my $actual_part_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"})? $::form->{"$name"}->id : $::form->{"$name"}) :
425 (ref $value && $value->can('id')) ? $value->id : '';
427 return $self->select_tag($name, SL::DB::Manager::Part->get_all(),
428 default => $actual_part_id,
429 title_sub => sub { $_[0]->partnumber . " : " . $_[0]->description },
439 foreach my $file (@_) {
440 $file .= '.js' unless $file =~ m/\.js$/;
441 $file = "js/${file}" unless $file =~ m|/|;
443 $code .= qq|<script type="text/javascript" src="${file}"></script>|;
450 my ($self, $tabs, @slurp) = @_;
451 my %params = _hashify(@slurp);
452 my $id = $params{id} || 'tab_' . _tag_id();
454 $params{selected} *= 1;
456 die 'L.tabbed needs an arrayred of tabs for first argument'
457 unless ref $tabs eq 'ARRAY';
459 my (@header, @blocks);
460 for my $i (0..$#$tabs) {
461 my $tab = $tabs->[$i];
465 my $selected = $params{selected} == $i;
466 my $tab_id = "__tab_id_$i";
467 push @header, $self->li_tag(
468 $self->link('', $tab->{name}, rel => $tab_id),
469 ($selected ? (class => 'selected') : ())
471 push @blocks, $self->div_tag($tab->{data},
472 id => $tab_id, class => 'tabcontent');
475 return '' unless @header;
476 return $self->ul_tag(
477 join('', @header), id => $id, class => 'shadetabs'
480 join('', @blocks), class => 'tabcontentstyle'
483 qq|var $id = new ddtabcontent("$id");$id.setpersist(true);| .
484 qq|$id.setselectedClassTarget("link");$id.init();|
489 my ($self, $name, $src, @slurp) = @_;
490 my %params = _hashify(@slurp);
492 $params{method} ||= 'process';
494 return () if defined $params{if} && !$params{if};
497 if ($params{method} eq 'raw') {
499 } elsif ($params{method} eq 'process') {
500 $data = $self->_context->process($src, %{ $params{args} || {} });
502 die "unknown tag method '$params{method}'";
505 return () unless $data;
507 return +{ name => $name, data => $data };
511 my ($self, $name, $value, @slurp) = @_;
512 my %attributes = _hashify(@slurp);
515 my $min = delete $attributes{min_rows} || 1;
517 if (exists $attributes{cols}) {
518 $cols = delete $attributes{cols};
519 $rows = $::form->numtextrows($value, $cols);
521 $rows = delete $attributes{rows} || 1;
525 ? $self->textarea_tag($name, $value, %attributes, rows => max($rows, $min), ($cols ? (cols => $cols) : ()))
526 : $self->input_tag($name, $value, %attributes, ($cols ? (size => $cols) : ()));
529 sub multiselect2side {
530 my ($self, $id, @slurp) = @_;
531 my %params = _hashify(@slurp);
533 $params{labelsx} = "\"" . _J($params{labelsx} || $::locale->text('Available')) . "\"";
534 $params{labeldx} = "\"" . _J($params{labeldx} || $::locale->text('Selected')) . "\"";
535 $params{moveOptions} = 'false';
537 my $vars = join(', ', map { "${_}: " . $params{$_} } keys %params);
539 <script type="text/javascript">
540 \$().ready(function() {
541 \$('#${id}').multiselect2side({ ${vars} });
549 sub sortable_element {
550 my ($self, $selector, @slurp) = @_;
551 my %params = _hashify(@slurp);
553 my %attributes = ( distance => 5,
554 helper => <<'JAVASCRIPT' );
555 function(event, ui) {
556 ui.children().each(function() {
557 $(this).width($(this).width());
565 if ($params{url} && $params{with}) {
566 my $as = $params{as} || $params{with};
567 my $filter = ".filter(function(idx) { return this.substr(0, " . length($params{with}) . ") == '$params{with}'; })";
568 $filter .= ".map(function(idx, str) { return str.replace('$params{with}_', ''); })";
570 $stop_event = <<JAVASCRIPT;
571 \$.post('$params{url}', { '${as}[]': \$(\$('${selector}').sortable('toArray'))${filter}.toArray() });
575 if (!$params{dont_recolor}) {
576 $stop_event .= <<JAVASCRIPT;
577 \$('${selector}>*:odd').removeClass('listrow1').removeClass('listrow0').addClass('listrow0');
578 \$('${selector}>*:even').removeClass('listrow1').removeClass('listrow0').addClass('listrow1');
583 $attributes{stop} = <<JAVASCRIPT;
584 function(event, ui) {
591 $params{handle} = '.dragdrop' unless exists $params{handle};
592 $attributes{handle} = "'$params{handle}'" if $params{handle};
594 my $attr_str = join(', ', map { "${_}: $attributes{$_}" } keys %attributes);
596 my $code = <<JAVASCRIPT;
597 <script type="text/javascript">
599 \$( "${selector}" ).sortable({ ${attr_str} })
607 sub online_help_tag {
608 my ($self, $tag, @slurp) = @_;
609 my %params = _hashify(@slurp);
610 my $cc = $::myconfig{countrycode};
611 my $file = "doc/online/$cc/$tag.html";
612 my $text = $params{text} || $::locale->text('Help');
614 die 'malformed help tag' unless $tag =~ /^[a-zA-Z0-9_]+$/;
615 return unless -f $file;
616 return $self->html_tag('a', $text, href => $file, class => 'jqModal')
621 require Data::Dumper;
622 return '<pre>' . Data::Dumper::Dumper(@_) . '</pre>';
626 my ($self, $text, @slurp) = @_;
627 my %params = _hashify(@slurp);
630 $params{at} = 3 if 3 > $params{at};
633 return $text if length($text) < $params{at};
634 return substr($text, 0, $params{at}) . '...';
637 sub sortable_table_header {
638 my ($self, $by, @slurp) = @_;
639 my %params = _hashify(@slurp);
641 my $controller = $self->{CONTEXT}->stash->get('SELF');
642 my $sort_spec = $controller->get_sort_spec;
643 my $by_spec = $sort_spec->{$by};
644 my %current_sort_params = $controller->get_current_sort_params;
645 my ($image, $new_dir) = ('', $current_sort_params{dir});
646 my $title = delete($params{title}) || $::locale->text($by_spec->{title});
648 if ($current_sort_params{by} eq $by) {
649 my $current_dir = $current_sort_params{dir} ? 'up' : 'down';
650 $image = '<img border="0" src="image/' . $current_dir . '.png">';
651 $new_dir = 1 - ($current_sort_params{dir} || 0);
654 $params{ $sort_spec->{FORM_PARAMS}->[0] } = $by;
655 $params{ $sort_spec->{FORM_PARAMS}->[1] } = ($new_dir ? '1' : '0');
657 return '<a href="' . $controller->get_callback(%params) . '">' . _H($title) . $image . '</a>';
660 sub paginate_controls {
663 my $controller = $self->{CONTEXT}->stash->get('SELF');
664 my $paginate_spec = $controller->get_paginate_spec;
665 my %paginate_params = $controller->get_current_paginate_params;
667 my %template_params = (
668 pages => \%paginate_params,
670 my %url_params = _hashify(@_);
671 $url_params{ $paginate_spec->{FORM_PARAMS}->[0] } = delete $url_params{page};
672 $url_params{ $paginate_spec->{FORM_PARAMS}->[1] } = delete $url_params{per_page} if exists $url_params{per_page};
674 return $controller->get_callback(%url_params);
679 $controller->_template_obj->process('templates/webpages/common/paginate.html', \%template_params, \$output);
689 SL::Templates::Plugin::L -- Layouting / tag generation
693 Usage from a template:
697 [% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right', 1 ] ]) %]
699 [% L.select_tag('direction', [ { direction => 'left', display => 'To the left' },
700 { direction => 'right', display => 'To the right' } ],
701 value_key => 'direction', title_key => 'display', default => 'right')) %]
703 [% L.select_tag('direction', [ { direction => 'left', display => 'To the left' },
704 { direction => 'right', display => 'To the right', selected => 1 } ],
705 value_key => 'direction', title_key => 'display')) %]
709 A module modeled a bit after Rails' ActionView helpers. Several small
710 functions that create HTML tags from various kinds of data sources.
714 =head2 LOW-LEVEL FUNCTIONS
718 =item C<name_to_id $name>
720 Converts a name to a HTML id by replacing various characters.
722 =item C<attributes %items>
724 Creates a string from all elements in C<%items> suitable for usage as
725 HTML tag attributes. Keys and values are HTML escaped even though keys
726 must not contain non-ASCII characters for browsers to accept them.
728 =item C<html_tag $tag_name, $content_string, %attributes>
730 Creates an opening and closing HTML tag for C<$tag_name> and puts
731 C<$content_string> between the two. If C<$content_string> is undefined
732 or empty then only a E<lt>tag/E<gt> tag will be created. Attributes
733 are key/value pairs added to the opening tag.
735 C<$content_string> is not HTML escaped.
739 =head2 HIGH-LEVEL FUNCTIONS
743 =item C<select_tag $name, \@collection, %attributes>
745 Creates a HTML 'select' tag named C<$name> with the contents of one
746 'E<lt>optionE<gt>' tag for each element in C<\@collection> and with arbitrary
747 HTML attributes from C<%attributes>. The value
748 to use and the title to display are extracted from the elements in
749 C<\@collection>. Each element can be one of four things:
753 =item 1. An array reference with at least two elements. The first element is
754 the value, the second element is its title. The third element is optional and and should contain a boolean.
755 If it is true, than the element will be used as default.
757 =item 2. A scalar. The scalar is both the value and the title.
759 =item 3. A hash reference. In this case C<%attributes> must contain
760 I<value_key>, I<title_key> and may contain I<default_key> keys that name the keys in the element to use
761 for the value, title and default respectively.
763 =item 4. A blessed reference. In this case C<%attributes> must contain
764 I<value_key>, I<title_key> and may contain I<default_key> keys that name functions called on the blessed
765 reference whose return values are used as the value, title and default
770 For cases 3 and 4 C<$attributes{value_key}> defaults to C<id>,
771 C<$attributes{title_key}> defaults to C<$attributes{value_key}>
772 and C<$attributes{default_key}> defaults to C<selected>.
774 In addition to pure keys/method you can also provide coderefs as I<value_sub>
775 and/or I<title_sub> and/or I<default_sub>. If present, these take precedence over keys or methods,
776 and are called with the element as first argument. It must return the value, title or default.
778 Lastly a joint coderef I<value_title_sub> may be provided, which in turn takes
779 precedence over the C<value_sub> and C<title_sub> subs. It will only be called once for each
780 element and must return a list of value and title.
782 If the option C<with_empty> is set then an empty element (value
783 C<undef>) will be used as the first element. The title to display for
784 this element can be set with the option C<empty_title> and defaults to
787 The option C<default> can be either a scalar or an array reference
788 containing the values of the options which should be set to be
791 The tag's C<id> defaults to C<name_to_id($name)>.
793 =item C<yes_no_tag $name, $value, %attributes>
795 Creates a HTML 'select' tag with the two entries C<yes> and C<no> by
796 calling L<select_tag>. C<$value> determines
797 which entry is selected. The C<%attributes> are passed through to
800 =item C<input_tag $name, $value, %attributes>
802 Creates a HTML 'input type=text' tag named C<$name> with the value
803 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
804 tag's C<id> defaults to C<name_to_id($name)>.
806 =item C<hidden_tag $name, $value, %attributes>
808 Creates a HTML 'input type=hidden' tag named C<$name> with the value
809 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
810 tag's C<id> defaults to C<name_to_id($name)>.
812 =item C<submit_tag $name, $value, %attributes>
814 Creates a HTML 'input type=submit class=submit' tag named C<$name> with the
815 value C<$value> and with arbitrary HTML attributes from C<%attributes>. The
816 tag's C<id> defaults to C<name_to_id($name)>.
818 If C<$attributes{confirm}> is set then a JavaScript popup dialog will
819 be added via the C<onclick> handler asking the question given with
820 C<$attributes{confirm}>. If request is only submitted if the user
821 clicks the dialog's ok/yes button.
823 =item C<textarea_tag $name, $value, %attributes>
825 Creates a HTML 'textarea' tag named C<$name> with the content
826 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
827 tag's C<id> defaults to C<name_to_id($name)>.
829 =item C<checkbox_tag $name, %attributes>
831 Creates a HTML 'input type=checkbox' tag named C<$name> with arbitrary
832 HTML attributes from C<%attributes>. The tag's C<id> defaults to
833 C<name_to_id($name)>. The tag's C<value> defaults to C<1>.
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 If C<%attributes> contains a key C<checkall> then the value is taken as a
840 JQuery selector and clicking this checkbox will also toggle all checkboxes
841 matching the selector.
843 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
845 Creates a date input field, with an attached javascript that will open a
846 calendar on click. The javascript ist by default anchoered at the bottom right
847 sight. This can be overridden with C<cal_align>, see Calendar documentation for
848 the details, usually you'll want a two letter abbreviation of the alignment.
849 Right + Bottom becomes C<BL>.
851 =item C<radio_button_tag $name, %attributes>
853 Creates a HTML 'input type=radio' tag named C<$name> with arbitrary
854 HTML attributes from C<%attributes>. The tag's C<value> defaults to
855 C<1>. The tag's C<id> defaults to C<name_to_id($name . "_" . $value)>.
857 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
858 created with said C<label>. No attribute named C<label> is created in
861 =item C<javascript_tag $file1, $file2, $file3...>
863 Creates a HTML 'E<lt>script type="text/javascript" src="..."E<gt>'
864 tag for each file name parameter passed. Each file name will be
865 postfixed with '.js' if it isn't already and prefixed with 'js/' if it
866 doesn't contain a slash.
868 =item C<stylesheet_tag $file1, $file2, $file3...>
870 Creates a HTML 'E<lt>link rel="text/stylesheet" href="..."E<gt>' tag
871 for each file name parameter passed. Each file name will be postfixed
872 with '.css' if it isn't already and prefixed with 'css/' if it doesn't
875 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
877 Creates a date input field, with an attached javascript that will open a
878 calendar on click. The javascript ist by default anchoered at the bottom right
879 sight. This can be overridden with C<cal_align>, see Calendar documentation for
880 the details, usually you'll want a two letter abbreviation of the alignment.
881 Right + Bottom becomes C<BL>.
883 =item C<tabbed \@tab, %attributes>
885 Will create a tabbed area. The tabs should be created with the helper function
889 L.tab(LxERP.t8('Basic Data'), 'part/_main_tab.html'),
890 L.tab(LxERP.t8('Custom Variables'), 'part/_cvar_tab.html', if => SELF.display_cvar_tab),
893 An optional attribute is C<selected>, which accepts the ordinal of a tab which
894 should be selected by default.
896 =item C<areainput_tag $name, $content, %PARAMS>
898 Creates a generic input tag or textarea tag, depending on content size. The
899 amount of desired rows must be either given with the C<rows> parameter or can
900 be computed from the value and the C<cols> paramter, Accepted parameters
901 include C<min_rows> for rendering a minimum of rows if a textarea is displayed.
903 You can force input by setting rows to 1, and you can force textarea by setting
906 =item C<multiselect2side $id, %params>
908 Creates a JavaScript snippet calling the jQuery function
909 C<multiselect2side> on the select control with the ID C<$id>. The
910 select itself is not created. C<%params> can contain the following
917 The label of the list of available options. Defaults to the
918 translation of 'Available'.
922 The label of the list of selected options. Defaults to the
923 translation of 'Selected'.
927 =item C<sortable_element $selector, %params>
929 Makes the children of the DOM element C<$selector> (a jQuery selector)
930 sortable with the I<jQuery UI Selectable> library. The children can be
931 dragged & dropped around. After dropping an element an URL can be
932 postet to with the element IDs of the sorted children.
934 If this is used then the JavaScript file C<js/jquery-ui.js> must be
935 included manually as well as it isn't loaded via C<$::form-gt;header>.
937 C<%params> can contain the following entries:
943 The URL to POST an AJAX request to after a dragged element has been
944 dropped. The AJAX request's return value is ignored. If given then
945 C<$params{with}> must be given as well.
949 A string that is interpreted as the prefix of the children's ID. Upon
950 POSTing the result each child whose ID starts with C<$params{with}> is
951 considered. The prefix and the following "_" is removed from the
952 ID. The remaining parts of the IDs of those children are posted as a
953 single array parameter. The array parameter's name is either
954 C<$params{as}> or, missing that, C<$params{with}>.
958 Sets the POST parameter name for AJAX request after dropping an
959 element (see C<$params{with}>).
963 An optional jQuery selector specifying which part of the child element
964 is dragable. If the parameter is not given then it defaults to
965 C<.dragdrop> matching DOM elements with the class C<dragdrop>. If the
966 parameter is set and empty then the whole child element is dragable,
967 and clicks through to underlying elements like inputs or links might
970 =item C<dont_recolor>
972 If trueish then the children will not be recolored. The default is to
973 recolor the children by setting the class C<listrow0> on odd and
974 C<listrow1> on even entries.
980 <script type="text/javascript" src="js/jquery-ui.js"></script>
982 <table id="thing_list">
984 <tr><td>This</td><td>That</td></tr>
987 <tr id="thingy_2"><td>stuff</td><td>more stuff</td></tr>
988 <tr id="thingy_15"><td>stuff</td><td>more stuff</td></tr>
989 <tr id="thingy_6"><td>stuff</td><td>more stuff</td></tr>
993 [% L.sortable_element('#thing_list tbody',
994 url => 'controller.pl?action=SystemThings/reorder',
997 recolor_rows => 1) %]
999 After dropping e.g. the third element at the top of the list a POST
1000 request would be made to the C<reorder> action of the C<SystemThings>
1001 controller with a single parameter called C<thing_ids> -- an array
1002 containing the values C<[ 6, 2, 15 ]>.
1006 Dumps the Argument using L<Data::Dumper> into a E<lt>preE<gt> block.
1008 =item C<sortable_table_header $by, %params>
1010 Create a link and image suitable for placement in a table
1011 header. C<$by> must be an index set up by the controller with
1012 L<SL::Controller::Helper::make_sorted>.
1014 The optional parameter C<$params{title}> can override the column title
1015 displayed to the user. Otherwise the column title from the
1016 controller's sort spec is used.
1018 The other parameters in C<%params> are passed unmodified to the
1019 underlying call to L<SL::Controller::Base::url_for>.
1021 See the documentation of L<SL::Controller::Helper::Sorted> for an
1022 overview and further usage instructions.
1024 =item C<paginate_controls>
1026 Create a set of links used to paginate a list view.
1028 See the documentation of L<SL::Controller::Helper::Paginated> for an
1029 overview and further usage instructions.
1033 =head2 CONVERSION FUNCTIONS
1037 =item C<tab, description, target, %PARAMS>
1039 Creates a tab for C<tabbed>. The description will be used as displayed name.
1040 The target should be a block or template that can be processed. C<tab> supports
1041 a C<method> parameter, which can override the process method to apply target.
1042 C<method => 'raw'> will just include the given text as is. I was too lazy to
1043 implement C<include> properly.
1045 Also an C<if> attribute is supported, so that tabs can be suppressed based on
1046 some occasion. In this case the supplied block won't even get processed, and
1047 the resulting tab will get ignored by C<tabbed>:
1049 L.tab('Awesome tab wih much info', '_much_info.html', if => SELF.wants_all)
1051 =item C<truncate $text, %params>
1053 Returns the C<$text> truncated after a certain number of
1056 The number of characters to truncate at is determined by the parameter
1057 C<at> which defaults to 50. If the text is longer than C<$params{at}>
1058 then it will be truncated and postfixed with '...'. Otherwise it will
1059 be returned unmodified.
1063 =head1 MODULE AUTHORS
1065 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
1067 L<http://linet-services.de>