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 $self->hidden_tag($name, (ref $value && $value->can('id')) ? $value->id : '') .
400 $self->input_tag("$name_e\_name", (ref $value && $value->can('name')) ? $value->name : '', %params) .
401 $self->javascript(<<JS);
402 function autocomplete_customer (selector, column) {
403 \$(function(){ \$(selector).autocomplete({
404 source: function(req, rsp) {
406 url: 'controller.pl?action=Customer/ajax_autocomplete',
411 current: function() { \$('#$name_e').val() },
414 success: function (data){ rsp(data) }
419 select: function(event, ui) {
420 \$('#$name_e').val(ui.item.id);
421 \$('#$name_e\_name').val(ui.item.name);
425 autocomplete_customer('#$name_e\_name');
429 # simple version with select_tag
430 sub vendor_selector {
431 my ($self, $name, $value, %params) = @_;
433 my $actual_vendor_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"}) ? $::form->{"$name"}->id : $::form->{"$name"}) :
434 (ref $value && $value->can('id')) ? $value->id : '';
436 return $self->select_tag($name, SL::DB::Manager::Vendor->get_all(),
437 default => $actual_vendor_id,
438 title_sub => sub { $_[0]->vendornumber . " : " . $_[0]->name },
444 # simple version with select_tag
446 my ($self, $name, $value, %params) = @_;
448 my $actual_part_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"})? $::form->{"$name"}->id : $::form->{"$name"}) :
449 (ref $value && $value->can('id')) ? $value->id : '';
451 return $self->select_tag($name, SL::DB::Manager::Part->get_all(),
452 default => $actual_part_id,
453 title_sub => sub { $_[0]->partnumber . " : " . $_[0]->description },
463 foreach my $file (@_) {
464 $file .= '.js' unless $file =~ m/\.js$/;
465 $file = "js/${file}" unless $file =~ m|/|;
467 $code .= qq|<script type="text/javascript" src="${file}"></script>|;
474 my ($self, $tabs, @slurp) = @_;
475 my %params = _hashify(@slurp);
476 my $id = $params{id} || 'tab_' . _tag_id();
478 $params{selected} *= 1;
480 die 'L.tabbed needs an arrayred of tabs for first argument'
481 unless ref $tabs eq 'ARRAY';
483 my (@header, @blocks);
484 for my $i (0..$#$tabs) {
485 my $tab = $tabs->[$i];
489 my $selected = $params{selected} == $i;
490 my $tab_id = "__tab_id_$i";
491 push @header, $self->li_tag(
492 $self->link('', $tab->{name}, rel => $tab_id),
493 ($selected ? (class => 'selected') : ())
495 push @blocks, $self->div_tag($tab->{data},
496 id => $tab_id, class => 'tabcontent');
499 return '' unless @header;
500 return $self->ul_tag(
501 join('', @header), id => $id, class => 'shadetabs'
504 join('', @blocks), class => 'tabcontentstyle'
507 qq|var $id = new ddtabcontent("$id");$id.setpersist(true);| .
508 qq|$id.setselectedClassTarget("link");$id.init();|
513 my ($self, $name, $src, @slurp) = @_;
514 my %params = _hashify(@slurp);
516 $params{method} ||= 'process';
518 return () if defined $params{if} && !$params{if};
521 if ($params{method} eq 'raw') {
523 } elsif ($params{method} eq 'process') {
524 $data = $self->_context->process($src, %{ $params{args} || {} });
526 die "unknown tag method '$params{method}'";
529 return () unless $data;
531 return +{ name => $name, data => $data };
535 my ($self, $name, $value, @slurp) = @_;
536 my %attributes = _hashify(@slurp);
539 my $min = delete $attributes{min_rows} || 1;
541 if (exists $attributes{cols}) {
542 $cols = delete $attributes{cols};
543 $rows = $::form->numtextrows($value, $cols);
545 $rows = delete $attributes{rows} || 1;
549 ? $self->textarea_tag($name, $value, %attributes, rows => max($rows, $min), ($cols ? (cols => $cols) : ()))
550 : $self->input_tag($name, $value, %attributes, ($cols ? (size => $cols) : ()));
553 sub multiselect2side {
554 my ($self, $id, @slurp) = @_;
555 my %params = _hashify(@slurp);
557 $params{labelsx} = "\"" . _J($params{labelsx} || $::locale->text('Available')) . "\"";
558 $params{labeldx} = "\"" . _J($params{labeldx} || $::locale->text('Selected')) . "\"";
559 $params{moveOptions} = 'false';
561 my $vars = join(', ', map { "${_}: " . $params{$_} } keys %params);
563 <script type="text/javascript">
564 \$().ready(function() {
565 \$('#${id}').multiselect2side({ ${vars} });
573 sub sortable_element {
574 my ($self, $selector, @slurp) = @_;
575 my %params = _hashify(@slurp);
577 my %attributes = ( distance => 5,
578 helper => <<'JAVASCRIPT' );
579 function(event, ui) {
580 ui.children().each(function() {
581 $(this).width($(this).width());
589 if ($params{url} && $params{with}) {
590 my $as = $params{as} || $params{with};
591 my $filter = ".filter(function(idx) { return this.substr(0, " . length($params{with}) . ") == '$params{with}'; })";
592 $filter .= ".map(function(idx, str) { return str.replace('$params{with}_', ''); })";
594 $stop_event = <<JAVASCRIPT;
595 \$.post('$params{url}', { '${as}[]': \$(\$('${selector}').sortable('toArray'))${filter}.toArray() });
599 if (!$params{dont_recolor}) {
600 $stop_event .= <<JAVASCRIPT;
601 \$('${selector}>*:odd').removeClass('listrow1').removeClass('listrow0').addClass('listrow0');
602 \$('${selector}>*:even').removeClass('listrow1').removeClass('listrow0').addClass('listrow1');
607 $attributes{stop} = <<JAVASCRIPT;
608 function(event, ui) {
615 $params{handle} = '.dragdrop' unless exists $params{handle};
616 $attributes{handle} = "'$params{handle}'" if $params{handle};
618 my $attr_str = join(', ', map { "${_}: $attributes{$_}" } keys %attributes);
620 my $code = <<JAVASCRIPT;
621 <script type="text/javascript">
623 \$( "${selector}" ).sortable({ ${attr_str} })
631 sub online_help_tag {
632 my ($self, $tag, @slurp) = @_;
633 my %params = _hashify(@slurp);
634 my $cc = $::myconfig{countrycode};
635 my $file = "doc/online/$cc/$tag.html";
636 my $text = $params{text} || $::locale->text('Help');
638 die 'malformed help tag' unless $tag =~ /^[a-zA-Z0-9_]+$/;
639 return unless -f $file;
640 return $self->html_tag('a', $text, href => $file, class => 'jqModal')
645 require Data::Dumper;
646 return '<pre>' . Data::Dumper::Dumper(@_) . '</pre>';
650 my ($self, $text, @slurp) = @_;
651 my %params = _hashify(@slurp);
654 $params{at} = 3 if 3 > $params{at};
657 return $text if length($text) < $params{at};
658 return substr($text, 0, $params{at}) . '...';
661 sub sortable_table_header {
662 my ($self, $by, @slurp) = @_;
663 my %params = _hashify(@slurp);
665 my $controller = $self->{CONTEXT}->stash->get('SELF');
666 my $sort_spec = $controller->get_sort_spec;
667 my $by_spec = $sort_spec->{$by};
668 my %current_sort_params = $controller->get_current_sort_params;
669 my ($image, $new_dir) = ('', $current_sort_params{dir});
670 my $title = delete($params{title}) || $::locale->text($by_spec->{title});
672 if ($current_sort_params{by} eq $by) {
673 my $current_dir = $current_sort_params{dir} ? 'up' : 'down';
674 $image = '<img border="0" src="image/' . $current_dir . '.png">';
675 $new_dir = 1 - ($current_sort_params{dir} || 0);
678 $params{ $sort_spec->{FORM_PARAMS}->[0] } = $by;
679 $params{ $sort_spec->{FORM_PARAMS}->[1] } = ($new_dir ? '1' : '0');
681 return '<a href="' . $controller->get_callback(%params) . '">' . _H($title) . $image . '</a>';
684 sub paginate_controls {
687 my $controller = $self->{CONTEXT}->stash->get('SELF');
688 my $paginate_spec = $controller->get_paginate_spec;
689 my %paginate_params = $controller->get_current_paginate_params;
691 my %template_params = (
693 cur => $paginate_params{page},
694 max => $paginate_params{num_pages},
695 common => $paginate_params{common_pages},
698 my %url_params = _hashify(@_);
699 $url_params{ $paginate_spec->{FORM_PARAMS}->[0] } = delete $url_params{page};
700 $url_params{ $paginate_spec->{FORM_PARAMS}->[1] } = delete $url_params{per_page} if exists $url_params{per_page};
702 return $controller->get_callback(%url_params);
707 $controller->_template_obj->process('templates/webpages/common/paginate.html', \%template_params, \$output);
717 SL::Templates::Plugin::L -- Layouting / tag generation
721 Usage from a template:
725 [% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right', 1 ] ]) %]
727 [% L.select_tag('direction', [ { direction => 'left', display => 'To the left' },
728 { direction => 'right', display => 'To the right' } ],
729 value_key => 'direction', title_key => 'display', default => 'right')) %]
731 [% L.select_tag('direction', [ { direction => 'left', display => 'To the left' },
732 { direction => 'right', display => 'To the right', selected => 1 } ],
733 value_key => 'direction', title_key => 'display')) %]
737 A module modeled a bit after Rails' ActionView helpers. Several small
738 functions that create HTML tags from various kinds of data sources.
742 =head2 LOW-LEVEL FUNCTIONS
746 =item C<name_to_id $name>
748 Converts a name to a HTML id by replacing various characters.
750 =item C<attributes %items>
752 Creates a string from all elements in C<%items> suitable for usage as
753 HTML tag attributes. Keys and values are HTML escaped even though keys
754 must not contain non-ASCII characters for browsers to accept them.
756 =item C<html_tag $tag_name, $content_string, %attributes>
758 Creates an opening and closing HTML tag for C<$tag_name> and puts
759 C<$content_string> between the two. If C<$content_string> is undefined
760 or empty then only a E<lt>tag/E<gt> tag will be created. Attributes
761 are key/value pairs added to the opening tag.
763 C<$content_string> is not HTML escaped.
767 =head2 HIGH-LEVEL FUNCTIONS
771 =item C<select_tag $name, \@collection, %attributes>
773 Creates a HTML 'select' tag named C<$name> with the contents of one
774 'E<lt>optionE<gt>' tag for each element in C<\@collection> and with arbitrary
775 HTML attributes from C<%attributes>. The value
776 to use and the title to display are extracted from the elements in
777 C<\@collection>. Each element can be one of four things:
781 =item 1. An array reference with at least two elements. The first element is
782 the value, the second element is its title. The third element is optional and and should contain a boolean.
783 If it is true, than the element will be used as default.
785 =item 2. A scalar. The scalar is both the value and the title.
787 =item 3. A hash reference. In this case C<%attributes> must contain
788 I<value_key>, I<title_key> and may contain I<default_key> keys that name the keys in the element to use
789 for the value, title and default respectively.
791 =item 4. A blessed reference. In this case C<%attributes> must contain
792 I<value_key>, I<title_key> and may contain I<default_key> keys that name functions called on the blessed
793 reference whose return values are used as the value, title and default
798 For cases 3 and 4 C<$attributes{value_key}> defaults to C<id>,
799 C<$attributes{title_key}> defaults to C<$attributes{value_key}>
800 and C<$attributes{default_key}> defaults to C<selected>.
802 In addition to pure keys/method you can also provide coderefs as I<value_sub>
803 and/or I<title_sub> and/or I<default_sub>. If present, these take precedence over keys or methods,
804 and are called with the element as first argument. It must return the value, title or default.
806 Lastly a joint coderef I<value_title_sub> may be provided, which in turn takes
807 precedence over the C<value_sub> and C<title_sub> subs. It will only be called once for each
808 element and must return a list of value and title.
810 If the option C<with_empty> is set then an empty element (value
811 C<undef>) will be used as the first element. The title to display for
812 this element can be set with the option C<empty_title> and defaults to
815 The option C<default> can be either a scalar or an array reference
816 containing the values of the options which should be set to be
819 The tag's C<id> defaults to C<name_to_id($name)>.
821 =item C<yes_no_tag $name, $value, %attributes>
823 Creates a HTML 'select' tag with the two entries C<yes> and C<no> by
824 calling L<select_tag>. C<$value> determines
825 which entry is selected. The C<%attributes> are passed through to
828 =item C<input_tag $name, $value, %attributes>
830 Creates a HTML 'input type=text' tag named C<$name> with the value
831 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
832 tag's C<id> defaults to C<name_to_id($name)>.
834 =item C<hidden_tag $name, $value, %attributes>
836 Creates a HTML 'input type=hidden' tag named C<$name> with the value
837 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
838 tag's C<id> defaults to C<name_to_id($name)>.
840 =item C<submit_tag $name, $value, %attributes>
842 Creates a HTML 'input type=submit class=submit' tag named C<$name> with the
843 value C<$value> and with arbitrary HTML attributes from C<%attributes>. The
844 tag's C<id> defaults to C<name_to_id($name)>.
846 If C<$attributes{confirm}> is set then a JavaScript popup dialog will
847 be added via the C<onclick> handler asking the question given with
848 C<$attributes{confirm}>. If request is only submitted if the user
849 clicks the dialog's ok/yes button.
851 =item C<textarea_tag $name, $value, %attributes>
853 Creates a HTML 'textarea' tag named C<$name> with the content
854 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
855 tag's C<id> defaults to C<name_to_id($name)>.
857 =item C<checkbox_tag $name, %attributes>
859 Creates a HTML 'input type=checkbox' tag named C<$name> with arbitrary
860 HTML attributes from C<%attributes>. The tag's C<id> defaults to
861 C<name_to_id($name)>. The tag's C<value> defaults to C<1>.
863 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
864 created with said C<label>. No attribute named C<label> is created in
867 If C<%attributes> contains a key C<checkall> then the value is taken as a
868 JQuery selector and clicking this checkbox will also toggle all checkboxes
869 matching the selector.
871 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
873 Creates a date input field, with an attached javascript that will open a
874 calendar on click. The javascript ist by default anchoered at the bottom right
875 sight. This can be overridden with C<cal_align>, see Calendar documentation for
876 the details, usually you'll want a two letter abbreviation of the alignment.
877 Right + Bottom becomes C<BL>.
879 =item C<radio_button_tag $name, %attributes>
881 Creates a HTML 'input type=radio' tag named C<$name> with arbitrary
882 HTML attributes from C<%attributes>. The tag's C<value> defaults to
883 C<1>. The tag's C<id> defaults to C<name_to_id($name . "_" . $value)>.
885 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
886 created with said C<label>. No attribute named C<label> is created in
889 =item C<javascript_tag $file1, $file2, $file3...>
891 Creates a HTML 'E<lt>script type="text/javascript" src="..."E<gt>'
892 tag for each file name parameter passed. Each file name will be
893 postfixed with '.js' if it isn't already and prefixed with 'js/' if it
894 doesn't contain a slash.
896 =item C<stylesheet_tag $file1, $file2, $file3...>
898 Creates a HTML 'E<lt>link rel="text/stylesheet" href="..."E<gt>' tag
899 for each file name parameter passed. Each file name will be postfixed
900 with '.css' if it isn't already and prefixed with 'css/' if it doesn't
903 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
905 Creates a date input field, with an attached javascript that will open a
906 calendar on click. The javascript ist by default anchoered at the bottom right
907 sight. This can be overridden with C<cal_align>, see Calendar documentation for
908 the details, usually you'll want a two letter abbreviation of the alignment.
909 Right + Bottom becomes C<BL>.
911 =item C<tabbed \@tab, %attributes>
913 Will create a tabbed area. The tabs should be created with the helper function
917 L.tab(LxERP.t8('Basic Data'), 'part/_main_tab.html'),
918 L.tab(LxERP.t8('Custom Variables'), 'part/_cvar_tab.html', if => SELF.display_cvar_tab),
921 An optional attribute is C<selected>, which accepts the ordinal of a tab which
922 should be selected by default.
924 =item C<areainput_tag $name, $content, %PARAMS>
926 Creates a generic input tag or textarea tag, depending on content size. The
927 amount of desired rows must be either given with the C<rows> parameter or can
928 be computed from the value and the C<cols> paramter, Accepted parameters
929 include C<min_rows> for rendering a minimum of rows if a textarea is displayed.
931 You can force input by setting rows to 1, and you can force textarea by setting
934 =item C<multiselect2side $id, %params>
936 Creates a JavaScript snippet calling the jQuery function
937 C<multiselect2side> on the select control with the ID C<$id>. The
938 select itself is not created. C<%params> can contain the following
945 The label of the list of available options. Defaults to the
946 translation of 'Available'.
950 The label of the list of selected options. Defaults to the
951 translation of 'Selected'.
955 =item C<sortable_element $selector, %params>
957 Makes the children of the DOM element C<$selector> (a jQuery selector)
958 sortable with the I<jQuery UI Selectable> library. The children can be
959 dragged & dropped around. After dropping an element an URL can be
960 postet to with the element IDs of the sorted children.
962 If this is used then the JavaScript file C<js/jquery-ui.js> must be
963 included manually as well as it isn't loaded via C<$::form-gt;header>.
965 C<%params> can contain the following entries:
971 The URL to POST an AJAX request to after a dragged element has been
972 dropped. The AJAX request's return value is ignored. If given then
973 C<$params{with}> must be given as well.
977 A string that is interpreted as the prefix of the children's ID. Upon
978 POSTing the result each child whose ID starts with C<$params{with}> is
979 considered. The prefix and the following "_" is removed from the
980 ID. The remaining parts of the IDs of those children are posted as a
981 single array parameter. The array parameter's name is either
982 C<$params{as}> or, missing that, C<$params{with}>.
986 Sets the POST parameter name for AJAX request after dropping an
987 element (see C<$params{with}>).
991 An optional jQuery selector specifying which part of the child element
992 is dragable. If the parameter is not given then it defaults to
993 C<.dragdrop> matching DOM elements with the class C<dragdrop>. If the
994 parameter is set and empty then the whole child element is dragable,
995 and clicks through to underlying elements like inputs or links might
998 =item C<dont_recolor>
1000 If trueish then the children will not be recolored. The default is to
1001 recolor the children by setting the class C<listrow0> on odd and
1002 C<listrow1> on even entries.
1008 <script type="text/javascript" src="js/jquery-ui.js"></script>
1010 <table id="thing_list">
1012 <tr><td>This</td><td>That</td></tr>
1015 <tr id="thingy_2"><td>stuff</td><td>more stuff</td></tr>
1016 <tr id="thingy_15"><td>stuff</td><td>more stuff</td></tr>
1017 <tr id="thingy_6"><td>stuff</td><td>more stuff</td></tr>
1021 [% L.sortable_element('#thing_list tbody',
1022 url => 'controller.pl?action=SystemThings/reorder',
1025 recolor_rows => 1) %]
1027 After dropping e.g. the third element at the top of the list a POST
1028 request would be made to the C<reorder> action of the C<SystemThings>
1029 controller with a single parameter called C<thing_ids> -- an array
1030 containing the values C<[ 6, 2, 15 ]>.
1034 Dumps the Argument using L<Data::Dumper> into a E<lt>preE<gt> block.
1036 =item C<sortable_table_header $by, %params>
1038 Create a link and image suitable for placement in a table
1039 header. C<$by> must be an index set up by the controller with
1040 L<SL::Controller::Helper::make_sorted>.
1042 The optional parameter C<$params{title}> can override the column title
1043 displayed to the user. Otherwise the column title from the
1044 controller's sort spec is used.
1046 The other parameters in C<%params> are passed unmodified to the
1047 underlying call to L<SL::Controller::Base::url_for>.
1049 See the documentation of L<SL::Controller::Helper::Sorted> for an
1050 overview and further usage instructions.
1052 =item C<paginate_controls>
1054 Create a set of links used to paginate a list view.
1056 See the documentation of L<SL::Controller::Helper::Paginated> for an
1057 overview and further usage instructions.
1061 =head2 CONVERSION FUNCTIONS
1065 =item C<tab, description, target, %PARAMS>
1067 Creates a tab for C<tabbed>. The description will be used as displayed name.
1068 The target should be a block or template that can be processed. C<tab> supports
1069 a C<method> parameter, which can override the process method to apply target.
1070 C<method => 'raw'> will just include the given text as is. I was too lazy to
1071 implement C<include> properly.
1073 Also an C<if> attribute is supported, so that tabs can be suppressed based on
1074 some occasion. In this case the supplied block won't even get processed, and
1075 the resulting tab will get ignored by C<tabbed>:
1077 L.tab('Awesome tab wih much info', '_much_info.html', if => SELF.wants_all)
1079 =item C<truncate $text, %params>
1081 Returns the C<$text> truncated after a certain number of
1084 The number of characters to truncate at is determined by the parameter
1085 C<at> which defaults to 50. If the text is longer than C<$params{at}>
1086 then it will be truncated and postfixed with '...'. Otherwise it will
1087 be returned unmodified.
1091 =head1 MODULE AUTHORS
1093 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
1095 L<http://linet-services.de>