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   89 use strict;
  25         47  
  25         569  
4 25     25   74 use warnings;
  25         26  
  25         439  
5              
6 25     25   78 no Carp();
  25         24  
  25         257  
7 25     25   10694 use URI;
  25         73332  
  25         2358  
8              
9             require HTML::DOM::Element;
10             require HTML::DOM::NodeList::Magic;
11             #require HTML::DOM::Collection::Elements;
12              
13             our $VERSION = '0.057';
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   4170 my $self = shift;
20 5008 100 100     27417 $self->isa(scalar caller) || caller->isa('HTML::DOM::_TreeBuilder')
21             and return $self;
22 23         43 $self->elements;
23 25     25   130 };
  25         29  
  25         153  
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 1419 my $self = shift;
33 56   66     83 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   2300 no warnings 'uninitialized';
  25         28  
  25         5004  
38             grep(
39             $elem_elems{tag $_} && attr $_ 'type', ne 'image',
40             $self->descendants
41             ),
42 25 100 100 25   64 @{ $self->{_HTML_DOM_mg_elems}||[] }
  25         45  
43             }
44 14         92 ));
45 14         50 $self->ownerDocument-> _register_magic_node_list($list);
46 14         162 $collection;
47             };
48 56         123 weaken $self;
49 56 100       86 if (wantarray) {
50 13         42 @$collection
51             }
52             else {
53 43         266 $collection;
54             }
55             }
56             sub add_element { # helper routine that formies use to add themselves to
57 9     9 0 9 my $self = shift; # the elements list
58 6   100     9 push @{ $self->{_HTML_DOM_mg_elems} ||= [] }, shift
59 9 100       19 if $elem_elems{ $_[0]->tag };
60 9         11 return;
61             }
62             sub remove_element { # and this is how formies remove themselves when they
63 7     7 0 8 my $self = shift; # get moved around the DOM
64 7         4 my $removee = shift;
65 7         8 @{ $self->{_HTML_DOM_mg_elems} }
66 7   50     6 = grep $_ != $removee, @{ $self->{_HTML_DOM_elems} ||= [] }
  7         9  
67             }
68              
69 2     2 1 6 sub length { shift->elements -> length }
70 25     25 1 102 sub name { no warnings; shift->_attr( name => @_) . '' }
  25     470   27  
  25         7090  
  470         5093  
71 49     49 1 18016 sub acceptCharset { shift->_attr('accept-charset' => @_) }
72             sub action {
73 39     39 1 822 my $self = shift;
74 39 100       83 (my $base = $self->ownerDocument->base)
75             or return $self->_attr('action', @_);
76 17         63 (new_abs URI
77             $self->_attr('action' => @_),
78             $self->ownerDocument->base)
79             ->as_string
80             }
81             sub enctype {
82 34     34 1 4786 my $ret = shift->_attr('enctype' => @_);
83 34 50       130 defined $ret ? $ret : 'application/x-www-form-urlencoded'
84             }
85             *encoding=*enctype;
86             sub method {
87 48     48 1 873 my $ret = shift->_attr('method' => @_);
88 48 50       129 defined $ret ? lc $ret : 'get'
89             }
90 5     5 1 827 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 88     88 1 118 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   40 $_->_reset for shift->target->elements
107             },
108 88         206 @_,
109             );
110             }
111              
112             # ------ HTML::Form compatibility methods ------ #
113              
114             sub inputs {
115 106     106 0 91 my @ret;
116             my %pos;
117 106         116 my $self = shift;
118             # This used to use ‘$self->elements’, but ->elements no longer
119             # includes image buttons.
120 106         247 for(
121             grep($elem_elems{tag $_}, $self->descendants),
122 106 50       145 @{ $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         793 my $tag = tag $_;
128              
129 25     25   105 no warnings 'uninitialized'; # for 5.11.0
  25         26  
  25         30865  
130 501 100       738 if(lc $_->attr('type') eq 'radio') {
131 104         115 my $name = name $_;
132 59         92 exists $pos{$name} ? push @{$ret[$pos{$name}]}, $_
133             :( push(@ret, [$_]),
134 104 100       196 $pos{$name} = $#ret );
135             next
136 104         130 }
137 397 100       770 push @ret, $tag eq 'select'
    100          
138             ? $_->attr('multiple')
139             ? $_->find('option')
140             : scalar $_->options
141             : $_
142             }
143 106 100       568 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 190 my $self = shift;
150 12         13 my $name;
151 12 50       32 $name = shift if (@_ % 2) == 1; # odd number of arguments
152              
153             # try to find first submit button to activate
154 12         22 for ($self->inputs) {
155 24   100     39 my $type = $_->type; my $tag = eval { $_->tag } || '';
  24         24  
156 24 100 100     150 next unless $tag eq 'input' && $type =~ /^(?:submit|image)\z/
      50        
      66        
      66        
157             || $tag eq 'button' && ($type || 'submit') eq 'submit';
158 6 50 33     14 next if $name && $_->name ne $name;
159 6 100       13 next if $_->disabled;
160 5         13 $_->click($self, @_);return
161 5         13 }
162 7 50       13 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 1810 my $self = shift;
172 15         18 my $key = shift;
173 15         30 my $input = $self->find_input($key);
174 15 50       68 Carp::croak("No such field '$key'") unless $input;
175 15         18 local $Carp::CarpLevel = 1;
176 15         37 $input->value(@_);
177             }
178              
179              
180             sub find_input
181             {
182             package
183             HTML::Form; # so caller tricks work
184 31     31 0 4061 my($self, $name, $type, $no) = @_;
185 31 100       83 if (wantarray) {
186 1         1 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         3 $c++;
195 2 50 33     10 next if $no && $no != $c;
196 2         3 push(@res, $_);
197             }
198 1         5 return @res;
199            
200             }
201             else {
202 30   50     96 $no ||= 1;
203 30         50 for ($self->inputs) {
204 72 50       105 if (defined $name) {
205 72 50       100 next unless defined(my $n = $_->name);
206 72 100       144 next if $name ne $n;
207             }
208 30 50 33     56 next if $type && $type ne $_->type;
209 30 50       44 next if --$no;
210 30         77 return $_;
211             }
212 0         0 return undef;
213             }
214             }
215              
216             sub param {
217             package
218             HTML::Form;
219 27     27 0 4277 my $self = shift;
220 27 100       50 if (@_) {
221 25         25 my $name = shift;
222 25         22 my @inputs;
223 25         41 for ($self->inputs) {
224 175         210 my $n = $_->name;
225 175 100 66     653 next if !defined($n) || $n ne $name;
226 43         54 push(@inputs, $_);
227             }
228              
229 25 100       47 if (@_) {
230             # set
231 8 50       17 die "No '$name' parameter exists" unless @inputs;
232 8         12 my @v = @_;
233 8 100 100     31 @v = @{$v[0]} if @v == 1 && ref($v[0]);
  3         5  
234 8         15 while (@v) {
235 8         8 my $v = shift @v;
236 8         8 my $err;
237 8         20 for my $i (0 .. @inputs-1) {
238 13         18 eval {
239 13         25 $inputs[$i]->value($v);
240             };
241 13 100       207 unless ($@) {
242 7         11 undef($err);
243 7         11 splice(@inputs, $i, 1);
244 7         9 last;
245             }
246 6   66     22 $err ||= $@;
247             }
248 8 100       23 die $err if $err;
249             }
250              
251             # the rest of the input should be cleared
252 7         17 for (@inputs) {
253 6         10 $_->value(undef);
254             }
255             }
256             else {
257             # get
258 17         27 my @v;
259 17         27 for (@inputs) {
260 28 100       43 if (defined(my $v = $_->value)) {
261 16         24 push(@v, $v);
262             }
263             }
264 17 100       82 return wantarray ? @v : $v[0];
265             }
266             }
267             else {
268             # list parameter names
269 2         3 my @n;
270             my %seen;
271 2         4 for ($self->inputs) {
272 14         19 my $n = $_->name;
273 14 100 66     55 next if !defined($n) || $seen{$n}++;
274 8         13 push(@n, $n);
275             }
276 2         12 return @n;
277             }
278             }
279              
280              
281             my $ascii_encodings_re;
282             my $encodings_re;
283              
284             sub _encoding_ok {
285 30     30   2487 my ($enc,$xwfu) =@ _;
286 30         67 $enc =~ s/^(?:x-?)?mac-?/mac/i;
287 30 100 100     54 ($enc) x (Encode'resolve_alias($enc)||return)
      66        
288             =~ ($xwfu ? $ascii_encodings_re : $encodings_re ||=qr/^${\
289 2         2213 join'|',map quotemeta,
290             encodings Encode 'Byte'
291             }\z/);
292             }
293              
294             sub _apply_charset {
295 36     36   44 my($charsets,$apply_to) = @_; # array refs
296 36         31 my ($charset,@ret);
297 36         68 for(@$charsets) {
298             #use DDS; Dump $_ if @$apply_to == 1;
299 23 100       218 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         29 for my $applyee(@$apply_to) {
305 41 100       431 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         227 $charset = $_;
313             } && last;
314             }
315 36 100       160 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         19 my $fallback;
321 26   66     95 $charset = $$charsets[0]||(++$fallback,'utf8');
322 26 100       90 @ret = map ref$_ ? $_ : Encode'encode($charset,$_),
323             @$apply_to;
324 26 100       1528 $fallback and $charset = 'utf-8';
325             }
326 36         94 $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 89 my $self = shift;
336 33         56 my $method = $self->method;
337 33         62 my $uri = $self->action;
338 33   100     12641 my $xwfu = $method eq 'get'
339             || $self->enctype !~ /^multipart\/form-data\z/i;
340 33         72 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         60 my @charsets;
349 33   100     26 { push @charsets, split ' ', $self->acceptCharset||next}
  33         60  
350 33         1157 require Encode;
351 33         12953 @charsets = map _encoding_ok($_, $xwfu),
352             @charsets;
353 33 100       6168 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     27 push @charsets, _encoding_ok(
  25         67  
359             ($self->ownerDocument||next)->charset || next, $xwfu
360             )
361             }}
362              
363 33 100       792 if ($method ne "post") {
364 18         766 require HTTP::Request;
365 18         20609 $uri = URI->new($uri, "http");
366             $uri->can('query_form')
367 18 100       5762 and $uri->query_form(@{_apply_charset \@charsets, \@form});
  17         41  
368 18         1214 return HTTP::Request->new(GET => $uri);
369             }
370             else {
371 15         947 require HTTP::Request::Common;
372 15 100       3204 if($xwfu) {
373 9         21 my($charset,$form) = _apply_charset \@charsets, \@form;
374 9         29 return HTTP::Request::Common::POST($uri, $form,
375             Content_Type =>
376             "application/x-www-form-urlencoded; charset=\"$charset\"");
377             }
378             else {
379 6         7 my @new_form;
380 6         15 while(@form) {
381 10         16 my($name,$val) = (shift @form, shift @form);
382             #my $origval = $val;
383 10         20 (my $charset, $val) = _apply_charset \@charsets, [$val];
384             #use DDS; Dump [$origval,$val, ];
385 10         15 my $enc = $name;
386 10 100       25 $enc = Encode'encode('MIME-B', $enc) if $enc =~ /[^ -~]/;
387 10 100       5098 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 37 my $self = shift;
405 36         67 map { $_->form_name_value($self) } $self->inputs;
  79         130  
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   127 use Carp 'croak';
  25         27  
  25         8972  
415             require HTML::DOM::NodeList;
416              
417             our $VERSION = '0.057';
418             our @ISA = qw'HTML::DOM::NodeList';
419              
420 0     0   0 sub type { 'radio' }
421              
422             sub name {
423 39     39   71 my $ret = (my $self = shift)->item(0)->attr('name');
424 39 50       64 if (@_) {
425 0         0 $self->item($_)->attr(name=>@_) for 0..$self->length-1;
426             }
427             $ret
428 39         490 }
429              
430             sub value { # ~~~ do case-folding and what-not, as in HTML::Form::ListInput
431 9     9   12 my $self = shift;
432              
433 9         9 my $checked_elem;
434 9         22 for (0..$self->length-1) {
435 17         29 my $btn = $self->item($_);
436 17 100       27 $btn->checked and
437             $checked_elem = $btn, last;
438             }
439              
440 9 100       17 if (@_) { for (0..$self->length-1) {
  7         17  
441 10         17 my $btn = $self->item($_);
442 10 100 66     20 $_[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       29 $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       8 $_->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   7 my $self = shift;
473 7         15 my $name = $self->name;
474 7 50 33     25 return unless defined $name && length $name;
475 7 50       10 return if $self->disabled;
476 7         15 my $value = $self->value;
477 7 100       15 return unless defined $value;
478 6         19 return ($name => $value);
479             }
480              
481              
482             package HTML::DOM::Collection::Elements;
483              
484 25     25   115 use strict;
  25         35  
  25         435  
485 25     25   78 use warnings;
  25         31  
  25         650  
486              
487 25     25   89 use Scalar::Util 'weaken';
  25         27  
  25         2857  
488              
489             our $VERSION = '0.057';
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   125 { no warnings 'misc';
  25         29  
  25         2057  
503             undef &nodelist; undef &tye; undef &seen; undef &position;
504             }
505              
506             sub namedItem {
507 30     30   41 my($self, $name) = @_;
508 30         36 my $list = $$self->[nodelist];
509 30         21 my $elem;
510             my @list;
511 30         70 for(0..$list->length - 1) {
512 25     25   99 no warnings 'uninitialized';
  25         30  
  25         1656  
513 166 100 66     272 push @list, $elem if
514             ($elem = $list->item($_))->id eq $name
515             or
516             $elem->attr('name') eq $name;
517             }
518 30 100       58 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   102 no warnings 'uninitialized';
  25         31  
  25         3593  
525 11   66 11   17 grep $_->id eq $name ||
526             $_->attr('name') eq $name, @$list;
527 13         73 });
528 13         44 return $ret;
529             }
530 17 50       69 @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.057
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.057';
671             our @ISA = 'HTML::DOM::Element';
672              
673 25     25   102 use overload fallback=>1, '@{}' => sub { shift->options };
  25     13   29  
  25         124  
  13         276  
674             # ~~~ Don't I need %{} as well?
675              
676 25     25   1183 use Scalar'Util 'weaken';
  25         34  
  25         12497  
677              
678 22     22   41 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   206 my $self = shift;
683 10         10 my $ret;
684 10 100       22 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         13 for($self->options) {
690 13 100       18 $_->selected and
691             $ret = $x,
692             last;
693 6         10 $x++;
694             }
695 8 100       19 defined $ret or
696             $ret = -1,
697             }
698 10 100       19 @_ and ($self->options)[$_[0]]->selected(1);
699 10         37 return $ret;
700             }
701 2     2   5 sub value { shift->options->value(@_) }
702 1     1   3 sub length { scalar(()= shift->options ) }
703             sub form {
704 78     78   107 my $self = shift;
705             my $ret = ($self->look_up(_tag => 'form'))[0] || $$self{_HTML_DOM_f}
706 78 100 66     296 if defined wantarray;
707             @_ and defined $_[0]
708             ? ( weaken($$self{_HTML_DOM_f} = $_[0]), shift->add_element($self) )
709 78 100 100     216 : (delete $$self{_HTML_DOM_f} or return $ret || ())
    100          
710             ->remove_element($self);
711 76 100       352 $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   93 my $self = shift;
717 114 100       147 if (wantarray) {
718 17         37 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   62 sub { grep tag $_ eq 'option', $self->descendants }
724 97         426 ), $self);
725 97         176 $self->ownerDocument-> _register_magic_node_list($list);
726 97         238 $collection;
727             }
728             }
729             sub disabled {
730 126 100   126   913 shift->_attr( disabled => @_ ? $_[0] ? 'disabled' : undef : () )
    100          
731             }
732             sub multiple {
733 17 100   17   578 shift->_attr( multiple => @_ ? (undef,'multiple')[!!$_[0]] : () )
734             }
735             *name = \&HTML::DOM::Element::Form::name;
736 11     11   1564 sub size { shift->_attr( size => @_) }
737 21     21   3223 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         2 eval{$sel->insertBefore($opt,$b4)};
  2         13  
743 2         5 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         6 return;
750             }
751              
752 3     3   10 sub blur { shift->trigger_event('blur') }
753 3     3   8 sub focus { shift->trigger_event('focus') }
754 2     2   2 sub _reset { my $self = shift;
755 2         4 $_->_reset for $self->options }
756              
757              
758             package HTML::DOM::Collection::Options;
759              
760 25     25   106 use strict;
  25         31  
  25         382  
761 25     25   115 use warnings;
  25         33  
  25         780  
762              
763             our $VERSION = '0.057';
764              
765 25     25   83 use Carp 'croak';
  25         26  
  25         889  
766 25     25   84 use constant sel => 5; # must not conflict with super
  25         28  
  25         1283  
767 25     25   88 { no strict 'refs'; delete ${__PACKAGE__."::"}{sel} } # after compilation
  25         29  
  25         10691  
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   207 my $self = shift->SUPER::new(shift);
775 97         209 $$$self[sel] = shift;
776 97         97 $self
777             }
778              
779 2     2   5 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   5 my $self = shift;
786              
787 5         5 my $sel_elem;
788 5         17 for (0..$self->length-1) {
789 11         213 my $opt = $self->item($_);
790 11 100       21 $opt->selected and
791             $sel_elem = $opt, last;
792             }
793              
794 5 100       13 if (@_) { for (0..$self->length-1) {
  1         2  
795 3         52 my $opt = $self->item($_);
796 3         7 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       51 !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         12 $sel_elem->value;
811             }
812              
813             sub name {
814 21     21   17 $${+shift}[sel]->name
  21         46  
815             }
816              
817             sub disabled {
818 6 100   6   172 (my $self = shift)->item(0)->look_up(_tag => 'select')->disabled
819             and return 1;
820 5         17 for (@$self) {
821 6 100       17 $_->disabled || return 0;
822             }
823 1         5 return 1
824             }
825              
826             sub length { # override
827 8     8   8 my $self = shift;
828 8 100       24 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         172 $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.057';
842             our @ISA = 'HTML::DOM::Element';
843              
844 10     10   1026 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.057';
852             our @ISA = qw'HTML::DOM::Element';
853              
854 25     25   103 use Carp 'croak';
  25         29  
  25         12732  
855             require HTML::DOM::Exception;
856              
857             *form = \&HTML::DOM::Element::Select::form;
858             sub defaultSelected {
859 8 100   8   522 shift->_attr( selected => @_ ? $_[0] ? 'selected' : undef : () )
    100          
860             }
861              
862             sub text {
863             shift->as_text
864 8     8   290 }
865              
866             sub index {
867 2     2   2 my $self = shift;
868 2         2 my $indx = 0;
869 2         7 my @options = (my $sel = $self->look_up(_tag => 'select'))
870             ->options;
871 2         3 for(@options){
872 3 100       7 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   46 my $self = shift;
899 51         43 my $ret;
900              
901 51 100       81 if(!defined $self->{_HTML_DOM_sel}) {
902 37   100     66 $ret = $self->attr('selected')||0;
903             }
904             else {
905             $ret = $self->{_HTML_DOM_sel}
906 14         13 }
907 51 100 66     128 if(@_ && !$ret != !$_[0]) {
908 11         45 my $sel = $self->look_up(_tag => 'select');
909 11 100 100     38 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         113 }
922              
923             sub value { # ~~~ do case-folding and what-not, as in HTML::Form::ListInput
924              
925 22     22   330 my $self = shift;
926 22         17 my $ret;
927              
928 22 100       77 if(caller =~ /^(?:HTML::Form(?:::Input)?|WWW::Mechanize)\z/) {
929             # ~~~ I can optimise this to call ->value once.
930 9 100       15 $ret = $self->selected ? $self->value : undef;
931 9 0       16 @_ 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       33 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   152 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.057';
965             our @ISA = qw'HTML::DOM::Element';
966              
967 25     25   108 use Carp 'croak';
  25         33  
  25         5040  
968              
969 122     122   1192 sub defaultValue { shift->_attr( value => @_) }
970             sub defaultChecked {
971 67 100   67   675 shift->_attr( checked => @_ ? $_[0] ? 'checked' : undef : () )
    100          
972             }
973             *form = \&HTML::DOM::Element::Select::form;
974 5     5   404 sub accept { shift->_attr( accept => @_) }
975 25     25   2456 sub accessKey { shift->_attr( accesskey => @_) }
976 10     10   1585 sub align { lc shift->_attr( align => @_) }
977 5     5   781 sub alt { shift->_attr( alt => @_) }
978             sub checked {
979 109     109   103 my $self = shift;
980 109         85 my $ret;
981 109 100       142 if(!defined $self->{_HTML_DOM_checked}) {
982 43         56 $ret = $self->defaultChecked
983             }
984             else {
985             $ret = $self->{_HTML_DOM_checked}
986 66         64 }
987 109 100 100     344 if( @_ && !$ret != not $self->{_HTML_DOM_checked} = shift
      100        
      100        
988             and !$ret and $self->type eq 'radio' ) {
989 8 50 33     266 if(
990             my $form = $self->form and defined(my $name = $self->name)
991             ) {
992 25     25   110 no warnings 'uninitialized';
  25         34  
  25         8602  
993             $_ != $self && $_->type eq 'radio'
994             && $_->name eq $name
995             and $_->{_HTML_DOM_checked} = 0
996 8   100     23 for $form->elements;
      100        
      100        
997             }
998             }
999 109         209 return $ret;
1000             }
1001             *disabled = \&HTML::DOM::Element::Select::disabled;
1002 5     5   801 sub maxLength { shift->_attr( maxlength => @_) }
1003             *name = \&HTML::DOM::Element::Form::name;
1004 12 100   12   1015 sub readOnly { shift->_attr(readonly => @_ ? $_[0]?'readonly':undef : ()) }
    100          
1005             *size = \&HTML::DOM::Element::Select::size;
1006 5     5   781 sub src { shift->_attr( src => @_) }
1007             *tabIndex = \&HTML::DOM::Element::Select::tabIndex;
1008             sub type {
1009 351     351   1467 my $ret = shift->_attr('type', @_);
1010 351 50       1103 return defined $ret ? lc $ret : 'text'
1011             }
1012 5     5   813 sub useMap { shift->_attr( usemap => @_) }
1013             sub value {
1014 187     187   1614 my $self = shift;
1015 187         144 my($ret,$type);
1016              
1017 187 100 66     728 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         341 my $value = $self->value;
1022 40 100       72 length $value or $value = 'on';
1023 40 100       51 $ret = $self->checked
1024             ? $value
1025             : undef;
1026 40 100       95 @_ 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         79 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       234 if(!defined $self->{_HTML_DOM_value}) {
1040 100         148 $ret = $self->defaultValue
1041             }
1042             else {
1043             $ret = $self->{_HTML_DOM_value}
1044 45         52 }
1045 145 100       252 @_ and $self->{_HTML_DOM_value} = shift;
1046 25     25   130 no warnings;
  25         37  
  25         24671  
1047 145         257 return "$ret";
1048             }
1049             sub _reset {
1050 17     17   12 my $self = shift;
1051 17         35 $self->checked($self->defaultChecked);
1052 17         23 $self->value($self->defaultValue);
1053             }
1054              
1055             *blur = \&HTML::DOM::Element::Select::blur;
1056             *focus = \&HTML::DOM::Element::Select::focus;
1057 2     2   8 sub select { shift->trigger_event('select') }
1058 7     7   16 sub click { for(shift){
1059 7         12 my(undef,$x,$y) = @_;
1060 7   50     33 defined or $_ = 1 for $x, $y;
1061 7         18 local($$_{_HTML_DOM_clicked}) = [$x,$y];
1062 7 50       14 $_->type eq 'checkbox' && $_->checked(!$_->checked);
1063 7         16 $_->trigger_event('click');
1064 7         35 return;
1065             }}
1066              
1067             sub trigger_event {
1068 100     100   113 my ($a,$evnt) = (shift,shift);
1069 100         153 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   8 ->trigger_event('submit') },
1079             sub { (shift->target->form||return)
1080 1   50 1   2 ->trigger_event('reset') })
1081 100 100       351 [!$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   58 my $self = shift;
1094 64         99 my $type = $self->type;
1095 64 100       127 if ($type =~ /^(image|submit)\z/) {
1096 6 100       15 return unless $self->{_HTML_DOM_clicked};
1097 4 100       12 if($1 eq 'image') {
1098 2         10 my $name = $self->name;
1099 2 100       5 $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       142 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   44 my $self = shift;
1115 56         79 my $name = $self->name;
1116 56 100 66     203 return unless defined $name && length $name;
1117 54 100       90 return if $self->disabled;
1118 53         85 my $value = $self->value;
1119 53 100       85 return unless defined $value;
1120 51         161 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     11 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         9 my $name = $self->name;
1132 4 50       12 return unless defined $name;
1133 4 50       10 return if $self->{disabled};
1134              
1135 4         10 my $file = $self->file;
1136 4         10 my $filename = $self->filename;
1137 4         9 my @headers = $self->headers;
1138 4         9 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         2 return;
1146             }
1147              
1148             # legacy (this used to be the way to do it)
1149 3 50       10 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   6 my $self = shift;
1166 4         5 my $old = $self->{_HTML_DOM_filename};
1167 4 50       9 $self->{_HTML_DOM_filename} = shift if @_;
1168 4 50       11 $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   5 my $self = shift;
1176 4         6 my $old = $self->{_HTML_DOM_content};
1177 4 50       7 $self->{_HTML_DOM_content} = shift if @_;
1178 4         6 $old;
1179             }
1180              
1181              
1182              
1183             # ------- HTMLTextAreaElement interface ---------- #
1184              
1185             package HTML::DOM::Element::TextArea;
1186             our $VERSION = '0.057';
1187             our @ISA = qw'HTML::DOM::Element';
1188              
1189             sub defaultValue { # same as HTML::DOM::Element::Title::text
1190 11   66 11   42 ($_[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   774 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   776 sub rows {shift->_attr( rows => @_) }
1204             *tabIndex = \&HTML::DOM::Element::Select::tabIndex;
1205 1     1   3 sub type { 'textarea' }
1206             sub value {
1207 5     5   7 my $self = shift;
1208 5         6 my $ret;
1209              
1210 5 100       13 if(!defined $self->{_HTML_DOM_value}) {
1211 4         9 $ret = $self->defaultValue
1212             }
1213             else {
1214             $ret = $self->{_HTML_DOM_value}
1215 1         2 }
1216 5 100       13 @_ and $self->{_HTML_DOM_value} = shift;
1217 5         16 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.057';
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   126 sub type { no warnings 'uninitialized'; lc shift->attr('type') }
  25     37   33  
  25         9617  
  37         78  
1243 5     5   14 sub value { shift->attr( value => @_) }
1244              
1245             sub form_name_value
1246             {
1247 8     8   7 my $self = shift;
1248 8         12 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         5 my $name = $self->name;
1252 2 50 33     11 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         14 local($$_{_HTML_DOM_clicked}) = 1;
1262 4         10 $_->trigger_event('click');
1263 4         18 return;
1264             }}
1265              
1266             sub trigger_event {
1267 27     27   38 my ($a,$evnt) = (shift,shift);
1268 27   100     54 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   7 ->trigger_event('submit') },
1278             sub { (shift->target->form||return)
1279 1   50 1   3 ->trigger_event('reset') })
1280 27 50       254 [!$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.057';
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   774 sub htmlFor { shift->_attr( for => @_) }
1297              
1298             # ------- HTMLFieldSetElement interface ---------- #
1299              
1300             package HTML::DOM::Element::FieldSet;
1301             our $VERSION = '0.057';
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.057';
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   113 no warnings;
  25         39  
  25         1928  
1318             !+~()#%$-*