# HTML::Macro::Loop; Loop.pm
# Copyright (c) 2001,2002 Michael Sokolov and Interactive Factory. All rights
# reserved. This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.

package HTML::Macro::Loop;

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

require Exporter;
require AutoLoader;

@ISA = qw(Exporter AutoLoader);
# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
@EXPORT = qw(
	
);
$VERSION = '1.04';


# Preloaded methods go here.

sub new ($$$)
{
    my ($class, $page) = @_;
    my $self = {
        'vars' => [],
        'rows' => [],
        '@parent' => $page,
        };
    bless $self, $class;
    return $self;
}

sub declare ($@)
# use this to indicate which vars are expected in each iteration.
# Fills the vars array.
{
    my ($self, @vars) = @_;
    @ {$$self {'vars'}} = @vars;
}

sub push_array ($@)
# values must be pushed in the same order as they were declared, and all
# must be present
{
    my ($self, @vals) = @_;
    die "HTML::Macro::Loop::push_array: number of vals pushed(" . (@vals+0) . ") does not match number declared: " . (@ {$$self{'vars'}} + 0)
        if (@vals + 0 != @ {$$self{'vars'}});
    my $row = &new_row;
    my $i = 0;
    foreach my $var (@ {$$self{'vars'}})
    {
        $row->set ($var, $vals[$i++]);
    }
    push @ {$$self{'rows'}}, $row;
}

sub new_row
{
    my ($self) = @_;
    my $row = new HTML::Macro;
    $row->set ('@parent', $self);
    $row->{'@attr'} = $self->{'@parent'}->{'@attr'};
    $row->{'@incpath'} = $self->{'@parent'}->{'@incpath'};
    return $row;
}

sub pushall_arrays ($@)
# values must be pushed in the same order as they were declared, and all
# must be present.  Arg is an array filled with refs to arrays for each row
{
    my ($self, @rows) = @_;
    foreach my $row (@rows) {
        $self->push_array (@$row);
    }
}

sub push_hash ($$)
# values passed with var labels so they may come in any order and some may be 
# absent (in which case zero is subtituted).  However, any values passed whose
# vars were not declared are -silently- ignored unless there has been no 
# declaration, in which case the keys of the hash are accepted as an implicit 
# declaration.
{
    my ($self, $pvals) = @_;
    my @ordered_vals;
    my $row = &new_row;
    $self->declare (keys %$pvals) if (!@ {$$self{'vars'}}) ;
    my $i = 0;
    foreach my $var (@ {$$self{'vars'}})
    {
        $row->set ($var, defined($$pvals{$var}) ? $$pvals{$var} : '');
    }
    push @ {$$self{'rows'}}, $row;;
}

sub set ($@ )
# set more values in the most recent row, or add a row if none exists
{
    my $self = shift;
    if (! $$self{'rows'} )
    {
        $self->push_hash (\@_);
    } else {
        my $rows = $$self{'rows'};
        my $row = $$rows[$#$rows];
        $row->set (@_);
    }
}

sub set_hash ($$ )
# set more values in the most recent row, or add a row if none exists
{
    my $self = shift;
    if (! $$self{'rows'} )
    {
        $self->push_hash (@_);
    } else {
        my $rows = $$self{'rows'};
        my $row = $$rows[$#$rows];
        $row->set_hash (@_);
    }
}

sub doloop ($$ )
# perform repeated processing a-la HTML::Macro on the loop body $body,
# concatenate the results and return that.
{
    my ($self, $body) = @_;
    my $buf = '';
    foreach my $row (@ {$$self{'rows'}})
    {
        my $iteration;
        $row->{'@cwd'} = $self->{'@parent'}->{'@cwd'};
        $buf .= $row->process_buf ($body);
    }
    return $buf;
}

sub new_loop ()
{
    my ($self, $name, @loop_vars) = @_;

    my $rows = $$self{'rows'};
    die "HTML::Loop::new_loop: no rows in loop - call a push method" if !@$rows;
    my $new_loop = new HTML::Macro::Loop ($$rows [$#$rows]);

    if ($name) {
        $self->set ($name, $new_loop);
    }
    if (@loop_vars) {
        $new_loop->declare (@loop_vars);
    }
    return $new_loop;
}

sub is_empty ()
{
    my ($self) = @_;
    return ! ($self->{'rows'} && (@ {$self->{'rows'}} > 0));
}

sub keys ()
{
    my ($self) = @_;
    return () if $self->is_empty();
    my $rows = $$self{'rows'};
    return ($$rows [$#$rows])->keys();
}

# Autoload methods go after =cut, and are processed by the autosplit program.

1;
__END__
# Below is the stub of documentation for your module. You better edit it!

=head1 NAME

HTML::Macro::Loop - looping construct for repeated HTML blocks

=head1 SYNOPSIS

  use HTML::Macro;
  use HTML::Macro::Loop;
  $htm = HTML::Macro->new();
  $loop = $htm->new_loop('loop-body', 'id', 'name', 'phone');
  $loop->push_array (1, 'mike', '222-2389');
  $loop->push_hash ({ 'id' => 2, 'name' => 'lou', 'phone' => '111-2389'});
  $htm->print ('test.html');

=head1 DESCRIPTION

  HTML::Macro::Loop processes tags like 

<loop id="loop-tag"> loop body </loop>

    Each loop body is treated as a nested HTML::Macro within which variable
substitutions, conditions and nested loops are processed as described under
HTML::Macro. For complete documentation see HTML::Macro.
=head1 AUTHOR

Michael Sokolov, sokolov@ifactory.com

=head1 SEE ALSO HTML::Macro

perl(1).

=cut

