File Coverage

lib/CGI/FormBuilder/Field/checkbox.pm
Criterion Covered Total %
statement 83 89 93.2
branch 35 48 72.9
condition 4 9 44.4
subroutine 8 8 100.0
pod 1 2 50.0
total 131 156 83.9


line stmt bran cond sub pod time code
1              
2             ###########################################################################
3             # Copyright (c) Nate Wiger http://nateware.com. All Rights Reserved.
4             # Please visit http://formbuilder.org for tutorials, support, and examples.
5             ###########################################################################
6              
7             # The majority of this module's methods (including new) are
8             # inherited directly from ::base, since they involve things
9             # which are common, such as parameter parsing. The only methods
10             # that are individual to different fields are those that affect
11             # the rendering, such as script() and tag()
12              
13             package CGI::FormBuilder::Field::checkbox;
14              
15 4     4   14 use strict;
  4         5  
  4         118  
16 4     4   18 use warnings;
  4         5  
  4         143  
17 4     4   15 no warnings 'uninitialized';
  4         5  
  4         145  
18              
19 4     4   13 use CGI::FormBuilder::Util;
  4         4  
  4         453  
20 4     4   17 use CGI::FormBuilder::Field;
  4         5  
  4         67  
21 4     4   12 use base 'CGI::FormBuilder::Field';
  4         100  
  4         3445  
22              
23              
24             our $VERSION = '3.10';
25              
26             sub script {
27 16     16 0 20 my $self = shift;
28 16         33 my $name = $self->name;
29              
30             # The way script() works is slightly backwards: First the
31             # type-specific JS DOM code is generated, then this is
32             # passed as a string to Field->jsfield, which wraps this
33             # in the generic handling.
34              
35             # Holders for different parts of JS code
36 16         20 my $jsfunc = '';
37 16         29 my $jsfield = tovar($name);
38 16         21 my $close_brace = '';
39 16         36 my $in = indent(my $idt = 1); # indent
40              
41 16         42 my $alertstr = escapejs($self->jsmessage); # handle embedded '
42 16         24 $alertstr .= '\n';
43              
44             #
45             # If you can believe this, the Javascript DOM actually changes
46             # based on whether there are more than 1 option to checkboxes.
47             # Damn damn damn I hate the JavaScript DOM so damn much!!!!!!
48             #
49             # Whoever designed this should be shot.
50             #
51 16 100       31 if ($self->options == 1) {
52              
53             # Simple single-element on/off checkbox
54 4         14 $jsfunc .= <
55             // $name: single-element checkbox
56             var $jsfield = null;
57             if (document.getElementById('$name') != null && form.elements['$name'].checked) {
58             $jsfield = form.elements['$name'].value;
59             }
60             EOJS
61              
62             } else {
63              
64             # Get field from radio buttons or checkboxes.
65             # Must cycle through all again to see which is checked. Yeesh.
66              
67 12         49 $jsfunc .= <
68             // $name: radio group or multiple checkboxes
69             var $jsfield = null;
70             var selected_$jsfield = 0;
71             for (var loop = 0; loop < form.elements['$name'].length; loop++) {
72             if (form.elements['$name']\[loop].checked) {
73             $jsfield = form.elements['$name']\[loop].value;
74             selected_$jsfield++;
75             EOJS
76              
77             # Add catch for "other" if applicable
78 12 50       25 if ($self->other) {
79 0         0 my $oth = $self->othername;
80 0         0 $jsfunc .= <
81             if ($jsfield == '$oth') $jsfield = form.elements['$oth'].value;
82             EOJS
83             }
84              
85 12         20 $close_brace = <
86              
87             } // if
88             } // for $name
89             EOJS
90              
91             # required?
92 12 50       61 $close_brace .= <required;
93             if (! selected_$jsfield) {
94             alertstr += '$alertstr';
95             invalid++;
96             }
97             EOJS
98              
99             # indent the very last if/else tests so they're in the for loop
100 12         31 $in = indent($idt += 2);
101             }
102            
103 16         44 return $self->jsfield($jsfunc, $close_brace, $in);
104             }
105              
106             *render = \&tag;
107             sub tag {
108 16     16 1 38 local $^W = 0; # -w sucks
109 16         48 my $self = shift;
110 16         50 my $attr = $self->attr;
111              
112 16         72 my $jspre = $self->{_form}->jsprefix;
113              
114 16         19 my $tag = '';
115 16         41 my @value = $self->tag_value; # sticky is different in
116 16         45 my @opt = $self->options;
117 16         62 debug 2, "my(@opt) = \$field->options";
118              
119             # Add in our "Other:" option if applicable
120 16 50       39 push @opt, [$self->othername, $self->{_form}{messages}->form_other_default]
121             if $self->other;
122              
123 16         53 debug 2, "$self->{name}: generating $attr->{type} input type";
124              
125 16         20 my $checkbox_table = 0; # toggle
126 16         14 my $checkbox_col = 0;
127 16 100       52 if ($self->columns > 0) {
128 12         14 $checkbox_table = 1;
129 12         29 my $c = $self->{_form}->class('_columns');
130 12         31 $tag .= $self->{_form}->table(class => $c) . "\n";
131             }
132              
133 16 50       35 belch "$self->{name}: No options specified for 'checkbox' field" unless @opt;
134 16         29 for my $opt (@opt) {
135             # Divide up checkboxes in a user-controlled manner
136 46 100       67 if ($checkbox_table) {
137 36 100       105 $tag .= " ".htmltag('tr')."\n" if $checkbox_col % $self->columns == 0;
138 36         68 $tag .= ' '.htmltag('td') . $self->{_form}->font;
139             }
140             # Since our data structure is a series of ['',''] things,
141             # we get the name from that. If not, then it's a list
142             # of regular old data that we toname() if nameopts => 1
143 46         86 my($o,$n) = optval($opt);
144              
145             # Must use defined() or else labels of "0" are lost
146 46 100       90 unless (defined($n)) {
147 36         44 $n = $attr->{labels}{$o};
148 36 50       51 unless (defined($n)) {
149 36 50       109 $n = $self->nameopts ? toname($o) : $o;
150             }
151             }
152              
153             ismember($o, @value) ? $attr->{checked} = 'checked'
154 46 100       87 : delete $attr->{checked};
155              
156             # reset some attrs
157 46         52 $attr->{value} = $o;
158 46 100       66 if (@opt == 1) {
159             # single option checkboxes do not modify id
160 4   33     19 $attr->{id} ||= tovar($attr->{name});
161             } else {
162             # all others add the current option name
163 42 50       98 $attr->{id} = tovar($o eq $self->othername
164             ? "_$attr->{name}" : "$attr->{name}_$o");
165             }
166              
167             #
168             # Special event handling for our _other field
169             #
170             # This is the only piece that's different from ::radio, since
171             # here what we do is just key off whether the "Other" box is
172             # being checked or unchecked.
173             #
174 46 50 33     108 if ($self->other && $self->javascript) {
175 0         0 my $b = $self->othername; # box
176 0 0       0 if ($n eq $self->{_form}{messages}->form_other_default) {
177             #
178             # Turn on when they click the "_other" field
179             #
180 0         0 my $vct = "${jspre}${b}_ct";
181 0         0 $attr->{onclick} = "var $vct;"
182             . "if (this.checked && ! $vct) ${jspre}other_on('$b'); "
183             . "else { ${jspre}other_off('$b'); $vct = 1 }";
184             }
185             }
186              
187             # Each radio/checkbox gets a human thingy with
188 46         152 $tag .= $self->add_before_option;
189 46         175 $tag .= htmltag('input', $attr);
190             $tag .= $checkbox_table
191 46 100       109 ? (htmltag('/td')."\n ".htmltag('td').$self->{_form}->font) : ' ';
192 46         108 my $c = $self->{_form}->class('_option');
193 46 100       93 $tag .= htmltag('label', for => $attr->{id}, class => $c)
194             . ($self->cleanopts ? escapehtml($n) : $n)
195             . htmltag('/label');
196 46         181 $tag .= $self->add_after_option;
197              
198 46 50       136 $tag .= '
' if $self->linebreaks;
199              
200 46 100       75 if ($checkbox_table) {
201 36         28 $checkbox_col++;
202 36         59 $tag .= htmltag('/td');
203 36 100       107 $tag .= "\n ".htmltag('/tr') if $checkbox_col % $self->columns == 0;
204             }
205 46         70 $tag .= "\n";
206             }
207 16 50 66     65 $tag .= ' '.htmltag('/tr') if $checkbox_table && ($checkbox_col % $self->columns > 0);
208 16 100       44 $tag .= ' '.htmltag('/table') if $checkbox_table;
209              
210             # add an additional tag for our _other field
211 16 50       36 $tag .= ' ' . $self->othertag if $self->other;
212              
213 16         54 debug 2, "$self->{name}: generated tag = $tag";
214 16         101 return $tag; # always return scalar tag
215             }
216              
217             1;
218              
219             __END__