use base qw( Template::Plugin );
use Template::Plugin;
+use Data::Dumper;
use List::MoreUtils qw(apply);
use List::Util qw(max);
+use Scalar::Util qw(blessed);
+
+use SL::Presenter;
+use SL::Util qw(_hashify);
use strict;
# Do not use these id's to store information across requests.
my $_id_sequence = int rand 1e7;
sub _tag_id {
- return $_id_sequence = ($_id_sequence + 1) % 1e7;
+ return "id_" . ( $_id_sequence = ($_id_sequence + 1) % 1e7 );
}
}
}
sub _J {
- my $string = "" . shift;
- $string =~ s/\"/\\\"/g;
+ my $string = shift;
+ $string =~ s/(\"|\'|\\)/\\$1/g;
return $string;
}
-sub _hashify {
- return (@_ && (ref($_[0]) eq 'HASH')) ? %{ $_[0] } : @_;
-}
-
sub new {
my ($class, $context, @args) = @_;
return $_[0]->{CONTEXT};
}
-sub name_to_id {
- my $self = shift;
- my $name = shift;
+sub _call_presenter {
+ my ($method, $self, @args) = @_;
- $name =~ s/[^\w_]/_/g;
- $name =~ s/_+/_/g;
+ my $presenter = $::request->presenter;
- return $name;
-}
-
-sub attributes {
- my ($self, @slurp) = @_;
- my %options = _hashify(@slurp);
-
- my @result = ();
- while (my ($name, $value) = each %options) {
- next unless $name;
- next if $name eq 'disabled' && !$value;
- $value = '' if !defined($value);
- push @result, _H($name) . '="' . _H($value) . '"';
+ if (!$presenter->can($method)) {
+ $::lxdebug->message(LXDebug::WARN(), "SL::Presenter has no method named '$method'!");
+ return '';
}
- return @result ? ' ' . join(' ', @result) : '';
+ splice @args, -1, 1, %{ $args[-1] } if @args && (ref($args[-1]) eq 'HASH');
+
+ $presenter->$method(@args);
}
-sub html_tag {
- my ($self, $tag, $content, @slurp) = @_;
- my $attributes = $self->attributes(@slurp);
+sub name_to_id { return _call_presenter('name_to_id', @_); }
+sub html_tag { return _call_presenter('html_tag', @_); }
+sub select_tag { return _call_presenter('select_tag', @_); }
+sub input_tag { return _call_presenter('input_tag', @_); }
+sub truncate { return _call_presenter('truncate', @_); }
+sub simple_format { return _call_presenter('simple_format', @_); }
+sub part_picker { return _call_presenter('part_picker', @_); }
- return "<${tag}${attributes}/>" unless defined($content);
- return "<${tag}${attributes}>${content}</${tag}>";
+sub _set_id_attribute {
+ my ($attributes, $name) = @_;
+ SL::Presenter::Tag::_set_id_attribute($attributes, $name);
}
-sub select_tag {
- my $self = shift;
- my $name = shift;
- my $options_str = shift;
- my %attributes = _hashify(@_);
+sub img_tag {
+ my ($self, %options) = _hashify(1, @_);
- $attributes{id} ||= $self->name_to_id($name);
- $options_str = $self->options_for_select($options_str) if ref $options_str;
+ $options{alt} ||= '';
- return $self->html_tag('select', $options_str, %attributes, name => $name);
+ return $self->html_tag('img', undef, %options);
}
sub textarea_tag {
- my ($self, $name, $content, @slurp) = @_;
- my %attributes = _hashify(@slurp);
+ my ($self, $name, $content, %attributes) = _hashify(3, @_);
- $attributes{id} ||= $self->name_to_id($name);
+ _set_id_attribute(\%attributes, $name);
+ $attributes{rows} *= 1; # required by standard
+ $attributes{cols} *= 1; # required by standard
$content = $content ? _H($content) : '';
return $self->html_tag('textarea', $content, %attributes, name => $name);
}
sub checkbox_tag {
- my ($self, $name, @slurp) = @_;
- my %attributes = _hashify(@slurp);
+ my ($self, $name, %attributes) = _hashify(2, @_);
- $attributes{id} ||= $self->name_to_id($name);
+ _set_id_attribute(\%attributes, $name);
$attributes{value} = 1 unless defined $attributes{value};
my $label = delete $attributes{label};
+ my $checkall = delete $attributes{checkall};
if ($attributes{checked}) {
$attributes{checked} = 'checked';
my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'checkbox');
$code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
+ $code .= $self->javascript(qq|\$('#$attributes{id}').checkall('$checkall');|) if $checkall;
return $code;
}
sub radio_button_tag {
- my $self = shift;
- my $name = shift;
- my %attributes = _hashify(@_);
+ my ($self, $name, %attributes) = _hashify(2, @_);
- $attributes{value} = 1 unless defined $attributes{value};
- $attributes{id} ||= $self->name_to_id($name . "_" . $attributes{value});
+ _set_id_attribute(\%attributes, $name);
+ $attributes{value} = 1 unless exists $attributes{value};
my $label = delete $attributes{label};
if ($attributes{checked}) {
return $code;
}
-sub input_tag {
- my ($self, $name, $value, @slurp) = @_;
- my %attributes = _hashify(@slurp);
-
- $attributes{id} ||= $self->name_to_id($name);
- $attributes{type} ||= 'text';
-
- return $self->html_tag('input', undef, %attributes, name => $name, value => $value);
-}
-
sub hidden_tag {
- return shift->input_tag(@_, type => 'hidden');
+ my ($self, $name, $value, %attributes) = _hashify(3, @_);
+ return $self->input_tag($name, $value, %attributes, type => 'hidden');
}
sub div_tag {
}
sub link {
- my ($self, $href, $content, @slurp) = @_;
- my %params = _hashify(@slurp);
+ my ($self, $href, $content, %params) = _hashify(3, @_);
$href ||= '#';
}
sub submit_tag {
- my ($self, $name, $value, @slurp) = @_;
- my %attributes = _hashify(@slurp);
+ my ($self, $name, $value, %attributes) = _hashify(3, @_);
- $attributes{onclick} = "if (confirm('" . delete($attributes{confirm}) . "')) return true; else return false;" if $attributes{confirm};
+ if ( $attributes{confirm} ) {
+ $attributes{onclick} = 'return confirm("'. _J(delete($attributes{confirm})) .'");';
+ }
return $self->input_tag($name, $value, %attributes, type => 'submit', class => 'submit');
}
sub button_tag {
- my ($self, $onclick, $value, @slurp) = @_;
- my %attributes = _hashify(@slurp);
+ my ($self, $onclick, $value, %attributes) = _hashify(3, @_);
- return $self->input_tag(undef, $value, %attributes, type => 'button', onclick => $onclick);
-}
+ _set_id_attribute(\%attributes, $attributes{name}) if $attributes{name};
+ $attributes{type} ||= 'button';
-sub options_for_select {
- my $self = shift;
- my $collection = shift;
- my %options = _hashify(@_);
+ $onclick = 'if (!confirm("'. _J(delete($attributes{confirm})) .'")) return false; ' . $onclick if $attributes{confirm};
- my $value_key = $options{value} || 'id';
- my $title_key = $options{title} || $value_key;
-
- my $value_sub = $options{value_sub};
- my $title_sub = $options{title_sub};
-
- my $value_title_sub = $options{value_title_sub};
-
- my %selected = map { ( $_ => 1 ) } @{ ref($options{default}) eq 'ARRAY' ? $options{default} : defined($options{default}) ? [ $options{default} ] : [] };
+ return $self->html_tag('input', undef, %attributes, value => $value, onclick => $onclick);
+}
- my $access = sub {
- my ($element, $index, $key, $sub) = @_;
- my $ref = ref $element;
- return $sub ? $sub->($element)
- : !$ref ? $element
- : $ref eq 'ARRAY' ? $element->[$index]
- : $ref eq 'HASH' ? $element->{$key}
- : $element->$key;
- };
+sub ajax_submit_tag {
+ my ($self, $url, $form_selector, $text, @slurp) = @_;
- my @elements = ();
- push @elements, [ undef, $options{empty_title} || '' ] if $options{with_empty};
- push @elements, map [
- $value_title_sub ? $value_title_sub->($_) : (
- $access->($_, 0, $value_key, $value_sub),
- $access->($_, 1, $title_key, $title_sub),
- )
- ], @{ $collection } if $collection && ref $collection eq 'ARRAY';
+ $url = _J($url);
+ $form_selector = _J($form_selector);
+ my $onclick = qq|kivi.submit_ajax_form('${url}', '${form_selector}')|;
- my $code = '';
- foreach my $result (@elements) {
- my %attributes = ( value => $result->[0] );
- $attributes{selected} = 'selected' if $selected{ defined($result->[0]) ? $result->[0] : '' };
+ return $self->button_tag($onclick, $text, @slurp);
+}
- $code .= $self->html_tag('option', _H($result->[1]), %attributes);
- }
+sub yes_no_tag {
+ my ($self, $name, $value, %attributes) = _hashify(3, @_);
- return $code;
+ return $self->select_tag($name, [ [ 1 => $::locale->text('Yes') ], [ 0 => $::locale->text('No') ] ], default => $value ? 1 : 0, %attributes);
}
sub javascript {
return $code;
}
+my $date_tag_id_idx = 0;
sub date_tag {
- my ($self, $name, $value, @slurp) = @_;
- my %params = _hashify(@slurp);
- my $name_e = _H($name);
- my $seq = _tag_id();
- my $datefmt = apply {
- s/d+/\%d/gi;
- s/m+/\%m/gi;
- s/y+/\%Y/gi;
- } $::myconfig{"dateformat"};
-
- $params{cal_align} ||= 'BR';
-
- $self->input_tag($name, $value,
- id => $name_e,
+ my ($self, $name, $value, %params) = _hashify(3, @_);
+
+ _set_id_attribute(\%params, $name);
+ my @onchange = $params{onchange} ? (onChange => delete $params{onchange}) : ();
+ my @classes = $params{no_cal} || $params{readonly} ? () : ('datepicker');
+ push @classes, delete($params{class}) if $params{class};
+ my %class = @classes ? (class => join(' ', @classes)) : ();
+
+ return $self->input_tag(
+ $name, blessed($value) ? $value->to_lxoffice : $value,
size => 11,
- title => _H($::myconfig{dateformat}),
- onBlur => 'check_right_date_format(this)',
- %params,
- ) . ((!$params{no_cal}) ?
- $self->html_tag('img', undef,
- src => 'image/calendar.png',
- id => "trigger$seq",
- title => _H($::myconfig{dateformat}),
+ onblur => "check_right_date_format(this);",
%params,
- ) .
- $self->javascript(
- "Calendar.setup({ inputField: '$name_e', ifFormat: '$datefmt', align: '$params{cal_align}', button: 'trigger$seq' });"
- ) : '');
+ %class, @onchange,
+ );
+}
+
+sub customer_picker {
+ my ($self, $name, $value, %params) = @_;
+ my $name_e = _H($name);
+
+ $::request->{layout}->add_javascripts('autocomplete_customer.js');
+
+ $self->hidden_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => 'customer_autocomplete') .
+ $self->input_tag("$name_e\_name", (ref $value && $value->can('name')) ? $value->name : '', %params);
+}
+
+# simple version with select_tag
+sub vendor_selector {
+ my ($self, $name, $value, %params) = @_;
+
+ my $actual_vendor_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"}) ? $::form->{"$name"}->id : $::form->{"$name"}) :
+ (ref $value && $value->can('id')) ? $value->id : '';
+
+ return $self->select_tag($name, SL::DB::Manager::Vendor->get_all(),
+ default => $actual_vendor_id,
+ title_sub => sub { $_[0]->vendornumber . " : " . $_[0]->name },
+ 'with_empty' => 1,
+ %params);
}
+
+# simple version with select_tag
+sub part_selector {
+ my ($self, $name, $value, %params) = @_;
+
+ my $actual_part_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"})? $::form->{"$name"}->id : $::form->{"$name"}) :
+ (ref $value && $value->can('id')) ? $value->id : '';
+
+ return $self->select_tag($name, SL::DB::Manager::Part->get_all(),
+ default => $actual_part_id,
+ title_sub => sub { $_[0]->partnumber . " : " . $_[0]->description },
+ with_empty => 1,
+ %params);
+}
+
+
sub javascript_tag {
my $self = shift;
my $code = '';
}
sub tabbed {
- my ($self, $tabs, @slurp) = @_;
- my %params = _hashify(@slurp);
+ my ($self, $tabs, %params) = _hashify(2, @_);
my $id = $params{id} || 'tab_' . _tag_id();
$params{selected} *= 1;
next if $tab eq '';
- my $selected = $params{selected} == $i;
- my $tab_id = "__tab_id_$i";
- push @header, $self->li_tag(
- $self->link('', $tab->{name}, rel => $tab_id),
- ($selected ? (class => 'selected') : ())
- );
- push @blocks, $self->div_tag($tab->{data},
- id => $tab_id, class => 'tabcontent');
+ my $tab_id = "__tab_id_$i";
+ push @header, $self->li_tag($self->link('#' . $tab_id, $tab->{name}));
+ push @blocks, $self->div_tag($tab->{data}, id => $tab_id);
}
return '' unless @header;
- return $self->ul_tag(
- join('', @header), id => $id, class => 'shadetabs'
- ) .
- $self->div_tag(
- join('', @blocks), class => 'tabcontentstyle'
- ) .
- $self->javascript(
- qq|var $id = new ddtabcontent("$id");$id.setpersist(true);| .
- qq|$id.setselectedClassTarget("link");$id.init();|
- );
+
+ my $ul = $self->ul_tag(join('', @header), id => $id);
+ return $self->div_tag(join('', $ul, @blocks), class => 'tabwidget');
}
sub tab {
- my ($self, $name, $src, @slurp) = @_;
- my %params = _hashify(@slurp);
+ my ($self, $name, $src, %params) = _hashify(3, @_);
$params{method} ||= 'process';
}
sub areainput_tag {
- my ($self, $name, $value, @slurp) = @_;
- my %attributes = _hashify(@slurp);
+ my ($self, $name, $value, %attributes) = _hashify(3, @_);
- my $rows = delete $attributes{rows} || 1;
+ my ($rows, $cols);
my $min = delete $attributes{min_rows} || 1;
+ if (exists $attributes{cols}) {
+ $cols = delete $attributes{cols};
+ $rows = $::form->numtextrows($value, $cols);
+ } else {
+ $rows = delete $attributes{rows} || 1;
+ }
+
return $rows > 1
- ? $self->textarea_tag($name, $value, %attributes, rows => max $rows, $min)
- : $self->input_tag($name, $value, %attributes);
+ ? $self->textarea_tag($name, $value, %attributes, rows => max($rows, $min), ($cols ? (cols => $cols) : ()))
+ : $self->input_tag($name, $value, %attributes, ($cols ? (size => $cols) : ()));
}
sub multiselect2side {
- my ($self, $id, @slurp) = @_;
- my %params = _hashify(@slurp);
+ my ($self, $id, %params) = _hashify(2, @_);
$params{labelsx} = "\"" . _J($params{labelsx} || $::locale->text('Available')) . "\"";
$params{labeldx} = "\"" . _J($params{labeldx} || $::locale->text('Selected')) . "\"";
}
sub sortable_element {
- my ($self, $selector, @slurp) = @_;
- my %params = _hashify(@slurp);
+ my ($self, $selector, %params) = _hashify(2, @_);
- my %attributes = ( helper => <<JAVASCRIPT );
+ my %attributes = ( distance => 5,
+ helper => <<'JAVASCRIPT' );
function(event, ui) {
ui.children().each(function() {
- \$(this).width(\$(this).width());
+ $(this).width($(this).width());
});
return ui;
}
JAVASCRIPT
+ my $stop_event = '';
+
if ($params{url} && $params{with}) {
my $as = $params{as} || $params{with};
my $filter = ".filter(function(idx) { return this.substr(0, " . length($params{with}) . ") == '$params{with}'; })";
$filter .= ".map(function(idx, str) { return str.replace('$params{with}_', ''); })";
+ my $params_js = $params{params} ? qq| + ($params{params})| : '';
+
+ $stop_event = <<JAVASCRIPT;
+ \$.post('$params{url}'${params_js}, { '${as}[]': \$(\$('${selector}').sortable('toArray'))${filter}.toArray() });
+JAVASCRIPT
+ }
+
+ if (!$params{dont_recolor}) {
+ $stop_event .= <<JAVASCRIPT;
+ \$('${selector}>*:odd').removeClass('listrow1').removeClass('listrow0').addClass('listrow0');
+ \$('${selector}>*:even').removeClass('listrow1').removeClass('listrow0').addClass('listrow1');
+JAVASCRIPT
+ }
+
+ if ($stop_event) {
$attributes{stop} = <<JAVASCRIPT;
function(event, ui) {
- \$.post('$params{url}', { '${as}[]': \$(\$('${selector}').sortable('toArray'))${filter}.toArray() });
+ ${stop_event}
return ui;
}
JAVASCRIPT
}
+ $params{handle} = '.dragdrop' unless exists $params{handle};
+ $attributes{handle} = "'$params{handle}'" if $params{handle};
+
my $attr_str = join(', ', map { "${_}: $attributes{$_}" } keys %attributes);
my $code = <<JAVASCRIPT;
<script type="text/javascript">
\$(function() {
- \$( "${selector}" ).sortable({ ${attr_str} }).disableSelection();
+ \$( "${selector}" ).sortable({ ${attr_str} })
});
</script>
JAVASCRIPT
}
sub online_help_tag {
- my ($self, $tag, @slurp) = @_;
- my %params = _hashify(@slurp);
+ my ($self, $tag, %params) = _hashify(2, @_);
my $cc = $::myconfig{countrycode};
my $file = "doc/online/$cc/$tag.html";
my $text = $params{text} || $::locale->text('Help');
die 'malformed help tag' unless $tag =~ /^[a-zA-Z0-9_]+$/;
return unless -f $file;
- return $self->html_tag('a', $text, href => $file, target => '_blank');
+ return $self->html_tag('a', $text, href => $file, class => 'jqModal')
}
sub dump {
my $self = shift;
- require Data::Dumper;
return '<pre>' . Data::Dumper::Dumper(@_) . '</pre>';
}
+sub sortable_table_header {
+ my ($self, $by, %params) = _hashify(2, @_);
+
+ my $controller = $self->{CONTEXT}->stash->get('SELF');
+ my $sort_spec = $controller->get_sort_spec;
+ my $by_spec = $sort_spec->{$by};
+ my %current_sort_params = $controller->get_current_sort_params;
+ my ($image, $new_dir) = ('', $current_sort_params{dir});
+ my $title = delete($params{title}) || $::locale->text($by_spec->{title});
+
+ if ($current_sort_params{by} eq $by) {
+ my $current_dir = $current_sort_params{dir} ? 'up' : 'down';
+ $image = '<img border="0" src="image/' . $current_dir . '.png">';
+ $new_dir = 1 - ($current_sort_params{dir} || 0);
+ }
+
+ $params{ $sort_spec->{FORM_PARAMS}->[0] } = $by;
+ $params{ $sort_spec->{FORM_PARAMS}->[1] } = ($new_dir ? '1' : '0');
+
+ return '<a href="' . $controller->get_callback(%params) . '">' . _H($title) . $image . '</a>';
+}
+
+sub paginate_controls {
+ my ($self, %params) = _hashify(1, @_);
+
+ my $controller = $self->{CONTEXT}->stash->get('SELF');
+ my $paginate_spec = $controller->get_paginate_spec;
+ my %paginate_params = $controller->get_current_paginate_params;
+
+ my %template_params = (
+ pages => \%paginate_params,
+ url_maker => sub {
+ my %url_params = _hashify(0, @_);
+ $url_params{ $paginate_spec->{FORM_PARAMS}->[0] } = delete $url_params{page};
+ $url_params{ $paginate_spec->{FORM_PARAMS}->[1] } = delete $url_params{per_page} if exists $url_params{per_page};
+
+ return $controller->get_callback(%url_params);
+ },
+ %params,
+ );
+
+ return SL::Presenter->get->render('common/paginate', %template_params);
+}
+
1;
__END__
[% USE L %]
- [% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right' ] ]) %]
+ [% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right', 1 ] ]) %]
- [% L.select_tag('direction', L.options_for_select([ { direction => 'left', display => 'To the left' },
- { direction => 'right', display => 'To the right' } ],
- value => 'direction', title => 'display', default => 'right')) %]
+ [% L.select_tag('direction', [ { direction => 'left', display => 'To the left' },
+ { direction => 'right', display => 'To the right' } ],
+ value_key => 'direction', title_key => 'display', default => 'right')) %]
+
+ [% L.select_tag('direction', [ { direction => 'left', display => 'To the left' },
+ { direction => 'right', display => 'To the right', selected => 1 } ],
+ value_key => 'direction', title_key => 'display')) %]
=head1 DESCRIPTION
A module modeled a bit after Rails' ActionView helpers. Several small
functions that create HTML tags from various kinds of data sources.
+The C<id> attribute is usually calculated automatically. This can be
+overridden by either specifying an C<id> attribute or by setting
+C<no_id> to trueish.
+
=head1 FUNCTIONS
=head2 LOW-LEVEL FUNCTIONS
-=over 4
+The following items are just forwarded to L<SL::Presenter::Tag>:
-=item C<name_to_id $name>
+=over 2
-Converts a name to a HTML id by replacing various characters.
+=item * C<name_to_id $name>
-=item C<attributes %items>
+=item * C<stringify_attributes %items>
-Creates a string from all elements in C<%items> suitable for usage as
-HTML tag attributes. Keys and values are HTML escaped even though keys
-must not contain non-ASCII characters for browsers to accept them.
+=item * C<html_tag $tag_name, $content_string, %attributes>
-=item C<html_tag $tag_name, $content_string, %attributes>
+=back
-Creates an opening and closing HTML tag for C<$tag_name> and puts
-C<$content_string> between the two. If C<$content_string> is undefined
-or empty then only a E<lt>tag/E<gt> tag will be created. Attributes
-are key/value pairs added to the opening tag.
+=head2 HIGH-LEVEL FUNCTIONS
-C<$content_string> is not HTML escaped.
+The following functions are just forwarded to L<SL::Presenter::Tag>:
-=back
+=over 2
-=head2 HIGH-LEVEL FUNCTIONS
+=item * C<input_tag $name, $value, %attributes>
-=over 4
+=item * C<select_tag $name, \@collection, %attributes>
-=item C<select_tag $name, $options_string, %attributes>
+=back
-Creates a HTML 'select' tag named C<$name> with the contents
-C<$options_string> and with arbitrary HTML attributes from
-C<%attributes>. The tag's C<id> defaults to C<name_to_id($name)>.
+Available high-level functions implemented in this module:
-The C<$options_string> is usually created by the
-L</options_for_select> function. If C<$options_string> is an array
-reference then it will be passed to L</options_for_select>
-automatically.
+=over 4
-=item C<input_tag $name, $value, %attributes>
+=item C<yes_no_tag $name, $value, %attributes>
-Creates a HTML 'input type=text' tag named C<$name> with the value
-C<$value> and with arbitrary HTML attributes from C<%attributes>. The
-tag's C<id> defaults to C<name_to_id($name)>.
+Creates a HTML 'select' tag with the two entries C<yes> and C<no> by
+calling L<select_tag>. C<$value> determines
+which entry is selected. The C<%attributes> are passed through to
+L<select_tag>.
=item C<hidden_tag $name, $value, %attributes>
If C<$attributes{confirm}> is set then a JavaScript popup dialog will
be added via the C<onclick> handler asking the question given with
-C<$attributes{confirm}>. If request is only submitted if the user
+C<$attributes{confirm}>. The request is only submitted if the user
clicks the dialog's ok/yes button.
+=item C<ajax_submit_tag $url, $form_selector, $text, %attributes>
+
+Creates a HTML 'input type="button"' tag with a very specific onclick
+handler that submits the form given by the jQuery selector
+C<$form_selector> to the URL C<$url> (the actual JavaScript function
+called for that is C<kivi.submit_ajax_form()> in
+C<js/client_js.js>). The button's label will be C<$text>.
+
+=item C<button_tag $onclick, $text, %attributes>
+
+Creates a HTML 'input type="button"' tag with an onclick handler
+C<$onclick> and a value of C<$text>. The button does not have a name
+nor an ID by default.
+
+If C<$attributes{confirm}> is set then a JavaScript popup dialog will
+be prepended to the C<$onclick> handler asking the question given with
+C<$attributes{confirm}>. The request is only submitted if the user
+clicks the dialog's "ok/yes" button.
+
=item C<textarea_tag $name, $value, %attributes>
Creates a HTML 'textarea' tag named C<$name> with the content
created with said C<label>. No attribute named C<label> is created in
that case.
-=item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
+If C<%attributes> contains a key C<checkall> then the value is taken as a
+JQuery selector and clicking this checkbox will also toggle all checkboxes
+matching the selector.
+
+=item C<date_tag $name, $value, %attributes>
Creates a date input field, with an attached javascript that will open a
-calendar on click. The javascript ist by default anchoered at the bottom right
-sight. This can be overridden with C<cal_align>, see Calendar documentation for
-the details, usually you'll want a two letter abbreviation of the alignment.
-Right + Bottom becomes C<BL>.
+calendar on click.
=item C<radio_button_tag $name, %attributes>
with '.css' if it isn't already and prefixed with 'css/' if it doesn't
contain a slash.
-=item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
-
-Creates a date input field, with an attached javascript that will open a
-calendar on click. The javascript ist by default anchoered at the bottom right
-sight. This can be overridden with C<cal_align>, see Calendar documentation for
-the details, usually you'll want a two letter abbreviation of the alignment.
-Right + Bottom becomes C<BL>.
-
=item C<tabbed \@tab, %attributes>
Will create a tabbed area. The tabs should be created with the helper function
L.tab(LxERP.t8('Custom Variables'), 'part/_cvar_tab.html', if => SELF.display_cvar_tab),
]) %]
-An optional attribute is C<selected>, which accepts the ordinal of a tab which
-should be selected by default.
-
=item C<areainput_tag $name, $content, %PARAMS>
Creates a generic input tag or textarea tag, depending on content size. The
-mount of desired rows must be given with C<rows> parameter, Accpeted parameters
+amount of desired rows must be either given with the C<rows> parameter or can
+be computed from the value and the C<cols> paramter, Accepted parameters
include C<min_rows> for rendering a minimum of rows if a textarea is displayed.
You can force input by setting rows to 1, and you can force textarea by setting
Sets the POST parameter name for AJAX request after dropping an
element (see C<$params{with}>).
+=item C<handle>
+
+An optional jQuery selector specifying which part of the child element
+is dragable. If the parameter is not given then it defaults to
+C<.dragdrop> matching DOM elements with the class C<dragdrop>. If the
+parameter is set and empty then the whole child element is dragable,
+and clicks through to underlying elements like inputs or links might
+not work.
+
+=item C<dont_recolor>
+
+If trueish then the children will not be recolored. The default is to
+recolor the children by setting the class C<listrow0> on odd and
+C<listrow1> on even entries.
+
+=item C<params>
+
+An optional JavaScript string that is evaluated before sending the
+POST request. The result must be a string that is appended to the URL.
+
=back
Example:
<table>
[% L.sortable_element('#thing_list tbody',
- 'url' => 'controller.pl?action=SystemThings/reorder',
- 'with' => 'thingy',
- 'as' => 'thing_ids') %]
+ url => 'controller.pl?action=SystemThings/reorder',
+ with => 'thingy',
+ as => 'thing_ids',
+ recolor_rows => 1) %]
After dropping e.g. the third element at the top of the list a POST
request would be made to the C<reorder> action of the C<SystemThings>
Dumps the Argument using L<Data::Dumper> into a E<lt>preE<gt> block.
-=back
-
-=head2 CONVERSION FUNCTIONS
+=item C<sortable_table_header $by, %params>
-=over 4
+Create a link and image suitable for placement in a table
+header. C<$by> must be an index set up by the controller with
+L<SL::Controller::Helper::make_sorted>.
-=item C<options_for_select \@collection, %options>
+The optional parameter C<$params{title}> can override the column title
+displayed to the user. Otherwise the column title from the
+controller's sort spec is used.
-Creates a string suitable for a HTML 'select' tag consisting of one
-'E<lt>optionE<gt>' tag for each element in C<\@collection>. The value
-to use and the title to display are extracted from the elements in
-C<\@collection>. Each element can be one of four things:
+The other parameters in C<%params> are passed unmodified to the
+underlying call to L<SL::Controller::Base::url_for>.
-=over 12
+See the documentation of L<SL::Controller::Helper::Sorted> for an
+overview and further usage instructions.
-=item 1. An array reference with at least two elements. The first element is
-the value, the second element is its title.
+=item C<paginate_controls>
-=item 2. A scalar. The scalar is both the value and the title.
+Create a set of links used to paginate a list view.
-=item 3. A hash reference. In this case C<%options> must contain
-I<value> and I<title> keys that name the keys in the element to use
-for the value and title respectively.
-
-=item 4. A blessed reference. In this case C<%options> must contain
-I<value> and I<title> keys that name functions called on the blessed
-reference whose return values are used as the value and title
-respectively.
+See the documentation of L<SL::Controller::Helper::Paginated> for an
+overview and further usage instructions.
=back
-For cases 3 and 4 C<$options{value}> defaults to C<id> and
-C<$options{title}> defaults to C<$options{value}>.
-
-In addition to pure keys/method you can also provide coderefs as I<value_sub>
-and/or I<title_sub>. If present, these take precedence over keys or methods,
-and are called with the element as first argument. It must return the value or
-title.
-
-Lastly a joint coderef I<value_title_sub> may be provided, which in turn takes
-precedence over each individual sub. It will only be called once for each
-element and must return a list of value and title.
-
-If the option C<with_empty> is set then an empty element (value
-C<undef>) will be used as the first element. The title to display for
-this element can be set with the option C<empty_title> and defaults to
-an empty string.
+=head2 CONVERSION FUNCTIONS
-The option C<default> can be either a scalar or an array reference
-containing the values of the options which should be set to be
-selected.
+=over 4
=item C<tab, description, target, %PARAMS>
L.tab('Awesome tab wih much info', '_much_info.html', if => SELF.wants_all)
+=item C<truncate $text, [%params]>
+
+See L<SL::Presenter::Text/truncate>.
+
+=item C<simple_format $text>
+
+See L<SL::Presenter::Text/simple_format>.
+
=back
=head1 MODULE AUTHORS