package HTML::DOM::Element;
use strict;
use warnings;
use HTML::DOM::Exception qw 'INVALID_CHARACTER_ERR
INUSE_ATTRIBUTE_ERR NOT_FOUND_ERR SYNTAX_ERR';
use HTML::DOM::Node 'ELEMENT_NODE';
use HTML'Entities;
use Scalar::Util qw'refaddr blessed weaken';
require HTML::DOM::Attr;
require HTML::DOM::Element::Form;
require HTML::DOM::Element::Table;
require HTML::DOM::NamedNodeMap;
require HTML::DOM::Node;
require HTML::DOM::NodeList::Magic;
our @ISA = qw'HTML::DOM::Node';
our $VERSION = '0.057';
{
# ~~~ Perhaps I should make class_for into a class method, rather
# than a function, so Element.pm can be subclassed. Maybe I'll
# wait until someone tries to subclass it. (Applies to Event.pm
# as well.) If a potential subclasser is reading this, will he
# please give me a holler?
my %class_for = (
'~text' => 'HTML::DOM::Text',
html => 'HTML::DOM::Element::HTML',
head => 'HTML::DOM::Element::Head',
link => 'HTML::DOM::Element::Link',
title => 'HTML::DOM::Element::Title',
meta => 'HTML::DOM::Element::Meta',
base => 'HTML::DOM::Element::Base',
isindex=> 'HTML::DOM::Element::IsIndex',
style => 'HTML::DOM::Element::Style',
body => 'HTML::DOM::Element::Body',
form => 'HTML::DOM::Element::Form',
select => 'HTML::DOM::Element::Select',
optgroup=> 'HTML::DOM::Element::OptGroup',
option => 'HTML::DOM::Element::Option',
input => 'HTML::DOM::Element::Input',
textarea=> 'HTML::DOM::Element::TextArea',
button => 'HTML::DOM::Element::Button',
label => 'HTML::DOM::Element::Label',
fieldset=> 'HTML::DOM::Element::FieldSet',
legend => 'HTML::DOM::Element::Legend',
ul => 'HTML::DOM::Element::UL',
ol => 'HTML::DOM::Element::OL',
dl => 'HTML::DOM::Element::DL',
dir => 'HTML::DOM::Element::Dir',
menu => 'HTML::DOM::Element::Menu',
li => 'HTML::DOM::Element::LI',
div => 'HTML::DOM::Element::Div',
p => 'HTML::DOM::Element::P',
map((
"h$_" => 'HTML::DOM::Element::Heading'
), 1..6),
q => 'HTML::DOM::Element::Quote',
blockquote=> 'HTML::DOM::Element::Quote',
pre => 'HTML::DOM::Element::Pre',
br => 'HTML::DOM::Element::Br',
basefont => 'HTML::DOM::Element::BaseFont',
font => 'HTML::DOM::Element::Font',
hr => 'HTML::DOM::Element::HR',
ins => 'HTML::DOM::Element::Mod',
del => 'HTML::DOM::Element::Mod',
a => 'HTML::DOM::Element::A',
img => 'HTML::DOM::Element::Img',
object => 'HTML::DOM::Element::Object',
param => 'HTML::DOM::Element::Param',
applet => 'HTML::DOM::Element::Applet',
map => 'HTML::DOM::Element::Map',
area => 'HTML::DOM::Element::Area',
script => 'HTML::DOM::Element::Script',
table => 'HTML::DOM::Element::Table',
caption => 'HTML::DOM::Element::Caption',
col => 'HTML::DOM::Element::TableColumn',
colgroup=> 'HTML::DOM::Element::TableColumn',
thead => 'HTML::DOM::Element::TableSection',
tfoot => 'HTML::DOM::Element::TableSection',
tbody => 'HTML::DOM::Element::TableSection',
tr => 'HTML::DOM::Element::TR',
th => 'HTML::DOM::Element::TableCell',
td => 'HTML::DOM::Element::TableCell',
frameset=> 'HTML::DOM::Element::FrameSet',
frame => 'HTML::DOM::Element::Frame',
iframe => 'HTML::DOM::Element::IFrame',
);
sub class_for {
$class_for{lc$_[0]} || __PACKAGE__
}
}
=head1 NAME
HTML::DOM::Element - A Perl class for representing elements in an HTML DOM tree
=head1 VERSION
Version 0.057
=head1 SYNOPSIS
use HTML::DOM;
$doc = HTML::DOM->new;
$elem = $doc->createElement('a');
$elem->setAttribute('href', 'http://www.perl.org/');
$elem->getAttribute('href');
$elem->tagName;
# etc
=head1 DESCRIPTION
This class represents elements in an HTML::DOM tree. It is the base class
for other element classes (see
L<HTML::DOM/CLASSES AND DOM INTERFACES>.) It implements the Element and
HTMLElement DOM interfaces.
=head1 METHODS
=head2 Constructor
You should normally use HTML::DOM's C<createElement> method. This is listed
here only for completeness:
$elem = new HTML::DOM::Element $tag_name;
C<$elem> will automatically be blessed into the appropriate class for
C<$tag_name>.
=cut
sub new {
my $tagname = $_[1];
# Hack to make parsing comments work
$tagname eq '~comment'
and require HTML'DOM'Comment, return new HTML'DOM'Comment;
# ~~~ The DOM spec does not specify which characters are invaleid.
# I think I need to check the HTML spec. For now, I'm simply
# letting HTML::Element do the insanity checking, and I'm turn-
# ing its errors into HTML::DOM::Exceptions.
my $ret;
eval {
$ret = bless shift->SUPER::new(@_), class_for $tagname;
# require can sometimes fail if it’s part of a tainted
# statement. That’s why it’s in a do block.
$tagname =~ /^html\z/i
and do { require HTML'DOM }; # paranoia
};
$@ or return $ret;
die HTML::DOM::Exception->new( INVALID_CHARACTER_ERR, $@);
}
=head2 Attributes
The following DOM attributes are supported:
=over 4
=item tagName
Returns the tag name.
=item id
=item title
=item lang
=item dir
=item className
These five get (optionally set) the corresponding HTML attributes. Note
that C<className> corresponds to the C<class> attribute.
=cut
sub tagName {
uc $_[0]->tag;
}
sub id { shift->_attr(id => @_) }
sub title { shift->_attr(title => @_) }
sub lang { shift->_attr(lang => @_) }
sub dir { lc shift->_attr(dir => @_) }
sub className { shift->_attr(class => @_) }
=item style
This returns a L<CSS::DOM::Style> object, representing the contents
of the 'style' HTML attribute.
=cut
sub style {
my $self = shift;
($self->getAttributeNode('style') || do {
$self->setAttribute('style','');
$self->getAttributeNode('style');
}) -> style;
}
=back
And there is also the following non-DOM attribute:
=over 4
=item content_offset
This contains the offset (in characters) within the HTML source of the
element's first child node, if it is a text node. This is set (indirectly)
by HTML::DOM's C<write> method. You can also set it yourself.
=back
=cut
sub content_offset {
my $old = (my $self = shift)->{_HTML_DOM_offset};
@_ and $self->{_HTML_DOM_offset} = shift;
$old;
}
=head2 Other Methods
=over 4
=item getAttribute ( $name )
Returns the attribute's value as a string.
=item setAttribute ( $name, $value )
Sets the attribute named C<$name> to C<$value>.
=item removeAttribute ( $name )
Deletes the C<$name>d attribute.
=item getAttributeNode ( $name )
Returns an attribute node (L<HTML::DOM::Attr>).
=item setAttributeNode ( $attr )
Sets the attribute whose name is C<< $attr->nodeName >> to the attribute
object itself. If it replaces another attribute object, the latter is
returned.
=item removeAttributeNode ( $attr )
Removes and returns the C<$attr>.
=item getElementsByTagName ( $tagname)
This finds all elements with that tag name under the current element,
returning them as a list in list context or a node list object in scalar
context.
=item getElementsByClassName ( $names )
This finds all elements whose class attribute contains all the names in
C<$names>, which is a space-separated list; returning the elements as a
list in list context or a node list object in scalar
context.
=item hasAttribute ( $name )
Returns true or false, indicating whether this element has an attribute
named C<$name>, even one that is implied.
=item click() (HTML 5)
This triggers a click event on the element; nothing more.
=item trigger_event
This overrides L<HTML::DOM::Node>'s method to trigger a DOMActivate event
after a click.
=back
=cut
my %attr_defaults = (
br => { clear => 'none' },
td => { colspan => '1', rowspan=>1},
th => { colspan => 1, rowspan=>1},
form => {
enctype => 'application/x-www-form-urlencoded',
method => 'GET',
},
frame =>{frameborder => 1,scrolling=> 'auto'},
iframe=> {frameborder => 1,scrolling=>'auto'},
'area'=> {'shape' => 'rect',},
'a' =>{'shape' => 'rect',},
'col'=>{ 'span' => 1,},
'colgroup'=>{ 'span' => 1,},
'input',{ 'type' => 'TEXT',},
'button' =>{'type' => 'submit',},
'param' =>{'valuetype' => 'DATA'},
);
# Note: The _HTML_DOM_unspecified key used below points to a hash that
# stores Attr objects for implicit attributes in this list.
sub getAttribute {
my $ret = $_[0]->attr($_[1]);
defined $ret ? "$ret" : do{
my $tag = $_[0]->tag;
if(!$_[0]->tag){warn $_[0]->as_HTML; Carp::cluck}
return '' unless exists $attr_defaults{$tag}
and exists $attr_defaults{$tag}{$_[1]}
or $tag eq 'html' and $_[1] eq 'version'
and exists $_[0]->{_HTML_DOM_version};
$_[1] eq 'version'
? $_[0]->{_HTML_DOM_version}
: $attr_defaults{$tag}{$_[1]}
};
}
sub setAttribute {
# ~~~ INVALID_CHARACTER_ERR
my $self = shift;
# If the current value is an Attr object, we have to modify that
# instead of just assigning to the attribute.
my $attr = $self->attr($_[0]);
if(defined blessed $attr && $attr->isa('HTML::DOM::Attr')){
$attr->value($_[1]);
}else{
my($name,$val) = @_;
my $str_val = "$val";
my $old = $self->attr($name,$str_val);
no warnings 'uninitialized';
$old ne $str_val
and $self->trigger_event('DOMAttrModified',
auto_viv => sub {
require HTML'DOM'Event'Mutation;
attr_name => $name,
attr_change_type =>
defined $old
? &HTML'DOM'Event'Mutation'MODIFICATION
: &HTML'DOM'Event'Mutation'ADDITION,
prev_value => $old,
new_value => $val,
rel_node => $self->getAttributeNode($name),
}
);
}
# possible event handler
if ($_[0] =~ /^on(.*)/is and my $listener_maker = $self->
ownerDocument->event_attr_handler) {
my $eavesdropper = &$listener_maker(
$self, my $name = lc $1, $_[1]
);
defined $eavesdropper and $self-> event_handler(
$name, $eavesdropper
);
}
return # nothing;
}
# This is just like attr, except that it triggers events.
sub _attr {
my($self,$name) = (shift,shift);
# ~~~ Can we change getAttribute to attr, to make it faster, or will attr reject a reference? (Do we have to stringify it?)
my $old = $self->getAttribute($name) if defined wantarray;
@_
and defined $_[0]
? $self->setAttribute($name, shift)
: $self->removeAttribute($name);
$old;
}
sub removeAttribute {
my $old = (my $self = shift)->attr(my $name = shift);
$self->attr($name => undef);
if(defined blessed $old and $old->isa('HTML::DOM::Attr')) {
# So the attr node can be reused:
$old->_element(undef);
$self->trigger_event('DOMAttrModified',
attr_name => $name,
attr_change_type => 3,
prev_value =>
(new_value => ($old->value) x 2)[-1..1],
rel_node => $old,
);
}
else {
return unless defined $old;
$self->trigger_event('DOMAttrModified',
auto_viv => sub {
(my $attr =
$self->ownerDocument
->createAttribute($name)
)->value($old);
attr_name => $name,
attr_change_type => 3,
prev_value => $old,
new_value => $old,
rel_node => $attr,
}
);
}
return # nothing;
}
sub getAttributeNode {
my $elem = shift;
my $name = lc shift;
my $attr = $elem->attr($name);
unless(defined $attr
) { # check to see whether it has a default value
my $tag = $elem->tag;
return $elem->{_HTML_DOM_unspecified}{$name} ||= do{
return unless exists $attr_defaults{$tag}
and exists $attr_defaults{$tag}{$name}
or $tag eq 'html' and $name eq 'version'
and exists $elem->{_HTML_DOM_version};
my $attr = HTML::DOM::Attr->new($name);
$attr->_set_ownerDocument($elem->ownerDocument);
$attr->_element($elem);
$attr->value($name eq 'version'
? $elem->{_HTML_DOM_version}
: $attr_defaults{$tag}{$name});
$attr;
};
}
if(!ref $attr) {
$elem->attr($name, my $new_attr =
HTML::DOM::Attr->new($name, $attr));
$new_attr->_set_ownerDocument($elem->ownerDocument);
$new_attr->_element($elem);
return $new_attr;
}
$attr;
}
sub setAttributeNode {
my $doc = $_[0]->ownerDocument;
# Even if it’s already the same document, it’s actually
# quicker just to set it than to check first.
$_[1]->_set_ownerDocument($doc);
my $e;
die HTML::DOM::Exception->new(INUSE_ATTRIBUTE_ERR,
'The attribute passed to setAttributeNode is in use')
if defined($e = $_[1]->_element) && $e != $_[0];
my $old = $_[0]->attr(my $name = $_[1]->nodeName, $_[1]);
$_[1]->_element($_[0]);
# possible event handler
if ($name =~ /^on(.*)/is and my $listener_maker = $_[0]->
ownerDocument->event_attr_handler) {
# ~~~ Is there a possibility that the listener-maker
# will have a reference to the old attr node, and
# that calling it when that attr still has an
# 'owner' element when it shouldn't will cause any
# problems? Yet I don't want to intertwine this
# section of code with the one below.
my $eavesdropper = &$listener_maker(
$_[0], $name = lc $1, $_[1]->nodeValue
);
defined $eavesdropper and $_[0]-> event_handler(
$name, $eavesdropper
);
}
my $ret;
if(defined $old) {
if(defined blessed $old and $old->isa("HTML::DOM::Attr")) {
$old->_element(undef);
$ret = $old;
} else {
$ret =
HTML::DOM::Attr->new($name);
$ret->_set_ownerDocument($doc);
$ret->_element($_[0]);
$ret->value($old);
}
}
defined $ret and $_[0]->trigger_event('DOMAttrModified',
attr_name => $name,
attr_change_type => 3,
prev_value =>
(new_value => ($ret->value) x 2)[-1..1],
rel_node => $ret,
);
$_[0]->trigger_event('DOMAttrModified',
attr_name => $_[1]->name,
attr_change_type => 2,
prev_value =>
(new_value => ($_[1]->value) x 2)[-1..1],
rel_node => $_[1],
);
return $ret if defined $ret;
return # nothing;
}
sub removeAttributeNode {
my($elem,$attr) = @_;
my $old_val = $elem->attr(my $name = $attr->nodeName);
defined($old_val)
? ref$old_val && refaddr $attr == refaddr $old_val
: exists $elem->{_HTML_DOM_unspecified}{$name}
or die HTML::DOM::Exception->new(NOT_FOUND_ERR,
"The node passed to removeAttributeNode is not an " .
"attribute of this element.");
$elem->attr($name, undef);
delete $elem->{_HTML_DOM_unspecified}{$name};
$attr->_element(undef);
$elem->trigger_event('DOMAttrModified',
attr_name => $name,
attr_change_type => 3,
prev_value =>
(new_value => ($attr->value) x 2)[-1..1],
rel_node => $attr,
);
return $attr
}
sub getElementsByTagName {
my($self,$tagname) = @_;
if (wantarray) {
return $tagname eq '*'
? grep tag $_ !~ /^~/, $self->descendants
: (
($tagname = lc $tagname)[()],
grep tag $_ eq $tagname, $self->descendants
);
}
else {
my $list = HTML::DOM::NodeList::Magic->new(
$tagname eq '*'
? sub { grep tag $_ !~ /^~/, $self->descendants }
: (
$tagname = lc $tagname,
sub {
grep tag $_ eq $tagname, $self->descendants
}
)[1]
);
$self->ownerDocument-> _register_magic_node_list($list);
$list;
}
}
sub getElementsByClassName {
splice @_, 2; # Remove extra elements
goto &_getElementsByClassName;
}
sub _getElementsByClassName {
my($self,$names,$is_doc) = @_;
my $cref;
if(defined $names) {
no warnings 'uninitialized';
# The DOM spec says to skip *ASCII* whitespace, and defines it as:
# U+0009, U+000A, U+000C, U+000D, and U+0020
# \t \n \f \r
$names
= join ".*", map " $_ ", sort split /[ \t\n\f\r]+/, $names;
$cref = sub {
(" ".join(" ", sort split /[ \t\n\f\r]+/, $_[0]->attr('class'))
." ")
=~ $names
};
}
else { $cref = sub {} }
if (wantarray) {
return $self->look_down($cref);
}
else {
my $list = HTML::DOM::NodeList::Magic->new(
sub { $self->look_down($cref); }
);
($is_doc ? $self : $self-> ownerDocument)
->_register_magic_node_list($list);
$list;
}
}
sub hasAttribute {
my ($self,$attrname)= (shift, lc shift);
my $tag;
defined $self->attr($attrname)
or exists $attr_defaults{$tag = $self->tag}
and exists $attr_defaults{$tag}{$attrname}
or $tag eq 'html' and $attrname eq 'version'
and exists $self->{_HTML_DOM_version}
}
sub _attr_specified { defined shift->attr(shift) }
sub click { shift->trigger_event('click') }
# used by innerHTML and insertAdjacentHTML
sub _html_fragment_parser {
require HTML'DOM; # paranoia
(my $tb = new HTML::DOM::Element::HTML:: no_magic_forms=>1)
->_set_ownerDocument(shift->ownerDocument);
$tb->parse(shift);
$tb->eof();
$_->implicit(1) for $tb, $tb->content_list; # more paranoia
$tb;
}
use constant _html_element_adds_newline =>
new HTML::DOM::_Element 'foo' =>->as_HTML =~ /\n/;
sub innerHTML {
my $self = shift;
my $old = join '', map $_->nodeType==ELEMENT_NODE
? _html_element_adds_newline
? substr(
$_->as_HTML((undef)x2,{}),0,-1
)
: $_->as_HTML((undef)x2,{})
: encode_entities($_->data),$self->content_list
if defined wantarray;
if(@_) {
my $tb = _html_fragment_parser($self,shift);
$self->delete_content;
$self->push_content($tb->guts);
{($self->ownerDocument||last)->_modified}
}
$old;
}
{
my %mm # method map
= qw(
beforebegin preinsert
afterend postinsert
afterbegin unshift_content
beforeend push_content
);
sub insertAdjacentHTML {
my $elem = shift;
die new HTML::DOM::Exception:: SYNTAX_ERR,
"$_[0]: invalid first argument to insertAdjacentHTML"
unless exists $mm{ my $where = lc $_[0] };
my $tb = _html_fragment_parser($elem,$_[1]);
$elem->${\$mm{$where}}(guts $tb);
{($elem->ownerDocument||last)->_modified}
()
}
sub insertAdjacentElement {
my $elem = shift;
die new HTML::DOM::Exception:: SYNTAX_ERR,
"$_[0]: invalid first argument to insertAdjacentElement"
unless exists $mm{ my $where = lc $_[0] };
$elem->${\$mm{$where}}($_[1]);
{($elem->ownerDocument||last)->_modified}
()
}
}
sub innerText {
my $self = shift;
my $old = $self->as_text
if defined wantarray;
if(@_) {
# The slow way (with removeChild instead of delete_content)
# in order to trigger mutation events. (This may change if
# there is a spec one day for innerText.)
$self->removeChild($_) for $self->childNodes;
$self->appendChild(
$self->ownerDocument->createTextNode(shift)
);
}
$old;
}
sub starttag {
my $self = shift;
my $tag = $self->SUPER::starttag(@ _);
$tag =~ s/ \/>\z/>/;
$tag
}
# ------- OVERRIDDEN NODE METHDOS ---------- #
*nodeName = \&tagName;
*nodeType = \& ELEMENT_NODE;
sub attributes {
my $self = shift;
$self->{_HTML_DOM_Element_map} ||=
HTML::DOM::NamedNodeMap->new($self);
}
sub cloneNode { # override of HTML::DOM::Node’s method
my $clown = shift->SUPER::cloneNode(@_);
unless(shift) { # if it’s shallow
# Flatten attr nodes, effectively cloning them:
$$clown{$_} = "$$clown{$_}" for grep !/^_/, keys %$clown;
delete $clown->{_HTML_DOM_Element_map};
} # otherwise clone takes care of this, so we don’t need to here
$clown;
}
sub clone { # override of HTML::Element’s method; this is called
# recursively during a deep clone
my $clown = shift->SUPER::clone;
$$clown{$_} = "$$clown{$_}" for grep !/^_/, keys %$clown;
delete $clown->{_HTML_DOM_Element_map};
$clown;
}
sub trigger_event {
my ($a,$evnt) = (shift,shift);
$a->SUPER::trigger_event(
$evnt,
click_default =>sub {
$_[0]->target->trigger_event(DOMActivate =>
detail => eval{$_[0]->detail}
);;
},
# We check magic_forms before adding this for efficiency’s
# sake: so as not to burden well-formed documents with
# the extra overhead of auto-vivving an event object
# unnecessarily.
$a->ownerDocument->magic_forms ? (
DOMNodeRemoved_default => sub {
my $targy = $_[0]->target;
for($targy, $targy->descendants) {
eval { $_->form(undef) };
}
return; # give the eval void context
},
) : (),
@_,
);
}
=head1 SEE ALSO
L<HTML::DOM>
L<HTML::DOM::Node>
L<HTML::Element>
All the HTML::DOM::Element subclasses listed under
L<HTML::DOM/CLASSES AND DOM INTERFACES>
=cut
# ------- HTMLHtmlElement interface ---------- #
# This has been moved to DOM.pm.
# ------- HTMLHeadElement interface ---------- #
package HTML::DOM::Element::Head;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
sub profile { shift->_attr('profile' => @_) }
# ------- HTMLLinkElement interface ---------- #
package HTML::DOM::Element::Link;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
use Scalar::Util 'blessed';
sub disabled {
if(@_ > 1) {
my $old = $_[0]->{_HTML_DOM_disabled};
$_[0]->{_HTML_DOM_disabled} = $_[1];
return $old;
}
else { $_[0]->{_HTML_DOM_disabled};}
}
sub charset { shift->_attr('charset' => @_) }
sub href { shift->_attr('href' => @_) }
sub hreflang { shift->_attr( hreflang => @_) }
sub media { shift->_attr('media' => @_) }
sub rel { shift->_attr('rel' => @_) }
sub rev { shift->_attr('rev' => @_) }
sub target { shift->_attr('target' => @_) }
sub type { shift->_attr('type' => @_) }
sub sheet {
my $self = shift;
no warnings 'uninitialized';
$self->attr('rel') =~
/(?:^|\p{IsSpacePerl})stylesheet(?:\z|\p{IsSpacePerl})/i
or return;
my $old = $$self{_HTML_DOM_sheet};
@_ and $self->{_HTML_DOM_sheet} = shift;
$old||();
}
# I need to override these four to update the document’s style sheet list.
# ~~~ These could be made more efficient if they checked the attribute
# name first, to avoid unnecessary method calls.
sub setAttribute {
for(shift) {
$_->SUPER::setAttribute(@_);
$_->ownerDocument->_populate_sheet_list;
}
return # nothing;
}
sub removeAttribute {
for(shift) {
$_->SUPER::removeAttribute(@_);
$_->ownerDocument->_populate_sheet_list
}
return # nothing;
}
sub setAttributeNode {
(my $self = shift)->SUPER::setAttributeNode(@_);
$self->ownerDocument->_populate_sheet_list;
return # nothing;
}
sub removeAttributeNode {
my $self = shift;
my $attr = $self->SUPER::removeAttributeNode(@_);
$self->ownerDocument->_populate_sheet_list;
$attr
}
sub trigger_event {
# ~~~ This defeats the purpose of having an auto-viv sub. I need to do
# some rethinking....
my $elem = shift;
if(defined blessed $_[0] and $_[0]->isa("HTML::DOM::Event")) {
return $elem->SUPER::trigger_event(@_)
unless $_[0]->type =~ /^domattrmodified\z/i;
my $attr_name = $_[0]->attrName;
if($attr_name eq 'href') { _reset_style_sheet($elem) }
}
elsif($_[0] !~ /^domattrmodified\z/i) {
return $elem->SUPER::trigger_event(@_);
}
else {
my($event,%args) = @_;
$args{auto_viv} and %args = &{$args{auto_viv}}, @_ = ($event, %args);
$args{attr_name} eq 'href' and _reset_style_sheet($elem);
}
SUPER'trigger_event $elem @_;
}
sub _reset_style_sheet {
my $elem = shift;
return
unless ($elem->attr('rel')||'')
=~ /(?:^|\p{IsSpacePerl})stylesheet(?:\z|\p{IsSpacePerl})/i;
my $doc = $elem->ownerDocument;
return unless my $fetcher = $doc->css_url_fetcher;
my $base = $doc->base;
my $url = defined $base
? new_abs URI
$elem->href, $doc->base
: $elem->href;
my ($css_code, %args)
= $fetcher->($url);
return unless defined $css_code;
require CSS'DOM;
VERSION CSS'DOM 0.03;
my $hint
= $doc->charset || 'iso-8859-1';
# default HTML charset
$elem->sheet(
# ’Tis true we create a new clo-
# sure for each style sheet, but
# what if the charset changes?
# ~~~ Is that even possible?
CSS'DOM'parse(
$css_code,
url_fetcher => sub {
my @ret = $fetcher->(shift);
@ret
? (
$ret[0],
encoding_hint => $hint,
@ret[1..$#ret]
) : ()
},
encoding_hint => $hint,
%args
)
);
}
# ------- HTMLTitleElement interface ---------- #
package HTML::DOM::Element::Title;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
# This is what I call FWP (no lexical vars):
sub text {
($_[0]->firstChild or
@_ > 1 && $_[0]->appendChild(
shift->ownerDocument->createTextNode(shift)
),
return '',
)->data(@_[1..$#_]);
}
# ------- HTMLMetaElement interface ---------- #
package HTML::DOM::Element::Meta;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
sub content { shift->_attr('content' => @_) }
sub httpEquiv { shift->_attr('http-equiv' => @_) }
sub name { shift->_attr('name' => @_) }
sub scheme { shift->_attr('scheme' => @_) }
# ------- HTMLBaseElement interface ---------- #
package HTML::DOM::Element::Base;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
*href =\& HTML::DOM::Element::Link::href;
*target =\& HTML::DOM::Element::Link::target;
# ------- HTMLIsIndexElement interface ---------- #
package HTML::DOM::Element::IsIndex;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
sub form { (shift->look_up(_tag => 'form'))[0] || () }
# ~~~ Should this be the same as Select::form? I.e., should isindex ele-
# ments get magic form associations?
sub prompt { shift->_attr('prompt' => @_) }
# ------- HTMLStyleElement interface ---------- #
package HTML::DOM::Element::Style;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
*disabled = \&HTML::DOM::Element::Link::disabled;
*media =\& HTML::DOM::Element::Link::media;
*type =\& HTML::DOM::Element::Link::type;
sub sheet {
my $self = shift;
$self->{_HTML_DOM_sheet} ||= do{
my $first_child = $self->firstChild;
local *@;
require CSS::DOM;
VERSION CSS::DOM .03;
CSS::DOM::parse($first_child?$first_child->data:'');
};
}
# ------- HTMLBodyElement interface ---------- #
package HTML::DOM::Element::Body;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
sub aLink { shift->_attr( aLink => @_) }
sub background { shift->_attr( background => @_) }
sub bgColor { shift->_attr('bgcolor' => @_) }
sub link { shift->_attr('link' => @_) }
sub text { shift->_attr('text' => @_) }
sub vLink { shift->_attr('vlink' => @_) }
sub event_handler {
my $self = shift;
my $target = $self->ownerDocument->event_parent;
$target
? $target->event_handler(@_)
: $self->SUPER::event_handler(@_);
}
# ------- HTMLFormElement interface ---------- #
# See Element/Form.pm
# ~~~ list other form things here for reference
# ------- HTMLUListElement interface ---------- #
package HTML::DOM::Element::UL;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
sub compact { shift->_attr( compact => @_ ? $_[0]?'compact': undef : () ) }
sub type { lc shift->_attr( type => @_) }
# ------- HTMLOListElement interface ---------- #
package HTML::DOM::Element::OL;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
sub start { shift->_attr( start => @_) }
*compact=\&HTML::DOM::Element::UL::compact;
* type = \ & HTML::DOM::Element::Link::type ;
# ------- HTMLDListElement interface ---------- #
package HTML::DOM::Element::DL;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
*compact=\&HTML::DOM::Element::UL::compact;
# ------- HTMLDirectoryElement interface ---------- #
package HTML::DOM::Element::Dir;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
*compact=\&HTML::DOM::Element::UL::compact;
# ------- HTMLMenuElement interface ---------- #
package HTML::DOM::Element::Menu;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
*compact=\&HTML::DOM::Element::UL::compact;
# ------- HTMLLIElement interface ---------- #
package HTML::DOM::Element::LI;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
*type =\& HTML::DOM::Element::Link::type;
sub value { shift->_attr( value => @_) }
# ------- HTMLDivElement interface ---------- #
package HTML::DOM::Element::Div;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
sub align { lc shift->_attr( align => @_) }
# ------- HTMLParagraphElement interface ---------- #
package HTML::DOM::Element::P;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
*align =\& HTML::DOM::Element::Div::align;
# ------- HTMLHeadingElement interface ---------- #
package HTML::DOM::Element::Heading;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
*align =\& HTML::DOM::Element::Div::align;
# ------- HTMLQuoteElement interface ---------- #
package HTML::DOM::Element::Quote;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
sub cite { shift->_attr( cite => @_) }
# ------- HTMLPreElement interface ---------- #
package HTML::DOM::Element::Pre;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
sub width { shift->_attr( width => @_) }
# ------- HTMLBRElement interface ---------- #
package HTML::DOM::Element::Br;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
sub clear { lc shift->_attr( clear => @_) }
# ------- HTMLBaseFontElement interface ---------- #
package HTML::DOM::Element::BaseFont;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
sub color { shift->_attr( color => @_) }
sub face { shift->_attr( face => @_) }
sub size { shift->_attr( size => @_) }
# ------- HTMLBaseFontElement interface ---------- #
package HTML::DOM::Element::Font;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
*color =\& HTML::DOM::Element::BaseFont::color;
*face =\& HTML::DOM::Element::BaseFont::face;
*size =\& HTML::DOM::Element::BaseFont::size;
# ------- HTMLHRElement interface ---------- #
package HTML::DOM::Element::HR;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
*align =\& HTML::DOM::Element::Div::align;
sub noShade { shift->_attr( noshade => @_ ? $_[0]?'noshade':undef : () ) }
*size =\& HTML::DOM::Element::BaseFont::size;
*width =\& HTML::DOM::Element::Pre::width;
# ------- HTMLModElement interface ---------- #
package HTML::DOM::Element::Mod;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
*cite =\& HTML::DOM::Element::Quote::cite;
sub dateTime { shift->_attr( datetime => @_) }
# ------- HTMLAnchorElement interface ---------- #
package HTML::DOM::Element::A;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
sub accessKey { shift->_attr( accesskey => @_) }
* charset =\&HTML::DOM::Element::Link::charset ;
* coords =\&HTML::DOM::Element::Area::coords ;
* href =\&HTML::DOM::Element::Link::href ;
* hreflang =\&HTML::DOM::Element::Link::hreflang ;
* name =\&HTML::DOM::Element::Meta::name ;
* rel =\&HTML::DOM::Element::Link::rel ;
* rev =\&HTML::DOM::Element::Link::rev ;
sub shape { shift->_attr( shape => @_) }
* tabIndex =\&HTML::DOM::Element::Object::tabIndex ;
* target =\&HTML::DOM::Element::Link::target ;
* type =\&HTML::DOM::Element::Link::type ;
sub blur { shift->trigger_event('blur') }
sub focus { shift->trigger_event('focus') }
sub trigger_event {
my ($a,$evnt) = (shift,shift);
$a->SUPER::trigger_event(
$evnt,
DOMActivate_default =>
$a->ownerDocument->
default_event_handler_for('link')
,
@_,
);
}
sub _get_abs_href {
my $elem = shift;
my $uri = new URI $elem->attr('href');
if(!$uri->scheme) {
my $base = $elem->ownerDocument->base;
return unless $base;
$uri = $uri->abs($base);
return unless $uri->scheme;
}
$uri
}
sub hash {
my $elem = shift;
defined(my $uri = _get_abs_href $elem) or return '';
my $old;
if(defined wantarray) {
$old = $uri->fragment;
$old = "#$old" if defined $old;
}
if (@_){
shift() =~ /#?(.*)/s;
$uri->fragment($1);
$elem->_attr(href => $uri);
}
$old||''
}
sub host {
my $elem = shift;
defined(my $uri = _get_abs_href $elem) or return '';
my $old = $uri->host_port if defined wantarray;
if (@_) {
$uri->port("");
$uri->host_port(shift);
$elem->attr(href => $uri);
}
$old
}
sub hostname {
my $elem = shift;
defined(my $uri = _get_abs_href $elem) or return '';
my $old = $uri->host if defined wantarray;
if (@_) {
$uri->host(shift);
$elem->attr(href => $uri);
}
$old
}
sub pathname {
my $elem = shift;
defined(my $uri = _get_abs_href $elem) or return '';
my $old = $uri->path if defined wantarray;
if (@_) {
$uri->path(shift);
$elem->attr(href => $uri);
}
$old
}
sub port {
my $elem = shift;
defined(my $uri = _get_abs_href $elem) or return '';
my $old = $uri->port if defined wantarray;
if (@_) {
$uri->port(shift);
$elem->attr(href => $uri);
}
$old
}
sub protocol {
my $elem = shift;
defined(my $uri = _get_abs_href $elem) or return '';
my $old = $uri->scheme . ':' if defined wantarray;
if (@_) {
shift() =~ /(.*):?/s;
$uri->scheme("$1");
$elem->attr(href => $uri);
}
$old
}
sub search {
my $elem = shift;
defined(my $uri = _get_abs_href $elem) or return '';
my $old;
if(defined wantarray) {
my $q = $uri->query;
$old = defined $q ? "?$q" : "";
}
if (@_){
shift() =~ /(\??)(.*)/s;
$uri->query(
$1||length$2 ? "$2" : undef
);
$elem->attr(href => $uri);
}
$old
}
# ------- HTMLImageElement interface ---------- #
package HTML::DOM::Element::Img;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
sub lowSrc { shift->attr( lowsrc => @_) }
* name = \&HTML::DOM::Element::Meta::name ;
* align = \&HTML::DOM::Element::Div::align ;
sub alt { shift->_attr( alt => @_) }
sub border { shift->_attr( border => @_) }
sub height { shift->_attr( height => @_) }
sub hspace { shift->_attr( hspace => @_) }
sub isMap { shift->_attr( ismap => @_ ? $_[0] ? 'ismap' : undef : () ) }
sub longDesc { shift->_attr( longdesc => @_) }
sub src { shift->_attr( src => @_) }
sub useMap { shift->_attr( usemap => @_) }
sub vspace { shift->_attr( vspace => @_) }
* width = \&HTML::DOM::Element::Pre::width ;
# ------- HTMLObjectElement interface ---------- #
package HTML::DOM::Element::Object;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
*form=\&HTML::DOM::Element::Select::form;
sub code { shift->_attr( code => @_) }
* align = \&HTML::DOM::Element::Div::align ;
sub archive { shift->_attr( archive => @_) }
sub border { shift->_attr( border => @_) }
sub codeBase { shift->_attr( codebase => @_) }
sub codeType { shift->_attr( codetype => @_) }
sub data { shift->_attr( data => @_) }
sub declare { shift->_attr( declare => @_ ? $_[0]?'declare':undef : () ) }
* height = \&HTML::DOM::Element::Img::height ;
* hspace = \&HTML::DOM::Element::Img::hspace ;
* name = \&HTML::DOM::Element::Meta::name ;
sub standby { shift->_attr( standby => @_) }
sub tabIndex { shift->_attr( tabindex => @_) }
*type =\& HTML::DOM::Element::Link::type;
*useMap =\& HTML::DOM::Element::Img::useMap;
*vspace =\& HTML::DOM::Element::Img::vspace;
* width = \&HTML::DOM::Element::Pre::width ;
sub contentDocument{}
# ------- HTMLParamElement interface ---------- #
package HTML::DOM::Element::Param;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
*name=\&HTML::DOM::Element::Meta::name;
*type=\&HTML::DOM::Element::Link::type;
*value=\&HTML::DOM::Element::LI::value;
sub valueType{lc shift->_attr(valuetype=>@_)}
# ------- HTMLAppletElement interface ---------- #
package HTML::DOM::Element::Applet;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
* align = \ & HTML::DOM::Element::Div::align ;
* alt = \ & HTML::DOM::Element::Img::alt ;
* archive = \ & HTML::DOM::Element::Object::archive ;
* code = \ & HTML::DOM::Element::Object::code ;
* codeBase = \ & HTML::DOM::Element::Object::codeBase ;
* height = \ & HTML::DOM::Element::Img::height ;
* hspace = \ & HTML::DOM::Element::Img::hspace ;
* name = \ & HTML::DOM::Element::Meta::name ;
sub object { shift -> _attr ( object => @_ ) }
* vspace = \ & HTML::DOM::Element::Img::vspace ;
* width = \ & HTML::DOM::Element::Pre::width ;
# ------- HTMLMapElement interface ---------- #
package HTML::DOM::Element::Map;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
sub areas { # ~~~ I need to make this cache the resulting collection obj
my $self = shift;
if (wantarray) {
return grep tag $_ eq 'area', $self->descendants;
}
else {
my $collection = HTML::DOM::Collection->new(
my $list = HTML::DOM::NodeList::Magic->new(
sub { grep tag $_ eq 'area', $self->descendants }
));
$self->ownerDocument-> _register_magic_node_list($list);
$collection;
}
}
* name = \ & HTML::DOM::Element::Meta::name ;
# ------- HTMLAreaElement interface ---------- #
package HTML::DOM::Element::Area;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
* alt = \ & HTML::DOM::Element::Img::alt ;
sub coords { shift -> _attr ( coords => @_ ) }
* href = \ & HTML::DOM::Element::Link::href ;
sub noHref { shift->attr ( nohref => @_ ? $_[0] ? 'nohref' : undef : () ) }
* tabIndex = \ & HTML::DOM::Element::Object::tabIndex ;
* target = \ & HTML::DOM::Element::Link::target ;
{
no strict 'refs';
*$_ = \&{"HTML::DOM::Element::A::$_"}
for qw(accessKey shape hash host hostname pathname port protocol search
trigger_event);
}
# ------- HTMLScriptElement interface ---------- #
package HTML::DOM::Element::Script;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
* text = \ &HTML::DOM::Element::Title::text ;
sub htmlFor { shift -> _attr ( for => @_ ) }
sub event { shift -> _attr ( event => @_ ) }
* charset = \ &HTML::DOM::Element::Link::charset ;
sub defer { shift -> _attr ( defer => @_ ? $_[0] ? 'defer' : undef : () ) }
* src = \ &HTML::DOM::Element::Img::src ;
* type = \ &HTML::DOM::Element::Link::type ;
# ------- HTMLFrameSetElement interface ---------- #
package HTML::DOM::Element::FrameSet;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
sub rows { shift -> _attr ( rows => @_ ) }
sub cols { shift -> _attr ( cols => @_ ) }
# ------- HTMLFrameElement interface ---------- #
package HTML::DOM::Element::Frame;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
sub frameBorder { lc shift -> _attr ( frameBorder => @_ ) }
sub longDesc { shift -> _attr ( longdesc => @_ ) }
sub marginHeight{ shift -> _attr ( marginheight => @_ ) }
sub marginWidth { shift -> _attr ( marginwidth => @_ ) }
* name = \ &HTML::DOM::Element::Meta::name ;
sub noResize { shift->_attr(noresize => @_ ? $_[0]?'noresize':undef : ()) }
sub scrolling { lc shift -> _attr ( scrolling => @_ ) }
* src = \ &HTML::DOM::Element::Img::src ;
sub contentDocument{ (shift->{_HTML_DOM_view} || return)->document }
sub contentWindow {
my $old = (my $self = shift)->{_HTML_DOM_view};
@_ and $self->{_HTML_DOM_view} = shift;
defined $old ? $old : ()
};
# ------- HTMLIFrameElement interface ---------- #
package HTML::DOM::Element::IFrame;
our $VERSION = '0.057';
our @ISA = 'HTML::DOM::Element';
*align = \&HTML::DOM::Element::Div::align;
*frameBorder = \&HTML::DOM::Element::Frame::frameBorder;
*height = \&HTML::DOM::Element::Img::height;
*longDesc = \&HTML::DOM::Element::Frame::longDesc;
* marginHeight = \&HTML::DOM::Element::Frame::marginHeight;
*marginWidth = \&HTML::DOM::Element::Frame::marginWidth;
*name = \&HTML::DOM::Element::Meta::name;
*scrolling = \&HTML::DOM::Element::Frame::scrolling;
*src = \&HTML::DOM::Element::Img::src;
*width = \&HTML::DOM::Element::Pre::width;
*contentDocument = \&HTML::DOM::Element::Frame::contentDocument;
*contentWindow = \&HTML::DOM::Element::Frame::contentWindow;
1
Copyright 2K16 - 2K18 Indonesian Hacker Rulez