
# Loop.pl
# Movable Type plugin tags for looping through a list of values
# by Kevin Shay
# http://www.staggernation.com/mtplugins/
# last modified July 12, 2004

package MT::Plugin::Loop;
use strict;
use vars qw( $VERSION );
$VERSION = '1.1';

use MT;
use MT::Template::Context;

eval{ require MT::Plugin };
unless ($@) {
    my $plugin = {
        name => "Loop $VERSION",
        description => 'Loop through a list of values (either hardcoded or generated by other MT tags), repeating a section of template code for each value.',
        doc_link => 'http://www.staggernation.com/mtplugins/Loop'
    }; 
    MT->add_plugin(new MT::Plugin($plugin));
}

MT::Template::Context->add_container_tag('Loop' => sub{&_hdlr_loop;});
MT::Template::Context->add_tag('LoopValue' => sub{&_hdlr_loop_value;});
MT::Template::Context->add_tag('LoopN' => sub{&_hdlr_loop_n;});

sub _hdlr_loop {
# handler for MT container tag to loop through a given list of values
# args to tag:
#	values: delimited list of values to loop through
#	delimiter (optional): indicates what string has been used to delimit
#		the list of values (default is semicolon)
#	sort (optional): pass "alpha" or "num" to sort the values
	my ($ctx, $args, $cond) = @_;
		# store initial builder and tokens so we can repeat them
	my $builder = $ctx->stash('builder');
	my $tokens = $ctx->stash('tokens');
	defined(my $values = $args->{'values'})
		|| return $ctx->error('No values passed');
	my $delim = defined($args->{'delimiter'}) ? $args->{'delimiter'} : ',';
	my $text = '';
		# get the non-tokenized contents of this tag
	my $uncompiled = $ctx->stash('uncompiled');
		# if they're using [MTLoopValue/N] (i.e. passing the value or N as an
		# argument to a tag), we'll need to recompile on each pass, subbing
		# in the value
	my $recompile = ($uncompiled =~ /\[MTLoop/) ? 1 : 0;
		# and now, a brief foray into the Backslash Jungle.
		# within the values argument, you can use MT tags, but with
		# square brackets instead of angle brackets and single quotes
		# instead of double quotes; literal square brackets and single
		# quotes must be escaped with a backslash
		# convert non-escaped []'
	$values =~ s/(?<!\\)\[/</g;
	$values =~ s/(?<!\\)\]/>/g;
	$values =~ s/(?<!\\)'/"/g;
		# de-escape escaped []'
	$values =~ s/\\([\[\]'])/$1/g;
		# any MT tags?
	if ($values =~ /<MT/) {
		my $tok = $builder->compile($ctx, $values);
		$values = $builder->build($ctx, $tok, $cond);
		return $ctx->error($builder->errstr) unless defined($values);
	}
	my @vals = split(/$delim/, $values);
	if (defined($args->{'sort'})) {
		if ($args->{'sort'} eq 'alpha') {
			@vals = sort {$a cmp $b} @vals;
		} elsif ($args->{'sort'} eq 'num') {
			@vals = sort {$a <=> $b} @vals;
		}
	}
	my $n = 0;
	for (@vals) {
		$n++;
		if ($recompile) {
			my $copy = $uncompiled;
			$copy =~ s/\[MTLoopValue\]/$_/g;
			$copy =~ s/\[MTLoopN\]/$n/g;
			$tokens = $builder->compile($ctx, $copy);
		}
		$ctx->stash('loop_n', $n);
		$ctx->stash('loop_value', $_);
		defined(my $iter = $builder->build($ctx, $tokens))
			|| return $ctx->error($ctx->errstr);
		$text .= $iter;
	}
	return $text;
}

sub _hdlr_loop_value {
# handler for MT tag that prints the current value within the loop
	my ($ctx, $args) = @_;
	defined(my $val = $ctx->stash('loop_value'))
		|| return $ctx->error('Not called from within MTLoop container');
	return $val;
}

sub _hdlr_loop_n {
# handler for MT tag that prints the number of the loop iteration
	my ($ctx, $args) = @_;
	defined(my $n = $ctx->stash('loop_n'))
		|| return $ctx->error('Not called from within MTLoop container');
	return $n;
}

1;
