The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w

# Copyright 2001-2006 The Apache Software Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

=head1 NAME

spod5 - Displays spod5 (pod to s5) presentations

=head1 SYNOPSIS

  <Location /presentations>
  DocumentRoot /path/to/presentations
  Plugin spod5
  </Location>

=head1 DESCRIPTION

This plugin uses Pod::SAX to turn pod files on the fly into S5 presentations.

See L<http://meyerweb.com/eric/tools/s5/> for details on S5's HTML format.

Requires the s5 javascript and css files under F<ui/default> in the directory
of your POD files. These are shipped with AxKit2 in the F<demo/spod5> directory.

=head1 POD FORMAT

Every C<=head1> heading creates a new slide. Everything else is just the good
old POD format you know and love.

You can use C<=img PATH> to include an image.

Use C<=meta ITEM DATA> to define metadata. The following pieces of metadata can
be used:

=over 4

=item * title

=item * author

=item * company

=item * presdate

(Presentation Date)

=item * view

"outline" or "slideshow" (default = "slideshow")

=item * controls

"hidden" or "visible" (default = "hidden")

=item * title_top

(A header)

=item * title_bottom

(A footer)

=back

Use C<=over -1> to tell spod5 that the list should display incrementally. Use
C<=over -2> to tell spod5 that the list should display incrementally with the
first item automatically shown.

=cut

use Pod::SAX;
use XML::LibXML::SAX::Builder;
use XML::LibXML;
use XML::LibXSLT;
use AxKit2::Utils qw(bytelength);
use AxKit2::Transformer::XSLT;

use constant EZPOD => eval { require XML::Filter::EzPod };

my $xslt   = XML::LibXSLT->new();
my $parser = XML::LibXML->new();
my $pod2s5;
my $add_params;

sub hook_response {
    my ($self) = @_;
    
    my $client = $self->client;
    
    my $file = $client->headers_in->filename;
    $self->log(LOGDEBUG, "request for file $file");
    return DECLINED unless $file =~ /\.(pod|pm)$/;
    return DECLINED unless -f $file;
    
    open(my $fh, $file) || die "Failed to open($file): $!";
    
    my $builder = XML::LibXML::SAX::Builder->new();
    my $handler = $builder;
    if (EZPOD) {
        $self->log(LOGDEBUG, "Using EzPod");
        $handler = XML::Filter::EzPod->new( Handler => $builder );
    }
    my $p = Pod::SAX->new( Handler => $handler );
    $p->parse_file($fh);

    my $doc = $builder->result();
    
    my $base_uri = $client->headers_in->uri . $client->headers_in->path_info;
    
    $base_uri =~ s/[^\/]*$//; # strip filename part
    $base_uri = "http://" . $client->headers_in->header('Host') . "$base_uri";
    
    my %params = (
        base => $base_uri,
        );
    
    my $addp_style = $xslt->parse_stylesheet($parser->parse_string($add_params));
    my $interim = $addp_style->transform($doc, 
                               AxKit2::Transformer::XSLT::fixup_params(%params)
                               );
    
    # print "Interim doc: ", $interim->toString;

    my $stylesheet = $xslt->parse_stylesheet($parser->parse_string($pod2s5));
    my $output = $stylesheet->transform($interim);
    
    my $ct  = $stylesheet->media_type;
    my $enc = $stylesheet->output_encoding;
    my $out = $stylesheet->output_string($output);

    $client->headers_out->header('Content-Length', bytelength($out));
    $client->headers_out->header('Content-Type', "$ct; charset=$enc");
    $client->send_http_headers;
    $client->write($out);
    
    return DONE;
}

$add_params = <<'EOT';
<xsl:stylesheet version="1.0"  
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    >

<xsl:param name="base"/>
<xsl:param name="style">slides.css</xsl:param>
<xsl:param name="uidir">default</xsl:param>

<xsl:template match="/pod">
  <pod>
    <meta>
        <base><xsl:value-of select="$base"/></base>
        <ui-dir><xsl:value-of select="$uidir"/></ui-dir>
        <slides><xsl:value-of select="$style"/></slides>
        <xsl:apply-templates select='meta'/>
    </meta>
    <xsl:apply-templates/>
  </pod>
</xsl:template>

<xsl:template match="meta">
    <xsl:element name="{substring-before(./text(), ' ')}">
        <xsl:value-of select="substring-after(./text(), ' ')"/>
    </xsl:element>
</xsl:template>

<xsl:template match="*|@*">
    <xsl:copy>
      <xsl:apply-templates select="*|@*|node()"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>
EOT

$pod2s5 = <<'EOT';
<xsl:stylesheet version="1.0"  
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    >

<xsl:output method="html" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
                          doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
                          indent="yes"/>

<xsl:template match="/pod">
    <html>
    
    <head>
    <title><xsl:value-of select="meta/title"/></title>
    <!-- metadata -->
    <meta name="generator" content="spod5" />
    <meta name="version" content="S5 1.1" />
    <meta name="presdate" content="{meta/presdate}" />
    <meta name="author" content="{meta/author}" />
    <meta name="company" content="{meta/company}" />
    
    <!-- configuration parameters -->
    <meta name="defaultView" content="{meta/view or 'slideshow'}" />
    <meta name="controlVis" content="{meta/controls or 'hidden'}" />
    
    <base href="{meta/base}ui/{meta/ui-dir}/"/>
    
    <!-- style sheet links -->
    <link rel="stylesheet" href="{meta/slides}" type="text/css" media="projection" id="slideProj" />
    <link rel="stylesheet" href="outline.css" type="text/css" media="screen" id="outlineStyle" />
    <link rel="stylesheet" href="print.css" type="text/css" media="print" id="slidePrint" />
    <link rel="stylesheet" href="opera.css" type="text/css" media="projection" id="operaFix" />
    
    <!-- S5 JS -->
    <script src="slides.js" type="text/javascript"></script>
    
    <style type="text/css">
    <!--
    /* 
     * perltidy's styles
     */
    .c  { color: #228B22;                    } /* comment */
    .cm { color: #000000;                    } /* comma */
    .co { color: #000000;                    } /* colon */
    .h  { color: #CD5555; font-weight:bold;  } /* here-doc-target */
    .hh { color: #CD5555; font-style:italic; } /* here-doc-text */
    .i  { color: #00688B;                    } /* identifier */
    .j  { color: #000000; font-weight:bold;  } /* label */
    .k  { color: #8B4513; font-weight:bold;  } /* keyword */
    .m  { color: #FF0000; font-weight:bold;  } /* subroutine */
    .n  { color: #B452CD;                    } /* numeric */
    .p  { color: #000000;                    } /* paren */
    .pd { color: #228B22; font-style:italic; } /* pod-text */
    .pu { color: #000000;                    } /* punctuation */
    .q  { color: #CD5555;                    } /* quote */
    .s  { color: #000000;                    } /* structure */
    .sc { color: #000000;                    } /* semicolon */
    .v  { color: #B452CD;                    } /* v-string */
    .w  { color: #000000;                    } /* bareword */
    
    /* ====================================================================== *
     * Sample stylesheet for Syntax::Highlight::HTML                          *
     *                                                                        *
     * Copyright (C)2004 Sebastien Aperghis-Tramoni, All Rights Reserved.     *
     *                                                                        *
     * This file is free software; you can redistribute it and/or modify      *
     * it under the same terms as Perl itself.                                *
     * ====================================================================== */
    
    .h-decl { color: #336699; font-style: italic; }   /* doctype declaration  */
    .h-pi   { color: #336699;                     }   /* process instruction  */
    .h-com  { color: #338833; font-style: italic; }   /* comment              */
    .h-ab   { color: #000000; font-weight: bold;  }   /* angles as tag delim. */
    .h-tag  { color: #993399; font-weight: bold;  }   /* tag name             */
    .h-attr { color: #000000; font-weight: bold;  }   /* attribute name       */
    .h-attv { color: #333399;                     }   /* attribute value      */
    .h-ent  { color: #cc3333;                     }   /* entity               */
    
    .h-lno  { color: #cccccc; background: #eee;   }   /* line numbers         */
    -->
    </style>
    
    
    </head>
    <body>
    
    <div class="layout">
    <div id="controls"><!-- DO NOT EDIT --></div>
    <div id="currentSlide"><!-- DO NOT EDIT --></div>
    <div id="header"></div>
    <div id="footer">
    <h1><xsl:value-of select="meta/presdate"/></h1>
    <h2><xsl:value-of select="meta/title"/></h2>
    </div>
    
    </div>
    
    
    <div class="presentation">
    
    <div class="slide">
    <xsl:value-of select="meta/title_top"/>
    <h1><xsl:value-of select="meta/title"/></h1>
    <h3><xsl:value-of select="meta/author"/></h3>
    <h4><xsl:value-of select="meta/company"/></h4>
    <xsl:value-of select="meta/title_bottom"/>
    </div>
    
    <xsl:apply-templates select="*[starts-with(name(), 'head')]"/>
    
    </div>
    
    </body>
    </html>
</xsl:template>

<xsl:template match="pod">
 <xsl:apply-templates/>
</xsl:template>

<xsl:template match="para">
  <xsl:choose>
    <xsl:when test="./*[name() != 'orderedlist' and name() != 'itemizedlist']">
      <p>
        <xsl:apply-templates/>
      </p>
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="verbatim">
  <pre class="verbatim"><xsl:apply-templates/></pre>
</xsl:template>

<xsl:template match="link">
  <xsl:choose>
    <xsl:when test='string-length(@section) and not( string-length(@page) )'>
      <xsl:choose>
        <xsl:when test='starts-with(@section, "/")'>
          <a href="/view{@section}"><xsl:apply-templates/></a>
        </xsl:when>
        <xsl:otherwise>
          <a href="#{translate(@section,' ','-')}"><xsl:apply-templates/></a>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <a href="./{@page}#{translate(@section,' ','-')}"><xsl:apply-templates/></a>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="xlink">
  <a href="{@href}"><xsl:apply-templates/></a><img src="/img/out.png"/>
</xsl:template>

<xsl:template match="head1">
  <div class="slide"><h1><xsl:apply-templates/></h1>
  <xsl:apply-templates select="following-sibling::*[generate-id(preceding-sibling::head1[1]) = generate-id(current())]"/>
  </div>
</xsl:template>

<xsl:template match="head2">
  <h2><xsl:apply-templates/></h2>
</xsl:template>

<xsl:template match="head3">
  <h3><xsl:apply-templates/></h3>
</xsl:template>

<xsl:template match="head4">
  <h4><xsl:apply-templates/></h4>
</xsl:template>

<xsl:template match="itemizedlist">
  <ul>
    <xsl:choose>
        <xsl:when test="@indent_width = -1">
            <xsl:attribute name="class">incremental</xsl:attribute>
        </xsl:when>
        <xsl:when test="@indent_width = -2">
            <xsl:attribute name="class">incremental show-first</xsl:attribute>
        </xsl:when>
    </xsl:choose>
    <xsl:apply-templates/>
  </ul>
</xsl:template>

<xsl:template match="orderedlist">
  <ol>
    <xsl:choose>
        <xsl:when test="@indent_width = -1">
            <xsl:attribute name="class">incremental</xsl:attribute>
        </xsl:when>
        <xsl:when test="@indent_width = -2">
            <xsl:attribute name="class">incremental show-first</xsl:attribute>
        </xsl:when>
    </xsl:choose>
    <xsl:apply-templates/>
  </ol>
</xsl:template>

<xsl:template match="listitem">
  <li><xsl:apply-templates/></li>
</xsl:template>

<xsl:template match="itemtext">
  <span class="itemtext"><xsl:apply-templates/></span>
</xsl:template>

<xsl:template match="hr">
  <hr/>
</xsl:template>

<xsl:template match="C">
  <code><xsl:apply-templates/></code>
</xsl:template>

<xsl:template match="B">
  <b><xsl:apply-templates/></b>
</xsl:template>

<xsl:template match="I">
  <i><xsl:apply-templates/></i>
</xsl:template>

<xsl:template match="F">
  <code class="file"><xsl:apply-templates/></code>
</xsl:template>

<xsl:template match="indent">
  <blockquote><xsl:apply-templates/></blockquote>
</xsl:template>

<xsl:template match="markup[@type='image']">
    <img src="{.}"/>
</xsl:template>

<xsl:template match="img">
    <img src="{.}"/>
</xsl:template>

<xsl:template match="*|@*">
    <xsl:copy>
      <xsl:apply-templates select="*|@*|node()"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>
EOT