# Normal.pm - The Normal Class derived from the Base HTML object. # Created by James Pattie, 04/28/2000. # Copyright (c) 2000 PC & Web Xperience, Inc. http://www.pcxperience.com/ # All rights reserved. This program is free software; you can redistribute it # and/or modify it under the same terms as Perl itself. # updated 02/24/2001 - naming scheme change. # updated 11/20/2001 - added support in display routines for using different HTML versions. package HTMLObject::Normal; use HTMLObject::Base; use strict; use vars qw($AUTOLOAD $VERSION @ISA @EXPORT @EXPORT_OK); require Exporter; @ISA = qw(HTMLObject::Base Exporter AutoLoader); @EXPORT = qw( ); $VERSION = '2.28'; =head1 NAME HTMLObject::Normal - Perl extension for HTMLObject. =head1 SYNOPSIS use HTMLObject::Normal; my $doc = HTMLObject::Normal->new(); $doc->setTitle("Test of HTMLObject::Normal"); $doc->setFocus("body"); $doc->print(<<"END_OF_BODY");

HTMLObject::Normal


This is cool! END_OF_BODY $doc->setCookie(name => 'cookie name', value => 'This rocks!'); $doc->setFocus("javascript"); $doc->print(<<"END_OF_JAVASCRIPT"); function helloWorld() { document.write("Hello World"); } END_OF_JAVASCRIPT # This must be left aligned all the way. # cause the helloWorld function to be called when the page has been # loaded! $doc->setOnload("helloWorld();"); # Actually generate the entire document, cookie and all! $doc->display(); =head1 DESCRIPTION HTMLObject::Normal builds on the HTMLObject::Base object and provides JavaScript support that facilitates making Dynamic HTML documents much easier than before. See the documentation.html file for more details. All core methods in the HTMLObject::Base module are available for use in this module. For documentation see the HTMLObject::Base man page. =head1 Exported FUNCTIONS =over 4 =cut # new sub new { my $class = shift; my $self = $class->SUPER::new(@_); $self->setErrorMessage(code => '3003', message => "In Buffering Mode"); $self->setErrorMessage(code => '3004', message => "In non-Buffering Mode"); $self->setErrorMessage(code => '3005', message => "Invalid variable or function"); $self->setErrorMessage(code => '3006', message => "Invalid value"); $self->setTitle("HTMLObject::Normal") if ($self eq "HTMLObject::Normal"); $self->{javascriptBody} = ""; $self->{javascriptOnload} = ""; $self->{javascriptOnunload} = ""; $self->{javascriptOnbeforeunload} = ""; $self->{javascriptVersion} = "1.1"; $self->{javascriptIncludes} = []; $self->{javascriptRequiredMessage} = ""; $self->{javascriptRequiredVersion} = ""; $self->{javascriptErrorHandler} = 0; $self->{javascriptErrorHandlerPrgName} = "change me"; $self->{javascriptErrorHandlerPrgVersion} = "1.0"; $self->{javascriptErrorHandlerEmail} = "nobody\@not-a-valid-address.com"; $self->{javascriptErrorHandlerCode} = ""; $self->{javascriptErrorHandlerBaseWindowName} = ""; return $self; } #reset sub reset { my $self = shift; $self->SUPER::reset(@_); $self->setErrorMessage(code => '3003', message => "In Buffering Mode"); $self->setErrorMessage(code => '3004', message => "In non-Buffering Mode"); $self->setErrorMessage(code => '3005', message => "Invalid variable or function"); $self->setErrorMessage(code => '3006', message => "Invalid value"); $self->setTitle("HTMLObject::Normal") if ($self eq "HTMLObject::Normal"); $self->{javascriptBody} = ""; $self->{javascriptOnload} = ""; $self->{javascriptOnunload} = ""; $self->{javascriptOnbeforeunload} = ""; $self->{javascriptVersion} = "1.1"; $self->{javascriptIncludes} = []; $self->{javascriptRequiredMessage} = ""; $self->{javascriptRequiredVersion} = ""; $self->{javascriptErrorHandler} = 0; $self->{javascriptErrorHandlerPrgName} = "change me"; $self->{javascriptErrorHandlerPrgVersion} = "1.0"; $self->{javascriptErrorHandlerEmail} = "nobody\@not-a-valid-address.com"; $self->{javascriptErrorHandlerCode} = ""; $self->{javascriptErrorHandlerBaseWindowName} = ""; } # displayJavaScriptRequirements sub displayJavaScriptRequirements { my $self = shift; my $output = ""; if (length $self->{javascriptRequiredMessage} > 0) { my $requiredVersion = '1.0'; $output = "\n"; if (length $self->{javascriptRequiredVersion} > 0) { $requiredVersion = $self->{javascriptRequiredVersion}; $output .= "\n"; } $output .= "\n"; $output .= "\n"; $output =~ s/^(.*)$/ $1/mg; # do the indentation. } return $output; } # displayJavaScriptIncludes sub displayJavaScriptIncludes { my $self = shift; my $output = ""; if (scalar @{$self->{javascriptIncludes}} > 0) { foreach my $include (@{$self->{javascriptIncludes}}) { $output .= " $include\n"; } } return $output; } # displayJavaScriptErrorHandler sub displayJavaScriptErrorHandler { my $self = shift; my $output = ""; if ($self->{javascriptErrorHandler}) { $output .= <<"END_OF_CODE"; END_OF_CODE $output =~ s/^(.*)$/ $1/mg; # do the indentation. } return $output; } =item string displayJavaScriptHead(void) returns the string of all javascript related code that needs to be output in the tag. =cut sub displayJavaScriptHead { my $self = shift; my $output = ""; # display JavaScript error handler. my $tempStr = $self->displayJavaScriptErrorHandler(); $output .= "$tempStr\n" if (length $tempStr > 0); # display Javascript Requirements if needed. $tempStr = $self->displayJavaScriptRequirements(); $output .= "$tempStr\n" if (length $tempStr > 0); # display Javascript Includes if defined. $tempStr = $self->displayJavaScriptIncludes(); $output .= "$tempStr\n" if (length $tempStr > 0); # display Javascript Block if needed. if (length $self->{javascriptOnload} > 0 || length $self->{javascriptOnunload} > 0 || length $self->{javascriptOnbeforeunload} > 0 || length $self->{javascriptBody} > 0) { $output .= " \n\n"; } return $output; } =item string displayJavaScriptBody(void) returns the string of javascript related attributes that needs to be output in the or tags. =cut sub displayJavaScriptBody { my $self = shift; my $output = ""; $output .= " onload=\"doOnLoad();\"" if (length $self->{javascriptOnload} > 0); $output .= " onunload=\"doOnUnLoad();\"" if (length $self->{javascriptOnunload} > 0); $output .= " onbeforeunload=\"return doOnBeforeUnLoad();\"" if (length $self->{javascriptOnbeforeunload} > 0); return $output; } # display # optional: debug (0|1 defaults to 0), print (0|1 defaults to 1) # returns: html document # summary: Will generate the HTML Document to display and as long # as debug is not set and print is set will print it to # standard out. sub display { my $self = shift; my %args = ( debug => 0, print => 1, @_ ); my $debug = $args{debug}; my $print = $args{print}; my $output = ""; if (!$self->{bufferMode}) { $self->setError(code => "1013"); $self->displayError(message => "You can not call display when in non-Buffer mode!"); } if ($self->{weHaveDisplayed}) { $self->setError(code => "1013"); $self->displayError(message => "You can not call display when we have already displayed ourselves!"); } $self->{weHaveDisplayed} = 1; # signal we have displayed ourselves. my ($tempHeadString, $tempBodyString); my $headString = $self->getHeadString(); my $contentTypeString = $self->getContentType(); my $titleString = $self->getTitle(); my $bodyString = $self->getBodyString(); my $language = $self->getLanguage(); # do any replacement for the tagBuffers that have been defined. foreach my $tag (keys %{$self->{tagBuffers}}) { if ($self->{tagBufferModes}->{$tag} eq "single") { $bodyString =~ s/($tag)/$self->{tagBuffers}->{$tag}/; } else { $bodyString =~ s/($tag)/$self->{tagBuffers}->{$tag}/g; } } if ($contentTypeString =~ /^(text\/html)/i) { # display Cookies if needed (They apparently must come before the Content-Type: header.) my $tempStr = $self->displayCookies(); $output .= $tempStr if (length $tempStr > 0); if (length $self->{location} > 0) { $output .= "Location: $self->{location}\n\n"; print $output if (!$debug && $print); if ($debug == 1) { $output = $self->formEncodeString(string => $output); # fixup all special characters. $output =~ s/ / /g; $output =~ s/\t/    /g; # replace each tab with 4 spaces $output =~ s/\n/
\n/gm; # make all line breaks be
's. } return $output; } #make sure that all output is properly indented, this way the user doesn't have to do any indentation to fit our output indentation. ($tempHeadString = $headString) =~ s/^(.*)$/ $1/mg; # currently 4 spaces. ($tempBodyString = $bodyString) =~ s/^(.*)$/ $1/mg; $tempBodyString =~ s/()((?s).*?<\/textarea>)/$1 . eval{(my $temp = $2) =~ s{^(\s{4})(.*?)$}{$2}mg; return $temp}/mxge if ($tempBodyString =~ //); $tempBodyString =~ s/(
)((?s).*?<\/pre>)/$1 . eval{(my $temp = $2) =~ s{^(\s{4})(.*?)$}{$2}mg; return $temp}/mxge if ($tempBodyString =~ /
/);

    $output .= "Content-Type: $contentTypeString\n\n"; # Display the Content-Type block.

    # output the Document Type header.
    if ($self->{xhtml})
    {
      $output .= "{docEncoding}\"?>\n";
      $output .= $self->{xhtmlDoctypes}->{$self->{htmlVersion}}->{$self->{htmlDTD}} . "\n" if ($self->{displayDTD});
    }
    else
    {
      $output .= $self->{doctypes}->{$self->{htmlVersion}}->{$self->{htmlDTD}} . "\n" if ($self->{displayDTD});
    }
    $output .= "{xhtml} ? "xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"$language\" " : "") . "lang=\"$language\">\n";
    $output .= "  \n";

    # display Meta Tags if needed.
    $tempStr = $self->displayMetaTags();
    $output .= "$tempStr\n" if (length $tempStr > 0);

    # display Base if needed.
    $tempStr = $self->displayBase();
    $output .= "    $tempStr\n" if (length $tempStr > 0);

    $output .= "    $titleString\n\n";

    # display Links if needed.
    $tempStr = $self->displayLinks();
    $output .= $tempStr if (length $tempStr > 0);

    # display CSS entries if needed.
    $tempStr = $self->displayCSS();
    $output .= "$tempStr\n" if (length $tempStr > 0);

    $output .= "$tempHeadString\n\n" if (length $headString > 0);

    # display the JavaScript related code.
    $tempStr = $self->displayJavaScriptHead();
    $output .= "$tempStr\n" if (length $tempStr > 0);

    $output .= "  \n\n";
    $output .= "  {xhtml})
    {
      $output .= " bgcolor=\"$self->{bodyBgcolor}\" text=\"$self->{bodyFgcolor}\" link=\"$self->{bodyLinkColor}\" vlink=\"$self->{bodyVlinkColor}\" alink=\"$self->{bodyAlinkColor}\"";
      $output .= " background=\"$self->{bodyImage}\"" if (length $self->{bodyImage} > 0);
    }
    $output .= " class=\"$self->{bodyClass}\"" if (length $self->{bodyClass} > 0);
    $output .= " id=\"$self->{bodyID}\"" if (length $self->{bodyID} > 0);
    $output .= " style=\"$self->{bodyStyle}\"" if (length $self->{bodyStyle} > 0);
    $output .= " title=\"$self->{bodyTitle}\"" if (length $self->{bodyTitle} > 0);
    $output .= $self->{bodyCustomArgs} if (length $self->{bodyCustomArgs} > 0);

    # display the JavaScript body attributes.
    $tempStr = $self->displayJavaScriptBody();
    $output .= $tempStr if (length $tempStr > 0);

    $output .= ">\n";
    $output .= "$tempBodyString\n\n" if (length $bodyString > 0);
    $output .= "  \n";
    $output .= "\n";
  }
  else
  {
    $output .= "Content-Type: $contentTypeString\n\n";
    $output .= $bodyString;  # don't want to possibly corrupt anything so we put the original out.
  }

  print $output if (!$debug && $print);

  if ($debug == 1)
  {
    $output = $self->formEncodeString(string => $output);  # fixup all special characters.
    $output =~ s/ / /g;
    $output =~ s/\t/    /g;  # replace each tab with 4 spaces
    $output =~ s/\n/
\n/gm; # make all line breaks be
's. } return $output; } # startDisplaying sub startDisplaying { my $self = shift; my $output = ""; if (!$self->{bufferMode}) { $self->setError(code => "1013"); $self->displayError(message => "You can not call startDisplaying again when in non-Buffer mode!"); } if ($self->{weHaveDisplayed}) { $self->setError(code => "1013"); $self->displayError(message => "You can not call startDisplaying when we have already displayed ourselves!"); } $self->{weHaveDisplayed} = 1; # signal we have displayed ourselves. my ($tempHeadString, $tempBodyString); my $headString = $self->getHeadString(); my $contentTypeString = $self->getContentType(); my $titleString = $self->getTitle(); my $bodyString = $self->getBodyString(); my $language = $self->getLanguage(); # do any replacement for the tagBuffers that have been defined. foreach my $tag (keys %{$self->{tagBuffers}}) { if ($self->{tagBufferModes}->{$tag} eq "single") { $bodyString =~ s/($tag)/$self->{tagBuffers}->{$tag}/; } else { $bodyString =~ s/($tag)/$self->{tagBuffers}->{$tag}/g; } } if ($contentTypeString =~ /^(text\/html)/i) { #make sure that all output is properly indented, this way the user doesn't have to do any indentation to fit our output indentation. ($tempHeadString = $headString) =~ s/^(.*)$/ $1/mg; # currently 4 spaces. ($tempBodyString = $bodyString) =~ s/^(.*)$/ $1/mg; $tempBodyString =~ s/()((?s).*?<\/textarea>)/$1 . eval{(my $temp = $2) =~ s{^(\s{4})(.*?)$}{$2}mg; return $temp}/mxge if ($tempBodyString =~ //); $tempBodyString =~ s/(
)((?s).*?<\/pre>)/$1 . eval{(my $temp = $2) =~ s{^(\s{4})(.*?)$}{$2}mg; return $temp}/mxge if ($tempBodyString =~ /
/);

    # display Cookies if needed (They apparently must come before the Content-Type: header.)
    my $tempStr = $self->displayCookies();
    $output .= $tempStr if (length $tempStr > 0);

    $output .= "Content-Type: $contentTypeString\n\n"; # Display the Content-Type block.

    # output the Document Type header.
    if ($self->{xhtml})
    {
      $output .= "{docEncoding}\"?>\n";
      $output .= $self->{xhtmlDoctypes}->{$self->{htmlVersion}}->{$self->{htmlDTD}} . "\n" if ($self->{displayDTD});
    }
    else
    {
      $output .= $self->{doctypes}->{$self->{htmlVersion}}->{$self->{htmlDTD}} . "\n" if ($self->{displayDTD});
    }
    $output .= "{xhtml} ? "xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"$language\" " : "") . "lang=\"$language\">\n";
    $output .= "  \n";

    # display Meta Tags if needed.
    $tempStr = $self->displayMetaTags();
    $output .= "$tempStr\n" if (length $tempStr > 0);

    # display Base if needed.
    $tempStr = $self->displayBase();
    $output .= "    $tempStr\n" if (length $tempStr > 0);

    $output .= "    $titleString\n\n";

    # display Links if needed.
    $tempStr = $self->displayLinks();
    $output .= $tempStr if (length $tempStr > 0);

    # display CSS entries if needed.
    $tempStr = $self->displayCSS();
    $output .= "$tempStr\n" if (length $tempStr > 0);

    $output .= "$tempHeadString\n\n" if (length $headString > 0);

    # display the JavaScript related code.
    $tempStr = $self->displayJavaScriptHead();
    $output .= "$tempStr\n" if (length $tempStr > 0);

    $output .= "  \n\n";
    $output .= "  {xhtml})
    {
      $output .= " bgcolor=\"$self->{bodyBgcolor}\" text=\"$self->{bodyFgcolor}\" link=\"$self->{bodyLinkColor}\" vlink=\"$self->{bodyVlinkColor}\" alink=\"$self->{bodyAlinkColor}\"";
      $output .= " background=\"$self->{bodyImage}\"" if (length $self->{bodyImage} > 0);
    }
    $output .= " class=\"$self->{bodyClass}\"" if (length $self->{bodyClass} > 0);
    $output .= " id=\"$self->{bodyID}\"" if (length $self->{bodyID} > 0);
    $output .= " style=\"$self->{bodyStyle}\"" if (length $self->{bodyStyle} > 0);
    $output .= " title=\"$self->{bodyTitle}\"" if (length $self->{bodyTitle} > 0);

    # display the JavaScript body attributes.
    $tempStr = $self->displayJavaScriptBody();
    $output .= $tempStr if (length $tempStr > 0);

    $output .= ">\n";
    $output .= "$tempBodyString\n\n" if (length $bodyString > 0);
  }
  else
  {
    $output .= "Content-Type: $contentTypeString\n\n";
    $output .= $bodyString;  # don't want to possibly corrupt anything so we put the original out.
  }

  print $output;

  $self->{bufferMode} = 0;
  $self->{currentSection} = "body";
  $|=1;  # turn off perls print buffering.
}

=item void setFocus(section) (scalar value)

 Validates the section name specified and then sets the internal
 pointer to the specified section. The output of any following print,
 read, or delete commands will work with the specified section.
 Modifies $currentSection.  This version handles "javascript" and
 passes everything else to the HTMLObject::Base class to handle.

=cut
sub setFocus
{
  my $self = shift;

  if (!$self->{bufferMode})
  {
    $self->setError(code => "1013");
    $self->displayError(message => "You can not call setFocus when in non-Buffer mode!");
  }

  if (!defined $_[0])
  {
    $self->doRequiredParameterError('Normal::setFocus', 'Section Name');
  }

  my $focus = shift;

  if ($focus ne "javascript")
  {
    # send it to the parent class to deal with
    $self->SUPER::setFocus($focus);
  }

  if ($self->{contentTypeString} !~ /^(text\/html)$/i)
  {
    $self->setError(code => '1006');
    $self->displayError(title => 'Error:  setFocus', message => 'Focus = "$focus" is invalid when Content-Type = "$self->{contentTypeString}" is used!');
  }

  $self->{currentSection} = $focus;
}

=item void print(string|hash)
    Appends the contents of string to the currently specified section,
    previously specified via setFocus().

    If you specify a hash, then we use the keys to determine what
    sections to append text to.  This allows you to work with the
    following sections and data structures by name:

    sections:
      - javascript
      - onload
      - onunload
      - onbeforeunload

    structures:
      - javascriptIncludes

    structures can be modified by sending in a single string or an
    arrayref of strings to be modified.

    All structures are checked to make sure duplicates are not
    entered.  For example, specifying the same javascript Include
    entry multiple times will only result in one of them being
    output in your document, as long as they are specified individually.
    If you lump multiple together in a single string, then the string
    as a whole is compared and the duplicate suppression isn't
    guaranteed to work.

    Ex:
    $doc->print(body => "$bodyStr", head => "$headStr",
                    javascript => "$javascriptStr");
    $doc->print(javascriptIncludes => [ qq{}, qq{} ]);

    would only cause the first instance to be output.

=cut
sub print
{
  my $self = shift;

  if (scalar @_ == 0)
  {
    $self->doRequiredParameterError('print', 'Text');
  }

  if (scalar @_ == 1)
  {
    my $text = shift;

    if (!$self->{bufferMode})
    {
      print $text;
      return;
    }

    if ($self->{currentSection} eq "javascript")
    {
      $self->{javascriptBody} .= $text;
    }
    else
    {
      $self->SUPER::print($text);
    }
  }
  else
  {
    my %args = ( @_ );

    if (!$self->{bufferMode})
    {
      print $args{body};  # we can only print to the "body".
      return;
    }

    $self->{javascriptBody} .= $args{javascript} if (exists $args{javascript});
    $self->{javascriptOnload} .= $args{onload} if (exists $args{onload});
    $self->{javascriptOnunload} .= $args{onunload} if (exists $args{onunload});
    $self->{javascriptOnbeforeunload} .= $args{onbeforeunload} if (exists $args{onbeforeunload});

    # handle the javascriptIncludes special case.
    if (exists $args{javascriptIncludes})
    {
      my $section = "javascriptIncludes";
      my $sectionEntry = "javascriptIncludes";
      my @tmpArray;
      if (ref ($args{$section}) eq "ARRAY")
      {
        @tmpArray = @{$args{$section}};
      }
      else
      {
        @tmpArray = ($args{$section});
      }
      foreach my $ientry (@tmpArray)
      {
        my $found = 0;
        foreach my $entry (@{$self->{$sectionEntry}})
        {
          if ($entry eq $ientry)
          {
            $found = 1;
            last;
          }
        }
        push @{$self->{$sectionEntry}}, $ientry if (!$found);
      }
    }

    $self->SUPER::print(%args);
  }
}

# read
sub read
{
  my $self = shift;
  my $text = "";

  if (!$self->{bufferMode})
  {
    $self->setError(code => "1013");
    $self->displayError(message => "You can not call read when in non-Buffer mode!");
  }

  if ($self->{currentSection} eq "javascript")
  {
    $text = $self->{javascriptBody};
  }
  else
  {
    $text = $self->SUPER::read();
  }

  return $text;
}

# delete
sub delete
{
  my $self = shift;

  if (!$self->{bufferMode})
  {
    $self->setError(code => "1013");
    $self->displayError(message => "You can not call delete when in non-Buffer mode!");
  }

  if ($self->{currentSection} eq "javascript")
  {
    $self->{javascriptBody} = "";
  }
  else
  {
    $self->SUPER::delete();
  }
}

# setJavascriptInclude
# parameters: version, file
sub setJavascriptInclude
{
  my $self = shift;
  my %args = ( @_, );

  if (!$self->{bufferMode})
  {
    $self->setError(code => "1013");
    $self->displayError(message => "You can not call setJavascriptInclude when in non-Buffer mode!");
  }

  if (!exists $args{'file'})
  {
    $self->doRequiredParameterError('setJavascriptInclude', 'file');
  }

  my $version = $args{'version'};

  if (!exists $args{'version'})
  { # use the default version currently set.
    $version = $self->{javascriptVersion};
  }

  my $file = $args{'file'};

  my $include = "";

  #first verify that we don't already have this include specified.
  foreach my $entry (@{$self->{javascriptIncludes}})
  {
    return if ($entry eq $include);
  }
  push @{$self->{javascriptIncludes}}, $include;
}

# setJavascriptVersion
# takes the string for the version
sub setJavascriptVersion
{
  my $self = shift;

  if (!$self->{bufferMode})
  {
    $self->setError(code => "1013");
    $self->displayError(message => "You can not call setJavascriptVersion when in non-Buffer mode!");
  }

  if (!defined $_[0])
  {
    $self->doRequiredParameterError('setJavascriptVersion', 'version');
  }

  my $version = shift;

  $self->{javascriptVersion} = $version;
}

# getJavascriptVersion
sub getJavascriptVersion
{
  my $self = shift;

  return $self->{javascriptVersion};
}

# printJavascriptRequired
# parameters: version, message
sub printJavascriptRequired
{
  my $self = shift;
  my %args = ( message => 'JavaScript is Required to properly view this content!', @_, );

  if (!$self->{bufferMode})
  {
    $self->setError(code => "1013");
    $self->displayError(message => "You can not call printJavascriptRequired when in non-Buffer mode!");
  }

  my $version = $args{'version'};
  my $message = $args{'message'};

  if (!exists $args{'version'})
  { # use the default version currently set.
    $version = $self->{javascriptVersion};
  }

  $self->{javascriptRequiredVersion} = $version;
  $self->{javascriptRequiredMessage} = $message;
}

# disableJavascriptErrorHandler
# takes nothing
sub disableJavascriptErrorHandler
{
  my $self = shift;

  if (!$self->{bufferMode})
  {
    $self->setError(code => "1013");
    $self->displayError(message => "You can not call disableJavascriptErrorHandler when in non-Buffer mode!");
  }

  $self->{javascriptErrorHandler} = 0;
}

# isJavascriptErrorHandlerEnabled
# returns the state of the disable flag.
sub isJavascriptErrorHandlerEnabled
{
  my $self = shift;

  return $self->{javascriptErrorHandler};
}

# void setJavascriptErrorHandlerVersion(version => 'both')
sub setJavascriptErrorHandlerVersion
{
  my $self = shift;
  my %args = ( version => "both", @_ );
  my $version = $args{version};
}

# scalar getJavascriptErrorHandlerVersion()
#   This function returns the value of javascriptErrorHandlerVersion
#   to allow you to determine what version of the internal JavaScript
#   Error Handler code will be generated.
sub getJavascriptErrorHandlerVersion
{
  my $self = shift;

  return "both";
}

# enableJavascripErrorHandler(email, prgName, prgVersion)

# shortcut for setting the email, prgName and prgVersion values.

sub enableJavascriptErrorHandler
{
  my $self = shift;
  my %args = ( email => "", prgName => "", prgVersion => "", @_ );

  $self->setJavascriptErrorHandlerEmail($args{email});
  $self->setJavascriptErrorHandlerPrgName($args{prgName});
  $self->setJavascriptErrorHandlerPrgVersion($args{prgVersion});
}

# setJavascriptErrorHandlerEmail
# parameters: email
sub setJavascriptErrorHandlerEmail
{
  my $self = shift;
  my $email;
  if (scalar @_ == 1)
  {
    $email = shift;
  }
  else
  {
    my %args = ( @_, );
    $email = $args{'email'};
  }

  if (!$self->{bufferMode})
  {
    $self->setError(code => "1013");
    $self->displayError(message => "You can not call setJavascriptErrorHandlerEmail when in non-Buffer mode!");
  }

  if (length $email == 0)
  {
    $self->doRequiredParameterError('setJavascriptErrorHandlerEmail', 'email');
  }

  $self->{javascriptErrorHandlerEmail} = $email;
  $self->{javascriptErrorHandler} = 1;  # enable the error handler.
}

# getJavascriptErrorHandlerEmail
sub getJavascriptErrorHandlerEmail
{
  my $self = shift;

  return $self->{javascriptErrorHandlerEmail};
}

# setJavascriptErrorHandlerPrgName
# parameters: name
# sets the PrgName value for use in the error handler.
sub setJavascriptErrorHandlerPrgName
{
  my $self = shift;
  my $name;
  if (scalar @_ == 1)
  {
    $name = shift;
  }
  else
  {
    my %args = ( @_, );
    $name = $args{name};
  }

  if (!$self->{bufferMode})
  {
    $self->setError(code => "1013");
    $self->displayError(message => "You can not call setJavascriptErrorHandlerPrgName when in non-Buffer mode!");
  }

  if (length $name == 0)
  {
    $self->doRequiredParameterError('setJavascriptErrorHandlerPrgName', 'name');
  }

  $self->{javascriptErrorHandlerPrgName} = $name;
}

# getJavascriptErrorHandlerPrgName
sub getJavascriptErrorHandlerPrgName
{
  my $self = shift;

  return $self->{javascriptErrorHandlerPrgName};
}

# setJavascriptErrorHandlerPrgVersion
# parameters: version
# sets the PrgVersion value for use in the error handler.
sub setJavascriptErrorHandlerPrgVersion
{
  my $self = shift;
  my $version;
  if (scalar @_ == 1)
  {
    $version = shift;
  }
  else
  {
    my %args = ( @_, );
    $version = $args{version};
  }

  if (!$self->{bufferMode})
  {
    $self->setError(code => "1013");
    $self->displayError(message => "You can not call setJavascriptErrorHandlerPrgVersion when in non-Buffer mode!");
  }

  if (length $version == 0)
  {
    $self->doRequiredParameterError('setJavascriptErrorHandlerPrgVersion', 'version');
  }

  $self->{javascriptErrorHandlerPrgVersion} = $version;
}

# getJavascriptErrorHandlerPrgVersion
sub getJavascriptErrorHandlerPrgVersion
{
  my $self = shift;

  return $self->{javascriptErrorHandlerPrgVersion};
}

# setJavascriptErrorHandlerWindow
# parameters: name
sub setJavascriptErrorHandlerWindow
{
  my $self = shift;
  my $name;
  if (scalar @_ == 1)
  {
    $name = shift;
  }
  else
  {
    my %args = ( @_, );
    $name = $args{name};
  }

  if (!$self->{bufferMode})
  {
    $self->setError(code => "1013");
    $self->displayError(message => "You can not call setJavascriptErrorHandlerWindow when in non-Buffer mode!");
  }

  if (length $name == 0)
  {
    $self->doRequiredParameterError('setJavascriptErrorHandlerWindow', 'name');
  }

  $self->{javascriptErrorHandlerBaseWindowName} = $name;
}

# getJavascriptErrorHandlerWindow
sub getJavascriptErrorHandlerWindow
{
  my $self = shift;

  return $self->{javascriptErrorHandlerBaseWindowName};
}

# setJavascriptErrorHandler
# parameters: code
sub setJavascriptErrorHandler
{
  my $self = shift;
  my $code;
  if (scalar @_ == 1)
  {
    $code = shift;
  }
  else
  {
    my %args = ( @_, );
    $code = $args{code};
  }

  if (!$self->{bufferMode})
  {
    $self->setError(code => "1013");
    $self->displayError(message => "You can not call setJavascriptErrorHandler when in non-Buffer mode!");
  }

  if (length $code == 0)
  {
    $self->doRequiredParameterError('setJavascriptErrorHandler', 'code');
  }

  $self->{javascriptErrorHandlerCode} = $code;
  $self->{javascriptErrorHandler} = 1;
}

=item void onload(replace => bool, code => '')

 Frontend to the setOnload() method.

=cut
sub onload
{
  my $self = shift;

  $self->setOnload(@_);
}

=item void setOnload(replace => bool, code => '')

 If not passing by name and only one argument specified, it is
 assumed to be the code argument.

 This function builds up a javascript function doOnLoad() that
 is called by the onload attribute on the  tag.

 The code snippet you specify is appended to any previously
 specified onload code unless you specify replace => 1.

=cut
sub setOnload
{
  my $self = shift;
  my $code = "";
  my $replace = 0;

  if (scalar @_ == 1)
  {
    $code = shift;
  }
  else
  {
    my %args = ( replace => 0, code => '', @_, );

    $code = $args{code};
    $replace = $args{replace};
  }

  if (!$self->{bufferMode})
  {
    $self->setError(code => "1013");
    $self->displayError(message => "You can not call setOnload when in non-Buffer mode!");
  }

  if ($replace !~ /^(0|1)$/)
  {
    $self->setError(code => '3000');
    $self->displayError(title => 'setOnload', message => "replace = '$replace' is invalid!");
  }

  $self->{javascriptOnload} = "" if ($replace);
  $self->{javascriptOnload} .= $code;
}

=item void onunload(replace => bool, code => '')

 Frontend to the setOnunload() method.

=cut
sub onunload
{
  my $self = shift;

  $self->setOnunload(@_);
}

=item void setOnunload(replace => bool, code => '')

 If not passing by name and only one argument specified, it is
 assumed to be the code argument.

 This function builds up a javascript function doOnUnLoad() that
 is called by the onunload attribute on the  tag.

 The code snippet you specify is appended to any previously
 specified onunload code unless you specify replace => 1.

=cut
sub setOnunload
{
  my $self = shift;
  my $code = "";
  my $replace = 0;

  if (scalar @_ == 1)
  {
    $code = shift;
  }
  else
  {
    my %args = ( replace => 0, code => '', @_, );

    $code = $args{code};
    $replace = $args{replace};
  }

  if (!$self->{bufferMode})
  {
    $self->setError(code => "1013");
    $self->displayError(message => "You can not call setOnunload when in non-Buffer mode!");
  }

  if ($replace !~ /^(0|1)$/)
  {
    $self->setError(code => '3000');
    $self->displayError(title => 'setOnunload', message => "replace = '$replace' is invalid!");
  }

  $self->{javascriptOnunload} = "" if ($replace);
  $self->{javascriptOnunload} .= $code;
}

=item void onbeforeunload(replace => bool, code => '')

 Frontend to the setOnbeforeunload() method.

=cut
sub onbeforeunload
{
  my $self = shift;

  $self->setOnbeforeunload(@_);
}

=item void setOnbeforeunload(replace => bool, code => '')

 If not passing by name and only one argument specified, it is
 assumed to be the code argument.

 This function builds up a javascript function doOnBeforeUnLoad() that
 is called by the onbeforeunload attribute on the  tag.

 The code snippet you specify is appended to any previously
 specified onbeforeunload code unless you specify replace => 1.

 How to take advantage of the onbeforeunload event:

 1) If you want the browser to alwasy prompt when leaving the current
 page, have your code be:

 return "";

 2) If you want to specify a message to be displayed when the prompt is
 output, have your code be:

 return "This is the message to display, or whatever you want.";

 3) If you decide you don't want to prompt when the user leaves the page,
 have your code be:

 return;


 You have to explicitly put the return call in.

=cut
sub setOnbeforeunload
{
  my $self = shift;
  my $code = "";
  my $replace = 0;

  if (scalar @_ == 1)
  {
    $code = shift;
  }
  else
  {
    my %args = ( replace => 0, code => '', @_, );

    $code = $args{code};
    $replace = $args{replace};
  }

  if (!$self->{bufferMode})
  {
    $self->setError(code => "1013");
    $self->displayError(message => "You can not call setOnbeforeunload when in non-Buffer mode!");
  }

  if ($replace !~ /^(0|1)$/)
  {
    $self->setError(code => '3000');
    $self->displayError(title => 'setOnbeforeunload', message => "replace = '$replace' is invalid!");
  }

  $self->{javascriptOnbeforeunload} = "" if ($replace);
  $self->{javascriptOnbeforeunload} .= $code;
}

=item hash generatePickerCode()

 This method now moved to HTMLObject::Widgets!

=cut

=back

=cut

1;
__END__

=head1 Exported FUNCTIONS - NonInline POD

  scalar new()
    Creates a new instance of the HTMLObject::Normal document type.

  void reset()
    Resets the HTMLObject::Normal document back to the defaults.

  void display( debug => 0|1, print => 0|1 )
    optional: debug (defaults to 0), print (defaults to 1)
    This function generates the Normal Document displaying any cookies,
    JavaScript, Base, Links, etc. that were specified by the user plus
    the contents of the Body that the user created.  This function prints
    the generated document to standard out which is then hopefully being
    sent to a web server to process, if print = 1.  If debug is defined
    (and equals 1), then the contents of the current Document are
    returned in a format ready to display in another Document so that the
    user can see what would have been generated and the string is not
    printed out.

    The generated output is always returned to the caller.

  void startDisplaying()
    This function generates the Base Document displaying any cookies,
    plus the contents of the Body that the user created.  This function
    prints the generated document to standard out which is then hopefully
    being sent to a web server to process.  This also sets a flag
    bufferMode to 0 so that the methods know that we are no longer
    buffering user input but should just print it to the standard output.
    The only valid commands are error related, endDisplaying and print.

  void endDisplaying()
    This function closes the document that is currently being displayed
    in non-Buffering mode.  It is not valid to call this more than once.

  void printTag(tag, value, mode)
    requires: tag, value
    optional: mode (global or single replace)
    appends the contents of value to the tagBuffers->{tag} string.
    The tagBufferMode is set for the tag based upon the value of mode.
    If no mode is specified and a mode has not yet been set for the tag,
    then it is defaulted to single replacement mode, not global
    replacement.  Tags are only worked with in the BODY section.

  scalar read()
    Returns the contents of the currently specified section. This could
    be $headString, $bodyString, $javascriptBody.

  scalar readTag(tag)
    requires: tag
    returns the string from tagBuffers identified by tag

  void delete()
    Deletes the contents of the currently specified section. You should
    call read() before doing this so you can restore if this was an
    accident. This could modify $headString, $bodyString,
    $javascriptBody.

  void deleteTag(tag)
    required: tag
    We remove the contents from tagBuffers for the tag.

  void setJavascriptInclude(version => '', file => '')
    This function creates an entry in the @javascriptIncludes array that
    will create the code to include the specified javascript file and run
    it at the specified language version. If the version is not
    specified, then "JavaScript" is the default language and version.
    Modifies @javascriptIncludes.

  void setJavascriptVersion(version) (scalar value)
    This function sets what the Javascript version should be for the
    JavaScript body section. If not specified before the user calls
    display(), then the version will be JavaScript1.1. Modifies
    $javascriptVersion.

  scalar getJavascriptVersion()
    This function returns the current JavaScript version that is set in
    javascriptVersion.

  void printJavascriptRequired(version => '', message => '')
    This function will create the  tags in
    the head that will be run if the browser does not support scripting
    at all and will also generate the code to display the message if the
    specified version of scripting is not supported. Modifies
    $javascriptRequiredMessage, $javascriptRequiredVersion.

  void disableJavascriptErrorHandler()
    This function sets the variable $javascriptErrorHandler to 0 so
    that I know not to generate that block of code when displaying the
    document.

  scalar isJavascriptErrorHandlerEnabled()
    This function returns the value of the javascriptErrorHandler to
    see if it is enabled or disabled.

  void setJavascriptErrorHandlerVersion(version => 'both')
    This function is now deprecated and will be removed in the next
    version of the HTMLObject since the javascript Error Handler
    output is now the same for Version 4 and 5 browsers.

    Mozilla's functionality has apparently been fixed since
    2001-11-01!  Argh!

  scalar getJavascriptErrorHandlerVersion()
    This function is now deprecated and will be removed in the next
    version of the HTMLObject.

    It now always returns "both".

  void setJavascriptErrorHandlerEmail(email => '')
    This function will set the value of $javascriptErrorHandlerEmail
    to the value specified.  This is used in the generic JavaScript error
    handler.

    This is the only way to get the javascript error handler enabled
    unless you use enableJavascriptErrorHandler().

    By default, the error handler is now disabled until a valid
    e-mail address is specified.  (new in version 2.18)

  scalar getJavascriptErrorHandlerEmail()
    This function returns the email address that will be used by the
    builtin error handling code.

  void setJavascriptErrorHandlerPrgName(name => '')
    This function will set the value of $javascriptErrorHandlerPrgName
    to the value specified.  This is used in the generic JavaScript error
    handler to specify the name of the Program we are generating an
    error handler for.

  scalar getJavascriptErrorHandlerPrgName()
    This function returns the name of the program that we are generating
    a JavaScript error handler for.

  void setJavascriptErrorHandlerPrgVersion(version => '')
    This function will set the value of $javascriptErrorHandlerPrgVersion
    to the value specified.  This is used in the generic JavaScript error
    handler to specify the version of the Program we are generating an
    error handler for.

  scalar getJavascriptErrorHandlerPrgVersion()
    This function returns the version of the program that we are generating
    a JavaScript error handler for.

  void setJavascriptErrorHandler(code => '')
    This function will set the code to use for the JavaScript error
    handler and will assign it to $javascriptErrorHandlerCode.  It is
    up to the user to properly set the return value so that JavaScript
    does not continue to process the error message.  The user must also
    create their own JavaScript window, etc.

  void setJavascriptErrorHandlerWindow(name => '')
    This function will set the base window name to prepend to the
    window creation code used by the builtin error handler.

  scalar getJavascriptErrorHandlerWindow()
    This function returns the window name that will be used by the
    builtin error handler.

=head1 JavaScript Include files available

The HTMLObject module provides several javascript libraries
that should be available as /htmlobject/js/libraryname in your
web environment.  They should be installed into the
/usr/share/htmlobject directory on your server and made available
via an apache Include configuration file.

=over 2

=item cookies.js

Cookie library providing the following functions:

=over 2

=item string encodeCookiePart(value)

Returns the converted string after making sure it contains
only the valid characters a cookie can contain.

=item string decodeCookiePart(value)

Returns the original string that was passed into
encodeCookiePart().

=item void createCookie(name, value, days, domain, path)

Creates a cookie of name=value.
If days == 0, then it expires when the browser closes.
If days > 0, then it expires days days later.
If days < 0, then it expires and is deleted.

If domain is specified, then the cookie is restricted to the
specified domain.  Ex: .pcxperience.com

If path is specified, then the cookie is restricted to the
specified path, else it defaults to '/'.

=item string readCookie(name)

Returns the un-encoded cookie value or null if the cookie was
not found.

=item void eraseCookie(name, domain, path)

Calls createCookie(name, "", -1, domain, path) to delete the
specified cookie.

=back

=item form_methods.js

Javascript library of methods that make working with forms
much nicer.

=over 2

=item void selectAll(widget)

If the widget is a select box, we then select all entries.

=item void unSelectAll(widget)

If the widget is a select box, we then un-select all entries.

=item void toggleSelection(widget)

If the widget is a multi-select box, we go through and invert
all the entries selected states.

=item string selectAllButton(form, item)

returns the html snippet used to create the "Select All"
button for the named form item in the specified form.

=item string toggleSelectButton(form, item)

returns the html snippet used to create the "Toggle"
button for the named form item in the specified form.

=item int getTimeStamp()

returns the current date/time via the getTime() method.

=item void calcDatePrev(field)

Calculates the previous date from the specified fields
value.  The date must be in ISO format and - seperated.
The new date is put back into field.value.

=item void calcDateNext(field)

Calculates the next date from the specified fields value.
The date must be in ISO format and - seperated.  The
new date is put back into field.value.

=item bool isValidColor(widget)

returns true/false according to if the widget.value
matches this regular expression:
/^((#([A-F]|[0-9]){6})|transparent|inherit)( !important)?$/

=item bool isValidISODate(widget, seperator)

returns true/false according to if the specified widget
contains a valid ISO Date using the specified seperator.
The seperator must be one of - or /.

=item string formatDateNumber(value)

Makes sure that the specified value is 2 digits and
is 0 prefixed, if < 10.  returns the updated value.

=item bool fixupISODate(widget, seperator, year)

returns true if the date specified by the widget was
already a valid ISO Date, or was able to be fixed.
returns false if the date specified by the widget was
not able to be turned into a valid ISO Date.
seperator is used to determine what the year, month
and day parts will be seperated by.  year is for
when the given date is only 2 parts, both with length
<= 2, then we treat them as month and day and use the
year to create the date from.

The updated ISO Date is put into widget.value.

=item void displayCalculatorHelp()

Pops up a window and outputs the calculator widgets
help content into it.

=item void calculateFormula(field)

The user passes in the form item object.
If the current value appears to be a valid formula, we first
save the current value on the calculatorUndo array under the
form items name, this allows for multiple calculator fields.
We then eval the formula and put the result in the fields value.

A valid formula is made up of (), +, -, /, *, numbers and
form field identifiers F{name} that are to be substituted with
the value of the specified form item (in the same form as the
specified field.

=item void calculateUndo(field)

pops the calculatorUndo array and replaces the
fields value with the popped value.
disables the Undo button once no more entries are available.

=item float processNumber(value)

takes the string to convert to a float.
returns 0.00 if the value evaled to NaN,
otherwise the float version of value.

=item bool validate_formula(field)

takes the form item to validate it's value as a formula.
Makes sure that the formula is well balanced and that it doesn't
reference itself as a Field to pull values from.

=item array parse_formula(formula)

builds up a result array with the following entries:
[0] = The error string if an error occured, empty otherwise.
[1] = the array of parsed tokens from the formula.

=item functions used by HTMLObject::Form select-picker code

=over 2

 a = assigned hidden element
 u = unassigned hidden element
 aS = assigned Select box
 uS = unassigned Select box
 sep = seperator string

=item void htmlForm_assignOneEntry(a, u, aS, uS, sep)

moves over those selected items from the unassigned select
box to the assigned select box.

calls htmlForm_updateEntries() to update the hidden form
items that depict the new state of things.

=item void htmlForm_unAssignOneEntry(a, u, aS, uS, sep)

moves over those selected items from the assigned select
box to the unassigned select box.

calls htmlForm_updateEntries() to update the hidden form
items that depict the new state of things.

=item void htmlForm_assignAllEntries(a, u, aS, uS, sep)

moves all items from the unassigned select box to the
assigned select box.

calls htmlForm_updateEntries() to update the hidden form
items that depict the new state of things.

=item void htmlForm_unAssignAllEntries(a, u, aS, uS, sep)

moves all items from the assigned select box to the
unassigned select box.

calls htmlForm_updateEntries() to update the hidden form
items that depict the new state of things.

=item void htmlForm_updateEntries(a, u, aS, uS, sep)

walks the assigned and unassigned select boxes and builds
up the strings to populate a and u with.

=back

=back

=head1 AUTHOR

James A. Pattie, htmlobject@pcxperience.com

=head1 SEE ALSO

perl(1), HTMLObject::Base(3), HTMLObject::FrameSet(3), HTMLObject::ReadCookie(3), HTMLObject::Form(3), HTMLObject::Widgets(3).

=cut