# ====================================================================
# Copyright (C) 1997,1998 Stephen Farrell <stephen@farrell.org>
#
# All rights reserved. This program is free software; you can
# redistribute it and/or modify it under the same terms as Perl
# itself.
#
# ====================================================================
# File: Component.pm
# Author: Stephen Farrell
# Created: August, 1997
# Locations: http://www.palefire.org/~sfarrell/TableLayout/
# CVS $Id: Component.pm,v 1.17 1998/09/20 21:05:28 sfarrell Exp $
# ====================================================================
##
## This class is abstract
##
package HTML::TableLayout::Component;
use HTML::TableLayout::Symbols;
@HTML::TableLayout::Component::ISA=qw(HTML::TableLayout::TL_BASE);
use Carp;
use strict;
##
## Default init
##
sub tl_init {
my $this = shift;
##
## QUIZ--how do i avoid this temporary variable?
##
my %params = @_;
$this->{TL_PARAMS} = \ %params;
$this->SUPER::tl_init();
}
##
## tl_setContext(): Sets the context in the heirarchy when packing and
## displaying. This is done "late"
##
sub tl_setContext {
my ($this, $container) = @_;
my $window = $container->{TL_WINDOW};
my $form = $container->{TL_FORM};
## ====================================================================
##
## DEBUGGING
##
confess "container is null" unless $container;
confess "window is null" unless $window;
##
## it's ok for the form to be null, but if it is, we don't want to
## clobber an existing value for it.
##
## ====================================================================
defined $container and $this->{TL_CONTAINER} = $container;
defined $window and $this->{TL_WINDOW} = $window;
defined $form and $this->{TL_FORM} = $form;
}
##
## tl_getContainer(),tl_getWindow(),tl_getForm(): Accessors for the
## above--notethat these might not be used much b/c we know the name
## of the data very well.
##
sub tl_getContainer { return shift->{TL_CONTAINER} }
sub tl_getWindow { return shift->{TL_WINDOW} }
sub tl_getForm { return shift->{TL_FORM} }
##
## tl_setup(): is called just before printing, and is meant to provide
## "late" packing and searching for requirements in containers (like
## looking for a Form). Actually, it's called everywhere before
## anything prints, so if you want to play with values in your
## neighboring components, have fun.
##
## If you override this, you must call your super's version. (like
## $this->SUPER::tl_setup()). ok ok I'm lying right now b/c as you can
## see, there is nothing here so obviously you don't HAVE to call it.
## but I might add something later. Also, if your parent is a
## componentcontainer, then you MUST call it (or do equivalent and
## keep your fingers crossed for future versions).
##
sub tl_setup { }
##
## tl_print(): uses i_print() and f_print() to display object.
##
sub tl_print { }
##
## tl_breakAfter(): The component has a break "<BR>" after it. This
## doesn't happen automatically--the component printing it needs to
## check if it is there and print it itself.
##
sub tl_breakAfter { return shift->{TL_BREAK_AFTER} }
sub tl_destroy {
my ($this) = @_;
undef $this->{TL_BREAK_AFTER};
undef $this->{TL_CONTAINER};
undef $this->{TL_WINDOW};
undef $this->{TL_FORM};
$this->SUPER::tl_destroy();
}
# ---------------------------------------------------------------------
package HTML::TableLayout::ComponentContainer;
use HTML::TableLayout::Symbols;
@HTML::TableLayout::ComponentContainer::ISA=qw(HTML::TableLayout::Component);
sub tl_init {
my $this = shift;
$this->SUPER::tl_init(@_);
$this->{TL_COMPONENTS} = [];
$this->{TL_BREAKS} = [];
}
##
## insert(): add a component. subclasses should always call this,
## like tl_setup()
##
sub insert {
my ($this, $obj, $br) = @_;
if (! ref $obj) {
$obj = HTML::TableLayout::Component::Text->new($obj);
}
if ($obj->isa("HTML::TableLayout::Form")) {
$this->{TL_FORM} = $obj;
$this->{form_is_mine} = $obj;
}
else {
push @{ $this->{TL_COMPONENTS} }, $obj;
push @{ $this->{TL_BREAKS} }, $br;
}
return $this;
}
##
## insertLn(): add a component w/ <BR> afterwards. Generally I've
## handled this as a wrapper method that calls insert with a second
## argument of "1".
##
sub insertLn { return shift->insert(shift,1) }
##
## tl_setup(): if you choose to override this method, then you must do
## what is done here, or call $this->SUPER::tl_setup(). Of course, if
## you replicate this method's functionality, you should be aware that
## in the future this function might change, and you might need to
## update your equivalent functionality in the future.... (yes, I'm
## scrounging for hints on OO design!)
##
sub tl_setup {
my ($this) = @_;
$this->tl_setup_form();
foreach my $cmp (@{ $this->{TL_COMPONENTS} }) {
die("null comp.") unless $cmp;
##
## Maybe it is a form input, in which case it needs to be inserted
## into the appropriate form.
##
if ($cmp->isa("HTML::TableLayout::FormComponent")) {
my $f = $this->{TL_FORM};
if ($f) {
$f->insert($cmp);
$cmp->tl_setContext($this);
}
else {
die("No Form to insert this FormComponent [$cmp] into [$this]");
}
}
$cmp->tl_setContext($this);
$cmp->tl_setup();
}
$this->SUPER::tl_setup();
}
sub tl_setup_form {
my $this = shift;
##
## If we have a form, this is the time to set its context
##
if ($this->{form_is_mine}) {
if ($this->{form_is_mine} ne $this->{TL_FORM}) {
die("Nested forms detected!");
}
else {
$this->{TL_FORM}->tl_setContext($this);
$this->{TL_WINDOW}->_incrementNumForms();
}
}
}
##
## this makes a ComponentContainer an implementable object--and a very
## useful one at that. YOu can just stick stuff in it and it'll print
## the various things with no added overhead. unfortunately,
## subclasses will need to reproduce any behavior here...
##
sub tl_print {
my ($this) = @_;
$this->{form_is_mine} and $this->{TL_FORM}->tl_print();
foreach (0..$#{ $this->{TL_COMPONENTS} }) {
$this->{TL_COMPONENTS}->[$_]->tl_print();
$this->{TL_BREAKS}->[$_] and $this->{TL_WINDOW}->i_print("><BR");
}
$this->{form_is_mine} and $this->{TL_FORM}->_print_end();
}
sub tl_destroy {
my ($this) = @_;
foreach(@{ $this->{TL_COMPONENTS} }) {
$_->tl_destroy();
}
undef $this->{TL_BREAKS};
undef $this->{TL_COMPONENTS};
$this->SUPER::tl_destroy();
}
sub getAllChildren {
my ($this, $what) = @_;
my @children;
if (scalar(@{ $this->{TL_COMPONENTS} })) {
foreach my $child (@{ $this->{TL_COMPONENTS} }) {
push @children, $child if (! $what or $child->isa($what));
push @children, $child->getAllChildren($what)
if $child->isa("HTML::TableLayout::ComponentContainer");
}
}
return @children;
}
# ---------------------------------------------------------------------
## clearly this is not what I meant... FIXME!
package HTML::TableLayout::ComponentCell;
@HTML::TableLayout::ComponentCell::ISA=qw(HTML::TableLayout::Cell);
# ---------------------------------------------------------------------
package HTML::TableLayout::ComponentTable;
@HTML::TableLayout::ComponentTable::ISA=qw(HTML::TableLayout::Table);
# ---------------------------------------------------------------------
package HTML::TableLayout::Component::Text;
use HTML::TableLayout::Symbols;
use Carp;
@HTML::TableLayout::Component::Text::ISA=qw(HTML::TableLayout::Component);
my %MARKUP = (bold => "B",
italic => "I",
big => "BIG",
small => "SMALL");
sub tl_init {
my $this = shift;
$this->{text} = shift;
$this->SUPER::tl_init(@_);
}
sub tl_getParameters {
my ($this) = @_;
confess("WAS DESTROYED") if $this->{WAS_DESTROYED};
confess("TL_PARAMS undef [$this]") unless $this->{TL_PARAMS};
confess("TL_WINDOW undef [$this]") unless $this->{TL_WINDOW};
my %params = ($this->{TL_WINDOW}->{PARAMETERS}->get($this),
%{ $this->{TL_PARAMS} });
foreach("italic","bold", "big", "small") {
if (exists $params{$_}) {
delete $params{$_};
push @{ $this->{markup} }, $MARKUP{$_};
}
}
return (%params);
}
sub tl_print {
my ($this) = @_;
my $w = $this->{TL_WINDOW};
my %p = $this->tl_getParameters();
$w->i_print();
my $m;
foreach $m (@{ $this->{markup} }) {
$w->f_print("><$m");
}
$w->f_print("><FONT".params(%p).">");
if ($this->{tl_do_not_pad}) {
$w->f_print($this->{"text"});
}
else {
$w->f_print(" " . $this->{"text"} . " ");
}
$w->f_print("</FONT");
foreach $m (reverse @{ $this->{markup} }) {
$w->f_print("></$m");
}
}
##
## Yuck. Padding of text is a messy issue after moving to the ><
## style tagging... the problem is that if we don't pad, the text is
## glued together unexpectedly. if i do pad, then links look bad.
## This function is here so a link can tell it's text components not
## to pad.
##
sub tl_do_not_pad { shift->{tl_do_not_pad} = 1 }
# ---------------------------------------------------------------------
package HTML::TableLayout::Component::Image;
use HTML::TableLayout::Symbols;
@HTML::TableLayout::Component::Image::ISA=qw(HTML::TableLayout::Component);
sub tl_init {
my ($this, $url, %params) = @_;
$this->SUPER::tl_init(%params);
$this->{url} = $url;
}
sub tl_print {
my ($this, %ops) = @_;
my $w = $this->{TL_WINDOW};
my $p = params($this->tl_getParameters()) || "";
$w->i_print(qq{><IMG SRC="$this->{url}" $p});
}
# ---------------------------------------------------------------------
package HTML::TableLayout::Component::Link;
use HTML::TableLayout::Symbols;
@HTML::TableLayout::Component::Link::ISA
=qw(HTML::TableLayout::ComponentContainer);
sub tl_init {
my $this = shift;
$this->{href} = shift;
$this->{anchor} = shift;
$this->SUPER::tl_init(@_);
if (ref $this->{anchor}) {
$this->{TL_COMPONENTS}->[0] = $this->{anchor};
}
else {
$this->{TL_COMPONENTS}->[0]
= HTML::TableLayout::Component::Text->new($this->{anchor});
}
if ($this->{TL_COMPONENTS}->[0]->isa("HTML::TableLayout::Component::Text")) {
##
## see comment for tl_do_not_pad() method of Text
##
$this->{TL_COMPONENTS}->[0]->tl_do_not_pad();
}
}
sub passCGI {
my ($this, $cgi, @pass) = @_;
if (! (ref $cgi eq "HASH")) { die("malformed passcgi") }
$this->{href} .= "?";
my @p = scalar(@pass) ? @pass : keys %$cgi;
my ($k, $v);
foreach (@p) {
if (/^([^=]+)=(.*)$/) {
($k, $v) = ($1, $2);
}
else {
($k, $v) = ($_, $cgi->{$_});
}
$this->{href} .= $k . "=" . escape_url($v) . "&";
}
return $this;
}
##
## stolen from cgi.pm
##
sub escape_url {
my $s = shift; $s eq undef and return undef;
$s=~s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg;
return $s
}
sub tl_print {
my ($this, %ops) = @_;
my $w = $this->{TL_WINDOW};
my $p = params($this->tl_getParameters()) || "";
$w->i_print(qq{><A HREF="$this->{href}" $p});
$this->{TL_COMPONENTS}->[0]->tl_print();
$w->f_print("></A");
}
# ---------------------------------------------------------------------
package HTML::TableLayout::Component::Preformat;
use HTML::TableLayout::Symbols;
@HTML::TableLayout::Component::Preformat::ISA=
qw(HTML::TableLayout::Component);
sub tl_init {
my $this = shift;
$this->{pre} = shift;
$this->SUPER::tl_init(@_);
}
sub tl_print {
my ($this) = @_;
my $w = $this->{TL_WINDOW};
$w->i_print("><PRE>");
$w->f_print($this->{"pre"}."");
$w->i_print("</PRE");
}
# ---------------------------------------------------------------------
package HTML::TableLayout::Component::Comment;
use HTML::TableLayout::Symbols;
@HTML::TableLayout::Component::Comment::ISA=
qw(HTML::TableLayout::Component);
sub tl_init {
my $this = shift;
$this->{comment} = shift;
$this->SUPER::tl_init(@_);
}
sub tl_print {
my ($this) = @_;
##
## This is a pretty ugly hack--note fake tag "<x>"
##
$this->{TL_WINDOW}->i_print("><!-- " . $this->{"comment"} . " --><x");
}
# ---------------------------------------------------------------------
package HTML::TableLayout::Component::HorizontalRule;
use HTML::TableLayout::Symbols;
@HTML::TableLayout::Component::HorizontalRule::ISA=
qw(HTML::TableLayout::Component);
sub tl_print {
my ($this) = @_;
$this->{TL_WINDOW}->i_print("><HR".params($this->tl_getParameters())."");
}
# ---------------------------------------------------------------------
package HTML::TableLayout::Component::Font;
use HTML::TableLayout::Symbols;
@HTML::TableLayout::Component::Font::ISA=
qw(HTML::TableLayout::ComponentContainer);
sub tl_print {
my $this = shift;
my %p = $this->tl_getParameters();
$this->{TL_WINDOW}->i_print("><FONT".params(%p)."");
foreach (@{ $this->{TL_COMPONENTS} }) {
$this->{TL_WINDOW}->_indentIncrement();
$_->tl_print();
$this->{TL_WINDOW}->_indentDecrement();
}
$this->{TL_WINDOW}->i_print("></FONT>");
}
# ---------------------------------------------------------------------
package HTML::TableLayout::Component::List;
use HTML::TableLayout::Symbols;
@HTML::TableLayout::Component::List::ISA=
qw(HTML::TableLayout::ComponentContainer);
sub tl_init {
my $this = shift;
$this->{numbered} = shift;
$this->{delimited} = shift;
$this->SUPER::tl_init(@_);
}
sub insert {
my ($this, $component, $br) = @_;
if (! ref $component) {
$component = HTML::TableLayout::Component::Text->new($component);
}
push @{ $this->{TL_BREAKS} }, $br;
$this->SUPER::insert($component);
}
sub tl_print {
my ($this) = @_;
my $w = $this->{TL_WINDOW};
my $list_denoter;
if ($this->{numbered}) {
$list_denoter = "OL";
}
else {
$list_denoter = "UL";
}
$w->i_print("><$list_denoter");
my $i;
foreach $i (0..$#{ $this->{TL_COMPONENTS} }) {
my $c = $this->{TL_COMPONENTS}->[$i];
if ($this->{delimited} and
! $c->isa("HTML::TableLayout::Component::List")) {
$w->f_print("><LI");
}
$w->_indentIncrement();
$c->tl_print();
$w->_indentDecrement();
## do this if the component is a list??
$this->{TL_BREAKS}->[$i] and $w->f_print("><BR");
}
$w->i_print("></$list_denoter");
}
1;