<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;"># Copyright (C) 1998-09  Stephane Galland &lt;galland@arakhne.org&gt;
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING.  If not, write to
# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.

=pod

=head1 NAME

Bib2HTML::General::HTML - HTML support definitions

=head1 DESCRIPTION

Bib2HTML::General::HTML is a Perl module, which permits to support
some HTML definitions. This is a fork of PhpDocGen::General::HTML.

=head1 METHOD DESCRIPTIONS

This section contains only the methods in HTML.pm itself.

=over

=cut

package Bib2HTML::General::HTML;

@ISA = ('Exporter');
@EXPORT = qw(&amp;get_html_entities &amp;get_restricted_html_entities &amp;translate_html_entities
	     &amp;htmlcatdir &amp;htmlcatfile &amp;htmldirname &amp;htmlfilename &amp;htmltoroot &amp;htmlpath
	     &amp;htmlsplit &amp;strip_html_tags &amp;html_add_unsecable_spaces &amp;html_uc &amp;html_lc
	     &amp;html_sc &amp;split_html_tags &amp;html_extract_tag_params &amp;htmltolocalpath
	     &amp;nl2br &amp;br2nl &amp;setAsValidHTML_small &amp;setAsValidHTML &amp;html_getinitiales
             &amp;html_ucwords &amp;html_ucfirst &amp;html_substr &amp;html_split_to_chars &amp;sortbyletters
	     &amp;remove_html_accents );
@EXPORT_OK = qw();

use strict;
use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION);

use Bib2HTML::General::Misc ;
use Bib2HTML::General::Encode ;

#------------------------------------------------------
#
# Global vars
#
#------------------------------------------------------

# Version number of the HTML support functions
my $VERSION = "4.0" ;

# Translation table
my %HTML_ENTITY_CODES = ( 'nbsp'       =&gt; 160, #no-break space = non-breaking space
			  'iexcl'      =&gt; 161, #inverted exclamation mark, U+00A1 ISOnum
			  'cent'       =&gt; 162, #cent sign
			  'pound'      =&gt; 163, #pound sign
			  'curren'     =&gt; 164, #currency sign
			  'yen'        =&gt; 165, #yen sign = yuan sign
			  'brvbar'     =&gt; 166, #broken bar = broken vertical bar
			  'sect'       =&gt; 167, #section sign
			  'uml'        =&gt; 168, #diaeresis = spacing diaeresis,
			  'copy'       =&gt; 169, #copyright sign
			  'ordf'       =&gt; 170, #feminine ordinal indicator
			  'laquo'      =&gt; 171, #left-pointing double angle quotation mark
			  'not'        =&gt; 172, #not sign
			  'shy'        =&gt; 173, #soft hyphen = discretionary hyphen
			  'reg'        =&gt; 174, #registered sign = registered trade mark sign
			  'macr'       =&gt; 175, #macron = spacing macron = overline = APL overbar
			  'deg'        =&gt; 176, #degree sign
			  'plusmn'     =&gt; 177, #plus-minus sign = plus-or-minus sign
			  'sup2'       =&gt; 178, #superscript two = superscript digit two = squared
			  'sup3'       =&gt; 179, #superscript three = superscript digit three = cubed
			  'acute'      =&gt; 180, #acute accent = spacing acute
			  'micro'      =&gt; 181, #micro sign
			  'para'       =&gt; 182, #pilcrow sign = paragraph sign
			  'middot'     =&gt; 183, #middle dot = Georgian comma = Greek middle dot
			  'cedil'      =&gt; 184, #cedilla = spacing cedilla
			  'sup1'       =&gt; 185, #superscript one = superscript digit one
			  'ordm'       =&gt; 186, #masculine ordinal indicator
			  'raquo'      =&gt; 187, #right-pointing double angle quotation mark = right pointing guillemet
			  'frac14'     =&gt; 188, #vulgar fraction one quarter = fraction one quarter
			  'frac12'     =&gt; 189, #vulgar fraction one half = fraction one half
			  'frac34'     =&gt; 190, #vulgar fraction three quarters = fraction three quarters
			  'iquest'     =&gt; 191, #inverted question mark = turned question mark
			  'Agrave'     =&gt; 192, #latin capital letter A with grave = latin capital letter A grave
			  'Aacute'     =&gt; 193, #latin capital letter A with acute
			  'Acirc'      =&gt; 194, #latin capital letter A with circumflex
			  'Atilde'     =&gt; 195, #latin capital letter A with tilde
			  'Auml'       =&gt; 196, #latin capital letter A with diaeresis
			  'Aring'      =&gt; 197, #latin capital letter A with ring above = latin capital letter A ring
			  'AElig'      =&gt; 198, #latin capital letter AE = latin capital ligature AE
			  'Ccedil'     =&gt; 199, #latin capital letter C with cedilla
			  'Egrave'     =&gt; 200, #latin capital letter E with grave
			  'Eacute'     =&gt; 201, #latin capital letter E with acute
			  'Ecirc'      =&gt; 202, #latin capital letter E with circumflex
			  'Euml'       =&gt; 203, #latin capital letter E with diaeresis
			  'Igrave'     =&gt; 204, #latin capital letter I with grave
			  'Iacute'     =&gt; 205, #latin capital letter I with acute
			  'Icirc'      =&gt; 206, #latin capital letter I with circumflex
			  'Iuml'       =&gt; 207, #latin capital letter I with diaeresis
			  'ETH'        =&gt; 208, #latin capital letter ETH
			  'Ntilde'     =&gt; 209, #latin capital letter N with tilde
			  'Ograve'     =&gt; 210, #latin capital letter O with grave
			  'Oacute'     =&gt; 211, #latin capital letter O with acute
			  'Ocirc'      =&gt; 212, #latin capital letter O with circumflex
			  'Otilde'     =&gt; 213, #latin capital letter O with tilde
			  'Ouml'       =&gt; 214, #latin capital letter O with diaeresis
			  'times'      =&gt; 215, #multiplication sign
			  'Oslash'     =&gt; 216, #latin capital letter O with stroke = latin capital letter O slash
			  'Ugrave'     =&gt; 217, #latin capital letter U with grave
			  'Uacute'     =&gt; 218, #latin capital letter U with acute
			  'Ucirc'      =&gt; 219, #latin capital letter U with circumflex
			  'Uuml'       =&gt; 220, #latin capital letter U with diaeresis
			  'Yacute'     =&gt; 221, #latin capital letter Y with acute
			  'THORN'      =&gt; 222, #latin capital letter THORN
			  'szlig'      =&gt; 223, #latin small letter sharp s = ess-zed
			  'agrave'     =&gt; 224, #latin small letter a with grave = latin small letter a grave
			  'aacute'     =&gt; 225, #latin small letter a with acute
			  'acirc'      =&gt; 226, #latin small letter a with circumflex
			  'atilde'     =&gt; 227, #latin small letter a with tilde
			  'auml'       =&gt; 228, #latin small letter a with diaeresis
			  'aring'      =&gt; 229, #latin small letter a with ring above = latin small letter a ring
			  'aelig'      =&gt; 230, #latin small letter ae = latin small ligature ae
			  'ccedil'     =&gt; 231, #latin small letter c with cedilla
			  'egrave'     =&gt; 232, #latin small letter e with grave
			  'eacute'     =&gt; 233, #latin small letter e with acute
			  'ecirc'      =&gt; 234, #latin small letter e with circumflex
			  'euml'       =&gt; 235, #latin small letter e with diaeresis
			  'igrave'     =&gt; 236, #latin small letter i with grave
			  'iacute'     =&gt; 237, #latin small letter i with acute
			  'icirc'      =&gt; 238, #latin small letter i with circumflex
			  'iuml'       =&gt; 239, #latin small letter i with diaeresis
			  'eth'        =&gt; 240, #latin small letter eth
			  'ntilde'     =&gt; 241, #latin small letter n with tilde
			  'ograve'     =&gt; 242, #latin small letter o with grave
			  'oacute'     =&gt; 243, #latin small letter o with acute
			  'ocirc'      =&gt; 244, #latin small letter o with circumflex
			  'otilde'     =&gt; 245, #latin small letter o with tilde
			  'ouml'       =&gt; 246, #latin small letter o with diaeresis
			  'divide'     =&gt; 247, #division sign
			  'oslash'     =&gt; 248, #latin small letter o with stroke = latin small letter o slash
			  'ugrave'     =&gt; 249, #latin small letter u with grave
			  'uacute'     =&gt; 250, #latin small letter u with acute
			  'ucirc'      =&gt; 251, #latin small letter u with circumflex
			  'uuml'       =&gt; 252, #latin small letter u with diaeresis
			  'yacute'     =&gt; 253, #latin small letter y with acute
			  'thorn'      =&gt; 254, #latin small letter thorn
			  'yuml'       =&gt; 255, #latin small letter y with diaeresis
			  'quot'       =&gt; 34, #quotation mark = APL quote
			  'amp'        =&gt; 38, #ampersand
			  'lt'         =&gt; 60, #less-than sign
			  'gt'         =&gt; 62, #greater-than sign
			  'OElig'      =&gt; 338, #latin capital ligature OE
			  'oelig'      =&gt; 339, #latin small ligature oe
			  'Scedil'     =&gt; 'x015e', #latin capital letter S with cedil
			  'scedil'     =&gt; 'x015f', #latin small letter s with cedil
			  'Tcedil'     =&gt; 'x0162', #latin capital letter T with cedil
			  'tcedil'     =&gt; 'x0163', #latin small letter t with cedil
			  'Yuml'       =&gt; 376, #latin capital letter Y with diaeresis
			  'circ'       =&gt; 710, #modifier letter circumflex accent
			  'tilde'      =&gt; 732, #small tilde
			  'Aucircle'   =&gt; 'x0102', #latin capital letter A with small u on top
			  'aucircle'   =&gt; 'x0103', #latin small letter a with small u on top
			  'Cacute'     =&gt; 'x262', #latin capital letter C with acute
			  'cacute'     =&gt; 'x263', #latin small letter c with acute
			  'Scaron'     =&gt; 352, #latin capital letter S with caron
			  'scaron'     =&gt; 353, #latin small letter s with caron
			  'Ccaron'     =&gt; 268, #latin capital letter C with caron
			  'ccaron'     =&gt; 269, #latin small letter c with caron
			  'Dcaron'     =&gt; 270, #latin capital letter D with caron
			  'dcaron'     =&gt; 271, #latin small letter d with caron
			  'Ecaron'     =&gt; 282, #latin capital letter E with caron
			  'ecaron'     =&gt; 283, #latin small letter e with caron
			  'Lcaron'     =&gt; 317, #latin capital letter L with caron
			  'lcaron'     =&gt; 318, #latin small letter l with caron
			  'Ncaron'     =&gt; 327, #latin capital letter N with caron
			  'ncaron'     =&gt; 328, #latin small letter n with caron
			  'Rcaron'     =&gt; 344, #latin capital letter R with caron
			  'rcaron'     =&gt; 345, #latin small letter r with caron
			  'Tcaron'     =&gt; 356, #latin capital letter T with caron
			  'tcaron'     =&gt; 357, #latin small letter t with caron
			  'Zcaron'     =&gt; 381, #latin capital letter Z with caron
			  'zcaron'     =&gt; 382, #latin small letter z with caron
			) ;

# The characters which are displayed for each HTML entity (except &amp;amp; &amp;gt; &amp;lt; &amp;quot; )
my %HTML_ENTITY_CHARS = (  'Ocirc'        =&gt; 'Ã”',
			   'szlig'        =&gt; 'ÃŸ',
			   'micro'        =&gt; 'Âµ',
			   'para'         =&gt; 'Â¶',
			   'not'          =&gt; 'Â¬',
			   'sup1'         =&gt; 'Â¹',
			   'oacute'       =&gt; 'Ã³',
			   'Uacute'       =&gt; 'Ãš',
			   'middot'       =&gt; 'Â·',
			   'ecirc'        =&gt; 'Ãª',
			   'pound'        =&gt; 'Â£',
			   'scaron'       =&gt; 'Å¡',
			   'ntilde'       =&gt; 'Ã±',
			   'igrave'       =&gt; 'Ã¬',
			   'atilde'       =&gt; 'Ã£',
			   'thorn'        =&gt; 'Ã¾',
			   'Euml'         =&gt; 'Ã‹',
			   'Ntilde'       =&gt; 'Ã‘',
			   'Auml'         =&gt; 'Ã„',
			   'plusmn'       =&gt; 'Â±',
			   'raquo'        =&gt; 'Â»',
			   'THORN'        =&gt; 'Ãž',
			   'laquo'        =&gt; 'Â«',
			   'Eacute'       =&gt; 'Ã‰',
			   'divide'       =&gt; 'Ã·',
			   'Uuml'         =&gt; 'Ãœ',
			   'Aring'        =&gt; 'Ã…',
			   'ugrave'       =&gt; 'Ã¹',
			   'Egrave'       =&gt; 'Ãˆ',
			   'Acirc'        =&gt; 'Ã‚',
			   'oslash'       =&gt; 'Ã¸',
			   'ETH'          =&gt; 'Ã',
			   'iacute'       =&gt; 'Ã­',
			   'Ograve'       =&gt; 'Ã’',
			   'Oslash'       =&gt; 'Ã˜',
			   'frac34'       =&gt; '3/4',
			   'Scaron'       =&gt; 'Å&nbsp;',
			   'eth'          =&gt; 'Ã°',
			   'icirc'        =&gt; 'Ã®',
			   'ordm'         =&gt; 'Âº',
			   'ucirc'        =&gt; 'Ã»',
			   'reg'          =&gt; 'Â®',
			   'tilde'        =&gt; '~',
			   'aacute'       =&gt; 'Ã¡',
			   'Agrave'       =&gt; 'Ã€',
			   'Yuml'         =&gt; 'Å¸',
			   'times'        =&gt; 'Ã—',
			   'deg'          =&gt; 'Â°',
			   'AElig'        =&gt; 'Ã†',
			   'Yacute'       =&gt; 'Ã',
			   'Otilde'       =&gt; 'Ã•',
			   'circ'         =&gt; '^',
			   'sup3'         =&gt; 'Â³',
			   'oelig'        =&gt; 'Å“',
			   'frac14'       =&gt; '1/4',
			   'Ouml'         =&gt; 'Ã–',
			   'ograve'       =&gt; 'Ã²',
			   'copy'         =&gt; 'Â©',
			   'shy'          =&gt; 'Â­',
			   'iuml'         =&gt; 'Ã¯',
			   'acirc'        =&gt; 'Ã¢',
			   'iexcl'        =&gt; 'Â¡',
			   'Iacute'       =&gt; 'Ã',
			   'Oacute'       =&gt; 'Ã“',
			   'ccedil'       =&gt; 'Ã§',
			   'frac12'       =&gt; '1/2',
			   'Icirc'        =&gt; 'ÃŽ',
			   'eacute'       =&gt; 'Ã©',
			   'egrave'       =&gt; 'Ã¨',
			   'euml'         =&gt; 'Ã«',
			   'Ccedil'       =&gt; 'Ã‡',
			   'OElig'        =&gt; 'Å’',
			   'Atilde'       =&gt; 'Ãƒ',
			   'ouml'         =&gt; 'Ã¶',
			   'cent'         =&gt; 'Â¢',
			   'Aacute'       =&gt; 'Ã',
			   'sect'         =&gt; 'Â§',
			   'Ugrave'       =&gt; 'Ã™',
			   'aelig'        =&gt; 'Ã¦',
			   'ordf'         =&gt; 'Âª',
			   'yacute'       =&gt; 'Ã½',
			   'Ecirc'        =&gt; 'ÃŠ',
			   'auml'         =&gt; 'Ã¤',
			   'macr'         =&gt; 'Â¯',
			   'iquest'       =&gt; 'Â¿',
			   'sup2'         =&gt; 'Â²',
			   'Ucirc'        =&gt; 'Ã›',
			   'aring'        =&gt; 'Ã¥',
			   'Igrave'       =&gt; 'ÃŒ',
			   'yen'          =&gt; 'Â¥',
			   'uuml'         =&gt; 'Ã¼',
			   'otilde'       =&gt; 'Ãµ',
		   	   'uacute'       =&gt; 'Ãº',
			   'yuml'         =&gt; 'Ã¿',
			   'ocirc'        =&gt; 'Ã´',
			   'Iuml'         =&gt; 'Ã',
			   'agrave'       =&gt; 'Ã&nbsp;',
			) ;


# The mapping between the accentuated characters and the non-accentuated characters
my %HTML_NO_ACCENT_ENTITY_CHARS = (
			   'aacute'       =&gt; 'a',
			   'Aacute'       =&gt; 'A',
			   'aelig'        =&gt; 'ae',
			   'AElig'        =&gt; 'ae',
			   'acirc'        =&gt; 'a',
			   'Acirc'        =&gt; 'A',
			   'agrave'       =&gt; 'a',
			   'Agrave'       =&gt; 'A',
			   'atilde'       =&gt; 'a',
			   'Atilde'       =&gt; 'A',
			   'Aring'        =&gt; 'A',
			   'aring'        =&gt; 'a',
			   'aucircle'     =&gt; 'a',
			   'Aucircle'     =&gt; 'A',
			   'auml'         =&gt; 'a',
			   'Auml'         =&gt; 'A',
			   'Cacute'       =&gt; 'C',
			   'cacute'       =&gt; 'c',
			   'Ccaron'       =&gt; 'C',
			   'ccaron'       =&gt; 'C',
			   'ccedil'       =&gt; 'c',
			   'Ccedil'       =&gt; 'C',
			   'Dcaron'       =&gt; 'D',
			   'dcaron'       =&gt; 'd',
			   'Eacute'       =&gt; 'E',
			   'eacute'       =&gt; 'e',
			   'Ecaron'       =&gt; 'E',
			   'ecaron'       =&gt; 'e',
			   'ecirc'        =&gt; 'e',
			   'Ecirc'        =&gt; 'E',
			   'Egrave'       =&gt; 'E',
			   'egrave'       =&gt; 'e',
			   'eth'          =&gt; 'd',
			   'ETH'          =&gt; 'D',
			   'Euml'         =&gt; 'E',
			   'euml'         =&gt; 'e',
			   'Iacute'       =&gt; 'i',
			   'iacute'       =&gt; 'i',
			   'icirc'        =&gt; 'i',
			   'Icirc'        =&gt; 'i',
			   'Igrave'       =&gt; 'I',
			   'igrave'       =&gt; 'i',
			   'Iuml'         =&gt; 'I',
			   'iuml'         =&gt; 'i',
			   'Lcaron'       =&gt; 'L',
			   'lcaron'       =&gt; 'l',
			   'Ncaron'       =&gt; 'N',
			   'ncaron'       =&gt; 'n',
			   'ntilde'       =&gt; 'n',
			   'Ntilde'       =&gt; 'N',
			   'Oacute'       =&gt; 'O',
			   'oacute'       =&gt; 'o',
			   'ocirc'        =&gt; 'o',
			   'Ocirc'        =&gt; 'O',
			   'oelig'        =&gt; 'oe',
			   'OElig'        =&gt; 'OE',
			   'ograve'       =&gt; 'o',
			   'Ograve'       =&gt; 'O',
			   'Oslash'       =&gt; 'O',
			   'oslash'       =&gt; 'o',
			   'otilde'       =&gt; 'o',
			   'Otilde'       =&gt; 'O',
			   'Ouml'         =&gt; 'O',
			   'ouml'         =&gt; 'o',
			   'Rcaron'       =&gt; 'R',
			   'rcaron'       =&gt; 'r',
			   'scaron'       =&gt; 's',
			   'Scaron'       =&gt; 'S',
			   'scedil'       =&gt; 's',
			   'Scedil'       =&gt; 'S',
			   'szlig'        =&gt; 'S',
			   'Tcaron'       =&gt; 'T',
			   'tcaron'       =&gt; 't',
			   'tcedil'       =&gt; 't',
			   'Tcedil'       =&gt; 'T',
			   'thorn'        =&gt; 't',
			   'THORN'        =&gt; 'T',
		   	   'uacute'       =&gt; 'u',
			   'Uacute'       =&gt; 'U',
			   'Ucirc'        =&gt; 'U',
			   'ucirc'        =&gt; 'u',
			   'ugrave'       =&gt; 'U',
			   'Ugrave'       =&gt; 'U',
			   'uuml'         =&gt; 'u',
			   'Uuml'         =&gt; 'U',
			   'Yacute'       =&gt; 'Y',
			   'yacute'       =&gt; 'y',
			   'Yuml'         =&gt; 'Y',
			   'yuml'         =&gt; 'y',
			   'Zcaron'       =&gt; 'Z',
			   'zcaron'       =&gt; 'z',
			) ;

#------------------------------------------------------
#
# Predefined PHP variables support
#
#------------------------------------------------------

=pod

=item * get_restricted_html_entities()

Replies the specified string in which some characters have been
replaced by the corresponding HTML entities except for &amp;amp;
&amp;quot; &amp;lt; and &amp;gt;
Takes 1 arg:

=over

=item * text (string)

is a I&lt;string&gt; which correspond to the text to translate.

=back

=cut
sub get_restricted_html_entities($) {
  my $text = $_[0] || '' ;
  foreach my $entity (keys %HTML_ENTITY_CHARS) {
    if (($entity ne 'amp')&amp;&amp;
        ($entity ne 'quot')&amp;&amp;
        ($entity ne 'lt')&amp;&amp;
        ($entity ne 'gt')) {
      my $validchar = get_encoded_str($HTML_ENTITY_CHARS{$entity});
      $text =~ s/\Q&amp;#$HTML_ENTITY_CODES{$entity};\E/&amp;$entity;/g ;
      $text =~ s/\Q$validchar\E/&amp;$entity;/g ;
    }
  }
  return $text ;
}

=pod

=item * get_html_entities()

Replies the specified string in which some characters have been
replaced by the corresponding HTML entities
Takes 1 arg:

=over

=item * text (string)

is a I&lt;string&gt; which correspond to the text to translate.

=back

=cut
sub get_html_entities($) {
  my $text = $_[0] || '' ;

  $text =~ s/\Q&amp;\E/&amp;amp;/g ;
  $text =~ s/\Q&lt;\E/&amp;lt;/g ;
  $text =~ s/\Q&gt;\E/&amp;gt;/g ;
  $text =~ s/\Q\"\E/&amp;quot;/g ;

  return get_restricted_html_entities($text) ;
}

=pod

=item * translate_html_entities()

Replies the specified string in which each HTML entity was replaced
by the corresponding character.
Takes 1 arg:

=over

=item * text (string)

is a I&lt;string&gt; which correspond to the text to translate.

=back

=cut
sub translate_html_entities($) {
  my $text = $_[0] || '' ;
  $text =~ s/\Q&amp;nbsp;\E/ /g ;
  $text =~ s/\Q&amp;quot;\E/\"/g ;
  $text =~ s/\Q&amp;lt;\E/&lt;/g ;
  $text =~ s/\Q&amp;gt;\E/&gt;/g ;
  $text =~ s/\Q&amp;amp;\E/&amp;/g ;
  while (my ($entity,$charcode) = each(%HTML_ENTITY_CODES)) {
    my $validchar = get_encoded_str($HTML_ENTITY_CHARS{$entity});
    if ($validchar) {
      $text =~ s/\Q&amp;#$charcode;\E/$validchar/g ;
    }
  }
  foreach my $entity (keys %HTML_ENTITY_CHARS) {
    my $validchar = get_encoded_str($HTML_ENTITY_CHARS{$entity});
    $text =~ s/\Q&amp;$entity;\E/$validchar/g ;
  }
  return $text ;
}

=pod

=item * htmlcatdir()

Concatenate two or more directory names to form a complete path ending
with a directory. But remove the trailing slash from the resulting
string.
Takes 2 args or more:

=over

=item * dir... (string)

is a I&lt;string&gt; which correspond to a directory name to merge

=back

=cut
sub htmlcatdir {
  my $path = '' ;
  $path = join('/', @_ ) if ( @_ ) ;
  $path =~ s/\/{2,}/\//g ;
  $path =~ s/\/$// ;
  return $path ;
}

=pod

=item * htmlcatfile()

Concatenate one or more directory names and a filename to form a
complete path ending with a filename
Takes 2 args or more:

=over

=item * dir... (string)

is a I&lt;string&gt; which correspond to a directory name to merge

=item * file (string)

is a I&lt;string&gt; which correspond to a file name to merge

=back

=cut
sub htmlcatfile {
  return '' unless @_ ;
  my $file = pop @_;
  return $file unless @_;
  my $dir = htmlcatdir(@_);
  $dir .= "/" unless substr($file,0,1) eq "/" ;
  return $dir.$file;
}

=pod

=item * htmldirname()

Replies the path of the from the specified file
Takes 1 arg:

=over

=item * path (string)

is a I&lt;string&gt; which correspond to the path from which
the dirname but be extracted

=back

=cut
sub htmldirname($) {
  my $dirname = $_[0] || '' ;
  $dirname =~ s/\/+\s*$// ;
  if ( $dirname =~ /^(.*?)\/[^\/]+$/ ) {
    $dirname = ($1) ? $1 : "/" ;
  }
  else {
    $dirname = "" ;
  }
  return $dirname ;
}

=pod

=item * htmlfilename()

Replies the filename of the from the specified file
Takes 1 arg:

=over

=item * path (string)

is a I&lt;string&gt; which correspond to the path from which
the filename but be extracted

=back

=cut
sub htmlfilename($) {
  my $filename = $_[0] || '' ;
  $filename =~ s/\/+\s*$// ;
  if ( $filename =~ /^.*?\/([^\/]+)$/ ) {
    $filename = $1 ;
  }
  return $filename ;
}

=pod

=item * htmltoroot()

Replies a relative path in wich each directory of the
specified parameter was replaced by ..
Takes 1 arg:

=over

=item * path (string)

is a I&lt;string&gt; which correspond to the path to translate

=back

=cut
sub htmltoroot($) {
  my $dir = $_[0] || '' ;
  $dir =~ s/\/\s*$// ;
  $dir =~ s/[^\/]+/../g ;
  return $dir ;
}

=pod

=item * htmlpath()

Replies a path in which all the OS path separators were replaced
by the HTML path separator '/'
Takes 1 arg:

=over

=item * path (string)

is a I&lt;string&gt; which correspond to the path to translate

=back

=cut
sub htmlpath($) {
  my $path = $_[0] || '' ;
  my $os_sep = "/" ;
  my $p = File::Spec-&gt;catdir("a","b") ;
  if ( $p =~ /a(.+)b/ ) {
    $os_sep = $1 ;
  }
  $path =~ s/\Q$os_sep\E/\//g ;
  return $path ;
}

=pod

=item * htmltolocalpath()

Replies a path in which all the HTML path separators '/' were replaced
by the OS path separator
Takes 1 arg:

=over

=item * path (string)

is a I&lt;string&gt; which correspond to the path to translate

=back

=cut
sub htmltolocalpath($) {
  my $path = $_[0] || '' ;
  my $os_sep = "/" ;
  my $p = File::Spec-&gt;catdir("a","b") ;
  if ( $p =~ /a(.+)b/ ) {
    $os_sep = $1 ;
  }
  $path =~ s/\//$os_sep/g ;
#  if ( ( "$^O" ne 'MSWin32' ) &amp;&amp;
#       ( "$^O" ne 'os2' ) &amp;&amp;
#       ( "$^O" ne 'NetWare' ) &amp;&amp;
#       ( "$^O" ne 'dos' ) &amp;&amp;
#       ( "$^O" ne 'cygwin' ) &amp;&amp;
#       ( "$^O" ne 'MacOS' ) ) {
#    $path =~ s/\ /\\ /g ;
#  }
  return $path ;
}

=pod

=item * htmlsplit()

Replies an array of directories which correspond to each
parts of the specified path.
Takes 1 arg:

=over

=item * path (string)

is a I&lt;string&gt; which correspond to the path to split

=back

=cut
sub htmlsplit($) {
  my $path = $_[0] || '' ;
  return split( /\//, $path ) ;
}

=pod

=item * strip_html_tags()

Removes the HTML tags from the specified string.
Takes 1 arg:

=over

=item * text (string)

is a I&lt;string&gt; which correspond to the text to translate.

=back

=cut
sub strip_html_tags($) {
  my $text = $_[0] || '' ;
  my $res = '' ;
  while ( ( $text ) &amp;&amp;
          ( $text =~ /^(.*?)&lt;(.*)$/ ) ) {
    my ($prev,$next) = ($1,$2) ;
    $res .= "$prev" ;
    $text = $next ;
    my $inside = 1 ;
    while ( ( $inside ) &amp;&amp; ( $text ) &amp;&amp;
            ( $text =~ /^.*?(&gt;|\"|\')(.*)$/ ) ) {
      my ($sep,$next) = ($1,$2) ;
      $text = $next ;
      if ( $sep eq "&gt;" ) {
        $inside = 0 ;
      }
      else {
        my $insidetext = 1 ;
        while ( ( $insidetext ) &amp;&amp; ( $text ) &amp;&amp;
                ( $text =~ /^.*?((?:\\)|$sep)(.*)$/ ) ) {
          my ($sepi,$rest) = ($1,$2) ;
          if ( $sepi eq '\\' ) {
            $text = substr($rest,1) ;
          }
          else {
            $text = $rest ;
            $insidetext = 0 ;
          }
        }
      }
    }
  }
  if ( $text ) {
    $res .= $text ;
  }
  return $res ;
}

=pod

=item * html_extract_tag_params()

Replies the tag parameters that are given
Takes 1 arg:

=over

=item * text (string)

is a I&lt;string&gt; which correspond to the text to parse.

=back

=cut
sub html_extract_tag_params($) : method {
  my $self = shift ;
  my $text = $_[0] || '' ;
  my %params = ( ) ;
  my @strings = () ;
  remove_strings( \@strings, $text ) ;
  $text =~ s/\s*=\s*/=/gm ;
  my @parts = split( /\s+/, $text ) ;
  foreach my $part ( @parts ) {
    my @elts = split( /=/, $part ) ;
    if ( @elts &gt; 1 ) {
      restore_strings( \@strings, $elts[1] ) ;
      $params{lc($elts[0])} = removeslashes( $elts[1] ) ;
    }
  }
  return \%params ;
}

=pod

=item * split_html_tags()

Replies an array in which each element
is text or HTML tag.
Takes 1 arg:

=over

=item * text (string)

is a I&lt;string&gt; which correspond to the text to parse.

=back

=cut
sub split_html_tags($) {
  my $text = $_[0] || '' ;
  my %res = () ;
  while ( ( $text ) &amp;&amp;
          ( $text =~ /^(.*?)&lt;(.*)$/ ) ) {
    my ($prev,$next) = ($1,$2) ;

    add_value_entry( \%res, 'result', "$prev" ) ;

    $text = $next ;
    my $inside = 1 ;
    my $tagcontent = '&lt;' ;

    while ( ( $inside ) &amp;&amp; ( $text ) &amp;&amp;
            ( $text =~ /^(.*?)(&gt;|\"|\')(.*)$/ ) ) {
      my ($inprev,$sep,$next) = ($1,$2,$3) ;

      $tagcontent .= "$inprev$sep" ;
      $text = $next ;
      if ( $sep eq "&gt;" ) {
        $inside = 0 ;
      }
      else {
        my $insidetext = 1 ;
        while ( ( $insidetext ) &amp;&amp; ( $text ) &amp;&amp;
                ( $text =~ /^(.*?)((?:\\)|$sep)(.*)$/ ) ) {
          my ($intprev,$sepi,$rest) = ($1,$2,$3) ;
	  $tagcontent .= "$intprev$sepi" ;
          if ( $sepi eq '\\' ) {
            $text = substr($rest,1) ;
          }
          else {
            $text = $rest ;
            $insidetext = 0 ;
          }
        }
      }
    }
    add_value_entry( \%res, 'result', $tagcontent ) ;
  }
  if ( $text ) {
    add_value_entry( \%res, 'result', $text ) ;
  }
  return $res{'result'} ;
}

sub __scan_html_text_and_apply($&amp;) {
  my $text = $_[0] || '' ;
  my $res = '' ;
  while ( ( $text ) &amp;&amp;
          ( $text =~ /^(.*?)&lt;(.*)$/ ) ) {
    my ($prev,$next) = ($1,$2) ;

    # Replacement
    &amp;{$_[1]}( $prev ) ;

    $res .= "$prev&lt;" ;
    $text = $next ;
    my $inside = 1 ;
    while ( ( $inside ) &amp;&amp; ( $text ) &amp;&amp;
            ( $text =~ /^(.*?)(&gt;|\"|\')(.*)$/ ) ) {
      my ($inprev,$sep,$next) = ($1,$2,$3) ;
      $res .= "$inprev$sep" ;
      $text = $next ;
      if ( $sep eq "&gt;" ) {
        $inside = 0 ;
      }
      else {
        my $insidetext = 1 ;
        while ( ( $insidetext ) &amp;&amp; ( $text ) &amp;&amp;
                ( $text =~ /^(.*?)((?:\\)|$sep)(.*)$/ ) ) {
          my ($intprev,$sepi,$rest) = ($1,$2,$3) ;
	  $res .= "$intprev$sepi" ;
          if ( $sepi eq '\\' ) {
            $text = substr($rest,1) ;
          }
          else {
            $text = $rest ;
            $insidetext = 0 ;
          }
        }
      }
    }
  }
  if ( $text ) {

    # Replacement
    &amp;{$_[1]}( $text ) ;

    $res .= $text ;
  }
  return $res ;
}

=pod

=item * html_add_unsecable_spaces()

Replaces the spaces by unsecable spaces inside an HTML string
Takes 1 arg:

=over

=item * text (string)

is a I&lt;string&gt; which correspond to the text to translate.

=back

=cut
sub html_add_unsecable_spaces($) {
  return __scan_html_text_and_apply( $_[0],
				     sub { $_[0] =~ s/[ \t\n\r]+/&amp;nbsp;/g ;
					 } ) ;
}

=pod

=item * html_uc()

Upper cases the specified text.
Takes 1 arg:

=over

=item * text (string)

is a I&lt;string&gt; which correspond to the text to translate.

=back

=cut
sub html_uc($) {
  return __scan_html_text_and_apply( $_[0],
				     sub { $_[0] = __html_uc($_[0]) ;
					 } ) ;
}

sub __html_uc($) {
  my $text = $_[0] || '' ;
  my $res = '' ;
  if ( $text ) {
    while ( ( $text ) &amp;&amp;
	    ( $text =~ /^(.*?)&amp;([^;]+);(.*)$/ ) ) {
      (my $prev,my $tag,$text) = ($1,$2,$3) ;
      $res .= uc( $prev ) ;
      if ( ( $tag =~ /^[a-z]/ ) &amp;&amp;
	   ( exists $HTML_ENTITY_CODES{$tag} ) &amp;&amp;
	   ( exists $HTML_ENTITY_CODES{ucfirst($tag)} ) ) {
	$tag = ucfirst($tag) ;
      }
      $res .= "&amp;".$tag.";" ;
    }
    if ( $text ) {
      $res .= uc( $text ) ;
    }
  }
  return $res ;
}

=pod

=item * html_lc()

Lower cases the specified text.
Takes 1 arg:

=over

=item * text (string)

is a I&lt;string&gt; which correspond to the text to translate.

=back

=cut
sub html_lc($) {
  return __scan_html_text_and_apply( $_[0],
				     sub { $_[0] = __html_lc($_[0]) ;
					 } ) ;
}

sub __html_lc($) {
  my $text = $_[0] || '' ;
  my $res = '' ;
  if ( $text ) {
    while ( ( $text ) &amp;&amp;
	    ( $text =~ /^(.*?)&amp;([^;]+);(.*)$/ ) ) {
      (my $prev,my $tag,$text) = ($1,$2,$3) ;
      $res .= lc( $prev ) ;
      if ( ( $tag =~ /^[a-z]/ ) &amp;&amp;
	   ( exists $HTML_ENTITY_CODES{$tag} ) &amp;&amp;
	   ( exists $HTML_ENTITY_CODES{lcfirst($tag)} ) ) {
	$tag = lcfirst($tag) ;
      }
      $res .= "&amp;".$tag.";" ;
    }
    if ( $text ) {
      $res .= lc( $text ) ;
    }
  }
  return $res ;
}

=pod

=item * html_ucwords()

Upper cases the first letters of each words.
Takes 1 arg:

=over

=item * text (string)

is a I&lt;string&gt; which correspond to the text to translate.

=back

=cut
sub html_ucwords($) {
  return __scan_html_text_and_apply( $_[0],
				     sub { $_[0] = __html_ucwords($_[0]) ;
					 } ) ;
}

sub __html_ucwords($) {
  my $text = $_[0] || '' ;
  my $res = '' ;
  while ( $text ) {
    if ( $text =~ /^([_\-\s]*)&amp;([^;]+);([^_\-\s]*)(.*)$/ ) {
      (my $prev,my $tag,my $after,$text) = ($1,$2,$3,$4);
      $res .= $1;
      if ( ( $tag =~ /^[a-z]/ ) &amp;&amp;
	   ( exists $HTML_ENTITY_CODES{$tag} ) &amp;&amp;
	   ( exists $HTML_ENTITY_CODES{ucfirst($tag)} ) ) {
	$tag = ucfirst($tag) ;
      }
      $res .= "&amp;".$tag.";".$after ;
    }
    elsif ( $text =~ /^([_\-\s]*)([^_\-\s]+)(.*)$/ ) {
      (my $prev,my $tag,$text) = ($1,$2,$3);
      $res .= $1;
      $res .= ucfirst($tag);
    }
    else {
      $res .= ucwords($text);
      $text = '';
    }
  }
  return $res ;
}

=pod

=item * html_ucfirst()

Upper cases the first letters of the first word.
Takes 1 arg:

=over

=item * text (string)

is a I&lt;string&gt; which correspond to the text to translate.

=back

=cut
sub html_ucfirst($) {
  my $found = 0;
  return __scan_html_text_and_apply( $_[0],
				     sub { $_[0] = __html_ucfirst($_[0],\$found) ;
					 } ) ;
}

sub __html_ucfirst($$) {
  my $text = $_[0] || '' ;
  if( !$_[1] ) {
    return $text;
  }
  else {
    my $res = '' ;
    if ( $text =~ /^([_\-\s]*)&amp;([^;]+);([^_\-\s]*)(.*)$/ ) {
      (my $prev,my $tag,my $after,$text) = ($1,$2,$3,$4);
      $res .= $1;
      if ( ( $tag =~ /^[a-z]/ ) &amp;&amp;
	   ( exists $HTML_ENTITY_CODES{$tag} ) &amp;&amp;
	   ( exists $HTML_ENTITY_CODES{ucfirst($tag)} ) ) {
	$tag = ucfirst($tag) ;
      }
      $res .= "&amp;".$tag.";".$after.$text ;
    }
    elsif ( $text =~ /^([_\-\s]*)([^_\-\s]+)(.*)$/ ) {
      (my $prev,my $tag,$text) = ($1,$2,$3);
      $res .= $1;
      $res .= ucfirst($tag).$text;
    }
    else {
      $res .= ucwords($text);
    }
    $_[1] = 1;
    return $res ;
  }
}

=pod

=item * html_sc()

Translates the specified text into small caps
Takes 1 arg:

=over

=item * text (string)

is a I&lt;string&gt; which correspond to the text to translate.

=back

=cut
sub html_sc($) {
  my $text = __scan_html_text_and_apply( $_[0],
					 sub { $_[0] = __html_sc($_[0]) ;
					     } ) ;
  $text =~ s/&lt;small&gt;&lt;\/small&gt;//g ;
  $text =~ s/&lt;\/small&gt;&lt;small&gt;//g ;
  return $text ;
}

sub __html_sc($) {
  my $text = $_[0] || '' ;
  my $res = '' ;
  if ( $text ) {
    while ( ( $text ) &amp;&amp;
	    ( $text =~ /^(.*?)&amp;([^;]+);(.*)$/ ) ) {
      (my $prev,my $tag,$text) = ($1,$2,$3) ;
      $prev =~ s/([a-z]+)/"&lt;small&gt;".uc($1)."&lt;\/small&gt;";/eg ;
      $res .= $prev ;
      if ( ( $tag =~ /^([a-z])/ ) &amp;&amp;
	   ( exists $HTML_ENTITY_CODES{$tag} ) &amp;&amp;
	   ( exists $HTML_ENTITY_CODES{ucfirst($tag)} ) ) {
	$tag = ucfirst($tag) ;
      }
      $res .= "&lt;small&gt;&amp;".$tag.";&lt;/small&gt;" ;
    }
    if ( $text ) {
      $text =~ s/([a-z]+)/"&lt;small&gt;".uc($1)."&lt;\/small&gt;";/eg ;
      $res .= $text ;
    }
  }
  return $res ;
}

=pod

=item * nl2br()

Translates newline characters into tags &lt;BR&gt;.
Takes 1 arg:

=over

=item * text (string)

is a I&lt;string&gt; which correspond to the text to translate.

=back

=cut
sub nl2br($) {
  my $text = $_[0] || '' ;
  $text =~ s/[\n\r]/&lt;BR&gt;\n/g ;
  return $text ;
}

=pod

=item * br2nl()

Translates tags &lt;BR&gt; into newline characters.
Takes 1 arg:

=over

=item * text (string)

is a I&lt;string&gt; which correspond to the text to translate.

=back

=cut
sub br2nl($) {
  my $text = $_[0] || '' ;
  $text =~ s/&lt;BR\s*\/?&gt;[\n\r]*/\n/g ;
  return $text ;
}

=pod

=item * setAsValidHTML_small()

Merge the "Valid HTML 4.1" small icon to the specified content.
This icon must be copied by the calling generator as the file
'path_to_root/valid-html401.gif'.
Takes 3 args:

=over

=item * text (string)

is a I&lt;string&gt; which correspond to the text to update.

=item * rootdir (string)

is the relative path to the root directory.

=item * valid (array)

is an associative array which content all the validation flags.
The keys are the validated formats: 'html', 'xhtml', 'css'.

=back

=cut
sub setAsValidHTML_small($$@) {
  my $t = \$_[0] ; shift ;
  my $rootdir = shift ;
  my ($html,$xhtml,$css) ;
  foreach my $p (@_) {
    $html = 1 if ( "$p" eq 'html') ;
    $xhtml = 1 if ( "$p" eq 'xhtml') ;
    $css = 1 if ( "$p" eq 'css') ;
  }

  if (($html)||($xhtml)||($css)) {
    $$t .= "&lt;p align=\"right\"&gt;" ;
    if ($html) {
      $$t .= join( '',
		   "&lt;a href=\"http://validator.w3.org/check?uri=referer\"&gt;",
		   "&lt;img border=\"0\" src=\"",
		   htmlcatfile($rootdir,"valid-html401.gif"),
		   "\" ",
		   "alt=\"Valid HTML 4.01!\" height=\"15\" width=\"44\"&gt;",
		   "&lt;/a&gt;" ) ;
    }
    if ($css) {
      $$t .= join( '',
		   "&lt;a href=\"http://jigsaw.w3.org/css-validator/\"&gt;",
		   "&lt;img style=\"border:0;width:44px;height:15px\" ",
		   "src=\"",
		   htmlcatfile($rootdir,"valid-css.gif"),
		   "\" alt=\"Valid CSS!\"&gt;",
		   "&lt;/a&gt;" ) ;
    }
    $$t .= "&lt;/p&gt;" ;
  }
}

=pod

=item * setAsValidHTML()

Merge the "Valid HTML 4.1" small icon to the specified content.
This icon must be copied by the calling generator as the directory
'path_to_root/'.
Takes 3 args:

=over

=item * text (string)

is a I&lt;string&gt; which correspond to the text to update.

=item * rootdir (string)

is the relative path to the root directory.

=item * valid (array)

is an associative array which content all the validation flags.
The keys are the validated formats: 'html', 'xhtml', 'css'.

=back

=cut
sub setAsValidHTML($$@) {
  my $t = \$_[0] ; shift ;
  my $rootdir = shift ;
  my ($html,$xhtml,$css) ;
  foreach my $p (@_) {
    $html = 1 if ( "$p" eq 'html') ;
    $xhtml = 1 if ( "$p" eq 'xhtml') ;
    $css = 1 if ( "$p" eq 'css') ;
  }

  if (($html)||($xhtml)||($css)) {
    $$t .= "&lt;p align=\"right\"&gt;" ;
    if ($html) {
      $$t .= join( '',
		   "&lt;a href=\"http://validator.w3.org/check?uri=referer\"&gt;",
		   "&lt;img border=\"0\" src=\"",
		   htmlcatfile($rootdir,"valid-html401.gif"),
		   "\" ",
		   "alt=\"Valid HTML 4.01!\" height=\"31\" width=\"88\"&gt;",
		   "&lt;/a&gt;" ) ;
    }
    if ($css) {
      $$t .= join( '',
		   "&lt;a href=\"http://jigsaw.w3.org/css-validator/\"&gt;",
		   "&lt;img style=\"border:0;width:88px;height:31px\" ",
		   "src=\"",
		   htmlcatfile($rootdir,"valid-css.gif"),
		   "\" alt=\"Valid CSS!\"&gt;",
		   "&lt;/a&gt;" ) ;
    }
    $$t .= "&lt;/p&gt;" ;
  }
}

=pod

=item * html_getinitiales()

Replies the initiales of the specified text
Takes 1 arg:

=over

=item * text (string)

=back

=cut
sub html_getinitiales($) {
  my $text = $_[0] || '' ;
  return __scan_html_text_and_apply( $text,
				     sub { $_[0] = __html_getinitiales($_[0]) ;
					 } ) ;
}

# TODO: leave the '-' inside the returned initials.
sub __html_getinitiales($) {
  my $text = $_[0] || '' ;
  my $res = '' ;
  while ( $text ) {
    if ( $text =~ /^[_\.\-\s]*&amp;([^;]+);([\.\-])(.*)$/ ) {
      # Name's initial starting with an accentuated letter
      (my $tag,my $sep,$text) = ($1,$2,$3);
      if ( exists $HTML_ENTITY_CODES{$tag} ) {
        $res .= "&amp;".$tag.";$sep" ;
      }
    }
    elsif ( $text =~ /^[_\.\-\s]*([^&amp;\s\-\._])([\.\-])(.*)$/ ) {
      # Name's initial starting with an accentuated letter
      (my $tag,my $sep,$text) = ($1,$2,$3);
      $res .= "$tag$sep";
    }
    elsif ( $text =~ /^[_\.\-\s]*&amp;([^;]+);[^\s\-\._]*(.*)$/ ) {
      # Long name starting with an accentuated letter
      (my $tag,$text) = ($1,$2);
      if ( exists $HTML_ENTITY_CODES{$tag} ) {
        $res .= "&amp;".$tag.";." ;
      }
    }
    elsif ( $text =~ /^[_\.\-\s]*([^&amp;\s\-\._])[^\s\-\._]*(.*)$/ ) {
      # Long name starting with a not-accentuated letter
      (my $tag,$text) = ($1,$2);
      $res .= $tag.".";
    }
    else {
      $text = '';
    }
  }
  return $res ;
}

=pod

=item * html_substr()

Replies a substring of the specified text
Takes 3 args:

=over

=item * text (string)

=item * start (optional integer)

=item * length (optional integer)

=back

=cut
sub html_substr($;$$) {
  my $text = $_[0] || '' ;
  my $startpos = $_[1] || 0;
  my $length = $_[2] || -1;
  return __scan_html_text_and_apply( $text,
				     sub { $_[0] = __html_substr($_[0],$startpos,$length) ;
					 } ) ;
}

sub __html_substr($$$) {
  my ($text,$pos,$length) = ($_[0],$_[1],$_[2]);
  my @chars = html_split_to_chars($text);
  $pos = @chars + $pos if ($pos&lt;0);
  $pos = @chars if ($pos&gt;@chars);
  $length = @chars if ($length&lt;0);
  my @res = ();
  for(my $i=$pos; ($i&lt;@chars)&amp;&amp;($i&lt;($pos+$length)); $i++) {
    push @res, $chars[$i];
  }
  return join('',@res);
}

=pod

=item * html_split_chars()

Replies an array of the characters in the given HTML string.
Takes 1 arg:

=over

=item * text (string)

an HTML string without HTML tags

=back

=cut

sub html_split_to_chars($) {
  my @chars = ();
  while ($_[0] =~ /((?:&amp;[^;]+;)|(?:[^&amp;]))/g) {
    push @chars, "$1";
  }
  return @chars;
}

=pod

=item * sortbyletters(@)

Sort by letter the specified parameter and
replies it.
Takes 1 arg:

=over

=item * array (string)

=back

=cut
sub sortbyletters(\@) {
  return sort {
    my $elt1 = remove_html_accents($a || "");
    my $elt2 = remove_html_accents($b || "");
    $elt1 =~ s/[^a-zA-Z0-9]//g;
    $elt2 =~ s/[^a-zA-Z0-9]//g;
    return ($elt1 cmp $elt2);
  } @{$_[0]};
}

=pod

=item * remove_html_accents($)

Remove the accents from the given html strings.
Takes 1 arg:

=over

=item * str (string)

=back

=cut
sub remove_html_accents($) {
  my $orig = shift | '';
  my $trans = "$orig";
  while (my ($html,$code) = each(%HTML_ENTITY_CODES)) {
	$trans =~ s/\&amp;\#\Q$code\E;/$HTML_NO_ACCENT_ENTITY_CHARS{$html}/gs;
  }
  while (my ($html,$letter) = each(%HTML_NO_ACCENT_ENTITY_CHARS)) {
	$trans =~ s/\&amp;\Q$html\E;/$letter/gs;
  }
  return "$trans";
}

1;
__END__

=back

=head1 COPYRIGHT

(c) Copyright 1998-09 StÃ©phane Galland &lt;galland@arakhne.org&gt;, under GPL.

=head1 AUTHORS

=over

=item *

Conceived and initially developed by StÃ©phane Galland E&lt;lt&gt;galland@arakhne.orgE&lt;gt&gt;.

=back

=head1 SEE ALSO

phpdocgen.pl
</pre></body></html>