File Coverage

blib/lib/CGI/Form/Table.pm
Criterion Covered Total %
statement 54 54 100.0
branch 18 18 100.0
condition 10 10 100.0
subroutine 12 12 100.0
pod 6 6 100.0
total 100 100 100.0


\n" }
line stmt bran cond sub pod time code
1              
2             package CGI::Form::Table;
3              
4 3     3   108979 use strict;
  3         7  
  3         115  
5 3     3   17 use warnings;
  3         5  
  3         2750  
6              
7             =head1 NAME
8              
9             CGI::Form::Table - create a table of form inputs
10              
11             =head1 VERSION
12              
13             version 0.161
14              
15             $Id: /my/cs/projects/formtable/trunk/lib/CGI/Form/Table.pm 27835 2006-11-11T04:18:20.685136Z rjbs $
16              
17             =cut
18              
19             our $VERSION = '0.161';
20              
21             =head1 SYNOPSIS
22              
23             use CGI::Form::Table;
24              
25             my $form = CGI::Form::Table->new(
26             prefix => 'employee',
27             columns => [qw(lname fname job age)]
28             );
29              
30             print $form->as_html;
31             print $form->javascript;
32              
33             =head1 DESCRIPTION
34              
35             This module simplifies the creation of an HTML table containing form inputs.
36             The table can be extended to include extra rows, and these rows can be removed.
37             Each has a unique name, and on form submission the inputs are effectively
38             serialized.
39              
40             L will use the CGI module to produce a data structure
41             based on the parameters submitted by a form of this type.
42              
43             =head1 METHODS
44              
45             =head2 C<< CGI::Form::Table->new(%arg) >>
46              
47             This method constructs a new form. The only required arguments are
48             C, which names the columns that will be in the form table, and
49             C, which gives the unique prefix for input fields.
50              
51             If given, C specifies how many rows should initially be in the
52             form.
53              
54             Instead of C, you can pass C, a reference to an
55             array of hashes providing values for the columns of each row. For example:
56              
57             my $table = CGI::Form::Table->new(
58             prefix => "charsheet",
59             columns => [ qw(ability score) ],
60             initial_values => [
61             { ability => 'Str', score => '18/00' },
62             { ability => 'Cha', score => '11' }
63             ]
64             );
65              
66             C, if passed, is a hash of text strings to use as column
67             headers. The keys are column names. Columns without C entries
68             are headed by their names.
69              
70             Another argument, C, may be passed. It must contain a hashref,
71             with entries providing subs to produce initial content. The subs are passed the
72             form object, the row number, and the name of the column. For example, to add a
73             reminder of the current row in the middle of each row, you might create a form
74             like this:
75              
76             my $form = CGI::Form::Table->new(
77             prefix => 'simpleform',
78             columns => [qw(one two reminder three four)],
79             column_content => {
80             reminder => sub { $_[1] }
81             }
82             );
83              
84             This can be useful for forms that require SELECT elements or other complicated
85             parts. (The JavaScript will just copy the column value when new rows are added,
86             updating the name attribute.)
87              
88             =cut
89              
90             sub new {
91 9     9 1 4214 my ($class, %arg) = @_;
92 9 100       36 return unless $arg{columns};
93 7 100       20 return unless $arg{prefix};
94 6 100       19 $arg{initial_rows} = 1 unless $arg{initial_rows};
95 1         20 $arg{initial_rows} = @{$arg{initial_values}}
  2         9  
96 6 100 100     19 if ($arg{initial_values} && @{$arg{initial_values}} > $arg{initial_rows});
97 6         25 bless \%arg => $class;
98             }
99              
100             =head2 C<< $form->as_html >>
101              
102             This returns HTML representing the form object. JavaScript is required to make
103             the form expandible/shrinkable; see the C method. (L)
104              
105             =cut
106              
107             sub as_html {
108 6     6 1 33 my ($self) = @_;
109 6         15 my $prefix = $self->{prefix};
110              
111 30         57 my $column_headers = join q{},
112 6         13 map { "\t\t\t" . $self->column_header($_) . "
113 6         8 @{$self->{columns}};
114              
115 6         18 my $html = <<"EOH";
116             " } \n";
117            
118            
119            
120            
121            
122             $column_headers
123            
124            
125            
126              
127            
128             EOH
129              
130 6         23 for my $row_number (1 .. $self->{initial_rows}) {
131 95         193 my $content = join q{},
132 19         33 map { "" . $self->cell_content($row_number, $_) . "
133 19         24 @{$self->{columns}};
134              
135 19         104 $html .= <<"EOH";
136            
137             $row_number
138            
139            
140            
141            
142            
143            
144             $content
145             $row_number
146            
147             EOH
148             }
149 6         13 $html .= "\t
150 6         7 $html .= "
\n";
151              
152 6         80 return $html;
153             }
154              
155             =head2 C<< $form->column_header($column_name) >>
156              
157             This method returns the text that should be used as the column header in the
158             table output. If no header was given in the initialization of the form, the
159             column name is returned verbatim. (No checking is done to ensure that the
160             named column actually exists.)
161              
162             =cut
163              
164             sub column_header {
165 30     30 1 35 my ($self, $name) = @_;
166 30 100       126 defined $self->{column_header}{$name} ? $self->{column_header}{$name} : $name;
167             }
168              
169             =head2 C<< $form->cell_content($row, $column_name) >>
170              
171             This method returns the text (HTML) that should appear in the given row and
172             column. If no C entry was given for the column, a basic input
173             element is generated.
174              
175             =cut
176              
177             sub cell_content {
178 95     95 1 116 my ($self, $row, $name) = @_;
179              
180 95 100       240 my $content_generator =
181             $self->{column_content}{$name}
182             ? $self->{column_content}{$name}
183             : $self->_input;
184 95         168 return $content_generator->($self, $row, $name);
185             }
186              
187             # $form->_select(\@pairs, \%arg)
188             #
189             # given a ref to a list of two-element arrayrefs (value, text), returns a
190             # coderef to produce a select element via column_content
191             sub _select {
192 2     2   1270 my ($self, $pairs, $arg) = @_;
193             sub {
194 5     5   6 my ($self, $row, $name) = @_;
195 5         14 my $content = "
196 5         13 $content .= " $_='$arg->{$_}'" for keys %$arg;
197 5         8 $content .= ">";
198 5         7 my $value = $self->cell_value($row, $name);
199 5         10 for (@$pairs) {
200 18 100 100     97 $content .= "
201             . (($value && $_->[0] && $value eq $_->[0]) ? " selected='selected'" : q{})
202             . ">$_->[1]\n";
203             }
204 5         7 $content .= "\n";
205 5         18 return $content;
206             }
207 2         27 }
208              
209             # $form->_input(\%arg)
210             #
211             # returns a coderef to produce an input element via column_content
212             sub _input {
213 86     86   105 my ($self, $arg) = @_;
214 86   100     290 $arg ||= {};
215             sub {
216 89     89   112 my ($self, $row, $name) = @_;
217 89 100 100     398 return "{type} ? "type='$arg->{type}'" : q{})
218             . "name='$self->{prefix}_${row}_$name' value='"
219             . ($self->cell_value($row,$name) || q{}) . "' />";
220             }
221 86         352 }
222              
223             =head2 C<< $form->cell_value($row, $column_name) >>
224              
225             This method returns the default value for the given row and column, taken from
226             the C passed to the initializer.
227              
228             =cut
229              
230             sub cell_value {
231 94     94 1 122 my ($self, $row, $column_name) = @_;
232 94 100       831 return unless defined $self->{initial_values}[--$row];
233 25         178 return $self->{initial_values}[$row]{$column_name};
234             }
235              
236             =head2 C<< $class->javascript >>
237              
238             This method returns JavaScript that will make the handlers for the HTML buttons
239             work. This code has been (poorly) tested in Firefox, MSIE, and WebKit-based
240             browsers.
241              
242             =cut
243              
244             sub javascript {
245 6     6 1 11 my $self = shift;
246 6         25 return <<"EOS";
247             function removeParentOf(child, prefix) {
248             tbody = child.parentNode.parentNode;
249             if (tbody.rows.length > 1)
250             tbody.removeChild(child.parentNode);
251             renumberRows(tbody, prefix);
252             }
253             function cloneParentOf(child, prefix) {
254             clone = child.parentNode.cloneNode( true );
255             tbody = child.parentNode.parentNode;
256             tbody.insertBefore( clone, child.parentNode.nextSibling );
257             renumberRows(tbody, prefix);
258             }
259             function renumberRows(tbody, prefix) {
260             var rowList = tbody.rows;
261             for (i = 0; i < rowList.length; i++) {
262             rowNumber = rowList.length - i;
263             rowList[i].cells[0].firstChild.nodeValue = rowNumber;
264             for (j = 0; j < rowList[i].cells.length; j++) {
265             prefix_pattern = new RegExp('^' + prefix + '_\\\\d+_');
266              
267             element_types = ['button', 'input', 'select', 'textarea'];
268             for (type in element_types) {
269             inputs = rowList[i].cells[j].getElementsByTagName(element_types[type]);
270             for (k = 0; k < inputs.length; k++) {
271             if (inputs[k].name.match(prefix_pattern))
272             inputs[k].name = inputs[k].name.replace(
273             prefix_pattern,
274             prefix + "_" + rowNumber + "_"
275             );
276             }
277             }
278             }
279             var cell_count = rowList[i].cells.length;
280             rowList[i].cells[cell_count - 1].firstChild.nodeValue = rowNumber;
281             }
282             }
283             EOS
284              
285             }
286              
287             =head1 SEE ALSO
288              
289             =over 4
290              
291             =item * L
292              
293             =item * L
294              
295             =back
296              
297             =head1 AUTHOR
298              
299             Ricardo SIGNES, C<< >>
300              
301             =head1 BUGS
302              
303             Please report any bugs or feature requests through the web interface at
304             L. I will be notified, and then you'll automatically be
305             notified of progress on your bug as I make changes.
306              
307             =head1 COPYRIGHT
308              
309             Copyright 2004 Ricardo SIGNES, All Rights Reserved.
310              
311             This program is free software; you can redistribute it and/or modify it
312             under the same terms as Perl itself.
313              
314             =cut
315              
316             1;
317              
318