57b2a918b6f2a1351abdadc515de642598f9fb3b
[perl/html-form-simple.git] / lib / HTML / Form / Simple.pm
1 package HTML::Form::Simple;
2
3 use strict;
4 use warnings;
5
6 use XML::Quote;
7
8 our $VERSION = '1.00';
9
10
11 sub new {
12         bless {}, $_[0];
13 }
14
15 sub quote {
16         my $self = shift;
17         return XML::Quote::xml_quote_min($_[0]);
18 }
19
20 sub tag {
21         my ($self, $tag, $attr) = @_;
22
23         # strip empty if it shouldn't be
24         defined $attr->{$_} and $attr->{$_} eq '' and delete $attr->{$_}
25                 for qw(id type class style);
26
27         my $return = '<' . $tag;
28
29         # add booleans
30         delete $attr->{$_} and $return .= ' '.$_
31                 for qw(selected checked disabled readonly);
32
33         $return .= sprintf ' %s="%s"', $_, $self->quote($attr->{$_})
34                 for sort grep { defined $attr->{$_} } keys %$attr;
35
36         return $return . '>';
37 }
38
39
40 sub start {
41         my ($self, $attr) = @_;
42
43         return $self->tag(form => $attr);
44 }
45
46 sub stop {
47         return '</form>';
48 }
49
50
51 sub submit {
52         my ($self, $value, $attr) = @_;
53
54         if (ref $value eq 'HASH') {
55                 $attr = $value;
56         }
57         else {
58                 $attr ||= {};
59                 $attr->{value} = $value;
60         }
61
62         $attr->{type} = 'submit' unless defined $attr->{type};
63
64         return $self->tag(input => $attr);
65 }
66
67 sub hidden {
68         my ($self, $name, $value) = @_;
69
70         #TODO: $attr
71
72         return $self->tag(input => {type => 'hidden', name => $name, value => $value});
73 }
74
75 sub input {
76         my ($self, $name, $value, $attr) = @_;
77
78         if (ref $name eq 'HASH') {
79                 # only attributes provided (first argument)
80                 $attr = $name;
81         }
82         elsif (ref $value eq 'HASH') {
83                 # name shorthand (attributes in value parameter)
84                 $attr = $value;
85                 $attr->{name} = $name;
86         }
87         else {
88                 # name and value shorthands (all vars keep their assigned values)
89                 $attr ||= {};
90                 $attr->{name} = $name;
91                 $attr->{value} = $value;
92         }
93
94         $attr->{id}   = $attr->{name} unless defined $attr->{id};
95         $attr->{type} = 'text' unless defined $attr->{type} or defined $attr->{rows};
96         $value = delete $attr->{value} if defined $attr->{rows};
97
98         return defined $attr->{rows} ? sprintf(
99                 '%s%s</textarea>',
100                 $self->tag(textarea => $attr),
101                 $self->quote(defined $value ? $value : '')
102         ) : $self->tag(input => $attr);
103 }
104
105 sub select {
106         my ($self, $name, $rows, $value, $attr) = @_;
107
108         if (ref $value eq 'HASH') {
109                 $attr = $value;
110                 $value = delete $attr->{value};
111         }
112         else {
113                 $attr ||= {};
114                 delete $attr->{value};
115         }
116         $attr->{name} = $name;
117         $attr->{id}   = $attr->{name} unless defined $attr->{id};
118         $attr->{type} = 'select' unless defined $attr->{type};
119
120         my @options = map { ref $_ ? $_ : {value => $_} } @$rows;
121
122         if ($attr->{type} eq 'select') {
123                 delete $attr->{type};
124                 if (defined $value) {
125                         for (@options) {
126                                 $_->{selected} = 1 if defined $_->{value} and $_->{value} eq $value;
127                         }
128                 }
129                 my @return = (
130                         $self->tag(select => $attr),
131                         (map { $self->tag(option => $_) } @options),
132                         '</select>',
133                 );
134                 return wantarray ? @return : join('', @return);
135         }
136         else {
137                 if (defined $attr->{id}) {
138                         defined $_->{id} or defined $_->{value} and $_->{id} = $attr->{id}.'_'.$_->{value}
139                                 for @options;
140                 }
141                 if (defined $attr->{label}) {
142                         defined $_->{value} and not defined $_->{label}
143                                 and $_->{label} = $attr->{label}->{$_->{value}}
144                                         for @options;
145                         delete $attr->{label};
146                 }
147                 if (defined $value) {
148                         for (@options) {
149                                 $_->{checked} = 1 if defined $_->{value} and $_->{value} eq $value;
150                         }
151                 }
152                 $_ = {%$attr, %$_} for @options;
153                 my @return = map {
154                         my $label = delete $_->{label};
155                         defined $label && $label ne ''
156                                 ? '<label>'.$self->tag(input => $_)." $label</label>"
157                                 :           $self->tag(input => $_)
158                 } @options;
159                 return wantarray ? @return : join('', @return);
160         }
161 }
162
163 sub radio {
164         my ($self, $name, $value, $attr) = @_;
165
166         if (ref $value eq 'HASH') {
167                 $attr = $value;
168         }
169         else {
170                 $attr ||= {};
171                 $attr->{value} = $value;
172         }
173
174         $self->select($name, [$attr], {type => 'radio'});
175 }
176
177 sub check {
178         my ($self, $name, $label, $checked, $attr) = @_;
179
180         if (ref $label eq 'HASH') {
181                 $attr = $label;
182                 undef $label;
183         }
184         elsif (ref $checked eq 'HASH') {
185                 $attr = $checked;
186                 $attr->{label} = $label;
187         }
188         else {
189                 $attr ||= {};
190                 $attr->{checked} = $checked;
191                 $attr->{label} = $label;
192         }
193         $attr->{value} = '1' unless exists $attr->{value};
194
195         $self->select($name, [$attr], {type => 'checkbox'});
196 }
197
198 1;
199
200 =head1 NAME
201
202 HTML::Form::Simple
203
204 =head1 SYNOPSIS
205
206         my $input = HTML::Form::Simple->new;
207         say $input->start; # <form>
208         printf "<label>%s: %s</label>\n", @$_ for (
209                 [ 'Your Name' => $input->text(
210                         user => 'Mr. Default'
211                 ) ],
212                 [ Message => $input->text(
213                         msg => 'Textarea default', {rows => 4, style => 'background:red'}
214                 ) ],
215                 [ Gender => join ' or ', $input->radio(
216                         sex => ['m', 'f']
217                 ) ],
218                 [ Colour => scalar $input->select(
219                         favcolour => [qw(Blue Green Red)], 'Green'
220                 ) ],
221                 [ Options => $input->check(
222                         spam => 'Receive weekly newsletter'
223                 ) ],
224         );
225         say $input->stop; # </form>
226