File Coverage

blib/lib/HTML/DOM/Element/Form.pm
Criterion Covered Total %
statement 516 545 94.6
branch 238 294 80.9
condition 111 155 71.6
subroutine 129 136 94.8
pod 12 20 60.0
total 1006 1150 87.4


line stmt bran cond sub pod time code
1             package HTML::DOM::Element::Form;
2              
3 25     25   140 use strict;
  25         44  
  25         594  
4 25     25   101 use warnings;
  25         40  
  25         466  
5              
6 25     25   95 no Carp();
  25         63  
  25         339  
7 25     25   9840 use URI;
  25         84430  
  25         2498  
8              
9             require HTML::DOM::Element;
10             require HTML::DOM::NodeList::Magic;
11             #require HTML::DOM::Collection::Elements;
12              
13             our $VERSION = '0.058';
14             our @ISA = qw'HTML::DOM::Element';
15              
16             use overload fallback => 1,
17 0     0   0 '@{}' => sub { shift->elements },
18             '%{}' => sub {
19 5043     5043   6527 my $self = shift;
20 5043 100 100     25210 $self->isa(scalar caller) || caller->isa('HTML::DOM::_TreeBuilder')
21             and return $self;
22 23         53 $self->elements;
23 25     25   150 };
  25         38  
  25         152  
24              
25             my %elem_elems = (
26             input => 1,
27             button => 1,
28             select => 1,
29             textarea => 1,
30             );
31             sub elements {
32 56     56 1 2458 my $self = shift;
33 56   66     96 my $collection = $self->{_HTML_DOM_elems} ||= do {
34             my $collection = HTML::DOM::Collection::Elements->new(
35             my $list = HTML::DOM::NodeList::Magic->new(
36             sub {
37 25     25   2888 no warnings 'uninitialized';
  25         43  
  25         5795  
38             grep(
39             $elem_elems{tag $_} && attr $_ 'type', ne 'image',
40             $self->descendants
41             ),
42 25 100 100 25   65 @{ $self->{_HTML_DOM_mg_elems}||[] }
  25         49  
43             }
44 14         88 ));
45 14         39 $self->ownerDocument-> _register_magic_node_list($list);
46 14         162 $collection;
47             };
48 56         172 weaken $self;
49 56 100       99 if (wantarray) {
50 13         30 @$collection
51             }
52             else {
53 43         231 $collection;
54             }
55             }
56             sub add_element { # helper routine that formies use to add themselves to
57 9     9 0 11 my $self = shift; # the elements list
58 6   100     13 push @{ $self->{_HTML_DOM_mg_elems} ||= [] }, shift
59 9 100       21 if $elem_elems{ $_[0]->tag };
60 9         12 return;
61             }
62             sub remove_element { # and this is how formies remove themselves when they
63 7     7 0 11 my $self = shift; # get moved around the DOM
64 7         8 my $removee = shift;
65 7         14 @{ $self->{_HTML_DOM_mg_elems} }
66 7   50     9 = grep $_ != $removee, @{ $self->{_HTML_DOM_elems} ||= [] }
  7         14  
67             }
68              
69 2     2 1 7 sub length { shift->elements -> length }
70 25     25 1 147 sub name { no warnings; shift->_attr( name => @_) . '' }
  25     471   38  
  25         8021  
  471         5993  
71 50     50 1 27224 sub acceptCharset { shift->_attr('accept-charset' => @_) }
72             sub action {
73 40     40 1 913 my $self = shift;
74 40 100       89 (my $base = $self->ownerDocument->base)
75             or return $self->_attr('action', @_);
76 18         80 (new_abs URI
77             $self->_attr('action' => @_),
78             $self->ownerDocument->base)
79             ->as_string
80             }
81             sub enctype {
82 34     34 1 7698 my $ret = shift->_attr('enctype' => @_);
83 34 50       131 defined $ret ? $ret : 'application/x-www-form-urlencoded'
84             }
85             *encoding=*enctype;
86             sub method {
87 49     49 1 1030 my $ret = shift->_attr('method' => @_);
88 49 50       146 defined $ret ? lc $ret : 'get'
89             }
90 5     5 1 854 sub target { shift->_attr('target' => @_) }
91              
92 3     3 1 8 sub submit { shift->trigger_event('submit') }
93              
94             sub reset {
95 2     2 1 6 shift->trigger_event('reset');
96             }
97              
98             sub trigger_event {
99 89     89 1 175 my ($a,$evnt) = (shift,shift);
100             $a->SUPER::trigger_event(
101             $evnt,
102             submit_default =>
103             $a->ownerDocument->
104             default_event_handler_for('submit'),
105             reset_default => sub {
106 4     4   10 $_->_reset for shift->target->elements
107             },
108 89         230 @_,
109             );
110             }
111              
112             # ------ HTML::Form compatibility methods ------ #
113              
114             sub inputs {
115 107     107 0 185 my @ret;
116             my %pos;
117 107         142 my $self = shift;
118             # This used to use ‘$self->elements’, but ->elements no longer
119             # includes image buttons.
120 107         308 for(
121             grep($elem_elems{tag $_}, $self->descendants),
122 107 50       225 @{ $self->{_HTML_DOM_mg_elems}||[] }
123             ) {
124             #next if (my $tag = tag $_) eq 'button'; # HTML::Form doesn't deal
125             # with
126             # NOW IT DOES :)
127 509         971 my $tag = tag $_;
128              
129 25     25   152 no warnings 'uninitialized'; # for 5.11.0
  25         40  
  25         35179  
130 509 100       942 if(lc $_->attr('type') eq 'radio') {
131 104         157 my $name = name $_;
132 59         109 exists $pos{$name} ? push @{$ret[$pos{$name}]}, $_
133             :( push(@ret, [$_]),
134 104 100       244 $pos{$name} = $#ret );
135             next
136 104         183 }
137 405 100       970 push @ret, $tag eq 'select'
    100          
138             ? $_->attr('multiple')
139             ? $_->find('option')
140             : scalar $_->options
141             : $_
142             }
143 107 100       614 map ref $_ eq 'ARRAY' ? new HTML::DOM::NodeList::Radio $_ : $_,
144             @ret
145             }
146              
147             sub click # 22/Sep/7: stolen from HTML::Form and modified (particularly
148             { # the last line) so I don't have to mess with Hook::LexWrap
149 12     12 1 520 my $self = shift;
150 12         20 my $name;
151 12 50       33 $name = shift if (@_ % 2) == 1; # odd number of arguments
152              
153             # try to find first submit button to activate
154 12         42 for ($self->inputs) {
155 24   100     56 my $type = $_->type; my $tag = eval { $_->tag } || '';
  24         32  
156 24 100 100     149 next unless $tag eq 'input' && $type =~ /^(?:submit|image)\z/
      50        
      66        
      66        
157             || $tag eq 'button' && ($type || 'submit') eq 'submit';
158 6 50 33     17 next if $name && $_->name ne $name;
159 6 100       14 next if $_->disabled;
160 5         15 $_->click($self, @_);return
161 5         13 }
162 7 50       21 Carp::croak("No clickable input with name $name") if $name;
163 7         16 $self->trigger_event('submit');
164             }
165              
166             # These three were shamelessly stolen from HTML::Form:
167             sub value
168             {
169             package
170             HTML::Form;
171 15     15 0 3122 my $self = shift;
172 15         37 my $key = shift;
173 15         44 my $input = $self->find_input($key);
174 15 50       37 Carp::croak("No such field '$key'") unless $input;
175 15         29 local $Carp::CarpLevel = 1;
176 15         66 $input->value(@_);
177             }
178              
179              
180             sub find_input
181             {
182             package
183             HTML::Form; # so caller tricks work
184 31     31 0 12207 my($self, $name, $type, $no) = @_;
185 31 100       74 if (wantarray) {
186 1         4 my @res;
187             my $c;
188 1         3 for ($self->inputs) {
189 2 50       6 if (defined $name) {
190 0 0       0 next unless defined(my $n = $_->name);
191 0 0       0 next if $name ne $n;
192             }
193 2 50 33     9 next if $type && $type ne $_->type;
194 2         4 $c++;
195 2 50 33     5 next if $no && $no != $c;
196 2         5 push(@res, $_);
197             }
198 1         5 return @res;
199            
200             }
201             else {
202 30   50     125 $no ||= 1;
203 30         72 for ($self->inputs) {
204 72 50       123 if (defined $name) {
205 72 50       142 next unless defined(my $n = $_->name);
206 72 100       160 next if $name ne $n;
207             }
208 30 50 33     60 next if $type && $type ne $_->type;
209 30 50       62 next if --$no;
210 30         177 return $_;
211             }
212 0         0 return undef;
213             }
214             }
215              
216             sub param {
217             package
218             HTML::Form;
219 27     27 0 9155 my $self = shift;
220 27 100       64 if (@_) {
221 25         35 my $name = shift;
222 25         52 my @inputs;
223 25         50 for ($self->inputs) {
224 175         288 my $n = $_->name;
225 175 100 66     561 next if !defined($n) || $n ne $name;
226 43         77 push(@inputs, $_);
227             }
228              
229 25 100       53 if (@_) {
230             # set
231 8 50       18 die "No '$name' parameter exists" unless @inputs;
232 8         17 my @v = @_;
233 8 100 100     30 @v = @{$v[0]} if @v == 1 && ref($v[0]);
  3         5  
234 8         19 while (@v) {
235 8         9 my $v = shift @v;
236 8         16 my $err;
237 8         35 for my $i (0 .. @inputs-1) {
238 13         15 eval {
239 13         29 $inputs[$i]->value($v);
240             };
241 13 100       211 unless ($@) {
242 7         12 undef($err);
243 7         17 splice(@inputs, $i, 1);
244 7         11 last;
245             }
246 6   66     25 $err ||= $@;
247             }
248 8 100       31 die $err if $err;
249             }
250              
251             # the rest of the input should be cleared
252 7         16 for (@inputs) {
253 6         12 $_->value(undef);
254             }
255             }
256             else {
257             # get
258 17         21 my @v;
259 17         33 for (@inputs) {
260 28 100       55 if (defined(my $v = $_->value)) {
261 16         36 push(@v, $v);
262             }
263             }
264 17 100       89 return wantarray ? @v : $v[0];
265             }
266             }
267             else {
268             # list parameter names
269 2         4 my @n;
270             my %seen;
271 2         6 for ($self->inputs) {
272 14         27 my $n = $_->name;
273 14 100 66     53 next if !defined($n) || $seen{$n}++;
274 8         15 push(@n, $n);
275             }
276 2         14 return @n;
277             }
278             }
279              
280              
281             my $ascii_encodings_re;
282             my $encodings_re;
283              
284             sub _encoding_ok {
285 30     30   3434 my ($enc,$xwfu) =@ _;
286 30         82 $enc =~ s/^(?:x-?)?mac-?/mac/i;
287 30 100 100     80 ($enc) x (Encode'resolve_alias($enc)||return)
      66        
288             =~ ($xwfu ? $ascii_encodings_re : $encodings_re ||=qr/^${\
289 2         2607 join'|',map quotemeta,
290             encodings Encode 'Byte'
291             }\z/);
292             }
293              
294             sub _apply_charset {
295 37     37   71 my($charsets,$apply_to) = @_; # array refs
296 37         62 my ($charset,@ret);
297 37         85 for(@$charsets) {
298             #use DDS; Dump $_ if @$apply_to == 1;
299 23 100       330 eval {
300 23         29 @ret = ();
301             # Can’t use map here, because it could die. (In
302             # perl5.8.x, dying inside a map is a very
303             # bad idea.)
304 23         35 for my $applyee(@$apply_to) {
305 41 100       681 push @ret, ref $applyee
306             ? $applyee
307             : Encode::encode(
308             $_,$applyee,9
309             ); # 1=croak, 8=leave src alone
310             }
311             # Phew, we survived!
312 10         326 $charset = $_;
313             } && last;
314             }
315 37 100       201 unless($charset) {
316             # If none of the charsets applied, we just use the first
317             # one in the list (or fall back to utf8, since that’s the
318             # sensible thing to do these days), replacing unknown
319             # chars with ?
320 27         37 my $fallback;
321 27   66     100 $charset = $$charsets[0]||(++$fallback,'utf8');
322 27 100       109 @ret = map ref$_ ? $_ : Encode'encode($charset,$_),
323             @$apply_to;
324 27 100       2289 $fallback and $charset = 'utf-8';
325             }
326 37         156 $charset,\@ret;
327             }
328              
329             # ~~~ This does not take non-ASCII file names into account, but I can’t
330             # really do that yet, since perl itself doesn’t support those properly
331             # yet, either.
332             # This one was stolen from HTML::Form but then modified extensively.
333             sub make_request
334             {
335 34     34 0 119 my $self = shift;
336 34         85 my $method = $self->method;
337 34         86 my $uri = $self->action;
338 34   100     15212 my $xwfu = $method eq 'get'
339             || $self->enctype !~ /^multipart\/form-data\z/i;
340 34         89 my @form = $self->form;
341              
342             # Get the charset and encode the form fields, if necessary. The HTML
343             # spec says that the a/x-w-f-u MIME type only accepts ASCII, but we’ll
344             # be more forgiving, for the sake of realism. But to be compliant with
345             # the spec in cases where it can apply (e.g., a UTF-16 page with just
346             # ASCII in its form data), we only accept ASCII-based encodings for
347             # this enctype.
348 34         71 my @charsets;
349 34   100     48 { push @charsets, split ' ', $self->acceptCharset||next}
  34         77  
350 34         1121 require Encode;
351 34         16624 @charsets = map _encoding_ok($_, $xwfu),
352             @charsets;
353 34 100       7637 unless(@charsets){{
354             # We only revert to the doc charset when accept-charset doesn’t
355             # have any usable encodings (even encodings which will cause char
356             # substitutions are considered usable; it’s non-ASCII with GET that
357             # we don’t want).
358 26   100     34 push @charsets, _encoding_ok(
  26         82  
359             ($self->ownerDocument||next)->charset || next, $xwfu
360             )
361             }}
362              
363 34 100       1018 if ($method ne "post") {
364 19         794 require HTTP::Request;
365 19         24681 $uri = URI->new($uri, "http");
366             $uri->can('query_form')
367 19 100       6903 and $uri->query_form(@{_apply_charset \@charsets, \@form});
  18         57  
368 19         1719 return HTTP::Request->new(GET => $uri);
369             }
370             else {
371 15         867 require HTTP::Request::Common;
372 15 100       3743 if($xwfu) {
373 9         26 my($charset,$form) = _apply_charset \@charsets, \@form;
374 9         39 return HTTP::Request::Common::POST($uri, $form,
375             Content_Type =>
376             "application/x-www-form-urlencoded; charset=\"$charset\"");
377             }
378             else {
379 6         14 my @new_form;
380 6         19 while(@form) {
381 10         26 my($name,$val) = (shift @form, shift @form);
382             #my $origval = $val;
383 10         29 (my $charset, $val) = _apply_charset \@charsets, [$val];
384             #use DDS; Dump [$origval,$val, ];
385 10         19 my $enc = $name;
386 10 100       30 $enc = Encode'encode('MIME-B', $enc) if $enc =~ /[^ -~]/;
387 10 100       6808 push @new_form, $enc,
388             ref $$val[0] ? $$val[0] : [(undef)x2,
389             Content_Type => "text/plain; charset=\"$charset\"",
390             Content => @$val,
391             ];
392             }
393 6         22 return HTTP::Request::Common::POST($uri, \@new_form,
394             Content_Type => 'multipart/form-data'
395             );
396             }
397             }
398             }
399              
400             sub form
401             {
402             package
403             HTML::Form; # so caller tricks work
404 37     37 0 58 my $self = shift;
405 37         90 map { $_->form_name_value($self) } $self->inputs;
  86         196  
406             }
407              
408              
409              
410              
411             package HTML::DOM::NodeList::Radio; # solely for HTML::Form compatibility
412             # Usually ::Input is used, but ::Radio
413             # is for a set of radio buttons.
414 25     25   169 use Carp 'croak';
  25         42  
  25         10351  
415             require HTML::DOM::NodeList;
416              
417             our $VERSION = '0.058';
418             our @ISA = qw'HTML::DOM::NodeList';
419              
420 0     0   0 sub type { 'radio' }
421              
422             sub name {
423 39     39   76 my $ret = (my $self = shift)->item(0)->attr('name');
424 39 50       81 if (@_) {
425 0         0 $self->item($_)->attr(name=>@_) for 0..$self->length-1;
426             }
427             $ret
428 39         464 }
429              
430             sub value { # ~~~ do case-folding and what-not, as in HTML::Form::ListInput
431 9     9   19 my $self = shift;
432              
433 9         12 my $checked_elem;
434 9         23 for (0..$self->length-1) {
435 17         40 my $btn = $self->item($_);
436 17 100       48 $btn->checked and
437             $checked_elem = $btn, last;
438             }
439              
440 9 100       19 if (@_) { for (0..$self->length-1) {
  7         17  
441 10         20 my $btn = $self->item($_);
442 10 100 66     23 $_[0] eq $btn->attr('value') and
443             $btn->disabled && croak(
444             "The value '$_[0]' has been disabled for field '${\
445             $self->name}'"
446             ),
447             $btn->checked(1),
448             last;
449             }}
450              
451 6 100       40 $checked_elem && $checked_elem->attr('value')
452             }
453              
454             sub possible_values {
455 0     0   0 my $self = shift;
456 0         0 map $self->item($_)->attr('value'), 0..$self->length-1
457             }
458              
459             sub disabled {
460 4     4   9 my $self = shift;
461 4         8 for(@$self) {
462 5 100       13 $_->disabled or return 0
463             }
464 1         7 return 1
465             }
466              
467             sub form_name_value
468             # Pilfered from HTML::Form with slight changes.
469             {
470             package
471             HTML::Form::Input;
472 7     7   8 my $self = shift;
473 7         19 my $name = $self->name;
474 7 50 33     25 return unless defined $name && length $name;
475 7 50       16 return if $self->disabled;
476 7         17 my $value = $self->value;
477 7 100       18 return unless defined $value;
478 6         16 return ($name => $value);
479             }
480              
481              
482             package HTML::DOM::Collection::Elements;
483              
484 25     25   167 use strict;
  25         61  
  25         470  
485 25     25   110 use warnings;
  25         38  
  25         672  
486              
487 25     25   110 use Scalar::Util 'weaken';
  25         40  
  25         2877  
488              
489             our $VERSION = '0.058';
490              
491             require HTML::DOM::Collection;
492             our @ISA = 'HTML::DOM::Collection';
493              
494             # Internals: \[$nodelist, $tie]
495              
496             # Field constants:
497             sub nodelist(){0}
498             sub tye(){1}
499             sub seen(){2} # whether this key has been seen
500             sub position(){3} # current (array) position used by NEXTKEY
501             sub ids(){4} # whether we are iterating through ids
502 25     25   129 { no warnings 'misc';
  25         44  
  25         2387  
503             undef &nodelist; undef &tye; undef &seen; undef &position;
504             }
505              
506             sub namedItem {
507 30     30   59 my($self, $name) = @_;
508 30         67 my $list = $$self->[nodelist];
509 30         43 my $elem;
510             my @list;
511 30         71 for(0..$list->length - 1) {
512 25     25   142 no warnings 'uninitialized';
  25         50  
  25         1923  
513 166 100 66     279 push @list, $elem if
514             ($elem = $list->item($_))->id eq $name
515             or
516             $elem->attr('name') eq $name;
517             }
518 30 100       63 if(@list > 1) {
519             # ~~~ Perhaps this should cache the new nodelist
520             # and return the same one each item. (Incident-
521             # ally, Firefox returns the same one but Safari
522             # makes a new one each time.)
523             my $ret = HTML::DOM::NodeList::Magic->new(sub {
524 25     25   182 no warnings 'uninitialized';
  25         48  
  25         4072  
525 11   66 11   19 grep $_->id eq $name ||
526             $_->attr('name') eq $name, @$list;
527 13         61 });
528 13         47 return $ret;
529             }
530 17 50       79 @list ? $list[0] :()
531             }
532              
533              
534              
535             # ----------------- Docs ----------------- #
536              
537             =head1 NAME
538              
539             HTML::DOM::Element::Form - A Perl class for representing 'form' elements in an HTML DOM tree
540              
541             =head1 VERSION
542              
543             Version 0.058
544              
545             =head1 SYNOPSIS
546              
547             use HTML::DOM;
548             $doc = HTML::DOM->new;
549             $elem = $doc->createElement('form');
550              
551             $elem->method('GET') # set attribute
552             $elem->method; # get attribute
553             $elem->enctype;
554             $elem->tagName;
555             # etc
556              
557             =head1 DESCRIPTION
558              
559             This class implements 'form' elements in an HTML::DOM tree. It
560             implements the HTMLFormElement DOM interface and inherits from
561             L (q.v.).
562              
563             A form object can be used as a hash or an array, to access its input
564             fields, so S<<< C<< $form->[0] >> >>> and S<<< C<< $form->{name} >> >>>
565             are shorthand for
566             S<<< C<< $form->elements->[0] >> >>> and
567             S<< C<<< $form->elements->{name} >>> >>, respectively.
568              
569             This class also tries to mimic L, but is not entirely
570             compatible
571             with its interface. See L, below.
572              
573             =head1 DOM METHODS
574              
575             In addition to those inherited from HTML::DOM::Element and
576             HTML::DOM::Node, this class implements the following DOM methods:
577              
578             =over 4
579              
580             =item elements
581              
582             Returns a collection (L object) in scalar
583             context,
584             or a list in list context, of all the input
585             fields this form contains. This differs slightly from the C method
586             (part of the HTML::Form interface) in that it includes 'button' elements,
587             whereas C does not (though it does include 'input' elements with
588             'button' for the type).
589              
590             =item length
591              
592             Same as C<< $form->elements->length >>.
593              
594             =item name
595              
596             =item acceptCharset
597              
598             =item action
599              
600             =item enctype
601              
602             =item method
603              
604             =item target
605              
606             Each of these returns the corresponding HTML attribute (C
607             corresponds to the 'accept-charset' attribute). If you pass an
608             argument, it will become the new value of the attribute, and the old value
609             will be returned.
610              
611             =item submit
612              
613             This triggers the form's 'submit' event, calling the default event handler
614             (see L). It is up to the default event handler to
615             take any further action. The form's C method may come in
616             handy.
617              
618             This method is actually just short for $form->trigger_event('submit'). (See
619             L.)
620              
621             =item reset
622              
623             This triggers the form's 'reset' event.
624              
625             =item trigger_event
626              
627             This class overrides the superclasses' method to trigger the default event
628             handler for form submissions, when the submit event occurs, and reset the
629             form when a reset event occurs.
630              
631             =back
632              
633             =head1 WWW::Mechanize COMPATIBILITY
634              
635             In order to work with L, this module mimics, and is
636             partly compatible with the
637             interface of, L.
638              
639             HTML::Form's class methods do not apply. If you call
640             C<< HTML::DOM::Element::Form->parse >>, for instance, you'll just get an
641             error, because it doesn't exist.
642              
643             The C and C methods do not exist either.
644              
645             The C method behaves differently from HTML::Form's, in that it does
646             not call C, but triggers a 'click' event if there is a
647             button to click, or a 'submit' event otherwise.
648              
649             The C, C, C, C, C, C,
650             C, C, C and C
651             methods should
652             work as expected.
653              
654             =head1 SEE ALSO
655              
656             L
657              
658             L
659              
660             L
661              
662             L
663              
664             =cut
665              
666              
667             # ------- HTMLSelectElement interface ---------- #
668              
669             package HTML::DOM::Element::Select;
670             our $VERSION = '0.058';
671             our @ISA = 'HTML::DOM::Element';
672              
673 25     25   146 use overload fallback=>1, '@{}' => sub { shift->options };
  25     13   45  
  25         170  
  13         419  
674             # ~~~ Don't I need %{} as well?
675              
676 25     25   1524 use Scalar'Util 'weaken';
  25         40  
  25         14542  
677              
678 22     22   48 sub type { 'select-' . qw/one multiple/[!!shift->attr('multiple')] }
679             sub selectedIndex {
680             # Unfortunately, we cannot cache this (as in v. 0.040 and earlier)
681             # as any change to the DOM will require it to be reset.
682 10     10   332 my $self = shift;
683 10         10 my $ret;
684 10 100       21 if(defined wantarray) {
685 8         9 my $x=0;
686             # ~~~ I can optimise this by using $self->traverse since
687             # I don't need the rest of the list once I've found
688             # a selected item.
689 8         15 for($self->options) {
690 13 100       22 $_->selected and
691             $ret = $x,
692             last;
693 6         9 $x++;
694             }
695 8 100       15 defined $ret or
696             $ret = -1,
697             }
698 10 100       18 @_ and ($self->options)[$_[0]]->selected(1);
699 10         29 return $ret;
700             }
701 2     2   5 sub value { shift->options->value(@_) }
702 1     1   5 sub length { scalar(()= shift->options ) }
703             sub form {
704 79     79   140 my $self = shift;
705             my $ret = ($self->look_up(_tag => 'form'))[0] || $$self{_HTML_DOM_f}
706 79 100 100     259 if defined wantarray;
707             @_ and defined $_[0]
708             ? ( weaken($$self{_HTML_DOM_f} = $_[0]), shift->add_element($self) )
709 79 100 100     227 : (delete $$self{_HTML_DOM_f} or return $ret || ())
    100          
710             ->remove_element($self);
711 77 100       339 $ret || ();
712             }
713             sub options { # ~~~ I need to make this cache the resulting collection obj
714             # but when I do so I need to weaken references to $self
715             # and make ::Options do the same.
716 114     114   162 my $self = shift;
717 114 100       182 if (wantarray) {
718 17         34 return grep tag $_ eq 'option', $self->descendants;
719             }
720             else {
721             my $collection = HTML::DOM::Collection::Options->new(
722             my $list = HTML::DOM::NodeList::Magic->new(
723 24     24   65 sub { grep tag $_ eq 'option', $self->descendants }
724 97         428 ), $self);
725 97         212 $self->ownerDocument-> _register_magic_node_list($list);
726 97         285 $collection;
727             }
728             }
729             sub disabled {
730 127 100   127   2174 shift->_attr( disabled => @_ ? $_[0] ? 'disabled' : undef : () )
    100          
731             }
732             sub multiple {
733 17 100   17   494 shift->_attr( multiple => @_ ? (undef,'multiple')[!!$_[0]] : () )
734             }
735             *name = \&HTML::DOM::Element::Form::name;
736 11     11   1769 sub size { shift->_attr( size => @_) }
737 21     21   3464 sub tabIndex { shift->_attr( tabindex => @_) }
738              
739             sub add {
740 2     2   5 my ($sel,$opt,$b4) = @_;
741             # ~~~ does the following always work or will an optgroup break it?
742 2         3 eval{$sel->insertBefore($opt,$b4)};
  2         11  
743 2         7 return;
744             }
745             sub remove {
746 1     1   3 my $self = shift;
747             # ~~~ and how about this one?
748 1   50     2 eval{$self->removeChild($self->options->item(shift) || return)};
  1         3  
749 1         4 return;
750             }
751              
752 3     3   8 sub blur { shift->trigger_event('blur') }
753 3     3   8 sub focus { shift->trigger_event('focus') }
754 2     2   11 sub _reset { my $self = shift;
755 2         5 $_->_reset for $self->options }
756              
757              
758             package HTML::DOM::Collection::Options;
759              
760 25     25   151 use strict;
  25         48  
  25         573  
761 25     25   106 use warnings;
  25         41  
  25         887  
762              
763             our $VERSION = '0.058';
764              
765 25     25   108 use Carp 'croak';
  25         49  
  25         940  
766 25     25   115 use constant sel => 5; # must not conflict with super
  25         44  
  25         1291  
767 25     25   126 { no strict 'refs'; delete ${__PACKAGE__."::"}{sel} } # after compilation
  25         40  
  25         12861  
768              
769             require HTML::DOM::Exception;
770             require HTML::DOM::Collection;
771             our @ISA = qw'HTML::DOM::Collection';
772              
773             sub new {
774 97     97   253 my $self = shift->SUPER::new(shift);
775 97         271 $$$self[sel] = shift;
776 97         137 $self
777             }
778              
779 2     2   6 sub type { 'option' }
780             sub possible_values {
781 0     0   0 map $_->value, @{+shift};
  0         0  
782             }
783              
784             sub value { # ~~~ do case-folding and what-not, as in HTML::Form::ListInput
785 5     5   8 my $self = shift;
786              
787 5         6 my $sel_elem;
788 5         16 for (0..$self->length-1) {
789 11         195 my $opt = $self->item($_);
790 11 100       25 $opt->selected and
791             $sel_elem = $opt, last;
792             }
793              
794 5 100       13 if (@_) { for (0..$self->length-1) {
  1         3  
795 3         47 my $opt = $self->item($_);
796 3         8 my $v = $opt->value;
797 3 50 0     10 $_[0] eq $v and
798             $opt->disabled && croak(
799             "The value '$_[0]' has been disabled for field '${\
800             $self->name}'"
801             ),
802             $opt->selected(1),
803             last;
804             }}
805              
806 5 100       47 !defined $sel_elem # Shouldn't happen in well-formed documents, but
807             and $sel_elem # how many documents are well-formed?
808             = $self->item(0);
809              
810 5         13 $sel_elem->value;
811             }
812              
813             sub name {
814 21     21   28 $${+shift}[sel]->name
  21         55  
815             }
816              
817             sub disabled {
818 6 100   6   162 (my $self = shift)->item(0)->look_up(_tag => 'select')->disabled
819             and return 1;
820 5         19 for (@$self) {
821 6 100       19 $_->disabled || return 0;
822             }
823 1         14 return 1
824             }
825              
826             sub length { # override
827 8     8   13 my $self = shift;
828 8 100       22 die new HTML::DOM::Exception
829             HTML::DOM::Exception::NOT_SUPPORTED_ERR,
830             "This implementation does not allow length to be set"
831             if @_;
832 7         167 $self->SUPER::length;
833             }
834              
835             *form_name_value = \& HTML::DOM::NodeList::Radio::form_name_value;
836              
837              
838             # ------- HTMLOptGroupElement interface ---------- #
839              
840             package HTML::DOM::Element::OptGroup;
841             our $VERSION = '0.058';
842             our @ISA = 'HTML::DOM::Element';
843              
844 10     10   920 sub label { shift->_attr( label => @_) }
845             *disabled = \&HTML::DOM::Element::Select::disabled;
846              
847              
848             # ------- HTMLOptionElement interface ---------- #
849              
850             package HTML::DOM::Element::Option;
851             our $VERSION = '0.058';
852             our @ISA = qw'HTML::DOM::Element';
853              
854 25     25   145 use Carp 'croak';
  25         47  
  25         15081  
855             require HTML::DOM::Exception;
856              
857             *form = \&HTML::DOM::Element::Select::form;
858             sub defaultSelected {
859 8 100   8   459 shift->_attr( selected => @_ ? $_[0] ? 'selected' : undef : () )
    100          
860             }
861              
862             sub text {
863             shift->as_text
864 8     8   516 }
865              
866             sub index {
867 2     2   3 my $self = shift;
868 2         4 my $indx = 0;
869 2         5 my @options = (my $sel = $self->look_up(_tag => 'select'))
870             ->options;
871 2         4 for(@options){
872 3 100       8 last if $self == $_;
873 1         2 $indx++;
874             }
875             # This should not happen, unless the tree is horribly mangled:
876 2 50       4 defined $indx or die new HTML::DOM::Exception
877             HTML::DOM::Exception::HIERARCHY_REQUEST_ERR,
878             "It seems this option element is not a descendant of its ancestor."
879             ;
880 2 50       5 if ( @_ ) {{
881 0         0 my $new_indx= shift;
  0         0  
882 0 0       0 last if $new_indx == $indx;
883 0 0       0 if ($new_indx == 0) {
884 0         0 $sel->insertBefore($self, $options[0]);
885 0         0 last;
886             }
887 0         0 $options[$new_indx-1]->parentNode->insertBefore(
888             $self, $options[$new_indx-1]->nextSibling
889             );
890             }}
891 2         7 $indx;
892             }
893              
894             *disabled = \&HTML::DOM::Element::Select::disabled;
895             *label = \&HTML::DOM::Element::OptGroup::label;
896              
897             sub selected {
898 51     51   67 my $self = shift;
899 51         58 my $ret;
900              
901 51 100       88 if(!defined $self->{_HTML_DOM_sel}) {
902 37   100     71 $ret = $self->attr('selected')||0;
903             }
904             else {
905             $ret = $self->{_HTML_DOM_sel}
906 14         17 }
907 51 100 66     110 if(@_ && !$ret != !$_[0]) {
908 11         26 my $sel = $self->look_up(_tag => 'select');
909 11 100 100     31 if(!$sel || $sel->multiple) {
    100          
910 8         13 $self->{_HTML_DOM_sel} = shift;
911             }
912             elsif($_[0]) { # You can't deselect the only selected
913             # option if exactly one option must be
914             # selected at any given time.
915 2         3 $self->{_HTML_DOM_sel} = shift;
916             $_ != $self and $_->{_HTML_DOM_sel} = 0
917 2   100     7 for $sel->options;
918             }
919             }
920             $ret
921 51         113 }
922              
923             sub value { # ~~~ do case-folding and what-not, as in HTML::Form::ListInput
924              
925 22     22   563 my $self = shift;
926 22         26 my $ret;
927              
928 22 100       88 if(caller =~ /^(?:HTML::Form(?:::Input)?|WWW::Mechanize)\z/) {
929             # ~~~ I can optimise this to call ->value once.
930 9 100       18 $ret = $self->selected ? $self->value : undef;
931 9 0       31 @_ and defined $_[0]
    0          
    50          
932             ? $_[0] eq $self->value
933             ? $self->selected(1)
934             : croak "Invalid value '$_[0]' for option "
935             . $self->name
936             : $self->selected(0);
937 9         24 return $ret;
938             }
939              
940 13 100       32 defined($ret = $self->attr(value => @_)) or
941             $ret = $self->text;
942              
943 13         37 return $ret;
944             }
945              
946             sub type() { 'option' }
947              
948             sub possible_values {
949             (undef, shift->value)
950 0     0   0 }
951              
952             sub name {
953 86     86   196 shift->look_up(_tag => 'select')->name
954             }
955              
956 8     8   14 sub _reset { delete shift->{_HTML_DOM_sel} }
957              
958             *form_name_value = \& HTML::DOM::NodeList::Radio::form_name_value;
959              
960              
961             # ------- HTMLInputElement interface ---------- #
962              
963             package HTML::DOM::Element::Input;
964             our $VERSION = '0.058';
965             our @ISA = qw'HTML::DOM::Element';
966              
967 25     25   155 use Carp 'croak';
  25         80  
  25         5886  
968              
969 122     122   1081 sub defaultValue { shift->_attr( value => @_) }
970             sub defaultChecked {
971 67 100   67   576 shift->_attr( checked => @_ ? $_[0] ? 'checked' : undef : () )
    100          
972             }
973             *form = \&HTML::DOM::Element::Select::form;
974 5     5   444 sub accept { shift->_attr( accept => @_) }
975 25     25   2636 sub accessKey { shift->_attr( accesskey => @_) }
976 10     10   1747 sub align { lc shift->_attr( align => @_) }
977 5     5   908 sub alt { shift->_attr( alt => @_) }
978             sub checked {
979 109     109   130 my $self = shift;
980 109         117 my $ret;
981 109 100       179 if(!defined $self->{_HTML_DOM_checked}) {
982 43         431 $ret = $self->defaultChecked
983             }
984             else {
985             $ret = $self->{_HTML_DOM_checked}
986 66         93 }
987 109 100 100     313 if( @_ && !$ret != not $self->{_HTML_DOM_checked} = shift
      100        
      100        
988             and !$ret and $self->type eq 'radio' ) {
989 8 50 33     19 if(
990             my $form = $self->form and defined(my $name = $self->name)
991             ) {
992 25     25   168 no warnings 'uninitialized';
  25         42  
  25         9997  
993             $_ != $self && $_->type eq 'radio'
994             && $_->name eq $name
995             and $_->{_HTML_DOM_checked} = 0
996 8   100     19 for $form->elements;
      100        
      100        
997             }
998             }
999 109         244 return $ret;
1000             }
1001             *disabled = \&HTML::DOM::Element::Select::disabled;
1002 5     5   901 sub maxLength { shift->_attr( maxlength => @_) }
1003             *name = \&HTML::DOM::Element::Form::name;
1004 12 100   12   926 sub readOnly { shift->_attr(readonly => @_ ? $_[0]?'readonly':undef : ()) }
    100          
1005             *size = \&HTML::DOM::Element::Select::size;
1006 5     5   847 sub src { shift->_attr( src => @_) }
1007             *tabIndex = \&HTML::DOM::Element::Select::tabIndex;
1008             sub type {
1009 351     351   1602 my $ret = shift->_attr('type', @_);
1010 351 50       1199 return defined $ret ? lc $ret : 'text'
1011             }
1012 5     5   858 sub useMap { shift->_attr( usemap => @_) }
1013             sub value {
1014 187     187   4004 my $self = shift;
1015 187         231 my($ret,$type);
1016              
1017 187 100 66     783 if(caller =~ /^(?:HTML::Form(?:::Input)?|WWW::Mechanize)\z/ and
      100        
1018             ($type = $self->type) =~ /^(?:button|reset)\z/ && return ||
1019             $type eq 'checkbox') {
1020             # ~~~ Do case-folding as in HTML::Input::ListInput
1021 40         83 my $value = $self->value;
1022 40 100       75 length $value or $value = 'on';
1023 40 100       84 $ret = $self->checked
1024             ? $value
1025             : undef;
1026 40 100       110 @_ and defined $_[0]
    100          
    100          
1027             ? $_[0] eq $value
1028             ? $self->checked(1)
1029             : croak
1030             "Invalid value '$_[0]' for checkbox "
1031             . $self->name
1032             : $self->checked(0);
1033 34         93 return $ret;
1034             }
1035              
1036             # ~~~ shouldn't I make sure that modifying the value attribute
1037             # (=defaultValue) leaves the value alone, even if the value has not
1038             # yet been accessed? (The same goes for checked and $option->selected)
1039 145 100       301 if(!defined $self->{_HTML_DOM_value}) {
1040 100         193 $ret = $self->defaultValue
1041             }
1042             else {
1043             $ret = $self->{_HTML_DOM_value}
1044 45         75 }
1045 145 100       273 @_ and $self->{_HTML_DOM_value} = shift;
1046 25     25   203 no warnings;
  25         53  
  25         28832  
1047 145         296 return "$ret";
1048             }
1049             sub _reset {
1050 17     17   21 my $self = shift;
1051 17         26 $self->checked($self->defaultChecked);
1052 17         28 $self->value($self->defaultValue);
1053             }
1054              
1055             *blur = \&HTML::DOM::Element::Select::blur;
1056             *focus = \&HTML::DOM::Element::Select::focus;
1057 2     2   6 sub select { shift->trigger_event('select') }
1058 7     7   24 sub click { for(shift){
1059 7         14 my(undef,$x,$y) = @_;
1060 7   50     28 defined or $_ = 1 for $x, $y;
1061 7         28 local($$_{_HTML_DOM_clicked}) = [$x,$y];
1062 7 50       16 $_->type eq 'checkbox' && $_->checked(!$_->checked);
1063 7         18 $_->trigger_event('click');
1064 7         36 return;
1065             }}
1066              
1067             sub trigger_event {
1068 100     100   187 my ($a,$evnt) = (shift,shift);
1069 100         187 my $input_type = $a->type;
1070             $a->SUPER::trigger_event(
1071             $evnt,
1072             $input_type =~ /^(?:(submi)|rese)t\z/
1073             ?( DOMActivate_default =>
1074             # I’m not using a closure here, because we
1075             # don’t want the overhead of cloning it
1076             # when it might not even be used.
1077             (sub { (shift->target->form||return)
1078 3   50 3   9 ->trigger_event('submit') },
1079             sub { (shift->target->form||return)
1080 1   50 1   3 ->trigger_event('reset') })
1081 100 100       382 [!$1]
1082             ) :(),
1083             @_
1084             );
1085             }
1086              
1087             sub possible_values {
1088 0 0   0   0 $_[0]->type eq 'checkbox' ? wantarray ? (undef, shift->value) : 2
    0          
1089             : ()
1090             }
1091             sub form_name_value
1092             {
1093 64     64   97 my $self = shift;
1094 64         131 my $type = $self->type;
1095 64 100       146 if ($type =~ /^(image|submit)\z/) {
1096 6 100       16 return unless $self->{_HTML_DOM_clicked};
1097 4 100       13 if($1 eq 'image') {
1098 2         4 my $name = $self->name;
1099 2 100       6 $name = length $name ? "$name." : '';
1100             return "${name}x" => $self->{_HTML_DOM_clicked}[0],
1101 2         8 "${name}y" => $self->{_HTML_DOM_clicked}[1]
1102             }
1103             }
1104 60 100       183 return $type eq 'file'
1105             ? $self->HTML_Form_FileInput_form_name_value(@_)
1106             : $self->HTML_Form_Input_form_name_value(@_);
1107             }
1108              
1109             # These two were stolen from HTML::Form with a few tweaks:
1110             sub HTML_Form_Input_form_name_value
1111             {
1112             package
1113             HTML::Form::Input;
1114 56     56   74 my $self = shift;
1115 56         103 my $name = $self->name;
1116 56 100 66     200 return unless defined $name && length $name;
1117 54 100       134 return if $self->disabled;
1118 53         112 my $value = $self->value;
1119 53 100       96 return unless defined $value;
1120 51         171 return ($name => $value);
1121             }
1122              
1123             sub HTML_Form_FileInput_form_name_value {
1124             package
1125             HTML::Form::ListInput;
1126 5     5   12 my($self, $form) = @_;
1127 5 100 66     14 return $self-> HTML_Form_Input_form_name_value($form)
1128             if uc $form->method ne "POST" ||
1129             lc $form->enctype ne "multipart/form-data";
1130              
1131 4         12 my $name = $self->name;
1132 4 50       13 return unless defined $name;
1133 4 50       12 return if $self->{disabled};
1134              
1135 4         11 my $file = $self->file;
1136 4         13 my $filename = $self->filename;
1137 4         13 my @headers = $self->headers;
1138 4         11 my $content = $self->content;
1139 4 50 66     23 if (defined $content) {
    100          
1140 0 0       0 $filename = $file unless defined $filename;
1141 0         0 $file = undef;
1142 0         0 unshift(@headers, "Content" => $content);
1143             }
1144             elsif (!defined($file) || length($file) == 0) {
1145 1         3 return;
1146             }
1147              
1148             # legacy (this used to be the way to do it)
1149 3 50       12 if (ref($file) eq "ARRAY") {
1150 0         0 my $f = shift @$file;
1151 0         0 my $fn = shift @$file;
1152 0         0 push(@headers, @$file);
1153 0         0 $file = $f;
1154 0 0       0 $filename = $fn unless defined $filename;
1155             }
1156              
1157 3         14 return ($name => [$file, $filename, @headers]);
1158             }
1159              
1160              
1161              
1162             *file = \&value;
1163              
1164             sub filename {
1165 4     4   7 my $self = shift;
1166 4         8 my $old = $self->{_HTML_DOM_filename};
1167 4 50       10 $self->{_HTML_DOM_filename} = shift if @_;
1168 4 50       12 $old = $self->file unless defined $old;
1169 4         7 $old;
1170             }
1171              
1172       4     sub headers { } # ~~~ Do I want to complete this?
1173              
1174             sub content {
1175 4     4   6 my $self = shift;
1176 4         8 my $old = $self->{_HTML_DOM_content};
1177 4 50       8 $self->{_HTML_DOM_content} = shift if @_;
1178 4         9 $old;
1179             }
1180              
1181              
1182              
1183             # ------- HTMLTextAreaElement interface ---------- #
1184              
1185             package HTML::DOM::Element::TextArea;
1186             our $VERSION = '0.058';
1187             our @ISA = qw'HTML::DOM::Element';
1188              
1189             sub defaultValue { # same as HTML::DOM::Element::Title::text
1190 11   66 11   37 ($_[0]->firstChild or
1191             @_ > 1 && $_[0]->appendChild(
1192             shift->ownerDocument->createTextNode(shift)
1193             ),
1194             return '',
1195             )->data(@_[1..$#_]);
1196             }
1197             *form = \&HTML::DOM::Element::Select::form;
1198             *accessKey = \&HTML::DOM::Element::Input::accessKey;
1199 5     5   895 sub cols { shift->_attr( cols => @_) }
1200             *disabled = \&HTML::DOM::Element::Select::disabled;
1201             *name = \&HTML::DOM::Element::Select::name;
1202             *readOnly = \&HTML::DOM::Element::Input::readOnly;
1203 5     5   897 sub rows {shift->_attr( rows => @_) }
1204             *tabIndex = \&HTML::DOM::Element::Select::tabIndex;
1205 1     1   5 sub type { 'textarea' }
1206             sub value {
1207 5     5   9 my $self = shift;
1208 5         7 my $ret;
1209              
1210 5 100       14 if(!defined $self->{_HTML_DOM_value}) {
1211 4         18 $ret = $self->defaultValue
1212             }
1213             else {
1214             $ret = $self->{_HTML_DOM_value}
1215 1         2 }
1216 5 100       20 @_ and $self->{_HTML_DOM_value} = shift;
1217 5         18 return $ret;
1218             }
1219             *blur = \&HTML::DOM::Element::Select::blur;
1220             *focus = \&HTML::DOM::Element::Select::focus;
1221             *select = \&HTML::DOM::Element::Input::select;
1222              
1223             sub _reset {
1224 0     0   0 my $self = shift;
1225 0         0 $self->value($self->defaultValue);
1226             }
1227              
1228             *form_name_value = \& HTML::DOM::NodeList::Radio::form_name_value;
1229              
1230              
1231             # ------- HTMLButtonElement interface ---------- #
1232              
1233             package HTML::DOM::Element::Button;
1234             our $VERSION = '0.058';
1235             our @ISA = qw'HTML::DOM::Element';
1236              
1237             *form = \&HTML::DOM::Element::Select::form;
1238             *accessKey = \&HTML::DOM::Element::Input::accessKey;
1239             *disabled = \&HTML::DOM::Element::Select::disabled;
1240             *name = \&HTML::DOM::Element::Form::name;
1241             *tabIndex = \&HTML::DOM::Element::Select::tabIndex;
1242 25     25   181 sub type { no warnings 'uninitialized'; lc shift->attr('type') }
  25     46   45  
  25         11103  
  46         100  
1243 6     6   16 sub value { shift->attr( value => @_) }
1244              
1245             sub form_name_value
1246             {
1247 15     15   19 my $self = shift;
1248 15         18 my $type = $self->type;
1249 15 100 66     42 return unless !$type or $type eq 'submit';
1250 9 100       19 return unless $self->{_HTML_DOM_clicked};
1251 3         7 my $name = $self->name;
1252 3 50 33     12 return unless defined $name && length $name;
1253 3 50       8 return if $self->disabled;
1254 3         8 my $value = $self->value;
1255 3 100       11 return ($name => defined $value ? $value : '');
1256             }
1257              
1258              
1259 5     5   17 sub click { for(shift){
1260 5         22 local($$_{_HTML_DOM_clicked}) = 1;
1261 5         19 $_->trigger_event('click');
1262 5         24 return;
1263             }}
1264              
1265             sub trigger_event {
1266 29     29   62 my ($a,$evnt) = (shift,shift);
1267 29   100     59 my $input_type = $a->type || 'submit';
1268             $a->SUPER::trigger_event(
1269             $evnt,
1270             $input_type =~ /^(?:(submi)|rese)t\z/
1271             ?( DOMActivate_default =>
1272             # I’m not using a closure here, because we
1273             # don’t want the overhead of cloning it
1274             # when it might not even be used.
1275             (sub { (shift->target->form||return)
1276 4   50 4   13 ->trigger_event('submit') },
1277             sub { (shift->target->form||return)
1278 1   50 1   2 ->trigger_event('reset') })
1279 29 50       282 [!$1]
1280             ) :(),
1281             @_
1282             );
1283             }
1284              
1285       1     sub _reset {}
1286              
1287             # ------- HTMLLabelElement interface ---------- #
1288              
1289             package HTML::DOM::Element::Label;
1290             our $VERSION = '0.058';
1291             our @ISA = qw'HTML::DOM::Element';
1292              
1293             *form = \&HTML::DOM::Element::Select::form;
1294             *accessKey = \&HTML::DOM::Element::Input::accessKey;
1295 5     5   944 sub htmlFor { shift->_attr( for => @_) }
1296              
1297             # ------- HTMLFieldSetElement interface ---------- #
1298              
1299             package HTML::DOM::Element::FieldSet;
1300             our $VERSION = '0.058';
1301             our @ISA = qw'HTML::DOM::Element';
1302              
1303             *form = \&HTML::DOM::Element::Select::form;
1304              
1305             # ------- HTMLLegendElement interface ---------- #
1306              
1307             package HTML::DOM::Element::Legend;
1308             our $VERSION = '0.058';
1309             our @ISA = qw'HTML::DOM::Element';
1310              
1311             *form = \&HTML::DOM::Element::Select::form;
1312             *accessKey = \&HTML::DOM::Element::Input::accessKey;
1313             *align = \*HTML::DOM::Element::Input::align;
1314              
1315              
1316 25     25   164 no warnings;
  25         101  
  25         2131  
1317             !+~()#%$-*