XQuery/Slideshow

Motivation.
Despite being a 20-year old program, albeit enhanced over the years, Microsoft PowerPoint is the ubiquitous presentation software. It provides a wide range of functionality, but most of us use it for simple text slides, perhaps with a bit of animation.
However Powerpoint does not cleanly separate the content of the presentation from its presentation (as slides, in printed form, as a index) and appearance (styles, colors), is an expensive proprietary product and over-weight for many tasks. Thus there is value in using simple XML tools to provide similar functionality.
Prior art.
There are a number of approaches to using XML technologies to provide light-weight, non-proprietorial presentation software. These typically rely on a web browser as the rendering engine (a design choice not open to Richard Gaskins in 1984). Core problems are the task of dividing the presentation into separate slides and supporting navigation in the slide sequence.
This project uses XQuery as the server-side language.
Presentation format.
Other approaches use a defined vocabulary but the choice here is to use XHTML with a little additional markup to define slide boundaries and slideshow properties. This provides a wide range of functionality needed such as formating, linking, images, embedded video.
&lt;ss:slideshow xmlns="http://www.w3.org/1999/xhtml" xmlns:ss="http://www.cems.uwe.ac.uk/xmlwiki/slideshow"&gt;
 &lt;ss:css&gt;
 &lt;ss:slide&gt;slide.css&lt;/ss:slide&gt;
 &lt;ss:print/&gt;
 &lt;/ss:css&gt;
 &lt;ss:header&gt;DSA 2008 - Lecture 1 - Chris Wallace&lt;/ss:header&gt;
 &lt;ss:footer/&gt; 
 &lt;ss:slide&gt;
 &lt;h1&gt;Teaching approach&lt;/h1&gt;
 &lt;ul&gt;
 &lt;li&gt;1 lecture a week 
 &lt;ul&gt;
 &lt;li&gt;Interaction using SMS whiteboard and Multi-choice cube&lt;/li&gt;
 &lt;/ul&gt;
 &lt;/li&gt;
 &lt;li&gt;1 2-hour workshop every 2 weeks - write down the weeks you have been allocated&lt;/li&gt;
 &lt;li&gt;2 hour Research time every 2 weeks (alternating with the workshops) - independent
 study with tutor support &lt;/li&gt;
 &lt;li&gt;Teaching resources in UWEOnline and in the &lt;a href="https://www.cems.uwe.ac.uk/studentwiki/index.php/UFIEKG-20-2/2008"&gt;studentWiki&lt;/a&gt;
 &lt;/li&gt;
 &lt;/ul&gt;
 &lt;/ss:slide&gt;
Here I have used two namespaces: the default is the XHTML used in the slide body, the ss namespace is used for the slideshow elements which define slideshow properties, master slide, and the slide boundaries.
This is a very minimal format which would be expanded in future.
The Script.
The XML document defining the slideshow content needs to be transformed into slides for projection and into a print format. In this implementation, both versions are generated from the same script.
Namespaces.
The two namespaces must be declared - an arbitrary prefix used for the default XHTML namespace.
declare namespace ss= "http://www.cems.uwe.ac.uk/xmlwiki/slideshow";
declare namespace h = "http://www.w3.org/1999/xhtml" ;
Parameters.
The slide parameters are the uri of the slideshow document (whether a database document or an external document), the slide number and the mode - slide or print. The parameters are passed in a semicolon-delimited query string rather than in the more usual key=value form because I was unable to get the &amp; separator to work in Javascript(??)
declare variable $params := tokenize(request:get-query-string(),";");
declare variable $uri := $params[1];
declare variable $n := xs:integer(($params[2],1)[1]);
declare variable $mode :=($params[3],"slide")[1];
Fetching the XML.
The slideshow is fetched - this is faster if stored in the database but may also be an external file.
declare variable $slideshow := doc($uri)/ss:slideshow;
declare variable $slides := $slideshow/ss:slide;
declare variable $count := count($slides);
The Slide Show.
Show a Slide.
A function generates the div holding a slide. The global variables define the common slideshow properties.
declare function local:show-slide($slide as element(ss:slide) ) as element(div) {
&lt;div class="slide"&gt;
 &lt;span class="header"&gt;{$slideshow/ss:master/ss:header/node()}&lt;/span&gt;
 &lt;span class="footer"&gt;{$n}/{$count} &amp;#160; {$slideshow/ss:footer/node()} &lt;/span&gt;
 &lt;/div&gt;
Contents Slide.
The &lt;h1&gt; element in each slide is used to generate a contents slide, numbered 0.
declare function local:show-contents() as element(div) {
&lt;div class="contents"&gt;
 &lt;span class="header"&gt;{$slideshow/ss:header/node()}&lt;/span&gt;
 &lt;h1&gt;Contents&lt;/h1&gt;
 &lt;ul&gt;
 {for $slide at $i in $slides
 return 
 &lt;li&gt;{$i} &amp;#160;&lt;a href="slide.xql?{$uri};{$i}"&gt;{string($slide/h:h1)}&lt;/a&gt; &lt;/li&gt;
 &lt;/ul&gt;
 &lt;span class="footer"&gt;0/{$count} &amp;#160; {$slideshow/ss:footer/node()} &lt;/span&gt;
&lt;/div&gt;
Navigation.
Navigation is handled by a JavaScript function which handles keypress events and is attached to the page body. This code is different for each slide so is generated for each slide. The keypress mapping is based partly on the codes generated by a common wireless presenter, the Labtec Notebook presenter which is designed for use with PowerPoint. Documentation on the device was hard to find, so the key mapping was analysed by capturing the keypresses observed by a simple javascript.
Other key mappings were added to allow the cursor keys to be used and to go to print mode.
Note that in generating this Javascript code, { } brackets need to be doubled.
declare function local:keypress-script() as element(script) {
let $prev := if ($n &gt; 0) then $n - 1 else 1
let $next := if ($n &lt; $count) then $n + 1 else $count
return
 &lt;script type="text/javascript"&gt; 
 function keypress(e) 
 &lt;/script&gt; 
Generate.
declare option exist:serialize "method=xhtml media-type=text/html";
if ($mode="slide")
then
&lt;html&gt;
&lt;head&gt;
 &lt;title&gt;{string($slideshow/ss:title)} - Slides&lt;/title&gt;
 &lt;link rel="stylesheet" type="text/css" href="{$slideshow/ss:css/ss:slide}"/&gt;
&lt;/head&gt;
&lt;body onkeydown="keypress(event)"&gt;
 { if ($n=0) 
 then local:show-contents() 
 else local:show-slide($slides[$n]) 
 &lt;/body&gt;
&lt;/html&gt;
else ...
Print format.
Other functions generate a printable version of the slide show. This comprises :
Contents Page.
declare function local:print-contents() as element(div) {
&lt;div class="contents"&gt;
 &lt;h2&gt;Contents&lt;/h2&gt;
 &lt;ul&gt;
 {for $slide at $i in $slides
 return 
 &lt;li&gt;{$i} . &lt;a href="slide.xql?{$uri};{$i}"&gt;{string($slide/h:h1)}&lt;/a&gt; &lt;/li&gt;
 &lt;/ul&gt;
&lt;/div&gt;
Slides.
declare function local:print-slides() as element(div)* {
 for $slide at $i in $slides
 return 
 $slide 
Links.
The URIs for links is not visible in the printed slides, so it is useful to add a final page with all the links which appear in the slides listed together.
declare function local:print-links() as element(div) {
 &lt;div class="links"&gt;
 &lt;h1&gt;Links&lt;/h1&gt;
 &lt;ul&gt;
 {for $slide at $i in $slides
 for $link in $slide//h:a
 order by upper-case($link)
 return 
 &lt;li&gt;{string($link)} :&lt;em&gt;{string($link/@href)}&lt;/em&gt; &lt;/li&gt;
 &lt;/ul&gt;
 &lt;/div&gt;
Generate Print View.
If the mode is "print" then generate the print format:
else
&lt;html&gt;
 &lt;head&gt;
 &lt;title&gt;{string($slideshow/ss:title)} - Print&lt;/title&gt;
 &lt;link rel="stylesheet" type="text/css" href="{$slideshow/ss:css/ss:print}"/&gt;
 &lt;/head&gt;
 &lt;body&gt;
 {local:print-slides()} 
 &lt;/body&gt;
&lt;/html&gt;