—package
HTML::DOM::Collection;
use
strict;
use
warnings;
our
$VERSION
=
'0.012'
;
# Internals: \[$nodelist, $tie]
# Field constants:
sub
nodelist(){0}
sub
tye(){1}
sub
seen(){2}
# whether this key has been seen
sub
position(){3}
# current (array) position used by NEXTKEY
sub
ids(){4}
# whether we are iterating through ids
# Number 5 is taken by ::Options (inside Element/Form.pm).
{
no
warnings
'misc'
;
undef
&nodelist
;
undef
&tye
;
undef
&seen
;
undef
&position
;
}
'%{}'
=>
sub
{
my
$self
=
shift
;
$
$$self
[tye] or
weaken(
tie
%{ $
$$self
[tye] }, __PACKAGE__,
$self
),
$
$$self
[tye];
},
'@{}'
=>
sub
{ ${+
shift
}->[nodelist] };
sub
new {
bless
\[
$_
[1]],
shift
;
}
my
%NameableElements
=
map
+(
$_
=> 1),
qw/
a area object param applet input select textarea button frame
iframe meta form img map
/
;
sub
namedItem {
my
(
$self
,
$name
) =
@_
;
my
$list
=
$$self
->[nodelist];
my
$named_elem
;
my
$elem
;
for
(0..
$list
->
length
- 1) {
no
warnings
'uninitialized'
;
(
$elem
=
$list
->item(
$_
))->id eq
$name
and
return
$elem
;
exists
$NameableElements
{
$elem
->tag} and
$elem
->attr(
'name'
) eq
$name
and
$named_elem
=
$elem
;
}
$named_elem
||()
}
# Delegated methosd
for
(
qw/length item/
) {
eval
"sub $_ { \${+shift}->["
. nodelist .
"]->$_(\@_) }"
}
sub
TIEHASH {
$_
[1] }
sub
FETCH {
$_
[0]->namedItem(
$_
[1]) }
sub
EXISTS {
$_
[0]->namedItem(
$_
[1]) }
# nodes are true, undef is false
sub
FIRSTKEY {
my
$self
=
shift
;
(
my
$guts
=
$$self
)->[seen] = {};
my
(
$id
,
$item
);
$guts
->[ids] = 1;
for
(0..
$self
->
length
- 1) {
defined
(
$id
= (
$item
=
$self
->item(
$_
))->id)
and ++
$guts
->[seen]{
$id
},
$guts
->[position] =
$_
,
return
(
$id
);
}
# If none of the items has an id...
$guts
->[ids] = 0;
for
(0..
$self
->
length
- 1) {
defined
(
$id
= (
$item
=
$self
->item(
$_
))->attr(
'name'
))
and ++
$guts
->[seen]{
$id
},
$guts
->[position] =
$_
,
return
(
$id
);
}
return
;
# empty list
}
sub
NEXTKEY{
my
$self
=
shift
;
my
$guts
=
$$self
;
my
(
$id
,
$item
);
if
(
$guts
->[ids]) {
for
(
$guts
->[position]..
$self
->
length
- 1) {
defined
(
$id
= (
$item
=
$self
->item(
$_
))->id)
and !
$guts
->[seen]{
$id
}++
and
$guts
->[position] =
$_
,
return
(
$id
);
}
}
# If we've exhausted all ids...
$guts
->[ids] = 0;
for
(0..
$self
->
length
- 1) {
defined
(
$id
= (
$item
=
$self
->item(
$_
))->attr(
'name'
))
and !
$guts
->[seen]{
$id
}++
and
$guts
->[position] =
$_
,
return
(
$id
);
}
return
;
}
sub
SCALAR {
defined
FIRSTKEY
@_
;
}
sub
DDS_freeze {
my
$self
=
shift
;
delete
$
$$self
[tye];
$self
}
1
__END__
=head1 NAME
HTML::DOM::Collection - A Perl implementation of the HTMLCollection interface
=head1 SYNOPSIS
use HTML::DOM;
$doc = HTML::DOM->new;
$doc->write('<html> ..... </html>');
$doc->close;
$images = $doc->images; # returns an HTML::DOM::Collection
$images->[0]; # first image
$images->{logo}; # image named 'logo'
$images->item(0);
$images->namedItem('logo');
$images->length; # same as scalar @$images
=head1 DESCRIPTION
This implements the HTMLCollection interface as described in the W3C's DOM
standard. This class is actually just a wrapper around the NodeList
classes. In addition to the methods below, you can use a collection as a
hash and as an array (both read-only).
=head1 CONSTRUCTOR
Normally you would simply call a method that returns an HTML collection
(as in the L</SYNOPSIS>). But if you wall to call the constructor, here is
the syntax:
$collection = HTML::DOM::Collection->new($nodelist)
$nodelist should be a node list object.
=head1 OBJECT METHODS
=over 4
=item $collection->length
Returns the number of items in the collection.
=item $collection->item($index)
Returns item number C<$index>, numbered from 0. Note that you call also use
C<< $collection->[$index] >> for short.
=item $collection->namedItem($name)
Returns the item named C<$name>. If an item with an ID of C<$name> exists,
that will be returned. Otherwise the first item whose C<name> attribute is
C<$name> will be returned. You can also write C<< $collection->{$name} >>.
=back
=head1 SEE ALSO
L<HTML::DOM>
L<HTML::DOM::NodeList>