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 = (
692 pages => \%paginate_params,
694 my %url_params = _hashify(@_);
695 $url_params{ $paginate_spec->{FORM_PARAMS}->[0] } = delete $url_params{page};
696 $url_params{ $paginate_spec->{FORM_PARAMS}->[1] } = delete $url_params{per_page} if exists $url_params{per_page};
698 return $controller->get_callback(%url_params);
703 $controller->_template_obj->process('templates/webpages/common/paginate.html', \%template_params, \$output);
713 SL::Templates::Plugin::L -- Layouting / tag generation
717 Usage from a template:
721 [% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right', 1 ] ]) %]
723 [% L.select_tag('direction', [ { direction => 'left', display => 'To the left' },
724 { direction => 'right', display => 'To the right' } ],
725 value_key => 'direction', title_key => 'display', default => 'right')) %]
727 [% L.select_tag('direction', [ { direction => 'left', display => 'To the left' },
728 { direction => 'right', display => 'To the right', selected => 1 } ],
729 value_key => 'direction', title_key => 'display')) %]
733 A module modeled a bit after Rails' ActionView helpers. Several small
734 functions that create HTML tags from various kinds of data sources.
738 =head2 LOW-LEVEL FUNCTIONS
742 =item C<name_to_id $name>
744 Converts a name to a HTML id by replacing various characters.
746 =item C<attributes %items>
748 Creates a string from all elements in C<%items> suitable for usage as
749 HTML tag attributes. Keys and values are HTML escaped even though keys
750 must not contain non-ASCII characters for browsers to accept them.
752 =item C<html_tag $tag_name, $content_string, %attributes>
754 Creates an opening and closing HTML tag for C<$tag_name> and puts
755 C<$content_string> between the two. If C<$content_string> is undefined
756 or empty then only a E<lt>tag/E<gt> tag will be created. Attributes
757 are key/value pairs added to the opening tag.
759 C<$content_string> is not HTML escaped.
763 =head2 HIGH-LEVEL FUNCTIONS
767 =item C<select_tag $name, \@collection, %attributes>
769 Creates a HTML 'select' tag named C<$name> with the contents of one
770 'E<lt>optionE<gt>' tag for each element in C<\@collection> and with arbitrary
771 HTML attributes from C<%attributes>. The value
772 to use and the title to display are extracted from the elements in
773 C<\@collection>. Each element can be one of four things:
777 =item 1. An array reference with at least two elements. The first element is
778 the value, the second element is its title. The third element is optional and and should contain a boolean.
779 If it is true, than the element will be used as default.
781 =item 2. A scalar. The scalar is both the value and the title.
783 =item 3. A hash reference. In this case C<%attributes> must contain
784 I<value_key>, I<title_key> and may contain I<default_key> keys that name the keys in the element to use
785 for the value, title and default respectively.
787 =item 4. A blessed reference. In this case C<%attributes> must contain
788 I<value_key>, I<title_key> and may contain I<default_key> keys that name functions called on the blessed
789 reference whose return values are used as the value, title and default
794 For cases 3 and 4 C<$attributes{value_key}> defaults to C<id>,
795 C<$attributes{title_key}> defaults to C<$attributes{value_key}>
796 and C<$attributes{default_key}> defaults to C<selected>.
798 In addition to pure keys/method you can also provide coderefs as I<value_sub>
799 and/or I<title_sub> and/or I<default_sub>. If present, these take precedence over keys or methods,
800 and are called with the element as first argument. It must return the value, title or default.
802 Lastly a joint coderef I<value_title_sub> may be provided, which in turn takes
803 precedence over the C<value_sub> and C<title_sub> subs. It will only be called once for each
804 element and must return a list of value and title.
806 If the option C<with_empty> is set then an empty element (value
807 C<undef>) will be used as the first element. The title to display for
808 this element can be set with the option C<empty_title> and defaults to
811 The option C<default> can be either a scalar or an array reference
812 containing the values of the options which should be set to be
815 The tag's C<id> defaults to C<name_to_id($name)>.
817 =item C<yes_no_tag $name, $value, %attributes>
819 Creates a HTML 'select' tag with the two entries C<yes> and C<no> by
820 calling L<select_tag>. C<$value> determines
821 which entry is selected. The C<%attributes> are passed through to
824 =item C<input_tag $name, $value, %attributes>
826 Creates a HTML 'input type=text' tag named C<$name> with the value
827 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
828 tag's C<id> defaults to C<name_to_id($name)>.
830 =item C<hidden_tag $name, $value, %attributes>
832 Creates a HTML 'input type=hidden' tag named C<$name> with the value
833 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
834 tag's C<id> defaults to C<name_to_id($name)>.
836 =item C<submit_tag $name, $value, %attributes>
838 Creates a HTML 'input type=submit class=submit' tag named C<$name> with the
839 value C<$value> and with arbitrary HTML attributes from C<%attributes>. The
840 tag's C<id> defaults to C<name_to_id($name)>.
842 If C<$attributes{confirm}> is set then a JavaScript popup dialog will
843 be added via the C<onclick> handler asking the question given with
844 C<$attributes{confirm}>. If request is only submitted if the user
845 clicks the dialog's ok/yes button.
847 =item C<textarea_tag $name, $value, %attributes>
849 Creates a HTML 'textarea' tag named C<$name> with the content
850 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
851 tag's C<id> defaults to C<name_to_id($name)>.
853 =item C<checkbox_tag $name, %attributes>
855 Creates a HTML 'input type=checkbox' tag named C<$name> with arbitrary
856 HTML attributes from C<%attributes>. The tag's C<id> defaults to
857 C<name_to_id($name)>. The tag's C<value> defaults to C<1>.
859 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
860 created with said C<label>. No attribute named C<label> is created in
863 If C<%attributes> contains a key C<checkall> then the value is taken as a
864 JQuery selector and clicking this checkbox will also toggle all checkboxes
865 matching the selector.
867 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
869 Creates a date input field, with an attached javascript that will open a
870 calendar on click. The javascript ist by default anchoered at the bottom right
871 sight. This can be overridden with C<cal_align>, see Calendar documentation for
872 the details, usually you'll want a two letter abbreviation of the alignment.
873 Right + Bottom becomes C<BL>.
875 =item C<radio_button_tag $name, %attributes>
877 Creates a HTML 'input type=radio' tag named C<$name> with arbitrary
878 HTML attributes from C<%attributes>. The tag's C<value> defaults to
879 C<1>. The tag's C<id> defaults to C<name_to_id($name . "_" . $value)>.
881 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
882 created with said C<label>. No attribute named C<label> is created in
885 =item C<javascript_tag $file1, $file2, $file3...>
887 Creates a HTML 'E<lt>script type="text/javascript" src="..."E<gt>'
888 tag for each file name parameter passed. Each file name will be
889 postfixed with '.js' if it isn't already and prefixed with 'js/' if it
890 doesn't contain a slash.
892 =item C<stylesheet_tag $file1, $file2, $file3...>
894 Creates a HTML 'E<lt>link rel="text/stylesheet" href="..."E<gt>' tag
895 for each file name parameter passed. Each file name will be postfixed
896 with '.css' if it isn't already and prefixed with 'css/' if it doesn't
899 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
901 Creates a date input field, with an attached javascript that will open a
902 calendar on click. The javascript ist by default anchoered at the bottom right
903 sight. This can be overridden with C<cal_align>, see Calendar documentation for
904 the details, usually you'll want a two letter abbreviation of the alignment.
905 Right + Bottom becomes C<BL>.
907 =item C<tabbed \@tab, %attributes>
909 Will create a tabbed area. The tabs should be created with the helper function
913 L.tab(LxERP.t8('Basic Data'), 'part/_main_tab.html'),
914 L.tab(LxERP.t8('Custom Variables'), 'part/_cvar_tab.html', if => SELF.display_cvar_tab),
917 An optional attribute is C<selected>, which accepts the ordinal of a tab which
918 should be selected by default.
920 =item C<areainput_tag $name, $content, %PARAMS>
922 Creates a generic input tag or textarea tag, depending on content size. The
923 amount of desired rows must be either given with the C<rows> parameter or can
924 be computed from the value and the C<cols> paramter, Accepted parameters
925 include C<min_rows> for rendering a minimum of rows if a textarea is displayed.
927 You can force input by setting rows to 1, and you can force textarea by setting
930 =item C<multiselect2side $id, %params>
932 Creates a JavaScript snippet calling the jQuery function
933 C<multiselect2side> on the select control with the ID C<$id>. The
934 select itself is not created. C<%params> can contain the following
941 The label of the list of available options. Defaults to the
942 translation of 'Available'.
946 The label of the list of selected options. Defaults to the
947 translation of 'Selected'.
951 =item C<sortable_element $selector, %params>
953 Makes the children of the DOM element C<$selector> (a jQuery selector)
954 sortable with the I<jQuery UI Selectable> library. The children can be
955 dragged & dropped around. After dropping an element an URL can be
956 postet to with the element IDs of the sorted children.
958 If this is used then the JavaScript file C<js/jquery-ui.js> must be
959 included manually as well as it isn't loaded via C<$::form-gt;header>.
961 C<%params> can contain the following entries:
967 The URL to POST an AJAX request to after a dragged element has been
968 dropped. The AJAX request's return value is ignored. If given then
969 C<$params{with}> must be given as well.
973 A string that is interpreted as the prefix of the children's ID. Upon
974 POSTing the result each child whose ID starts with C<$params{with}> is
975 considered. The prefix and the following "_" is removed from the
976 ID. The remaining parts of the IDs of those children are posted as a
977 single array parameter. The array parameter's name is either
978 C<$params{as}> or, missing that, C<$params{with}>.
982 Sets the POST parameter name for AJAX request after dropping an
983 element (see C<$params{with}>).
987 An optional jQuery selector specifying which part of the child element
988 is dragable. If the parameter is not given then it defaults to
989 C<.dragdrop> matching DOM elements with the class C<dragdrop>. If the
990 parameter is set and empty then the whole child element is dragable,
991 and clicks through to underlying elements like inputs or links might
994 =item C<dont_recolor>
996 If trueish then the children will not be recolored. The default is to
997 recolor the children by setting the class C<listrow0> on odd and
998 C<listrow1> on even entries.
1004 <script type="text/javascript" src="js/jquery-ui.js"></script>
1006 <table id="thing_list">
1008 <tr><td>This</td><td>That</td></tr>
1011 <tr id="thingy_2"><td>stuff</td><td>more stuff</td></tr>
1012 <tr id="thingy_15"><td>stuff</td><td>more stuff</td></tr>
1013 <tr id="thingy_6"><td>stuff</td><td>more stuff</td></tr>
1017 [% L.sortable_element('#thing_list tbody',
1018 url => 'controller.pl?action=SystemThings/reorder',
1021 recolor_rows => 1) %]
1023 After dropping e.g. the third element at the top of the list a POST
1024 request would be made to the C<reorder> action of the C<SystemThings>
1025 controller with a single parameter called C<thing_ids> -- an array
1026 containing the values C<[ 6, 2, 15 ]>.
1030 Dumps the Argument using L<Data::Dumper> into a E<lt>preE<gt> block.
1032 =item C<sortable_table_header $by, %params>
1034 Create a link and image suitable for placement in a table
1035 header. C<$by> must be an index set up by the controller with
1036 L<SL::Controller::Helper::make_sorted>.
1038 The optional parameter C<$params{title}> can override the column title
1039 displayed to the user. Otherwise the column title from the
1040 controller's sort spec is used.
1042 The other parameters in C<%params> are passed unmodified to the
1043 underlying call to L<SL::Controller::Base::url_for>.
1045 See the documentation of L<SL::Controller::Helper::Sorted> for an
1046 overview and further usage instructions.
1048 =item C<paginate_controls>
1050 Create a set of links used to paginate a list view.
1052 See the documentation of L<SL::Controller::Helper::Paginated> for an
1053 overview and further usage instructions.
1057 =head2 CONVERSION FUNCTIONS
1061 =item C<tab, description, target, %PARAMS>
1063 Creates a tab for C<tabbed>. The description will be used as displayed name.
1064 The target should be a block or template that can be processed. C<tab> supports
1065 a C<method> parameter, which can override the process method to apply target.
1066 C<method => 'raw'> will just include the given text as is. I was too lazy to
1067 implement C<include> properly.
1069 Also an C<if> attribute is supported, so that tabs can be suppressed based on
1070 some occasion. In this case the supplied block won't even get processed, and
1071 the resulting tab will get ignored by C<tabbed>:
1073 L.tab('Awesome tab wih much info', '_much_info.html', if => SELF.wants_all)
1075 =item C<truncate $text, %params>
1077 Returns the C<$text> truncated after a certain number of
1080 The number of characters to truncate at is determined by the parameter
1081 C<at> which defaults to 50. If the text is longer than C<$params{at}>
1082 then it will be truncated and postfixed with '...'. Otherwise it will
1083 be returned unmodified.
1087 =head1 MODULE AUTHORS
1089 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
1091 L<http://linet-services.de>