Projekt

Allgemein

Profil

Herunterladen (30,4 KB) Statistiken
| Zweig: | Markierung: | Revision:
8177c150 Moritz Bunkus
package SL::Template::Plugin::L;

use base qw( Template::Plugin );
use Template::Plugin;
e3f1b7c3 Sven Schöling
use List::MoreUtils qw(apply);
7714d4d0 Sven Schöling
use List::Util qw(max);
b9894fd0 Sven Schöling
use Scalar::Util qw(blessed);
8177c150 Moritz Bunkus
use strict;

22ae0bf0 Sven Schöling
{ # This will give you an id for identifying html tags and such.
# It's guaranteed to be unique unless you exceed 10 mio calls per request.
# Do not use these id's to store information across requests.
my $_id_sequence = int rand 1e7;
sub _tag_id {
ca9e9f88 Sven Schöling
return "id_" . ( $_id_sequence = ($_id_sequence + 1) % 1e7 );
22ae0bf0 Sven Schöling
}
}

442d43e3 Sven Schöling
my %_valueless_attributes = map { $_ => 1 } qw(
checked compact declare defer disabled ismap multiple noresize noshade nowrap
readonly selected
);

47978b5b Moritz Bunkus
sub _H {
my $string = shift;
return $::locale->quote_special_chars('HTML', $string);
}

9796c91f Moritz Bunkus
sub _J {
my $string = "" . shift;
$string =~ s/\"/\\\"/g;
return $string;
}

3cc77e53 Moritz Bunkus
sub _hashify {
return (@_ && (ref($_[0]) eq 'HASH')) ? %{ $_[0] } : @_;
}

8177c150 Moritz Bunkus
sub new {
e5205a23 Moritz Bunkus
my ($class, $context, @args) = @_;
8177c150 Moritz Bunkus
e5205a23 Moritz Bunkus
return bless {
CONTEXT => $context,
}, $class;
}

sub _context {
die 'not an accessor' if @_ > 1;
return $_[0]->{CONTEXT};
8177c150 Moritz Bunkus
}

71f55f78 Moritz Bunkus
sub name_to_id {
my $self = shift;
my $name = shift;

$name =~ s/[^\w_]/_/g;
$name =~ s/_+/_/g;

return $name;
}

8177c150 Moritz Bunkus
sub attributes {
f5db53cd Sven Schöling
my ($self, @slurp) = @_;
my %options = _hashify(@slurp);
8177c150 Moritz Bunkus
my @result = ();
3cc77e53 Moritz Bunkus
while (my ($name, $value) = each %options) {
8177c150 Moritz Bunkus
next unless $name;
442d43e3 Sven Schöling
next if $_valueless_attributes{$name} && !$value;
c4e27e00 Moritz Bunkus
$value = '' if !defined($value);
442d43e3 Sven Schöling
push @result, $_valueless_attributes{$name} ? _H($name) : _H($name) . '="' . _H($value) . '"';
8177c150 Moritz Bunkus
}

return @result ? ' ' . join(' ', @result) : '';
}

sub html_tag {
f5db53cd Sven Schöling
my ($self, $tag, $content, @slurp) = @_;
my $attributes = $self->attributes(@slurp);
8177c150 Moritz Bunkus
2447f511 Sven Schöling
return "<${tag}${attributes}>" unless defined($content);
8177c150 Moritz Bunkus
return "<${tag}${attributes}>${content}</${tag}>";
}

sub select_tag {
3cc77e53 Moritz Bunkus
my $self = shift;
my $name = shift;
my $options_str = shift;
my %attributes = _hashify(@_);

$attributes{id} ||= $self->name_to_id($name);
368f9611 Moritz Bunkus
$options_str = $self->options_for_select($options_str) if ref $options_str;
3cc77e53 Moritz Bunkus
return $self->html_tag('select', $options_str, %attributes, name => $name);
}

11e4b8f3 Moritz Bunkus
sub textarea_tag {
7714d4d0 Sven Schöling
my ($self, $name, $content, @slurp) = @_;
my %attributes = _hashify(@slurp);
11e4b8f3 Moritz Bunkus
$attributes{id} ||= $self->name_to_id($name);
993ce983 Sven Schöling
$attributes{rows} *= 1; # required by standard
$attributes{cols} *= 1; # required by standard
1a8f5fe0 Moritz Bunkus
$content = $content ? _H($content) : '';
11e4b8f3 Moritz Bunkus
return $self->html_tag('textarea', $content, %attributes, name => $name);
}

3cc77e53 Moritz Bunkus
sub checkbox_tag {
f5db53cd Sven Schöling
my ($self, $name, @slurp) = @_;
my %attributes = _hashify(@slurp);
3cc77e53 Moritz Bunkus
$attributes{id} ||= $self->name_to_id($name);
$attributes{value} = 1 unless defined $attributes{value};
my $label = delete $attributes{label};
4edfabd2 Sven Schöling
my $checkall = delete $attributes{checkall};
3cc77e53 Moritz Bunkus
if ($attributes{checked}) {
$attributes{checked} = 'checked';
} else {
delete $attributes{checked};
}
8177c150 Moritz Bunkus
3cc77e53 Moritz Bunkus
my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'checkbox');
$code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
4edfabd2 Sven Schöling
$code .= $self->javascript(qq|\$('#$attributes{id}').checkall('$checkall');|) if $checkall;
8177c150 Moritz Bunkus
3cc77e53 Moritz Bunkus
return $code;
}

11e4b8f3 Moritz Bunkus
sub radio_button_tag {
my $self = shift;
my $name = shift;
my %attributes = _hashify(@_);

$attributes{value} = 1 unless defined $attributes{value};
$attributes{id} ||= $self->name_to_id($name . "_" . $attributes{value});
my $label = delete $attributes{label};

if ($attributes{checked}) {
$attributes{checked} = 'checked';
} else {
delete $attributes{checked};
}

my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'radio');
$code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;

return $code;
}

3cc77e53 Moritz Bunkus
sub input_tag {
e5205a23 Moritz Bunkus
my ($self, $name, $value, @slurp) = @_;
7714d4d0 Sven Schöling
my %attributes = _hashify(@slurp);
3cc77e53 Moritz Bunkus
$attributes{id} ||= $self->name_to_id($name);
$attributes{type} ||= 'text';

return $self->html_tag('input', undef, %attributes, name => $name, value => $value);
8177c150 Moritz Bunkus
}

9f75fa25 Sven Schöling
sub hidden_tag {
return shift->input_tag(@_, type => 'hidden');
}

e5205a23 Moritz Bunkus
sub div_tag {
my ($self, $content, @slurp) = @_;
return $self->html_tag('div', $content, @slurp);
}

sub ul_tag {
my ($self, $content, @slurp) = @_;
return $self->html_tag('ul', $content, @slurp);
}

sub li_tag {
my ($self, $content, @slurp) = @_;
return $self->html_tag('li', $content, @slurp);
}

sub link {
my ($self, $href, $content, @slurp) = @_;
my %params = _hashify(@slurp);

$href ||= '#';

return $self->html_tag('a', $content, %params, href => $href);
}

9f75fa25 Sven Schöling
sub submit_tag {
7714d4d0 Sven Schöling
my ($self, $name, $value, @slurp) = @_;
my %attributes = _hashify(@slurp);
a30ad33e Moritz Bunkus
$attributes{onclick} = "if (confirm('" . delete($attributes{confirm}) . "')) return true; else return false;" if $attributes{confirm};

return $self->input_tag($name, $value, %attributes, type => 'submit', class => 'submit');
9f75fa25 Sven Schöling
}

7714d4d0 Sven Schöling
sub button_tag {
my ($self, $onclick, $value, @slurp) = @_;
my %attributes = _hashify(@slurp);

bd1e1e7d Sven Schöling
$attributes{id} ||= $self->name_to_id($attributes{name}) if $attributes{name};
$attributes{type} ||= 'button';

return $self->html_tag('input', undef, %attributes, value => $value, onclick => $onclick);
7714d4d0 Sven Schöling
}

8177c150 Moritz Bunkus
sub options_for_select {
93bae7aa Sven Schöling
my $self = shift;
my $collection = shift;
my %options = _hashify(@_);
8177c150 Moritz Bunkus
93bae7aa Sven Schöling
my $value_key = $options{value} || 'id';
my $title_key = $options{title} || $value_key;
8177c150 Moritz Bunkus
93bae7aa Sven Schöling
my $value_sub = $options{value_sub};
my $title_sub = $options{title_sub};
ee61dd2b Moritz Bunkus
93bae7aa Sven Schöling
my $value_title_sub = $options{value_title_sub};
8177c150 Moritz Bunkus
4fe15703 Moritz Bunkus
my %selected = map { ( $_ => 1 ) } @{ ref($options{default}) eq 'ARRAY' ? $options{default} : defined($options{default}) ? [ $options{default} ] : [] };
927ead50 Moritz Bunkus
93bae7aa Sven Schöling
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;
};

my @elements = ();
push @elements, [ undef, $options{empty_title} || '' ] if $options{with_empty};
push @elements, map [
89834c12 Sven Schöling
$value_title_sub ? @{ $value_title_sub->($_) } : (
93bae7aa Sven Schöling
$access->($_, 0, $value_key, $value_sub),
$access->($_, 1, $title_key, $title_sub),
)
], @{ $collection } if $collection && ref $collection eq 'ARRAY';
8177c150 Moritz Bunkus
ee61dd2b Moritz Bunkus
my $code = '';
foreach my $result (@elements) {
my %attributes = ( value => $result->[0] );
4fe15703 Moritz Bunkus
$attributes{selected} = 'selected' if $selected{ defined($result->[0]) ? $result->[0] : '' };
ee61dd2b Moritz Bunkus
$code .= $self->html_tag('option', _H($result->[1]), %attributes);
}

return $code;
8177c150 Moritz Bunkus
}

1ec0f541 Moritz Bunkus
sub yes_no_tag {
my ($self, $name, $value) = splice @_, 0, 3;
my %attributes = _hashify(@_);

my $options = $self->options_for_select([ [ 1, $::locale->text('Yes') ], [ 0, $::locale->text('No') ] ], default => $value ? 1 : 0);
return $self->select_tag($name, $options, %attributes);
}

22ae0bf0 Sven Schöling
sub javascript {
my ($self, $data) = @_;
return $self->html_tag('script', $data, type => 'text/javascript');
}

6df470c3 Moritz Bunkus
sub stylesheet_tag {
my $self = shift;
my $code = '';

foreach my $file (@_) {
$file .= '.css' unless $file =~ m/\.css$/;
$file = "css/${file}" unless $file =~ m|/|;

$code .= qq|<link rel="stylesheet" href="${file}" type="text/css" media="screen" />|;
}

return $code;
}

22ae0bf0 Sven Schöling
sub date_tag {
my ($self, $name, $value, @slurp) = @_;
my %params = _hashify(@slurp);
my $name_e = _H($name);
my $seq = _tag_id();
e3f1b7c3 Sven Schöling
my $datefmt = apply {
s/d+/\%d/gi;
s/m+/\%m/gi;
s/y+/\%Y/gi;
} $::myconfig{"dateformat"};
22ae0bf0 Sven Schöling
08ed38c5 Sven Schöling
my $cal_align = delete $params{cal_align} || 'BR';
my $onchange = delete $params{onchange};
b9894fd0 Sven Schöling
my $str_value = blessed $value ? $value->to_lxoffice : $value;

$self->input_tag($name, $str_value,
094aaf62 Sven Schöling
id => $name_e,
22ae0bf0 Sven Schöling
size => 11,
title => _H($::myconfig{dateformat}),
onBlur => 'check_right_date_format(this)',
08ed38c5 Sven Schöling
($onchange ? (
onChange => $onchange,
) : ()),
22ae0bf0 Sven Schöling
%params,
3afb4275 Sven Schöling
) . ((!$params{no_cal} && !$params{readonly}) ?
22ae0bf0 Sven Schöling
$self->html_tag('img', undef,
src => 'image/calendar.png',
b28312f7 Sven Schöling
alt => $::locale->text('Calendar'),
22ae0bf0 Sven Schöling
id => "trigger$seq",
title => _H($::myconfig{dateformat}),
%params,
) .
$self->javascript(
b28312f7 Sven Schöling
"Calendar.setup({ inputField: '$name_e', ifFormat: '$datefmt', align: '$cal_align', button: 'trigger$seq' });"
22ae0bf0 Sven Schöling
) : '');
d6bb5c26 Moritz Bunkus
}
62ba378c Moritz Bunkus
cb3a2467 Sven Schöling
sub customer_picker {
my ($self, $name, $value, %params) = @_;
my $name_e = _H($name);

$self->hidden_tag($name, (ref $value && $value->can('id')) ? $value->id : '') .
$self->input_tag("$name_e\_name", (ref $value && $value->can('name')) ? $value->name : '', %params) .
$self->javascript(<<JS);
function autocomplete_customer (selector, column) {
\$(function(){ \$(selector).autocomplete({
source: function(req, rsp) {
\$.ajax({
url: 'controller.pl?action=Customer/ajax_autocomplete',
dataType: "json",
data: {
column: column,
term: req.term,
current: function() { \$('#$name_e').val() },
obsolete: 0,
},
success: function (data){ rsp(data) }
});
},
limit: 20,
delay: 50,
select: function(event, ui) {
\$('#$name_e').val(ui.item.id);
\$('#$name_e\_name').val(ui.item.name);
},
})});
}
autocomplete_customer('#$name_e\_name');
JS
}

eb518737 Bernd Bleßmann
# 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 : '';
my $options_str = $self->options_for_select(SL::DB::Manager::Vendor->get_all(),
default => $actual_vendor_id,
title_sub => sub { $_[0]->vendornumber . " : " . $_[0]->name },
'with_empty' => 1);
e8a2b965 Moritz Bunkus
eb518737 Bernd Bleßmann
return $self->select_tag($name, $options_str, %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 : '';
my $options_str = $self->options_for_select(SL::DB::Manager::Part->get_all(),
default => $actual_part_id,
title_sub => sub { $_[0]->partnumber . " : " . $_[0]->description },
'with_empty' => 1);
e8a2b965 Moritz Bunkus
eb518737 Bernd Bleßmann
return $self->select_tag($name, $options_str, %params);
}


62ba378c Moritz Bunkus
sub javascript_tag {
my $self = shift;
my $code = '';

foreach my $file (@_) {
$file .= '.js' unless $file =~ m/\.js$/;
$file = "js/${file}" unless $file =~ m|/|;

$code .= qq|<script type="text/javascript" src="${file}"></script>|;
}

return $code;
22ae0bf0 Sven Schöling
}

e5205a23 Moritz Bunkus
sub tabbed {
my ($self, $tabs, @slurp) = @_;
my %params = _hashify(@slurp);
f5db53cd Sven Schöling
my $id = $params{id} || 'tab_' . _tag_id();
e5205a23 Moritz Bunkus
$params{selected} *= 1;

die 'L.tabbed needs an arrayred of tabs for first argument'
unless ref $tabs eq 'ARRAY';

my (@header, @blocks);
for my $i (0..$#$tabs) {
my $tab = $tabs->[$i];

next if $tab eq '';

my $selected = $params{selected} == $i;
f5db53cd Sven Schöling
my $tab_id = "__tab_id_$i";
e5205a23 Moritz Bunkus
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');
}

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();|
);
}

sub tab {
my ($self, $name, $src, @slurp) = @_;
my %params = _hashify(@slurp);

$params{method} ||= 'process';

return () if defined $params{if} && !$params{if};

my $data;
if ($params{method} eq 'raw') {
$data = $src;
} elsif ($params{method} eq 'process') {
$data = $self->_context->process($src, %{ $params{args} || {} });
} else {
die "unknown tag method '$params{method}'";
}

return () unless $data;

return +{ name => $name, data => $data };
}

7714d4d0 Sven Schöling
sub areainput_tag {
my ($self, $name, $value, @slurp) = @_;
my %attributes = _hashify(@slurp);

796598ed Sven Schöling
my ($rows, $cols);
7714d4d0 Sven Schöling
my $min = delete $attributes{min_rows} || 1;

796598ed Sven Schöling
if (exists $attributes{cols}) {
$cols = delete $attributes{cols};
$rows = $::form->numtextrows($value, $cols);
} else {
$rows = delete $attributes{rows} || 1;
}

7714d4d0 Sven Schöling
return $rows > 1
796598ed Sven Schöling
? $self->textarea_tag($name, $value, %attributes, rows => max($rows, $min), ($cols ? (cols => $cols) : ()))
: $self->input_tag($name, $value, %attributes, ($cols ? (size => $cols) : ()));
7714d4d0 Sven Schöling
}

04854ac2 Moritz Bunkus
sub multiselect2side {
my ($self, $id, @slurp) = @_;
my %params = _hashify(@slurp);

$params{labelsx} = "\"" . _J($params{labelsx} || $::locale->text('Available')) . "\"";
$params{labeldx} = "\"" . _J($params{labeldx} || $::locale->text('Selected')) . "\"";
$params{moveOptions} = 'false';

my $vars = join(', ', map { "${_}: " . $params{$_} } keys %params);
my $code = <<EOCODE;
<script type="text/javascript">
\$().ready(function() {
\$('#${id}').multiselect2side({ ${vars} });
});
</script>
EOCODE

return $code;
}

b7383969 Moritz Bunkus
sub sortable_element {
my ($self, $selector, @slurp) = @_;
my %params = _hashify(@slurp);

1b522616 Moritz Bunkus
my %attributes = ( distance => 5,
helper => <<'JAVASCRIPT' );
b7383969 Moritz Bunkus
function(event, ui) {
ui.children().each(function() {
1b522616 Moritz Bunkus
$(this).width($(this).width());
b7383969 Moritz Bunkus
});
return ui;
}
JAVASCRIPT

3c0769d8 Moritz Bunkus
my $stop_event = '';

b7383969 Moritz Bunkus
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}_', ''); })";

3c0769d8 Moritz Bunkus
$stop_event = <<JAVASCRIPT;
\$.post('$params{url}', { '${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) {
b7383969 Moritz Bunkus
$attributes{stop} = <<JAVASCRIPT;
function(event, ui) {
3c0769d8 Moritz Bunkus
${stop_event}
b7383969 Moritz Bunkus
return ui;
}
JAVASCRIPT
}

7328cbd6 Moritz Bunkus
$params{handle} = '.dragdrop' unless exists $params{handle};
1b522616 Moritz Bunkus
$attributes{handle} = "'$params{handle}'" if $params{handle};

b7383969 Moritz Bunkus
my $attr_str = join(', ', map { "${_}: $attributes{$_}" } keys %attributes);

my $code = <<JAVASCRIPT;
<script type="text/javascript">
\$(function() {
1b522616 Moritz Bunkus
\$( "${selector}" ).sortable({ ${attr_str} })
b7383969 Moritz Bunkus
});
</script>
JAVASCRIPT

return $code;
}

c574ecd3 Sven Schöling
sub online_help_tag {
my ($self, $tag, @slurp) = @_;
my %params = _hashify(@slurp);
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;
82cb442c Sven Schöling
return $self->html_tag('a', $text, href => $file, class => 'jqModal')
c574ecd3 Sven Schöling
}

f4f55336 Sven Schöling
sub dump {
my $self = shift;
require Data::Dumper;
return '<pre>' . Data::Dumper::Dumper(@_) . '</pre>';
}

46b49275 Moritz Bunkus
sub truncate {
my ($self, $text, @slurp) = @_;
my %params = _hashify(@slurp);

$params{at} ||= 50;
$params{at} = 3 if 3 > $params{at};
$params{at} -= 3;

return $text if length($text) < $params{at};
return substr($text, 0, $params{at}) . '...';
}

9deadd1d Moritz Bunkus
sub sortable_table_header {
my ($self, $by, @slurp) = @_;
my %params = _hashify(@slurp);

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}) || $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>';
}

8177c150 Moritz Bunkus
1;

__END__

=head1 NAME

SL::Templates::Plugin::L -- Layouting / tag generation

=head1 SYNOPSIS

Usage from a template:

[% USE L %]

[% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right' ] ]) %]

[% 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')) %]

=head1 DESCRIPTION

A module modeled a bit after Rails' ActionView helpers. Several small
functions that create HTML tags from various kinds of data sources.

=head1 FUNCTIONS

71f55f78 Moritz Bunkus
=head2 LOW-LEVEL FUNCTIONS

8177c150 Moritz Bunkus
=over 4

71f55f78 Moritz Bunkus
=item C<name_to_id $name>

Converts a name to a HTML id by replacing various characters.

3cc77e53 Moritz Bunkus
=item C<attributes %items>
8177c150 Moritz Bunkus
3cc77e53 Moritz Bunkus
Creates a string from all elements in C<%items> suitable for usage as
8177c150 Moritz Bunkus
HTML tag attributes. Keys and values are HTML escaped even though keys
must not contain non-ASCII characters for browsers to accept them.

3cc77e53 Moritz Bunkus
=item C<html_tag $tag_name, $content_string, %attributes>
8177c150 Moritz Bunkus
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.

71f55f78 Moritz Bunkus
C<$content_string> is not HTML escaped.

=back

=head2 HIGH-LEVEL FUNCTIONS

=over 4

3cc77e53 Moritz Bunkus
=item C<select_tag $name, $options_string, %attributes>
71f55f78 Moritz Bunkus
3cc77e53 Moritz Bunkus
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)>.
71f55f78 Moritz Bunkus
368f9611 Moritz Bunkus
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.
71f55f78 Moritz Bunkus
1ec0f541 Moritz Bunkus
=item C<yes_no_tag $name, $value, %attributes>

Creates a HTML 'select' tag with the two entries C<yes> and C<no> by
calling L<select_tag> and L<options_for_select>. C<$value> determines
which entry is selected. The C<%attributes> are passed through to
L<select_tag>.

3cc77e53 Moritz Bunkus
=item C<input_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)>.

93bae7aa Sven Schöling
=item C<hidden_tag $name, $value, %attributes>

Creates a HTML 'input type=hidden' 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)>.

=item C<submit_tag $name, $value, %attributes>

Creates a HTML 'input type=submit class=submit' 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)>.

a30ad33e Moritz Bunkus
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
clicks the dialog's ok/yes button.

11e4b8f3 Moritz Bunkus
=item C<textarea_tag $name, $value, %attributes>

Creates a HTML 'textarea' tag named C<$name> with the content
C<$value> and with arbitrary HTML attributes from C<%attributes>. The
tag's C<id> defaults to C<name_to_id($name)>.

3cc77e53 Moritz Bunkus
=item C<checkbox_tag $name, %attributes>

Creates a HTML 'input type=checkbox' tag named C<$name> with arbitrary
HTML attributes from C<%attributes>. The tag's C<id> defaults to
C<name_to_id($name)>. The tag's C<value> defaults to C<1>.

If C<%attributes> contains a key C<label> then a HTML 'label' tag is
created with said C<label>. No attribute named C<label> is created in
that case.

4edfabd2 Sven Schöling
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.

22ae0bf0 Sven Schöling
=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>.

11e4b8f3 Moritz Bunkus
=item C<radio_button_tag $name, %attributes>

Creates a HTML 'input type=radio' tag named C<$name> with arbitrary
HTML attributes from C<%attributes>. The tag's C<value> defaults to
C<1>. The tag's C<id> defaults to C<name_to_id($name . "_" . $value)>.

If C<%attributes> contains a key C<label> then a HTML 'label' tag is
created with said C<label>. No attribute named C<label> is created in
that case.

62ba378c Moritz Bunkus
=item C<javascript_tag $file1, $file2, $file3...>

Creates a HTML 'E<lt>script type="text/javascript" src="..."E<gt>'
tag for each file name parameter passed. Each file name will be
postfixed with '.js' if it isn't already and prefixed with 'js/' if it
doesn't contain a slash.

6df470c3 Moritz Bunkus
=item C<stylesheet_tag $file1, $file2, $file3...>

Creates a HTML 'E<lt>link rel="text/stylesheet" href="..."E<gt>' tag
for each file name parameter passed. Each file name will be postfixed
with '.css' if it isn't already and prefixed with 'css/' if it doesn't
contain a slash.

e5205a23 Moritz Bunkus
=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
ffdfdc7e Sven Schöling
C<tab>. Example:

[% L.tabbed([
L.tab(LxERP.t8('Basic Data'), 'part/_main_tab.html'),
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.
e5205a23 Moritz Bunkus
7714d4d0 Sven Schöling
=item C<areainput_tag $name, $content, %PARAMS>

Creates a generic input tag or textarea tag, depending on content size. The
796598ed Sven Schöling
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
7714d4d0 Sven Schöling
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
rows to anything >1.

04854ac2 Moritz Bunkus
=item C<multiselect2side $id, %params>

Creates a JavaScript snippet calling the jQuery function
C<multiselect2side> on the select control with the ID C<$id>. The
select itself is not created. C<%params> can contain the following
entries:

=over 2

=item C<labelsx>

The label of the list of available options. Defaults to the
translation of 'Available'.

=item C<labeldx>

The label of the list of selected options. Defaults to the
translation of 'Selected'.

=back

b7383969 Moritz Bunkus
=item C<sortable_element $selector, %params>

Makes the children of the DOM element C<$selector> (a jQuery selector)
sortable with the I<jQuery UI Selectable> library. The children can be
dragged & dropped around. After dropping an element an URL can be
postet to with the element IDs of the sorted children.

82dcbe71 Moritz Bunkus
If this is used then the JavaScript file C<js/jquery-ui.js> must be
included manually as well as it isn't loaded via C<$::form-gt;header>.

b7383969 Moritz Bunkus
C<%params> can contain the following entries:

=over 2

=item C<url>

The URL to POST an AJAX request to after a dragged element has been
dropped. The AJAX request's return value is ignored. If given then
C<$params{with}> must be given as well.

=item C<with>

A string that is interpreted as the prefix of the children's ID. Upon
POSTing the result each child whose ID starts with C<$params{with}> is
considered. The prefix and the following "_" is removed from the
ID. The remaining parts of the IDs of those children are posted as a
single array parameter. The array parameter's name is either
C<$params{as}> or, missing that, C<$params{with}>.

=item C<as>

Sets the POST parameter name for AJAX request after dropping an
element (see C<$params{with}>).

1b522616 Moritz Bunkus
=item C<handle>

An optional jQuery selector specifying which part of the child element
7328cbd6 Moritz Bunkus
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.
1b522616 Moritz Bunkus
3c0769d8 Moritz Bunkus
=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.

b7383969 Moritz Bunkus
=back

Example:

82dcbe71 Moritz Bunkus
<script type="text/javascript" src="js/jquery-ui.js"></script>

b7383969 Moritz Bunkus
<table id="thing_list">
<thead>
<tr><td>This</td><td>That</td></tr>
</thead>
<tbody>
<tr id="thingy_2"><td>stuff</td><td>more stuff</td></tr>
<tr id="thingy_15"><td>stuff</td><td>more stuff</td></tr>
<tr id="thingy_6"><td>stuff</td><td>more stuff</td></tr>
</tbody>
<table>

[% L.sortable_element('#thing_list tbody',
3c0769d8 Moritz Bunkus
url => 'controller.pl?action=SystemThings/reorder',
with => 'thingy',
as => 'thing_ids',
recolor_rows => 1) %]
b7383969 Moritz Bunkus
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>
controller with a single parameter called C<thing_ids> -- an array
containing the values C<[ 6, 2, 15 ]>.

f4f55336 Sven Schöling
=item C<dump REF>

Dumps the Argument using L<Data::Dumper> into a E<lt>preE<gt> block.

9deadd1d Moritz Bunkus
=item C<sortable_table_header $by, %params>

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>.

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.

The other parameters in C<%params> are passed unmodified to the
underlying call to L<SL::Controller::Base::url_for>.

See the documentation of L<SL::Controller::Helper::Sorted> for an
overview and further usage instructions.

71f55f78 Moritz Bunkus
=back

=head2 CONVERSION FUNCTIONS

=over 4

3cc77e53 Moritz Bunkus
=item C<options_for_select \@collection, %options>
8177c150 Moritz Bunkus
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:

=over 12

=item 1. An array reference with at least two elements. The first element is
the value, the second element is its title.

=item 2. A scalar. The scalar is both the value and the title.

3cc77e53 Moritz Bunkus
=item 3. A hash reference. In this case C<%options> must contain
8177c150 Moritz Bunkus
I<value> and I<title> keys that name the keys in the element to use
for the value and title respectively.

3cc77e53 Moritz Bunkus
=item 4. A blessed reference. In this case C<%options> must contain
8177c150 Moritz Bunkus
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.

=back

For cases 3 and 4 C<$options{value}> defaults to C<id> and
C<$options{title}> defaults to C<$options{value}>.

93bae7aa Sven Schöling
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.

ee61dd2b Moritz Bunkus
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.

927ead50 Moritz Bunkus
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.

ffdfdc7e Sven Schöling
=item C<tab, description, target, %PARAMS>

Creates a tab for C<tabbed>. The description will be used as displayed name.
The target should be a block or template that can be processed. C<tab> supports
a C<method> parameter, which can override the process method to apply target.
C<method => 'raw'> will just include the given text as is. I was too lazy to
implement C<include> properly.

Also an C<if> attribute is supported, so that tabs can be suppressed based on
some occasion. In this case the supplied block won't even get processed, and
the resulting tab will get ignored by C<tabbed>:

L.tab('Awesome tab wih much info', '_much_info.html', if => SELF.wants_all)

46b49275 Moritz Bunkus
=item C<truncate $text, %params>

Returns the C<$text> truncated after a certain number of
characters.

The number of characters to truncate at is determined by the parameter
C<at> which defaults to 50. If the text is longer than C<$params{at}>
then it will be truncated and postfixed with '...'. Otherwise it will
be returned unmodified.

8177c150 Moritz Bunkus
=back

=head1 MODULE AUTHORS

Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>

L<http://linet-services.de>