home *** CD-ROM | disk | FTP | other *** search
/ Chip 2000 May / Chip_2000-05_cd1.bin / zkuste / Perl / ActivePerl-5.6.0.613.msi / 䆊䌷䈹䈙䏵-䞅䞆䞀㡆䞃䄦䠥 / _cb3dea81a9a44edff5ac2675614634a2 < prev    next >
Text File  |  2000-03-15  |  13KB  |  606 lines

  1. package HTML::Element;
  2.  
  3. # $Id: Element.pm,v 1.39 1998/03/26 20:30:13 aas Exp $
  4.  
  5. =head1 NAME
  6.  
  7. HTML::Element - Class for objects that represent HTML elements
  8.  
  9. =head1 SYNOPSIS
  10.  
  11.  require HTML::Element;
  12.  $a = new HTML::Element 'a', href => 'http://www.oslonett.no/';
  13.  $a->push_content("Oslonett AS");
  14.  
  15.  $tag = $a->tag;
  16.  $tag = $a->starttag;
  17.  $tag = $a->endtag;
  18.  $ref = $a->attr('href');
  19.  
  20.  $links = $a->extract_links();
  21.  
  22.  print $a->as_HTML;
  23.  
  24. =head1 DESCRIPTION
  25.  
  26. Objects of the HTML::Element class can be used to represent elements
  27. of HTML.  These objects have attributes and content.  The content is an
  28. array of text segments and other HTML::Element objects.  Thus a
  29. tree of HTML::Element objects as nodes can represent the syntax tree
  30. for a HTML document.
  31.  
  32. The following methods are available:
  33.  
  34. =over 4
  35.  
  36. =cut
  37.  
  38.  
  39. use strict;
  40. use Carp ();
  41. use HTML::Entities ();
  42.  
  43. use vars qw($VERSION
  44.         %emptyElement %optionalEndTag %linkElements %boolean_attr
  45.            );
  46.  
  47. $VERSION = sprintf("%d.%02d", q$Revision: 1.39 $ =~ /(\d+)\.(\d+)/);
  48. sub Version { $VERSION; }
  49.  
  50. # Elements that does not have corresponding end tags (i.e. are empty)
  51. %emptyElement   = map { $_ => 1 } qw(base link meta isindex
  52.                          img br hr wbr
  53.                          input area param
  54.                         );
  55. %optionalEndTag = map { $_ => 1 } qw(p li dt dd option); # th tr td);
  56.  
  57. # Elements that might contain links and the name of the link attribute
  58. %linkElements =
  59. (
  60.  body   => 'background',
  61.  base   => 'href',
  62.  a      => 'href',
  63.  img    => [qw(src lowsrc usemap)],   # lowsrc is a Netscape invention
  64.  form   => 'action',
  65.  input  => 'src',
  66. 'link'  => 'href',          # need quoting since link is a perl builtin
  67.  frame  => 'src',
  68.  applet => 'codebase',
  69.  area   => 'href',
  70. );
  71.  
  72. # These attributes are normally printed without showing the "='value'".
  73. # This representation works as long as no element has more than one
  74. # attribute like this.
  75. %boolean_attr = (
  76.  area   => 'nohref',
  77.  dir    => 'compact',
  78.  dl     => 'compact',
  79.  hr     => 'noshade',
  80.  img    => 'ismap',
  81.  input  => { checked => 1, readonly => 1, disabled => 1 },
  82.  menu   => 'compact',
  83.  ol     => 'compact',
  84.  option => 'selected',
  85. 'select'=> 'multiple',
  86.  td     => 'nowrap',
  87.  th     => 'nowrap',
  88.  ul     => 'compact',
  89. );
  90.  
  91. =item $h = HTML::Element->new('tag', 'attrname' => 'value',...)
  92.  
  93. The object constructor.  Takes a tag name as argument. Optionally,
  94. allows you to specify initial attributes at object creation time.
  95.  
  96. =cut
  97.  
  98. #
  99. # An HTML::Element is represented by blessed hash reference.  Key-names
  100. # not starting with '_' are reserved for the SGML attributes of the element.
  101. # The following special keys are used:
  102. #
  103. #    '_tag':    The tag name
  104. #    '_parent': A reference to the HTML::Element above (when forming a tree)
  105. #    '_pos':    The current position (a reference to a HTML::Element) is
  106. #               where inserts will be placed (look at the insert_element method)
  107. #
  108. # Example: <img src="gisle.jpg" alt="Gisle's photo"> is represented like this:
  109. #
  110. #  bless {
  111. #     _tag => 'img',
  112. #     src  => 'gisle.jpg',
  113. #     alt  => "Gisle's photo",
  114. #  }, HTML::Element;
  115. #
  116.  
  117. sub new
  118. {
  119.     my $class = shift;
  120.     my $tag   = shift;
  121.     Carp::croak("No tag") unless defined $tag or length $tag;
  122.     my $self  = bless { _tag => lc $tag }, $class;
  123.     my($attr, $val);
  124.     while (($attr, $val) = splice(@_, 0, 2)) {
  125.     $val = $attr unless defined $val;
  126.     $self->{lc $attr} = $val;
  127.     }
  128.     if ($tag eq 'html') {
  129.     $self->{'_pos'} = undef;
  130.     }
  131.     $self;
  132. }
  133.  
  134.  
  135.  
  136. =item $h->tag()
  137.  
  138. Returns (optionally sets) the tag name for the element.  The tag is
  139. always converted to lower case.
  140.  
  141. =cut
  142.  
  143. sub tag
  144. {
  145.     my $self = shift;
  146.     if (@_) {
  147.     $self->{'_tag'} = lc $_[0];
  148.     } else {
  149.     $self->{'_tag'};
  150.     }
  151. }
  152.  
  153.  
  154.  
  155. =item $h->starttag()
  156.  
  157. Returns the complete start tag for the element.  Including leading
  158. "<", trailing ">" and attributes.
  159.  
  160. =cut
  161.  
  162. sub starttag
  163. {
  164.     my $self = shift;
  165.     my $name = $self->{'_tag'};
  166.     my $tag = "<\U$name";
  167.     for (sort keys %$self) {
  168.     next if /^_/;
  169.     my $val = $self->{$_};
  170.     if ($_ eq $val &&
  171.         exists($boolean_attr{$name}) &&
  172.         (ref($boolean_attr{$name}) ? $boolean_attr{$name}{$_} : 
  173.                       $boolean_attr{$name} eq $_)) {
  174.         $tag .= " \U$_";
  175.     } else {
  176.         if ($val !~ /^\d+$/) {
  177.         # count number of " compared to number of '
  178.         if (($val =~ tr/\"/\"/) > ($val =~ tr/\'/\'/)) {
  179.             # use single quotes around the attribute value
  180.             HTML::Entities::encode_entities($val, "&'>");
  181.             $val = qq('$val');
  182.         } else {
  183.             HTML::Entities::encode_entities($val, '&">');
  184.             $val = qq{"$val"};
  185.         }
  186.         }
  187.         $tag .= qq{ \U$_\E=$val};
  188.     }
  189.     }
  190.     "$tag>";
  191. }
  192.  
  193.  
  194.  
  195. =item $h->endtag()
  196.  
  197. Returns the complete end tag.  Includes leading "</" and the trailing
  198. ">".
  199.  
  200. =cut
  201.  
  202. sub endtag
  203. {
  204.     "</\U$_[0]->{'_tag'}>";
  205. }
  206.  
  207.  
  208.  
  209. =item $h->parent([$newparent])
  210.  
  211. Returns (optionally sets) the parent for this element.
  212.  
  213. =cut
  214.  
  215. sub parent
  216. {
  217.     my $self = shift;
  218.     if (@_) {
  219.     $self->{'_parent'} = $_[0];
  220.     } else {
  221.     $self->{'_parent'};
  222.     }
  223. }
  224.  
  225.  
  226.  
  227. =item $h->implicit([$bool])
  228.  
  229. Returns (optionally sets) the implicit attribute.  This attribute is
  230. used to indicate that the element was not originally present in the
  231. source, but was inserted in order to conform to HTML strucure.
  232.  
  233. =cut
  234.  
  235. sub implicit
  236. {
  237.     shift->attr('_implicit', @_);
  238. }
  239.  
  240.  
  241.  
  242. =item $h->is_inside('tag',...)
  243.  
  244. Returns true if this tag is contained inside one of the specified tags.
  245.  
  246. =cut
  247.  
  248. sub is_inside
  249. {
  250.     my $self = shift;
  251.     my $p = $self;
  252.     while (defined $p) {
  253.     my $ptag = $p->{'_tag'};
  254.     for (@_) {
  255.         return 1 if $ptag eq $_;
  256.     }
  257.     $p = $p->{'_parent'};
  258.     }
  259.     0;
  260. }
  261.  
  262.  
  263.  
  264. =item $h->pos()
  265.  
  266. Returns (and optionally sets) the current position.  The position is a
  267. reference to a HTML::Element object that is part of the tree that has
  268. the current object as root.  This restriction is not enforced when
  269. setting pos(), but unpredictable things will happen if this is not
  270. true.
  271.  
  272.  
  273. =cut
  274.  
  275. sub pos
  276. {
  277.     my $self = shift;
  278.     my $pos = $self->{'_pos'};
  279.     if (@_) {
  280.     $self->{'_pos'} = $_[0];
  281.     }
  282.     return $pos if defined($pos);
  283.     $self;
  284. }
  285.  
  286.  
  287.  
  288. =item $h->attr('attr', [$value])
  289.  
  290. Returns (and optionally sets) the value of some attribute.
  291.  
  292. =cut
  293.  
  294. sub attr
  295. {
  296.     my $self = shift;
  297.     my $attr = lc shift;
  298.     my $old = $self->{$attr};
  299.     if (@_) {
  300.     $self->{$attr} = $_[0];
  301.     }
  302.     $old;
  303. }
  304.  
  305.  
  306.  
  307. =item $h->content()
  308.  
  309. Returns the content of this element.  The content is represented as a
  310. reference to an array of text segments and references to other
  311. HTML::Element objects.
  312.  
  313. =cut
  314.  
  315. sub content
  316. {
  317.     shift->{'_content'};
  318. }
  319.  
  320.  
  321.  
  322. =item $h->is_empty()
  323.  
  324. Returns true if there is no content.
  325.  
  326. =cut
  327.  
  328. sub is_empty
  329. {
  330.     my $self = shift;
  331.     !exists($self->{'_content'}) || !@{$self->{'_content'}};
  332. }
  333.  
  334.  
  335.  
  336. =item $h->insert_element($element, $implicit)
  337.  
  338. Inserts a new element at current position and updates pos() to point
  339. to the inserted element.  Returns $element.
  340.  
  341. =cut
  342.  
  343. sub insert_element
  344. {
  345.     my($self, $tag, $implicit) = @_;
  346.     my $e;
  347.     if (ref $tag) {
  348.     $e = $tag;
  349.     $tag = $e->tag;
  350.     } else {
  351.     $e = new HTML::Element $tag;
  352.     }
  353.     $e->{'_implicit'} = 1 if $implicit;
  354.     my $pos = $self->{'_pos'};
  355.     $pos = $self unless defined $pos;
  356.     $pos->push_content($e);
  357.     unless ($emptyElement{$tag}) {
  358.     $self->{'_pos'} = $e;
  359.     $pos = $e;
  360.     }
  361.     $pos;
  362. }
  363.  
  364.  
  365. =item $h->push_content($element_or_text,...)
  366.  
  367. Adds to the content of the element.  The content should be a text
  368. segment (scalar) or a reference to a HTML::Element object.
  369.  
  370. =cut
  371.  
  372. sub push_content
  373. {
  374.     my $self = shift;
  375.     $self->{'_content'} = [] unless exists $self->{'_content'};
  376.     my $content = $self->{'_content'};
  377.     for (@_) {
  378.     if (ref $_) {
  379.         $_->{'_parent'} = $self;
  380.         push(@$content, $_);
  381.     } else {
  382.         # The current element is a text segment
  383.         if (@$content && !ref $content->[-1]) {
  384.         # last content element is also text segment
  385.         $content->[-1] .= $_;
  386.         } else {
  387.         push(@$content, $_);
  388.         }
  389.     }
  390.     }
  391.     $self;
  392. }
  393.  
  394.  
  395.  
  396. =item $h->delete_content()
  397.  
  398. Clears the content.
  399.  
  400. =cut
  401.  
  402. sub delete_content
  403. {
  404.     my $self = shift;
  405.     for (@{$self->{'_content'}}) {
  406.     $_->delete if ref $_;
  407.     }
  408.     delete $self->{'_content'};
  409.     $self;
  410. }
  411.  
  412.  
  413.  
  414. =item $h->delete()
  415.  
  416. Frees memory associated with the element and all children.  This is
  417. needed because perl's reference counting does not work since we use
  418. circular references.
  419.  
  420. =cut
  421. #'
  422.  
  423. sub delete
  424. {
  425.     $_[0]->delete_content;
  426.     delete $_[0]->{'_parent'};
  427.     delete $_[0]->{'_pos'};
  428.     $_[0] = undef;
  429. }
  430.  
  431.  
  432.  
  433. =item $h->traverse(\&callback, [$ignoretext])
  434.  
  435. Traverse the element and all of its children.  For each node visited, the
  436. callback routine is called with the node, a startflag and the depth as
  437. arguments.  If the $ignoretext parameter is true, then the callback
  438. will not be called for text content.  The flag is 1 when we enter a
  439. node and 0 when we leave the node.
  440.  
  441. If the returned value from the callback is false then we will not
  442. traverse the children.
  443.  
  444. =cut
  445.  
  446. sub traverse
  447. {
  448.     my($self, $callback, $ignoretext, $depth) = @_;
  449.     $depth ||= 0;
  450.  
  451.     if (&$callback($self, 1, $depth)) {
  452.     for (@{$self->{'_content'}}) {
  453.         if (ref $_) {
  454.         $_->traverse($callback, $ignoretext, $depth+1);
  455.         } else {
  456.         &$callback($_, 1, $depth+1) unless $ignoretext;
  457.         }
  458.     }
  459.     &$callback($self, 0, $depth) unless $emptyElement{$self->{'_tag'}};
  460.     }
  461.     $self;
  462. }
  463.  
  464.  
  465.  
  466. =item $h->extract_links([@wantedTypes])
  467.  
  468. Returns links found by traversing the element and all of its children.
  469. The return value is a reference to an array.  Each element of the
  470. array is an array with 2 values; the link value and a reference to the
  471. corresponding element.
  472.  
  473. You might specify that you just want to extract some types of links.
  474. For instance if you only want to extract <a href="..."> and <img
  475. src="..."> links you might code it like this:
  476.  
  477.   for (@{ $e->extract_links(qw(a img)) }) {
  478.       ($link, $linkelem) = @$_;
  479.       ...
  480.   }
  481.  
  482. =cut
  483.  
  484. sub extract_links
  485. {
  486.     my $self = shift;
  487.     my %wantType; @wantType{map { lc $_ } @_} = (1) x @_;
  488.     my $wantType = scalar(@_);
  489.     my @links;
  490.     $self->traverse(
  491.     sub {
  492.         my($self, $start, $depth) = @_;
  493.         return 1 unless $start;
  494.         my $tag = $self->{'_tag'};
  495.         return 1 if $wantType && !$wantType{$tag};
  496.         my $attr = $linkElements{$tag};
  497.         return 1 unless defined $attr;
  498.         $attr = [$attr] unless ref $attr;
  499.             for (@$attr) {
  500.            my $val = $self->attr($_);
  501.            push(@links, [$val, $self]) if defined $val;
  502.             }
  503.         1;
  504.     }, 'ignoretext');
  505.     \@links;
  506. }
  507.  
  508.  
  509.  
  510. =item $h->dump()
  511.  
  512. Prints the element and all its children to STDOUT.  Mainly useful for
  513. debugging.  The structure of the document is shown by indentation (no
  514. end tags).
  515.  
  516. =cut
  517.  
  518. sub dump
  519. {
  520.     my $self = shift;
  521.     my $depth = shift || 0;
  522.     print STDERR "  " x $depth;
  523.     print STDERR $self->starttag, "\n";
  524.     for (@{$self->{'_content'}}) {
  525.     if (ref $_) {
  526.         $_->dump($depth+1);
  527.     } else {
  528.         print STDERR "  " x ($depth + 1);
  529.         print STDERR qq{"$_"\n};
  530.     }
  531.     }
  532. }
  533.  
  534.  
  535.  
  536. =item $h->as_HTML()
  537.  
  538. Returns a string (the HTML document) that represents the element and
  539. its children.
  540.  
  541. =cut
  542.  
  543. sub as_HTML
  544. {
  545.     my $self = shift;
  546.     my @html = ();
  547.     $self->traverse(
  548.         sub {
  549.         my($node, $start, $depth) = @_;
  550.         if (ref $node) {
  551.         my $tag = $node->tag;
  552.         if ($start) {
  553.             push(@html, $node->starttag);
  554.         } elsif (not ($emptyElement{$tag} or $optionalEndTag{$tag})) {
  555.             push(@html, $node->endtag);
  556.         }
  557.         } else {
  558.         # simple text content
  559.         HTML::Entities::encode_entities($node, "<>&");
  560.         push(@html, $node);
  561.         }
  562.         }
  563.     );
  564.     join('', @html, "\n");
  565. }
  566.  
  567. sub format
  568. {
  569.     my($self, $formatter) = @_;
  570.     unless (defined $formatter) {
  571.     require HTML::FormatText;
  572.     $formatter = new HTML::FormatText;
  573.     }
  574.     $formatter->format($self);
  575. }
  576.  
  577.  
  578. 1;
  579.  
  580. __END__
  581.  
  582. =back
  583.  
  584. =head1 BUGS
  585.  
  586. If you want to free the memory assosiated with a tree built of
  587. HTML::Element nodes then you will have to delete it explicitly.  The
  588. reason for this is that perl currently has no proper garbage
  589. collector, but depends on reference counts in the objects.  This
  590. scheme fails because the parse tree contains circular references
  591. (parents have references to their children and children have a
  592. reference to their parent).
  593.  
  594. =head1 SEE ALSO
  595.  
  596. L<HTML::AsSubs>
  597.  
  598. =head1 COPYRIGHT
  599.  
  600. Copyright 1995-1998 Gisle Aas.
  601.  
  602. This library is free software; you can redistribute it and/or
  603. modify it under the same terms as Perl itself.
  604.  
  605. =cut
  606.