#!/usr/bin/perl
#
# Name:
#	slide-pom2.pl.
#
# Purpose:
#	Convert a POD file into a set of slides and a ToC.
#
# V 2.1  5-Sep-2002
# -----------------
# o Stop printing one too many </div>s in sub save_toc
#
# V 2.0 28-May-2002
# -----------------
# o Rewrite HTML so as to use CSS entirely and not tables
# o The output assumes the build-in CSS is being used
#
# V 1.0 3-May-2002
# ----------------
# o Initial version based on fancy-pom2.pl
# o Add notes on running the program twice, using the first
#		run just to create the CSS file. See 'Running the program'
# o To insert an image into a slide, use <img src = 'slide-9.png'>
#		on a line by itself
#
# Note:
#	o tab = 4 spaces || die.
#	o See fancy-pom2.pl for details of the development of:
#		- fancy-pom2.pl
#		- local-pom2.pl
#		- slide-pom2.pl
#
# Running the program:
#	Run it the first time like this, to create a CSS file:
#	shell>perl slide-pom2.pl html css killer-app.pod > killer-app.html
#		Outputs ./css/killer-app.css inside the HTML's link tag
#		and outputs the file ./css/killer-app.css itself.
#	Thereafter, run it like this:
#	shell>perl slide-pom2.pl html -css killer-app.pod > killer-app.html
#		Outputs /css/slide.css inside the HTML's link tag
#		but does not output the file /css/slide.css.
#	Think of the '-' in '-css' as losing the output CSS file.
#	Then you can rename killer-app.css to slide.css, or
#	to any name you choose. After that, install it where your
#	web server can find it. If you do not wish to call it
#	slide.css, edit killer-app.html to use your chosen name.
#
# Author:
#	Ron Savage <ron@savage.net.au>
#	http://savage.net.au/index.html
#
# Licence:
#	Australian Copyright (c) 1999-2002 Ron Savage.
#
#	All Programs of mine are 'OSI Certified Open Source Software';
#	you can redistribute them and/or modify them under the terms of
#	The Artistic License, a copy of which is available at:
#	http://www.opensource.org/licenses/index.html

use strict;
use warnings;

use File::Basename;
use File::Path;
use Pod::POM;

my($VERSION) = '1.0';

# -----------------------------------------------------------------------------

package My::View;

use base qw(Pod::POM::View::HTML);

# -----------------------------------------------------------------------------

# Encapsulated class data.

{
	my(%_attr_data) =
	(	# Alphabetical order.
		_bg_color		=> '',
		_css_dir_name	=> '',
		_css_file_name	=> '',
		_slide_count	=> 0,
		_slide_name		=> '',
		_title			=> '',
	);

	sub _default_for
	{
		my($self, $attr_name) = @_;

		$_attr_data{$attr_name};
	}

	sub _standard_keys
	{
		sort keys %_attr_data;
	}
}

# -----------------------------------------------------------------------------

sub new
{
	my($caller, %arg)		= @_;
	my($caller_is_obj)		= ref($caller);
	my($class)				= $caller_is_obj || $caller;
	my($self)				= bless({}, $class);
	$$self{'_head_count'}	= 0;
	$$self{'_toc'}			= [];

	for my $attr_name ($self -> _standard_keys() )
	{
		my($arg_name) = $attr_name =~ /^_(.*)/;

		if (exists($arg{$arg_name}) )
		{
			$$self{$attr_name} = $arg{$arg_name};
		}
		elsif ($caller_is_obj)
		{
			$$self{$attr_name} = $$caller{$attr_name};
		}
		else
		{
			$$self{$attr_name} = $self -> _default_for($attr_name);
		}
	}

	return $self;

}	# End of new.

# -----------------------------------------------------------------------------

sub end_html
{
	my($self) = @_;

	print OUT	"</div>\n",
				"</body></html>\n";
	close(OUT);

}	# End of end_html.

# -----------------------------------------------------------------------------

sub save_toc
{
	my($self) = @_;

	$self -> end_html();

	my($file_name) = $$self{'_slide_name'} . 'toc.html';

	open(OUT, "> $file_name") || die("Can't open(> $file_name): $!");
	print OUT	"<html xmlns='http://www.w3.org/1999/xhtml' lang='en-US'>\n",
				"<head>\n",
					"<title>$$self{'_title'}</title>\n",
					"<link rel='stylesheet' type='text/css' href='$$self{'_css_dir_name'}/$$self{'_css_file_name'}' />\n",
				"</head>\n",
				"<body bgcolor='#ffffff'>\n",
				"<div id = 'header'>$$self{'_title'}</div>\n",
				"<div id = 'content'>\n",
				"<h2>Table of Contents</h2>";

	my($page);

	print OUT map{$page = $_ + 1; "<a href = './$$self{'_slide_name'}$page.html'>$page: $$self{'_toc'}[$_]</a><br>"} 0 .. $#{$$self{'_toc'} };

	$self -> end_html();

}	# End of save_toc.

# -----------------------------------------------------------------------------

sub start_html
{
	my($self) = @_;

	$$self{'_head_count'}++;

	my($file_name)					= "$$self{'_slide_name'}$$self{'_head_count'}.html";
	my($previous_page, $next_page)	= ($$self{'_head_count'} - 1, $$self{'_head_count'} + 1);
	my($previous_link, $next_link)	= ("<a href = './$file_name'>First</a>", "<a href = './$file_name'>Last</a>");
	$previous_link					= "<a href = './$$self{'_slide_name'}$previous_page.html'>Previous</a>"	if ($previous_page >= 1);
	$next_link						= "<a href = './$$self{'_slide_name'}$next_page.html'>Next</a>"			if ($next_page <= $$self{'_slide_count'});

	$self -> end_html() if ($$self{'_head_count'} > 1);

	open(OUT, "> $file_name") || die("Can't open(> $file_name): $!");
	print OUT	"<html xmlns='http://www.w3.org/1999/xhtml' lang='en-US'>\n",
				"<head>\n",
					"<title>$$self{'_title'}</title>\n",
					"<link rel='stylesheet' type='text/css' href='$$self{'_css_dir_name'}/$$self{'_css_file_name'}' />\n",
				"</head>\n",
				"<body bgcolor='#ffffff'>\n",
				"<div id = 'header'>$$self{'_title'}</div>\n",
				"<div id = 'menu'>\n",
				"<a href = './$$self{'_slide_name'}1.html'>First</a>\n",
				"$previous_link\n",
				"<a href = './$$self{'_slide_name'}toc.html'>ToC</a>\n",
				"$next_link\n",
				"<a href = './$$self{'_slide_name'}$$self{'_slide_count'}.html'>Last</a>\n",
				"</div>\n";

}	# End of start_html.

# -----------------------------------------------------------------------------

sub view_pod
{
	my($self, $pod) = @_;

	return $pod -> content -> present($self);

}	# End of view_pod.

# -----------------------------------------------------------------------------

sub view_head1
{
	my($self, $item) = @_;

	$self -> start_html();

	push(@{$$self{'_toc'} }, $item -> title -> present($self));

	my($content)	= $item -> content -> present($self);
	$content		=~ s/&amp;copy;/&copy;/g;	# Fix copyright.
	$content		=~ s/\((c|C)\)\s+(\d{4})/&copy; $2/g;
	$content		=~ s|<p>&lt;|<p><|;			# Fix <img src='x.png'>.
	$content		=~ s|&gt;</p>|></p>|;		# Fix <img src='x.png'>.

	print OUT	"<div id = 'content'>\n",
				"<h2>",
				$item -> title -> present($self),
				"</h2>\n",
				$content,
				"Slide $$self{'_head_count'} of $$self{'_slide_count'}";

	"$$self{'_head_count'}/$$self{'_slide_count'} " . $item -> title -> present($self) . "\n";

}	# End of view_head1.

# -----------------------------------------------------------------------------

package main;

#------------------------------------------------------------------------

my($bg_color, $absolute_css_dir, $css_dir_name, $css_file_name, $PROGRAM, $program);

#------------------------------------------------------------------------

sub usage
{
	my($msg) = shift || '';

	if ($program =~ /^$PROGRAM(\.pl)?$/)
	{
		$program .= ' html';
	}

	return <<EOF;
${msg}
usage: $program [css|-css] pod-file
EOF

}	# End of usage.

#------------------------------------------------------------------------

$absolute_css_dir	= '/css';
$css_dir_name		= './css';
$css_file_name		= 'slide.css';
$bg_color			= '#80c0ff';
$PROGRAM			= 'slide-pom2';
$program			= basename($0);
my($views)			= {html => 'HTML'};

die usage() if grep(/^--?h(elp)?$/, @ARGV);

my($format);

# See if we're running via a file linked from $program to $PROGRAM.
# If so, extract link suffix and use it as the name of the output format.
# The logic of this will be clearer to Unix users than to Windows users,
# since the former are much more likely to be familiar with the ln command.
# Otherwise, get the name of the format from the 1st command line parameter.

if ( ($program =~ /^$PROGRAM(.+)$/) && ($1 ne '.pl') )
{
	$format = $1;
}
else
{
	$format = shift || die usage('No output format specified');
}

# See if we're expected to process the 'css' command line parameter.
# Then get the name of the file to process from the command line.
# Then validate the output format.

my($output_css) = ($ARGV[0] =~ /^css$/i) ? shift : 0;

if ($ARGV[0] =~ /^-\s?css$/i)
{
	shift;
	$css_dir_name	= $absolute_css_dir;
	$output_css		= 1;
}

my($file)	= shift || die usage('No filename specified');
$format		= lc $format;
my($view)	= $$views{$format} || die usage("Invalid format '$format'. Try one of: " . join(', ', keys %$views) );
$view		= "Pod::POM::View::$view";

Pod::POM -> default_view($view) || die "$Pod::POM::ERROR\n";

# Parse the file, and clean up the file name for use in headings
# and in naming the CSS file to create, if requested.

my($parser)			= Pod::POM -> new(warn => 1)	|| die "$Pod::POM::ERROR\n";
my($pom)			= $parser -> parse_file($file)	|| die $parser->error(), "\n";
$file				=~ s|\\|/|g;
$file				=~ s|.*/||;
$file				= $1 if ($file =~ /^(.+)\..+$/);
my($slide_name)		= "$file-slide-";
$slide_name			=~ tr/ /_/;
$css_file_name		= "$file.css" if ($css_dir_name ne $absolute_css_dir);
my($title)			= ucfirst lc $file;
$title				=~ tr/-/ /;
my($slide_count)	= 0;

for my $head1 ($pom -> head1() )
{
	$slide_count++;
}

my($my_view) = My::View -> new
(
	bg_color		=> $bg_color,
	css_dir_name	=> $css_dir_name,
	css_file_name	=> $css_file_name,
	slide_count		=> $slide_count,
	slide_name		=> $slide_name,
	title			=> $title,
);

# If a CSS file was requested, fabricate it.

if ($output_css && ($css_dir_name ne $absolute_css_dir) )
{
	mkpath([$css_dir_name], 0);
	open(OUT, "> $css_dir_name/$css_file_name") || die("Can't open(> $css_dir_name/$css_file_name): $!");
	print OUT while (<DATA>);
	close(OUT);
}

print $pom -> present($my_view);

$my_view -> save_toc();

__END__
<style type = 'text/css'>
<!--

body {
	margin:0px;
	padding:0px;
	font-family:verdana, arial, helvetica, sans-serif;
	color:#333;
	background-color:white;
	}
h1 {
	margin:0px 0px 15px 0px;
	padding:0px;
	font-size:28px;
	line-height:28px;
	font-weight:900;
	color:maroon;
	}
h2 {
	margin:0px 0px 15px 0px;
	padding:0px;
	font-size:20px;
	line-height:20px;
	font-weight:900;
	color:maroon;
	background-color:white;
	}
p {
	font:16px verdana, arial, helvetica, sans-serif;
	margin:0px 0px 16px 0px;
	padding:0px;
	}
#content>p {margin:0px;}
#content>p+p {text-indent:30px;}

a {
	color:#09c;
	font-size:16px;
	text-decoration:none;
	font-weight:600;
	font-family:verdana, arial, helvetica, sans-serif;
	}
a:link {color:#09c;}
a:visited {color:#07a;}
a:hover {
	background-color:#eee;
	text-decoration:underline;
	}

#header {
	margin:0px 0px 10px 0px;
	padding:12px 0px 0px 10px; /* Tip: think of a clock: 12, 3, 6, 9 */
	/* For IE5/Win's benefit height = [correct height] + [top padding] + [top and bottom border widths] */
	height:50px; /* 36px + 12px + 2px = 50px */
	border-style:solid;
	border-color:black;
	border-width:1px 0px; /* top and bottom borders: 1px; left and right borders: 0px */
	line-height:36px;
	color:maroon;
	font-size:36;
	font-weight:bold;
	text-align:center;

/* Here is the ugly brilliant hack that protects IE5/Win from its own stupidity.
Thanks to Tantek Celik for the hack and to Eric Costello for publicizing it.
IE5/Win incorrectly parses the "\"}"" value, prematurely closing the style
declaration. The incorrect IE5/Win value is above, while the correct value is
below. See http://glish.com/css/hacks.asp for details. */
	voice-family:"\"}\"";
	voice-family:inherit;
	height:36px; /* the correct height */
	}
/* I've heard this called the "be nice to Opera 5" rule. Basically, it feeds correct
length values to user agents that exhibit the parsing error exploited above yet get
the CSS box model right and understand the CSS2 parent-child selector. ALWAYS include
a "be nice to Opera 5" rule every time you use the Tantek Celik hack (above). */
body>#header {height:36px;}

#content {
	margin:0px 140px 10px 10px;
	padding:5px;
	border:5px solid #666666;
	background-color:#80c0ff;
	}

#menu {
	font:16px verdana, arial, helvetica, sans-serif;
	position:absolute;
	top:10%;
	right:20px;
	width:100px;
	padding:10px;
	background-color:#eee;
	border:2px solid #666666;
	line-height:17px;
/* Again, the ugly brilliant hack. */
	voice-family:"\"}\"";
	voice-family:inherit;
	width:100px;
	}
/* Again, "be nice to Opera 5". */
body>#menu {width:100px;}

-->
</style>