File Coverage

blib/lib/HTML/DOM/Element/Form.pm
Criterion Covered Total %
statement 517 546 94.6
branch 237 294 80.6
condition 109 155 70.3
subroutine 129 136 94.8
pod 12 20 60.0
total 1004 1151 87.2


line stmt bran cond sub pod time code
1             package HTML::DOM::Element::Form;
2              
3 25     25   83 use strict;
  25         24  
  25         547  
4 25     25   69 use warnings;
  25         31  
  25         417  
5              
6 25     25   71 no Carp();
  25         20  
  25         243  
7 25     25   10455 use URI;
  25         71384  
  25         2269  
8              
9             require HTML::DOM::Element;
10             require HTML::DOM::NodeList::Magic;
11             #require HTML::DOM::Collection::Elements;
12              
13             our $VERSION = '0.056';
14             our @ISA = qw'HTML::DOM::Element';
15              
16             use overload fallback => 1,
17 0     0   0 '@{}' => sub { shift->elements },
18             '%{}' => sub {
19 5008     5008   4149 my $self = shift;
20 5008 100 100     26140 $self->isa(scalar caller) || caller->isa('HTML::DOM::_TreeBuilder')
21             and return $self;
22 23         41 $self->elements;
23 25     25   119 };
  25         26  
  25         140  
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 1405 my $self = shift;
33 56   66     97 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   2106 no warnings 'uninitialized';
  25         28  
  25         4819  
38             grep(
39             $elem_elems{tag $_} && attr $_ 'type', ne 'image',
40             $self->descendants
41             ),
42 25 100 100 25   70 @{ $self->{_HTML_DOM_mg_elems}||[] }
  25         80  
43             }
44 14         93 ));
45 14         40 $self->ownerDocument-> _register_magic_node_list($list);
46 14         163 $collection;
47             };
48 56         126 weaken $self;
49 56 100       92 if (wantarray) {
50 13         34 @$collection
51             }
52             else {
53 43         270 $collection;
54             }
55             }
56             sub add_element { # helper routine that formies use to add themselves to
57 9     9 0 7 my $self = shift; # the elements list
58 6   100     9 push @{ $self->{_HTML_DOM_mg_elems} ||= [] }, shift
59 9 100       20 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 6 my $self = shift; # get moved around the DOM
64 7         6 my $removee = shift;
65 7         8 @{ $self->{_HTML_DOM_mg_elems} }
66 7   50     6 = grep $_ != $removee, @{ $self->{_HTML_DOM_elems} ||= [] }
  7         12  
67             }
68              
69 2     2 1 6 sub length { shift->elements -> length }
70 25     25 1 104 sub name { no warnings; shift->_attr( name => @_) . '' }
  25     470   26  
  25         6702  
  470         4464  
71 49     49 1 17248 sub acceptCharset { shift->_attr('accept-charset' => @_) }
72             sub action {
73 39     39 1 703 my $self = shift;
74 39 100       80 (my $base = $self->ownerDocument->base)
75             or return $self->_attr('action', @_);
76 17         58 (new_abs URI
77             $self->_attr('action' => @_),
78             $self->ownerDocument->base)
79             ->as_string
80             }
81             sub enctype {
82 34     34 1 4602 my $ret = shift->_attr('enctype' => @_);
83 34 50       129 defined $ret ? $ret : 'application/x-www-form-urlencoded'
84             }
85             *encoding=*enctype;
86             sub method {
87 48     48 1 784 my $ret = shift->_attr('method' => @_);
88 48 50       116 defined $ret ? lc $ret : 'get'
89             }
90 5     5 1 712 sub target { shift->_attr('target' => @_) }
91              
92 3     3 1 6 sub submit { shift->trigger_event('submit') }
93              
94             sub reset {
95 2     2 1 7 shift->trigger_event('reset');
96             }
97              
98             sub trigger_event {
99 88     88 1 143 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   8 $_->_reset for shift->target->elements
107             },
108 88         192 @_,
109             );
110             }
111              
112             # ------ HTML::Form compatibility methods ------ #
113              
114             sub inputs {
115 106     106 0 90 my @ret;
116             my %pos;
117 106         131 my $self = shift;
118             # This used to use ‘$self->elements’, but ->elements no longer
119             # includes image buttons.
120 106         233 for(
121             grep($elem_elems{tag $_}, $self->descendants),
122 106 50       157 @{ $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 501         775 my $tag = tag $_;
128              
129 25     25   96 no warnings 'uninitialized'; # for 5.11.0
  25         28  
  25         30018  
130 501 100       745 if(lc $_->attr('type') eq 'radio') {
131 104         113 my $name = name $_;
132 59         82 exists $pos{$name} ? push @{$ret[$pos{$name}]}, $_
133             :( push(@ret, [$_]),
134 104 100       206 $pos{$name} = $#ret );
135             next
136 104         142 }
137 397 100       733 push @ret, $tag eq 'select'
    100          
138             ? $_->attr('multiple')
139             ? $_->find('option')
140             : scalar $_->options
141             : $_
142             }
143 106 100       582 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 172 my $self = shift;
150 12         7 my $name;
151 12 50       28 $name = shift if (@_ % 2) == 1; # odd number of arguments
152              
153             # try to find first submit button to activate
154 12         34 for ($self->inputs) {
155 24   100     37 my $type = $_->type; my $tag = eval { $_->tag } || '';
  24         23  
156 24 100 100     142 next unless $tag eq 'input' && $type =~ /^(?:submit|image)\z/
      50        
      66        
      66        
157             || $tag eq 'button' && ($type || 'submit') eq 'submit';
158 6 50 33     15 next if $name && $_->name ne $name;
159 6 100       9 next if $_->disabled;
160 5         11 $_->click($self, @_);return
161 5         9 }
162 7 50       15 Carp::croak("No clickable input with name $name") if $name;
163 7         15 $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 1696 my $self = shift;
172 15         17 my $key = shift;
173 15         28 my $input = $self->find_input($key);
174 15 50       74 Carp::croak("No such field '$key'") unless $input;
175 15         20 local $Carp::CarpLevel = 1;
176 15         36 $input->value(@_);
177             }
178              
179              
180             sub find_input
181             {
182             package
183             HTML::Form; # so caller tricks work
184 31     31 0 3978 my($self, $name, $type, $no) = @_;
185 31 100       56 if (wantarray) {
186 1         2 my @res;
187             my $c;
188 1         2 for ($self->inputs) {
189 2 50       4 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     8 next if $type && $type ne $_->type;
194 2         2 $c++;
195 2 50 33     4 next if $no && $no != $c;
196 2         4 push(@res, $_);
197             }
198 1         3 return @res;
199            
200             }
201             else {
202 30   50     103 $no ||= 1;
203 30         56 for ($self->inputs) {
204 72 50       106 if (defined $name) {
205 72 50       98 next unless defined(my $n = $_->name);
206 72 100       121 next if $name ne $n;
207             }
208 30 50 33     69 next if $type && $type ne $_->type;
209 30 50       42 next if --$no;
210 30         86 return $_;
211             }
212 0         0 return undef;
213             }
214             }
215              
216             sub param {
217             package
218             HTML::Form;
219 27     27 0 4171 my $self = shift;
220 27 100       52 if (@_) {
221 25         28 my $name = shift;
222 25         23 my @inputs;
223 25         40 for ($self->inputs) {
224 175         222 my $n = $_->name;
225 175 100 66     606 next if !defined($n) || $n ne $name;
226 43         56 push(@inputs, $_);
227             }
228              
229 25 100       42 if (@_) {
230             # set
231 8 50       17 die "No '$name' parameter exists" unless @inputs;
232 8         9 my @v = @_;
233 8 100 100     27 @v = @{$v[0]} if @v == 1 && ref($v[0]);
  3         6  
234 8         15 while (@v) {
235 8         9 my $v = shift @v;
236 8         5 my $err;
237 8         19 for my $i (0 .. @inputs-1) {
238 13         14 eval {
239 13         25 $inputs[$i]->value($v);
240             };
241 13 100       190 unless ($@) {
242 7         7 undef($err);
243 7         9 splice(@inputs, $i, 1);
244 7         8 last;
245             }
246 6   66     23 $err ||= $@;
247             }
248 8 100       25 die $err if $err;
249             }
250              
251             # the rest of the input should be cleared
252 7         16 for (@inputs) {
253 6         10 $_->value(undef);
254             }
255             }
256             else {
257             # get
258 17         15 my @v;
259 17         21 for (@inputs) {
260 28 100       59 if (defined(my $v = $_->value)) {
261 16         25 push(@v, $v);
262             }
263             }
264 17 100       74 return wantarray ? @v : $v[0];
265             }
266             }
267             else {
268             # list parameter names
269 2         2 my @n;
270             my %seen;
271 2         5 for ($self->inputs) {
272 14         19 my $n = $_->name;
273 14 100 66     60 next if !defined($n) || $seen{$n}++;
274 8         11 push(@n, $n);
275             }
276 2         13 return @n;
277             }
278             }
279              
280              
281             my $ascii_encodings_re;
282             my $encodings_re;
283              
284             sub _encoding_ok {
285 30     30   2574 my ($enc,$xwfu) =@ _;
286 30         65 $enc =~ s/^(?:x-?)?mac-?/mac/i;
287 30 100 100     56 ($enc) x (Encode'resolve_alias($enc)||return)
      66        
288             =~ ($xwfu ? $ascii_encodings_re : $encodings_re ||=qr/^${\
289 2         2028 join'|',map quotemeta,
290             encodings Encode 'Byte'
291             }\z/);
292             }
293              
294             sub _apply_charset {
295 36     36   39 my($charsets,$apply_to) = @_; # array refs
296 36         27 my ($charset,@ret);
297 36         78 for(@$charsets) {
298             #use DDS; Dump $_ if @$apply_to == 1;
299 23 100       240 eval {
300 23         23 @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         24 for my $applyee(@$apply_to) {
305 41 100       463 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         224 $charset = $_;
313             } && last;
314             }
315 36 100       152 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 26         16 my $fallback;
321 26   66     86 $charset = $$charsets[0]||(++$fallback,'utf8');
322 26 100       93 @ret = map ref$_ ? $_ : Encode'encode($charset,$_),
323             @$apply_to;
324 26 100       1403 $fallback and $charset = 'utf-8';
325             }
326 36         89 $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 33     33 0 78 my $self = shift;
336 33         59 my $method = $self->method;
337 33         51 my $uri = $self->action;
338 33   100     11651 my $xwfu = $method eq 'get'
339             || $self->enctype !~ /^multipart\/form-data\z/i;
340 33         60 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 33         49 my @charsets;
349 33   100     29 { push @charsets, split ' ', $self->acceptCharset||next}
  33         60  
350 33         1068 require Encode;
351 33         13775 @charsets = map _encoding_ok($_, $xwfu),
352             @charsets;
353 33 100       5750 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 25   100     23 push @charsets, _encoding_ok(
  25         65  
359             ($self->ownerDocument||next)->charset || next, $xwfu
360             )
361             }}
362              
363 33 100       761 if ($method ne "post") {
364 18         839 require HTTP::Request;
365 18         20085 $uri = URI->new($uri, "http");
366             $uri->can('query_form')
367 18 100       5759 and $uri->query_form(@{_apply_charset \@charsets, \@form});
  17         35  
368 18         1065 return HTTP::Request->new(GET => $uri);
369             }
370             else {
371 15         887 require HTTP::Request::Common;
372 15 100       3136 if($xwfu) {
373 9         19 my($charset,$form) = _apply_charset \@charsets, \@form;
374 9         28 return HTTP::Request::Common::POST($uri, $form,
375             Content_Type =>
376             "application/x-www-form-urlencoded; charset=\"$charset\"");
377             }
378             else {
379 6         6 my @new_form;
380 6         14 while(@form) {
381 10         16 my($name,$val) = (shift @form, shift @form);
382             #my $origval = $val;
383 10         22 (my $charset, $val) = _apply_charset \@charsets, [$val];
384             #use DDS; Dump [$origval,$val, ];
385 10         14 my $enc = $name;
386 10 100       28 $enc = Encode'encode('MIME-B', $enc) if $enc =~ /[^ -~]/;
387 10 100       5195 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         20 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 36     36 0 35 my $self = shift;
405 36         60 map { $_->form_name_value($self) } $self->inputs;
  79         128  
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   120 use Carp 'croak';
  25         28  
  25         8851  
415             require HTML::DOM::NodeList;
416              
417             our $VERSION = '0.056';
418             our @ISA = qw'HTML::DOM::NodeList';
419              
420 0     0   0 sub type { 'radio' }
421              
422             sub name {
423 39     39   73 my $ret = (my $self = shift)->item(0)->attr('name');
424 39 50       62 if (@_) {
425 0         0 $self->item($_)->attr(name=>@_) for 0..$self->length-1;
426             }
427             $ret
428 39         533 }
429              
430             sub value { # ~~~ do case-folding and what-not, as in HTML::Form::ListInput
431 9     9   14 my $self = shift;
432              
433 9         6 my $checked_elem;
434 9         24 for (0..$self->length-1) {
435 17         35 my $btn = $self->item($_);
436 17 100       30 $btn->checked and
437             $checked_elem = $btn, last;
438             }
439              
440 9 100       19 if (@_) { for (0..$self->length-1) {
  7         16  
441 10         40 my $btn = $self->item($_);
442 10 100 66     21 $_[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       30 $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   8 my $self = shift;
461 4         8 for(@$self) {
462 5 100       10 $_->disabled or return 0
463             }
464 1         6 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         13 my $name = $self->name;
474 7 50 33     26 return unless defined $name && length $name;
475 7 50       12 return if $self->disabled;
476 7         12 my $value = $self->value;
477 7 100       13 return unless defined $value;
478 6         14 return ($name => $value);
479             }
480              
481              
482             package HTML::DOM::Collection::Elements;
483              
484 25     25   120 use strict;
  25         30  
  25         459  
485 25     25   76 use warnings;
  25         24  
  25         633  
486              
487 25     25   88 use Scalar::Util 'weaken';
  25         22  
  25         2930  
488              
489             our $VERSION = '0.056';
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   101 { no warnings 'misc';
  25         22  
  25         2019  
503             undef &nodelist; undef &tye; undef &seen; undef &position;
504             }
505              
506             sub namedItem {
507 30     30   39 my($self, $name) = @_;
508 30         34 my $list = $$self->[nodelist];
509 30         22 my $elem;
510             my @list;
511 30         68 for(0..$list->length - 1) {
512 25     25   92 no warnings 'uninitialized';
  25         34  
  25         1725  
513 166 100 66     264 push @list, $elem if
514             ($elem = $list->item($_))->id eq $name
515             or
516             $elem->attr('name') eq $name;
517             }
518 30 100       61 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   94 no warnings 'uninitialized';
  25         23  
  25         3687  
525 11   66 11   30 grep $_->id eq $name ||
526             $_->attr('name') eq $name, @$list;
527 13         66 });
528 13         45 return $ret;
529             }
530 17 50       74 @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.056
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.056';
671             our @ISA = 'HTML::DOM::Element';
672              
673 25     25   96 use overload fallback=>1, '@{}' => sub { shift->options };
  25     13   27  
  25         116  
  13         248  
674             # ~~~ Don't I need %{} as well?
675              
676 25     25   1216 use Scalar'Util 'weaken';
  25         64  
  25         12055  
677              
678 22     22   43 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   184 my $self = shift;
683 10         7 my $ret;
684 10 100       23 if(defined wantarray) {
685 8         6 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       19 $_->selected and
691             $ret = $x,
692             last;
693 6         8 $x++;
694             }
695 8 100       17 defined $ret or
696             $ret = -1,
697             }
698 10 100       19 @_ and ($self->options)[$_[0]]->selected(1);
699 10         32 return $ret;
700             }
701 2     2   3 sub value { shift->options->value(@_) }
702 1     1   2 sub length { scalar(()= shift->options ) }
703             sub form {
704 78     78   94 my $self = shift;
705             my $ret = ($self->look_up(_tag => 'form'))[0] || $$self{_HTML_DOM_f}
706 78 100 66     274 if defined wantarray;
707             @_ and defined $_[0]
708             ? ( weaken($$self{_HTML_DOM_f} = $_[0]), shift->add_element($self) )
709 78 100 100     195 : (delete $$self{_HTML_DOM_f} or return $ret || ())
    100          
710             ->remove_element($self);
711 76 100       389 $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   103 my $self = shift;
717 114 100       139 if (wantarray) {
718 17         35 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   60 sub { grep tag $_ eq 'option', $self->descendants }
724 97         412 ), $self);
725 97         192 $self->ownerDocument-> _register_magic_node_list($list);
726 97         220 $collection;
727             }
728             }
729             sub disabled {
730 126 100   126   902 shift->_attr( disabled => @_ ? $_[0] ? 'disabled' : undef : () )
    100          
731             }
732             sub multiple {
733 17 100   17   396 shift->_attr( multiple => @_ ? (undef,'multiple')[!!$_[0]] : () )
734             }
735             *name = \&HTML::DOM::Element::Form::name;
736 11     11   1444 sub size { shift->_attr( size => @_) }
737 21     21   2686 sub tabIndex { shift->_attr( tabindex => @_) }
738              
739             sub add {
740 2     2   3 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         12  
743 2         4 return;
744             }
745             sub remove {
746 1     1   2 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   11 sub blur { shift->trigger_event('blur') }
753 3     3   12 sub focus { shift->trigger_event('focus') }
754 2     2   4 sub _reset { my $self = shift;
755 2         3 $_->_reset for $self->options }
756              
757              
758             package HTML::DOM::Collection::Options;
759              
760 25     25   105 use strict;
  25         23  
  25         375  
761 25     25   71 use warnings;
  25         27  
  25         726  
762              
763             our $VERSION = '0.056';
764              
765 25     25   78 use Carp 'croak';
  25         21  
  25         891  
766 25     25   81 use constant sel => 5; # must not conflict with super
  25         25  
  25         1151  
767 25     25   81 { no strict 'refs'; delete ${__PACKAGE__."::"}{sel} } # after compilation
  25         24  
  25         9976  
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   195 my $self = shift->SUPER::new(shift);
775 97         227 $$$self[sel] = shift;
776 97         94 $self
777             }
778              
779 2     2   4 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   20 my $self = shift;
786              
787 5         5 my $sel_elem;
788 5         11 for (0..$self->length-1) {
789 11         196 my $opt = $self->item($_);
790 11 100       22 $opt->selected and
791             $sel_elem = $opt, last;
792             }
793              
794 5 100       11 if (@_) { for (0..$self->length-1) {
  1         3  
795 3         46 my $opt = $self->item($_);
796 3         6 my $v = $opt->value;
797 3 50 0     8 $_[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       45 !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         11 $sel_elem->value;
811             }
812              
813             sub name {
814 21     21   18 $${+shift}[sel]->name
  21         40  
815             }
816              
817             sub disabled {
818 6 100   6   173 (my $self = shift)->item(0)->look_up(_tag => 'select')->disabled
819             and return 1;
820 5         15 for (@$self) {
821 6 100       14 $_->disabled || return 0;
822             }
823 1         3 return 1
824             }
825              
826             sub length { # override
827 8     8   10 my $self = shift;
828 8 100       23 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         161 $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.056';
842             our @ISA = 'HTML::DOM::Element';
843              
844 10     10   749 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.056';
852             our @ISA = qw'HTML::DOM::Element';
853              
854 25     25   93 use Carp 'croak';
  25         31  
  25         12405  
855             require HTML::DOM::Exception;
856              
857             *form = \&HTML::DOM::Element::Select::form;
858             sub defaultSelected {
859 8 100   8   392 shift->_attr( selected => @_ ? $_[0] ? 'selected' : undef : () )
    100          
860             }
861              
862             sub text {
863             shift->as_text
864 8     8   271 }
865              
866             sub index {
867 2     2   3 my $self = shift;
868 2         3 my $indx = 0;
869 2         6 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         6 $indx;
892             }
893              
894             *disabled = \&HTML::DOM::Element::Select::disabled;
895             *label = \&HTML::DOM::Element::OptGroup::label;
896              
897             sub selected {
898 51     51   44 my $self = shift;
899 51         39 my $ret;
900              
901 51 100       75 if(!defined $self->{_HTML_DOM_sel}) {
902 37   100     67 $ret = $self->attr('selected')||0;
903             }
904             else {
905             $ret = $self->{_HTML_DOM_sel}
906 14         13 }
907 51 100 66     105 if(@_ && !$ret != !$_[0]) {
908 11         31 my $sel = $self->look_up(_tag => 'select');
909 11 100 100     35 if(!$sel || $sel->multiple) {
    100          
910 8         15 $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         4 $self->{_HTML_DOM_sel} = shift;
916             $_ != $self and $_->{_HTML_DOM_sel} = 0
917 2   100     4 for $sel->options;
918             }
919             }
920             $ret
921 51         105 }
922              
923             sub value { # ~~~ do case-folding and what-not, as in HTML::Form::ListInput
924              
925 22     22   304 my $self = shift;
926 22         19 my $ret;
927              
928 22 100       72 if(caller =~ /^(?:HTML::Form(?:::Input)?|WWW::Mechanize)\z/) {
929             # ~~~ I can optimise this to call ->value once.
930 9 100       14 $ret = $self->selected ? $self->value : undef;
931 9 0       15 @_ 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         17 return $ret;
938             }
939              
940 13 100       28 defined($ret = $self->attr(value => @_)) or
941             $ret = $self->text;
942              
943 13         35 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   141 shift->look_up(_tag => 'select')->name
954             }
955              
956 8     8   12 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.056';
965             our @ISA = qw'HTML::DOM::Element';
966              
967 25     25   104 use Carp 'croak';
  25         32  
  25         4758  
968              
969 122     122   959 sub defaultValue { shift->_attr( value => @_) }
970             sub defaultChecked {
971 67 100   67   504 shift->_attr( checked => @_ ? $_[0] ? 'checked' : undef : () )
    100          
972             }
973             *form = \&HTML::DOM::Element::Select::form;
974 5     5   353 sub accept { shift->_attr( accept => @_) }
975 25     25   2294 sub accessKey { shift->_attr( accesskey => @_) }
976 10     10   1388 sub align { lc shift->_attr( align => @_) }
977 5     5   718 sub alt { shift->_attr( alt => @_) }
978             sub checked {
979 109     109   85 my $self = shift;
980 109         86 my $ret;
981 109 100       147 if(!defined $self->{_HTML_DOM_checked}) {
982 43         58 $ret = $self->defaultChecked
983             }
984             else {
985             $ret = $self->{_HTML_DOM_checked}
986 66         67 }
987 109 100 100     374 if( @_ && !$ret != not $self->{_HTML_DOM_checked} = shift
      100        
      100        
988             and !$ret and $self->type eq 'radio' ) {
989 8 50 33     20 if(
990             my $form = $self->form and defined(my $name = $self->name)
991             ) {
992 25     25   99 no warnings 'uninitialized';
  25         26  
  25         8323  
993             $_ != $self && $_->type eq 'radio'
994             && $_->name eq $name
995             and $_->{_HTML_DOM_checked} = 0
996 8   100     18 for $form->elements;
      100        
      100        
997             }
998             }
999 109         211 return $ret;
1000             }
1001             *disabled = \&HTML::DOM::Element::Select::disabled;
1002 5     5   647 sub maxLength { shift->_attr( maxlength => @_) }
1003             *name = \&HTML::DOM::Element::Form::name;
1004 12 100   12   721 sub readOnly { shift->_attr(readonly => @_ ? $_[0]?'readonly':undef : ()) }
    100          
1005             *size = \&HTML::DOM::Element::Select::size;
1006 5     5   647 sub src { shift->_attr( src => @_) }
1007             *tabIndex = \&HTML::DOM::Element::Select::tabIndex;
1008             sub type {
1009 351     351   1305 my $ret = shift->_attr('type', @_);
1010 351 50       1069 return defined $ret ? lc $ret : 'text'
1011             }
1012 5     5   648 sub useMap { shift->_attr( usemap => @_) }
1013             sub value {
1014 187     187   1607 my $self = shift;
1015 187         166 my($ret,$type);
1016              
1017 187 100 66     759 if(caller =~ /^(?:HTML::Form(?:::Input)?|WWW::Mechanize)\z/ and
      66        
1018             ($type = $self->type) =~ /^(?:button|reset)\z/ && return ||
1019             $type eq 'checkbox') {
1020             # ~~~ Do case-folding as in HTML::Input::ListInput
1021 40         59 my $value = $self->value;
1022 40 100       60 length $value or $value = 'on';
1023 40 100       54 $ret = $self->checked
1024             ? $value
1025             : undef;
1026 40 100       97 @_ 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       233 if(!defined $self->{_HTML_DOM_value}) {
1040 100         154 $ret = $self->defaultValue
1041             }
1042             else {
1043             $ret = $self->{_HTML_DOM_value}
1044 45         55 }
1045 145 100       226 @_ and $self->{_HTML_DOM_value} = shift;
1046 25     25   104 no warnings;
  25         26  
  25         23766  
1047 145         232 return "$ret";
1048             }
1049             sub _reset {
1050 17     17   12 my $self = shift;
1051 17         20 $self->checked($self->defaultChecked);
1052 17         22 $self->value($self->defaultValue);
1053             }
1054              
1055             *blur = \&HTML::DOM::Element::Select::blur;
1056             *focus = \&HTML::DOM::Element::Select::focus;
1057 2     2   7 sub select { shift->trigger_event('select') }
1058 7     7   13 sub click { for(shift){
1059 7         13 my(undef,$x,$y) = @_;
1060 7   50     33 defined or $_ = 1 for $x, $y;
1061 7         21 local($$_{_HTML_DOM_clicked}) = [$x,$y];
1062 7 50       13 $_->type eq 'checkbox' && $_->checked(!$_->checked);
1063 7         14 $_->trigger_event('click');
1064 7         32 return;
1065             }}
1066              
1067             sub trigger_event {
1068 100     100   124 my ($a,$evnt) = (shift,shift);
1069 100         149 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   7 ->trigger_event('submit') },
1079             sub { (shift->target->form||return)
1080 1   50 1   2 ->trigger_event('reset') })
1081 100 100       367 [!$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   60 my $self = shift;
1094 64         94 my $type = $self->type;
1095 64 100       115 if ($type =~ /^(image|submit)\z/) {
1096 6 100       12 return unless $self->{_HTML_DOM_clicked};
1097 4 100       11 if($1 eq 'image') {
1098 2         3 my $name = $self->name;
1099 2 100       6 $name = length $name ? "$name." : '';
1100             return "${name}x" => $self->{_HTML_DOM_clicked}[0],
1101 2         9 "${name}y" => $self->{_HTML_DOM_clicked}[1]
1102             }
1103             }
1104 60 100       138 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   43 my $self = shift;
1115 56         82 my $name = $self->name;
1116 56 100 66     200 return unless defined $name && length $name;
1117 54 100       86 return if $self->disabled;
1118 53         81 my $value = $self->value;
1119 53 100       84 return unless defined $value;
1120 51         138 return ($name => $value);
1121             }
1122              
1123             sub HTML_Form_FileInput_form_name_value {
1124             package
1125             HTML::Form::ListInput;
1126 5     5   8 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         11 my $name = $self->name;
1132 4 50       9 return unless defined $name;
1133 4 50       11 return if $self->{disabled};
1134              
1135 4         7 my $file = $self->file;
1136 4         9 my $filename = $self->filename;
1137 4         9 my @headers = $self->headers;
1138 4         9 my $content = $self->content;
1139 4 50 66     22 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         2 return;
1146             }
1147              
1148             # legacy (this used to be the way to do it)
1149 3 50       9 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         15 return ($name => [$file, $filename, @headers]);
1158             }
1159              
1160              
1161              
1162             *file = \&value;
1163              
1164             sub filename {
1165 4     4   6 my $self = shift;
1166 4         5 my $old = $self->{_HTML_DOM_filename};
1167 4 50       8 $self->{_HTML_DOM_filename} = shift if @_;
1168 4 50       10 $old = $self->file unless defined $old;
1169 4         6 $old;
1170             }
1171              
1172       4     sub headers { } # ~~~ Do I want to complete this?
1173              
1174             sub content {
1175 4     4   5 my $self = shift;
1176 4         5 my $old = $self->{_HTML_DOM_content};
1177 4 50       8 $self->{_HTML_DOM_content} = shift if @_;
1178 4         5 $old;
1179             }
1180              
1181              
1182              
1183             # ------- HTMLTextAreaElement interface ---------- #
1184              
1185             package HTML::DOM::Element::TextArea;
1186             our $VERSION = '0.056';
1187             our @ISA = qw'HTML::DOM::Element';
1188              
1189             sub defaultValue { # same as HTML::DOM::Element::Title::text
1190 11   66 11   41 ($_[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   653 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   678 sub rows {shift->_attr( rows => @_) }
1204             *tabIndex = \&HTML::DOM::Element::Select::tabIndex;
1205 1     1   4 sub type { 'textarea' }
1206             sub value {
1207 5     5   13 my $self = shift;
1208 5         4 my $ret;
1209              
1210 5 100       15 if(!defined $self->{_HTML_DOM_value}) {
1211 4         10 $ret = $self->defaultValue
1212             }
1213             else {
1214             $ret = $self->{_HTML_DOM_value}
1215 1         2 }
1216 5 100       14 @_ and $self->{_HTML_DOM_value} = shift;
1217 5         17 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.056';
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   117 sub type { no warnings 'uninitialized'; lc shift->attr('type') }
  25     37   24  
  25         9200  
  37         75  
1243 5     5   15 sub value { shift->attr( value => @_) }
1244              
1245             sub form_name_value
1246             {
1247 8     8   9 my $self = shift;
1248 8         10 my $type = $self->type;
1249 8 100 66     26 return unless !$type or $type eq 'submit';
1250 4 100       8 return unless $self->{_HTML_DOM_clicked};
1251 2         4 my $name = $self->name;
1252 2 50 33     9 return unless defined $name && length $name;
1253 2 50       5 return if $self->disabled;
1254 2         6 my $value = $self->value;
1255 2 50       6 return unless defined $value;
1256 2         5 return ($name => $value);
1257             }
1258              
1259              
1260 4     4   11 sub click { for(shift){
1261 4         10 local($$_{_HTML_DOM_clicked}) = 1;
1262 4         8 $_->trigger_event('click');
1263 4         18 return;
1264             }}
1265              
1266             sub trigger_event {
1267 27     27   37 my ($a,$evnt) = (shift,shift);
1268 27   100     53 my $input_type = $a->type || 'submit';
1269             $a->SUPER::trigger_event(
1270             $evnt,
1271             $input_type =~ /^(?:(submi)|rese)t\z/
1272             ?( DOMActivate_default =>
1273             # I’m not using a closure here, because we
1274             # don’t want the overhead of cloning it
1275             # when it might not even be used.
1276             (sub { (shift->target->form||return)
1277 3   50 3   10 ->trigger_event('submit') },
1278             sub { (shift->target->form||return)
1279 1   50 1   3 ->trigger_event('reset') })
1280 27 50       257 [!$1]
1281             ) :(),
1282             @_
1283             );
1284             }
1285              
1286       1     sub _reset {}
1287              
1288             # ------- HTMLLabelElement interface ---------- #
1289              
1290             package HTML::DOM::Element::Label;
1291             our $VERSION = '0.056';
1292             our @ISA = qw'HTML::DOM::Element';
1293              
1294             *form = \&HTML::DOM::Element::Select::form;
1295             *accessKey = \&HTML::DOM::Element::Input::accessKey;
1296 5     5   672 sub htmlFor { shift->_attr( for => @_) }
1297              
1298             # ------- HTMLFieldSetElement interface ---------- #
1299              
1300             package HTML::DOM::Element::FieldSet;
1301             our $VERSION = '0.056';
1302             our @ISA = qw'HTML::DOM::Element';
1303              
1304             *form = \&HTML::DOM::Element::Select::form;
1305              
1306             # ------- HTMLLegendElement interface ---------- #
1307              
1308             package HTML::DOM::Element::Legend;
1309             our $VERSION = '0.056';
1310             our @ISA = qw'HTML::DOM::Element';
1311              
1312             *form = \&HTML::DOM::Element::Select::form;
1313             *accessKey = \&HTML::DOM::Element::Input::accessKey;
1314             *align = \*HTML::DOM::Element::Input::align;
1315              
1316              
1317 25     25   106 no warnings;
  25         23  
  25         2077  
1318             !+~()#%$-*