From 61f8dd4da87bb4b8d536d81ea9430dad39cd975e Mon Sep 17 00:00:00 2001
From: Jonas Smedegaard <dr@jones.dk>
Date: Thu, 24 May 2012 10:31:18 +0200
Subject: [PATCH] Add support for Slideous output.

---
 INSTALL                         |   1 +
 README                          |  49 +++--
 pandoc.cabal                    |   6 +-
 slideous/slideous.css           |  95 ++++++++++
 slideous/slideous.js            | 321 ++++++++++++++++++++++++++++++++
 src/Text/Pandoc.hs              |   1 +
 src/Text/Pandoc/Shared.hs       |   3 +-
 src/Text/Pandoc/Slides.hs       |   2 +-
 src/Text/Pandoc/Writers/HTML.hs |   1 +
 src/pandoc.hs                   |   7 +-
 10 files changed, 463 insertions(+), 23 deletions(-)
 create mode 100644 slideous/slideous.css
 create mode 100644 slideous/slideous.js

diff --git a/INSTALL b/INSTALL
index 94627de05..8c4a07e2b 100644
--- a/INSTALL
+++ b/INSTALL
@@ -119,6 +119,7 @@ you please, and copy the following data files to the same place:
     data/
     s5/
     slidy/
+    slideous/
     dzslides/
     pcre-license.txt
     pcre3.dll
diff --git a/README b/README
index fda10c988..470ca18aa 100644
--- a/README
+++ b/README
@@ -18,8 +18,8 @@ another, and a command-line tool that uses this library. It can read
 slide shows), [ConTeXt], [RTF], [DocBook XML], [OpenDocument XML],
 [ODT], [Word docx], [GNU Texinfo], [MediaWiki markup], [EPUB],
 [Textile], [groff man] pages, [Emacs Org-Mode], [AsciiDoc], and [Slidy],
-[DZSlides], or [S5] HTML slide shows. It can also produce [PDF] output
-on systems where LaTeX is installed.
+[Slideous], [DZSlides], or [S5] HTML slide shows. It can also produce
+[PDF] output on systems where LaTeX is installed.
 
 Pandoc's enhanced version of markdown includes syntax for footnotes,
 tables, flexible ordered lists, definition lists, delimited code blocks,
@@ -153,6 +153,7 @@ General options
     `docbook` (DocBook XML), `opendocument` (OpenDocument XML), `odt`
     (OpenOffice text document), `docx` (Word docx), `epub` (EPUB book),
     `asciidoc` (AsciiDoc), `slidy` (Slidy HTML and javascript slide show),
+    `slideous` (Slideous HTML and javascript slide show),
     `dzslides` (HTML5 + javascript slide show), `s5` (S5 HTML and javascript
     slide show), or `rtf` (rich text format). Note that `odt` and `epub`
     output will not be directed to *stdout*; an output filename must
@@ -178,8 +179,8 @@ General options
         C:\Documents And Settings\USERNAME\Application Data\pandoc
 
     in Windows. A `reference.odt`, `reference.docx`, `default.csl`,
-    `epub.css`, `templates`, `slidy`, or `s5` directory placed in this
-    directory will override pandoc's normal defaults.
+    `epub.css`, `templates`, `slidy`, `slideous`, or `s5` directory
+    placed in this directory will override pandoc's normal defaults.
 
 `-v`, `--version`
 :   Print version.
@@ -199,7 +200,8 @@ Reader options
 `-R`, `--parse-raw`
 :   Parse untranslatable HTML codes and LaTeX environments as raw HTML
     or LaTeX, instead of ignoring them.  Affects only HTML and LaTeX
-    input. Raw HTML can be printed in markdown, reStructuredText, HTML, Slidy,
+    input. Raw HTML can be printed in markdown, reStructuredText, HTML,
+    Slidy, Slideous,
     DZSlides, and S5 output; raw LaTeX can be printed in markdown,
     reStructuredText, LaTeX, and ConTeXt output. The default is for the
     readers to omit untranslatable HTML codes and LaTeX environments.
@@ -280,7 +282,7 @@ General writer options
 :   Include an automatically generated table of contents (or, in
     the case of `latex`, `context`, and `rst`, an instruction to create
     one) in the output document. This option has no effect on `man`,
-    `docbook`, `slidy`, or `s5` output.
+    `docbook`, `slidy`, `slideous`, or `s5` output.
 
 `--no-highlight`
 :   Disables syntax highlighting for code blocks and inlines, even when
@@ -323,7 +325,8 @@ Options affecting specific writers
     in the sense that it needs no external files and no net access to be
     displayed properly by a browser. This option works only with HTML output
     formats, including `html`, `html5`, `html+lhs`, `html5+lhs`, `s5`,
-    `slidy`, and `dzslides`. Scripts, images, and stylesheets at absolute URLs
+    `slidy`, `slideous`,
+    and `dzslides`. Scripts, images, and stylesheets at absolute URLs
     will be downloaded; those at relative URLs will be sought first relative
     to the working directory, then relative to the user data directory (see
     `--data-dir`), and finally relative to pandoc's default data directory.
@@ -378,7 +381,7 @@ Options affecting specific writers
 
 `--slide-level`=*NUMBER*
 :   Specifies that headers with the specified level create
-    slides (for `beamer`, `s5`, `slidy`, `dzslides`).  Headers
+    slides (for `beamer`, `s5`, `slidy`, `slideous`, `dzslides`).  Headers
     above this level in the hierarchy are used to divide the
     slide show into sections; headers below this level create
     subheads within a slide.  The default is to set the slide level
@@ -683,6 +686,8 @@ depending on the output format, but include:
 `slidy-url`
 :   base URL for Slidy documents (defaults to
     `http://www.w3.org/Talks/Tools/Slidy2`)
+`slideous-url`
+:   base URL for Slideous documents (defaults to `default`)
 `s5-url`
 :   base URL for S5 documents (defaults to `ui/default`)
 `fontsize`
@@ -1698,7 +1703,7 @@ Docbook
 Docx
   ~ It will be rendered using OMML math markup.
 
-HTML, Slidy, DZSlides, S5, EPUB
+HTML, Slidy, Slideous, DZSlides, S5, EPUB
   ~ The way math is rendered in HTML will depend on the
     command-line options selected:
 
@@ -1749,7 +1754,8 @@ Markdown allows you to insert raw HTML (or DocBook) anywhere in a document
 (except verbatim contexts, where `<`, `>`, and `&` are interpreted
 literally).
 
-The raw HTML is passed through unchanged in HTML, S5, Slidy, DZSlides, EPUB,
+The raw HTML is passed through unchanged in HTML, S5, Slidy, Slideous,
+DZSlides, EPUB,
 Markdown, and Textile output, and suppressed in other formats.
 
 *Pandoc extension*.
@@ -2070,9 +2076,9 @@ Producing slide shows with Pandoc
 =================================
 
 You can use Pandoc to produce an HTML + javascript slide presentation
-that can be viewed via a web browser.  There are three ways to do this,
-using [S5], [DZSlides], or [Slidy].  You can also produce a PDF slide
-show using LaTeX [beamer].
+that can be viewed via a web browser.  There are four ways to do this,
+using [S5], [DZSlides], [Slidy], or [Slideous].  You can also produce a
+PDF slide show using LaTeX [beamer].
 
 Here's the markdown source for a simple slide show, `habits.txt`:
 
@@ -2118,6 +2124,10 @@ for S5,
 
 for Slidy,
 
+    pandoc -t slideous -s habits.txt -o habits.html
+
+for Slideous,
+
     pandoc -t dzslides -s habits.txt -o habits.html
 
 for DZSlides, or
@@ -2164,11 +2174,14 @@ you can just use level 1 headers for all each slide. (In that case, level 1
 will be the slide level.) But you can also structure the slide show into
 sections, as in the example above.
 
-For Slidy and S5, the file produced by pandoc with the `-s/--standalone`
+For Slidy, Slideous and S5, the file produced by pandoc with the
+`-s/--standalone`
 option embeds a link to javascripts and CSS files, which are assumed to
-be available at the relative path `s5/default` (for S5) or at the Slidy
+be available at the relative path `s5/default` (for S5) or `slideous`
+(for Slideous), or at the Slidy
 website at `w3.org` (for Slidy). (These paths can be changed by setting
-the `slidy-url` or `s5-url` variables; see `--variable`, above.) For DZSlides,
+the `slidy-url`, `slideous-url` or `s5-url` variables; see `--variable`,
+above.) For DZSlides,
 the (relatively short) javascript and css are included in the file by default.
 
 Incremental lists
@@ -2190,7 +2203,8 @@ Styling the slides
 ------------------
 
 You can change the style of HTML slides by putting customized CSS files
-in `$DATADIR/s5/default` (for S5) or `$DATADIR/slidy` (for Slidy),
+in `$DATADIR/s5/default` (for S5), `$DATADIR/slidy` (for Slidy),
+or `$DATADIR/slideous` (for Slideous),
 where `$DATADIR` is the user data directory (see `--data-dir`, above).
 The originals may be found in pandoc's system data directory (generally
 `$CABALDIR/pandoc-VERSION/s5/default`). Pandoc will look there for any
@@ -2267,6 +2281,7 @@ Latter, Ralf Stephan, Eric Seidel, B. Scott Michel.
 [reStructuredText]: http://docutils.sourceforge.net/docs/ref/rst/introduction.html
 [S5]: http://meyerweb.com/eric/tools/s5/
 [Slidy]: http://www.w3.org/Talks/Tools/Slidy/
+[Slideous]: http://goessner.net/articles/slideous/
 [HTML]:  http://www.w3.org/TR/html40/
 [HTML 5]:  http://www.w3.org/TR/html5/
 [XHTML]:  http://www.w3.org/TR/xhtml1/
diff --git a/pandoc.cabal b/pandoc.cabal
index dc38e8787..b7d1093c8 100644
--- a/pandoc.cabal
+++ b/pandoc.cabal
@@ -20,7 +20,7 @@ Description:     Pandoc is a Haskell library for converting from one markup
                  markdown, reStructuredText, HTML, LaTeX, ConTeXt, Docbook,
                  OpenDocument, ODT, Word docx, RTF, MediaWiki, Textile,
                  groff man pages, plain text, Emacs Org-Mode, AsciiDoc, EPUB,
-                 and S5 and Slidy HTML slide shows.
+                 and S5, Slidy and Slideous HTML slide shows.
                  .
                  Pandoc extends standard markdown syntax with footnotes,
                  embedded LaTeX, definition lists, tables, and other
@@ -45,6 +45,7 @@ Data-Files:
                  templates/default.rst, templates/default.plain,
                  templates/default.mediawiki, templates/default.rtf,
                  templates/default.s5, templates/default.slidy,
+                 templates/default.slideous,
                  templates/default.dzslides, templates/default.asciidoc,
                  templates/default.textile, templates/default.org,
                  templates/epub-titlepage.html, templates/epub-page.html,
@@ -78,6 +79,9 @@ Data-Files:
                  slidy/graphics/nofold-dim.gif,
                  slidy/graphics/unfold-dim.gif,
                  slidy/graphics/fold-dim.gif,
+                 -- data for slideous writer
+                 slideous/slideous.css,
+                 slideous/slideous.js,
                  -- data for dzslides writer
                  dzslides/template.html,
                  -- data for citeproc
diff --git a/slideous/slideous.css b/slideous/slideous.css
new file mode 100644
index 000000000..bfe6e608b
--- /dev/null
+++ b/slideous/slideous.css
@@ -0,0 +1,95 @@
+/*	This work is licensed under Creative Commons GNU LGPL License.
+
+	License: http://creativecommons.org/licenses/LGPL/2.1/
+	Version: 1.0
+
+	Author:  Stefan Goessner/2005
+	Web:     http://goessner.net/ 
+*/
+@media screen, projection {
+body {
+  background-color: #e3eee7;
+  padding: 0;
+  margin: 0;
+  color: #132; 
+  border-color: #678; 
+  font-size: 125%;
+}
+#statusbar {
+  display: none;
+  position: absolute; z-index: 10;
+  top: auto; bottom: 0; left: 0; right: 0;
+  height: 2em;
+  background-color: #f0fff8;
+  color: #132;
+  font-size: 75%;
+  padding: 0.5em 0.5em 0 2px;
+  border-top: solid 1px #000;
+}
+#statusbar button, #tocbox {
+  cursor: pointer; 
+  color: #031;
+  background-color: #e0eee7;
+  margin: 1px;
+  padding: 0 0.5em;
+  border: inset 1px black;
+}
+#statusbar button:hover, #tocbox:hover {
+  color: #031;
+  background-color: #c0ccc6;
+  border: outset 1px black;
+}
+#tocbox {
+  width: 15em;
+}
+#eos {
+  visibility: hidden;
+  color: #021;
+  background-color: #fffafa;
+  border: inset 1px black;
+  font-size: 120%;
+}
+div.slide {
+   display: block;
+   margin: 0 0 2em 0;
+   padding: 0 150px;
+}
+
+div.slide h1 {
+  background: #a0aaa4;
+  color: #f0fff8;
+  padding: 0 0.5em 0 0.5em;
+  margin: 0 -150px;
+  font-size: 120%;
+  border-bottom: solid 1px black;
+}
+
+div.slide h1:before { content: "# "; }
+div.handout { display: block; }
+   
+body>#statusbar {   /* ie6 hack for fixing the statusbar - in quirks mode */
+  position: fixed;  /* thanks to Anne van Kesteren and Arthur Steiner */
+}                   /* see http://limpid.nl/lab/css/fixed/footer */
+* html body {
+  overflow: hidden;
+}
+* html div.slide {
+  height: 100%;
+  padding-bottom: 2em;
+  overflow: auto;
+}                   /* end ie6-hack */
+
+} /* @media screen, projection */
+
+@media print {
+body {
+  color: black;
+  font-family: sans-serif;
+  font-size: 11pt;
+}
+
+#statusbar { display: none; }
+div.slide { page-break-after: always; }
+div.handout { display: block; }
+
+} /* @media print */
diff --git a/slideous/slideous.js b/slideous/slideous.js
new file mode 100644
index 000000000..3e7a63d4a
--- /dev/null
+++ b/slideous/slideous.js
@@ -0,0 +1,321 @@
+/*	This work is licensed under Creative Commons GNU LGPL License.
+
+	License: http://creativecommons.org/licenses/LGPL/2.1/
+
+	Author:  Stefan Goessner/2005-2006
+	Web:     http://goessner.net/ 
+*/
+var Slideous = {
+   version: 1.0,
+   // == user customisable ===
+   clickables: { a: true, button: true, img: true, input: true, object: true, textarea: true, select: true, option: true },
+   incrementables: { blockquote: { filter: "self, parent" }, 
+                     dd: { filter: "self, parent" },
+                     dt: { filter: "self, parent" },
+                     h2: { filter: "self, parent" },
+                     h3: { filter: "self, parent" },
+                     h4: { filter: "self, parent" },
+                     h5: { filter: "self, parent" },
+                     h6: { filter: "self, parent" },
+                     li: { filter: "self, parent" },
+                     p: { filter: "self" }, 
+                     pre: { filter: "self" }, 
+                     img: { filter: "self, parent" }, 
+                     object: { filter: "self, parent" },
+                     table: { filter: "self, parent" },
+                     td: { filter: "self, parent" },
+                     th: { filter: "self, parent" },
+                     tr: { filter: "parent, grandparent" }
+                   },
+   autoincrementables: { ol: true, ul: true, dl: true },
+   autoincrement: false,
+   statusbar: true,
+   navbuttons: { incfontbutton:   function(){Slideous.changefontsize(+Slideous.fontdelta);},
+                 decfontbutton:   function(){Slideous.changefontsize(-Slideous.fontdelta);},
+                 contentbutton:   function(){Slideous.gotoslide(Slideous.tocidx(), true, true);},
+                 homebutton:      function(){Slideous.gotoslide(1, true, true);},
+                 prevslidebutton: function(){Slideous.previous(false);},
+                 previtembutton:  function(){Slideous.previous(true);},
+                 nextitembutton:  function(){Slideous.next(true);},
+                 nextslidebutton: function(){Slideous.next(false);},
+                 endbutton:       function(){Slideous.gotoslide(Slideous.count,true,true);} },
+   fontsize: 125,  // in percent, corresponding to body.font-size in css file
+   fontdelta: 5,   // increase/decrease fontsize by this value
+   mousesensitive: true,
+   tocidx: 0,
+   tocitems: { toc: "<li><a href=\"#s{\$slideidx}\">{\$slidetitle}</a></li>",
+               tocbox: "<option value=\"#s{\$slideidx}\" title=\"{\$slidetitle}\">{\$slidetitle}</option>" },
+   keydown: function(evt) {
+      evt = evt || window.event;
+      var key = evt.keyCode || evt.which;
+      if (key && !evt.ctrlKey && !evt.altKey) {
+         switch (key) {
+            case 33: // page up  ... previous slide
+               Slideous.previous(false); evt.cancel = !Slideous.showall; break;
+            case 37: // left arrow ... previous item
+               Slideous.previous(true); evt.cancel = !Slideous.showall; break;
+            case 32: // space bar
+            case 39: // right arrow
+               Slideous.next(true); evt.cancel = !Slideous.showall; break;
+            case 13: // carriage return  ... next slide
+            case 34: // page down
+               Slideous.next(false); evt.cancel = !Slideous.showall; break;
+            case 35: // end  ... last slide (not recognised by opera)
+               Slideous.gotoslide(Slideous.count, true, true); evt.cancel = !Slideous.showall; break;
+            case 36: // home ... first slide (not recognised by opera)
+               Slideous.gotoslide(1, true, true); evt.cancel = !Slideous.showall; break;
+            case 65: // A ... show All
+            case 80: // P ... Print mode
+               Slideous.toggleshowall(!Slideous.showall); evt.cancel = true; break;
+            case 67: // C ... goto contents
+               Slideous.gotoslide(Slideous.tocidx, true, true); evt.cancel = true; break;
+            case 77: // M ... toggle mouse sensitivity
+               Slideous.mousenavigation(Slideous.mousesensitive = !Slideous.mousesensitive); evt.cancel = true; break;
+            case 83: // S ... toggle statusbar
+               Slideous.togglestatusbar(); evt.cancel = true; break;
+            case 61:  // + ... increase fontsize
+            case 107:
+               Slideous.changefontsize(+Slideous.fontdelta); evt.cancel = true; break;
+            case 109:  // - ... decrease fontsize
+               Slideous.changefontsize(-Slideous.fontdelta); evt.cancel = true; break;
+            default: break;
+         }
+         if (evt.cancel) evt.returnValue = false;
+      }
+      return !evt.cancel;
+   },
+
+   // == program logic ===
+   count: 0,                       // # of slides ..
+   curidx: 0,                      // current slide index ..
+   mousedownpos: null,             // last mouse down position ..
+   contentselected: false,         // indicates content selection ..
+   showall: true,
+   init: function() {
+      Slideous.curidx = 1;
+      Slideous.importproperties();
+      Slideous.registerslides();
+      document.body.innerHTML = Slideous.injectproperties(document.body.innerHTML);
+      Slideous.buildtocs();
+      Slideous.registeranchors();
+      Slideous.toggleshowall(false);
+      Slideous.updatestatus();
+      document.body.style.fontSize = Slideous.fontsize+"%";
+      document.getElementById("s1").style.display = "block";
+      document.onkeydown = Slideous.keydown;
+      Slideous.mousenavigation(Slideous.mousesensitive);
+      Slideous.registerbuttons();
+      if (window.location.hash)
+         Slideous.gotoslide(window.location.hash.substr(2), true, true);
+   },
+   registerslides: function() {
+      var div = document.getElementsByTagName("div");
+      Slideous.count = 0;
+      for (var i in div)
+         if (Slideous.hasclass(div[i], "slide"))
+            div[i].setAttribute("id", "s"+(++Slideous.count));
+   },
+   registeranchors: function() {
+      var a = document.getElementsByTagName("a"),
+          loc = (window.location.hostname+window.location.pathname).replace(/\\/g, "/");
+      for (var i in a) {
+         if (a[i].href && a[i].href.indexOf(loc) >= 0 && a[i].href.lastIndexOf("#") >= 0) {
+            a[i].href = "javascript:Slideous.gotoslide(" + a[i].href.substr(a[i].href.lastIndexOf("#")+2)+",true,true)";
+         }
+      }
+   },
+   registerbuttons: function() {
+      var button;
+      for (var b in Slideous.navbuttons)
+         if (button = document.getElementById(b))
+            button.onclick = Slideous.navbuttons[b];
+   },
+   importproperties: function() {  // from html meta section ..
+      var meta = document.getElementsByTagName("meta"), elem;
+      for (var i in meta)
+         if (meta[i].attributes && meta[i].attributes["name"] && meta[i].attributes["name"].value in Slideous)
+            switch (typeof(Slideous[meta[i].attributes["name"].value])) {
+               case "number": Slideous[meta[i].attributes["name"].value] = parseInt(meta[i].attributes["content"].value); break;
+               case "boolean": Slideous[meta[i].attributes["name"].value] = meta[i].attributes["content"].value == "true" ? true : false; break;
+               default: Slideous[meta[i].attributes["name"].value] = meta[i].attributes["content"].value; break;
+            }
+   },
+   injectproperties: function(str) {
+      var meta = document.getElementsByTagName("meta"), elem;
+      for (var i in meta) {
+         if (meta[i].attributes && meta[i].attributes["name"])
+            str = str.replace(new RegExp("{\\$"+meta[i].attributes["name"].value+"}","g"), meta[i].attributes["content"].value);
+      }
+      return str = str.replace(/{\$generator}/g, "Slideous")
+                      .replace(/{\$version}/g, Slideous.version)
+                      .replace(/{\$title}/g, document.title)
+                      .replace(/{\$slidecount}/g, Slideous.count);
+   },
+   buildtocs: function() {
+      var toc = document.getElementById("toc"), list = "",
+          tocbox = document.getElementById("tocbox");
+      if (toc) {
+         for (var i=0; i<Slideous.count; i++)
+            list += Slideous.tocitems.toc.replace(/{\$slideidx}/g, i+1).replace(/{\$slidetitle}/, document.getElementById("s"+(i+1)).getElementsByTagName("h1")[0].innerHTML);
+         toc.innerHTML = list;
+         while (toc && !Slideous.hasclass(toc, "slide")) toc = toc.parentNode;
+         if (toc) Slideous.tocidx = toc.getAttribute("id").substr(1);
+      }
+      if (tocbox) {
+         tocbox.innerHTML = "";
+         for (var i=0; i<Slideous.count; i++)
+            tocbox.options[tocbox.length] = new Option((i+1)+". "+document.getElementById("s"+(i+1)).getElementsByTagName("h1")[0].innerHTML, "#s"+(i+1));
+         tocbox.onchange = function() { Slideous.gotoslide(this.selectedIndex+1, true, true); };
+      }
+   },
+   next: function(deep) {
+      if (!Slideous.showall) {
+         var slide = document.getElementById("s"+Slideous.curidx),
+             item = Slideous.firstitem(slide, Slideous.isitemhidden);
+         if (deep) {  // next item
+            if (item)
+               Slideous.displayitem(item, true);
+            else
+               Slideous.gotoslide(Slideous.curidx+1, false, false);
+         }
+         else if (item)  // complete slide ..
+            while (item = Slideous.firstitem(slide, Slideous.isitemhidden))
+               Slideous.displayitem(item, true);
+         else           // next slide
+            Slideous.gotoslide(Slideous.curidx+1, true, false);
+         Slideous.updatestatus();
+      }
+   },
+   previous: function(deep) {
+      if (!Slideous.showall) {
+         var slide = document.getElementById("s"+Slideous.curidx);
+         if (deep) {
+            var item = Slideous.lastitem(slide, Slideous.isitemvisible);
+            if (item)
+               Slideous.displayitem(item, false);
+            else
+               Slideous.gotoslide(Slideous.curidx-1, true, false);
+         }
+         else
+            Slideous.gotoslide(Slideous.curidx-1, true, false);
+         Slideous.updatestatus();
+      }
+   },
+   gotoslide: function(i, showitems, updatestatus) {
+      if (!Slideous.showall && i > 0 && i <= Slideous.count && i != Slideous.curidx) {
+         document.getElementById("s"+Slideous.curidx).style.display = "none";
+         var slide = document.getElementById("s"+(Slideous.curidx=i)), item;
+         while (item = Slideous.firstitem(slide, showitems ? Slideous.isitemhidden : Slideous.isitemvisible))
+            Slideous.displayitem(item, showitems);
+         slide.style.display = "block";
+         if (updatestatus)
+            Slideous.updatestatus();
+      }
+   },
+   firstitem: function(root, filter) {
+      var found = filter(root);
+      for (var node=root.firstChild; node!=null && !found; node = node.nextSibling)
+         found = Slideous.firstitem(node, filter);
+      return found;
+   },
+   lastitem: function(root, filter) {
+      var found = null;
+      for (var node=root.lastChild; node!=null && !found; node = node.previousSibling)
+         found = Slideous.lastitem(node, filter);
+      return found || filter(root);
+   },
+   isitem: function(node, visible) {
+      var nodename;
+      return node && node.nodeType == 1   // elements only ..
+          && (nodename=node.nodeName.toLowerCase()) in Slideous.incrementables
+          && (   Slideous.incrementables[nodename].filter.match("\\bself\\b") && (Slideous.hasclass(node, "incremental") || (Slideous.autoincrement && nodename in Slideous.autoincrementables))
+              || Slideous.incrementables[nodename].filter.match("\\bparent\\b") && (Slideous.hasclass(node.parentNode, "incremental") || (Slideous.autoincrement && node.parentNode.nodeName.toLowerCase() in Slideous.autoincrementables))
+              || Slideous.incrementables[nodename].filter.match("\\bgrandparent\\b") && (Slideous.hasclass(node.parentNode.parentNode, "incremental") || (Slideous.autoincrement && node.parentNode.parentNode.nodeName.toLowerCase() in Slideous.autoincrementables))
+             )
+          && (visible ? (node.style.visibility != "hidden")
+                      : (node.style.visibility == "hidden"))
+          ? node : null;
+   },
+   isitemvisible: function(node) { return Slideous.isitem(node, true); },
+   isitemhidden: function(node) { return Slideous.isitem(node, false); },
+   displayitem: function(item, show) {
+      if (item) item.style.visibility = (show ? "visible" : "hidden");
+   },
+   updatestatus: function() {
+      if (Slideous.statusbar) {
+         var eos = document.getElementById("eos"), 
+             idx = document.getElementById("slideidx"),
+             tocbox = document.getElementById("tocbox");
+         if (eos) 
+            eos.style.visibility = Slideous.firstitem(document.getElementById("s"+Slideous.curidx), Slideous.isitemhidden) != null
+                                 ? "visible" : "hidden";
+         if (idx) 
+            idx.innerHTML = Slideous.curidx;
+         if (tocbox)
+            tocbox.selectedIndex = Slideous.curidx-1;
+      }
+   },
+   changefontsize: function(delta) {
+      document.body.style.fontSize = (Slideous.fontsize+=delta)+"%";
+   },
+   togglestatusbar: function() {
+      document.getElementById("statusbar").style.display = (Slideous.statusbar = !Slideous.statusbar) ? "block" : "none";
+   },
+   toggleshowall: function(showall) {
+      var slide, item;
+      for (var i=0; i<Slideous.count; i++) {
+         slide = document.getElementById("s"+(i+1));
+         slide.style.display = showall ? "block" : "none";
+         while (item = Slideous.firstitem(slide, showall ? Slideous.isitemhidden : Slideous.isitemvisible)) 
+            Slideous.displayitem(item, showall);
+         var divs = slide.getElementsByTagName("div");
+         for (var j in divs)
+            if (Slideous.hasclass(divs[j], "handout"))
+               divs[j].style.display = showall ? "block" : "none";
+      }
+      if (!showall)
+         document.getElementById("s"+Slideous.curidx).style.display = "block";
+      if (Slideous.statusbar) 
+         document.getElementById("statusbar").style.display = showall ? "none" : "block";
+      Slideous.showall = showall;
+   },
+   hasclass: function(elem, classname) {
+      var classattr = null;
+      return (classattr=(elem.attributes && elem.attributes["class"])) 
+          && classattr.nodeValue.match("\\b"+classname+"\\b");
+   },
+   selectedcontent: function() {
+      return window.getSelection ? window.getSelection().toString() 
+                                 : document.getSelection ? document.getSelection() 
+                                                         : document.selection ? document.selection.createRange().text
+                                                                              : "";
+   },
+   mousenavigation: function(on) {
+      if (on) {
+         document.onmousedown = Slideous.mousedown;
+         document.onmouseup = Slideous.mouseup;
+      }
+      else
+         document.onmousedown = document.onmouseup = null;
+   },
+   mousepos: function(e) {
+      return e.pageX ? {x: e.pageX, y: e.pageY} 
+                     : {x: e.x+document.body.scrollLeft, y: e.y+document.body.scrollTop};
+   },
+   mousedown: function(evt) {
+      evt = evt||window.event;
+      Slideous.mousedownpos = Slideous.mousepos(evt);
+      Slideous.contentselected = !!Slideous.selectedcontent() || ((evt.target || evt.srcElement).nodeName.toLowerCase() in Slideous.clickables);
+      return true;
+   },
+   mouseup: function(evt) {
+      evt = evt||window.event;
+      var pos = Slideous.mousepos(evt);
+      if (pos.x == Slideous.mousedownpos.x && pos.y == Slideous.mousedownpos.y && !Slideous.contentselected) {
+         Slideous.next(true);
+         return evt.returnValue = !(evt.cancel = true);
+      }
+      return false;
+   }
+};
+window.onload = Slideous.init;
diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs
index ce54a61c0..432a5c2ba 100644
--- a/src/Text/Pandoc.hs
+++ b/src/Text/Pandoc.hs
@@ -187,6 +187,7 @@ writers = [("native"       , writeNative)
                                                 writerHtml5 = True })
           ,("s5"           , writeHtmlString)
           ,("slidy"        , writeHtmlString)
+          ,("slideous"     , writeHtmlString)
           ,("dzslides"     , writeHtmlString)
           ,("docbook"      , writeDocbook)
           ,("opendocument" , writeOpenDocument)
diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs
index 1fb07ca05..f14a57c1f 100644
--- a/src/Text/Pandoc/Shared.hs
+++ b/src/Text/Pandoc/Shared.hs
@@ -482,6 +482,7 @@ data ObfuscationMethod = NoObfuscation
 -- | Varieties of HTML slide shows.
 data HTMLSlideVariant = S5Slides
                       | SlidySlides
+                      | SlideousSlides
                       | DZSlides
                       | NoSlides
                       deriving (Show, Read, Eq)
@@ -494,7 +495,7 @@ data WriterOptions = WriterOptions
   , writerEPUBMetadata     :: String -- ^ Metadata to include in EPUB
   , writerTabStop          :: Int    -- ^ Tabstop for conversion btw spaces and tabs
   , writerTableOfContents  :: Bool   -- ^ Include table of contents
-  , writerSlideVariant     :: HTMLSlideVariant -- ^ Are we writing S5 or Slidy?
+  , writerSlideVariant     :: HTMLSlideVariant -- ^ Are we writing S5, Slidy or Slideous?
   , writerIncremental      :: Bool   -- ^ True if lists should be incremental
   , writerXeTeX            :: Bool   -- ^ Create latex suitable for use by xetex
   , writerHTMLMathMethod   :: HTMLMathMethod  -- ^ How to print math in HTML
diff --git a/src/Text/Pandoc/Slides.hs b/src/Text/Pandoc/Slides.hs
index 1df556d38..c74e99279 100644
--- a/src/Text/Pandoc/Slides.hs
+++ b/src/Text/Pandoc/Slides.hs
@@ -26,7 +26,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    Portability : portable
 
 Utility functions for splitting documents into slides for slide
-show formats (dzslides, s5, slidy, beamer).
+show formats (dzslides, s5, slidy, slideous, beamer).
 -}
 module Text.Pandoc.Slides ( getSlideLevel, prepSlides ) where
 import Text.Pandoc.Definition
diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs
index 59666da08..93ad6eb6e 100644
--- a/src/Text/Pandoc/Writers/HTML.hs
+++ b/src/Text/Pandoc/Writers/HTML.hs
@@ -199,6 +199,7 @@ inTemplate opts tit auths authsMeta date toc body' newvars =
                     , ("date", date')
                     , ("idprefix", writerIdentifierPrefix opts)
                     , ("slidy-url", "http://www.w3.org/Talks/Tools/Slidy2")
+                    , ("slideous-url", "slideous")
                     , ("s5-url", "s5/default") ] ++
                     [ ("html5","true") | writerHtml5 opts ] ++
                     (case toc of
diff --git a/src/pandoc.hs b/src/pandoc.hs
index 11ea88ec2..34d136aa8 100644
--- a/src/pandoc.hs
+++ b/src/pandoc.hs
@@ -101,7 +101,7 @@ data Opt = Opt
     , optOutputFile        :: String  -- ^ Name of output file
     , optNumberSections    :: Bool    -- ^ Number sections in LaTeX
     , optSectionDivs       :: Bool    -- ^ Put sections in div tags in HTML
-    , optIncremental       :: Bool    -- ^ Use incremental lists in Slidy/S5
+    , optIncremental       :: Bool    -- ^ Use incremental lists in Slidy/Slideous/S5
     , optSelfContained     :: Bool    -- ^ Make HTML accessible offline
     , optSmart             :: Bool    -- ^ Use smart typography
     , optOldDashes         :: Bool    -- ^ Parse dashes like pandoc <=1.8.2.1
@@ -454,7 +454,7 @@ options =
     , Option "i" ["incremental"]
                  (NoArg
                   (\opt -> return opt { optIncremental = True }))
-                 "" -- "Make list items display incrementally in Slidy/S5"
+                 "" -- "Make list items display incrementally in Slidy/Slideous/S5"
 
     , Option "" ["slide-level"]
                  (ReqArg
@@ -887,6 +887,7 @@ main = do
   let slideVariant = case writerName' of
                            "s5"       -> S5Slides
                            "slidy"    -> SlidySlides
+                           "slideous" -> SlideousSlides
                            "dzslides" -> DZSlides
                            _          -> NoSlides
 
@@ -1052,7 +1053,7 @@ main = do
           where result       = w writerOptions doc2 ++ ['\n' | not standalone']
                 htmlFormat = writerName' `elem`
                                ["html","html+lhs","html5","html5+lhs",
-                               "s5","slidy","dzslides"]
+                               "s5","slidy","slideous","dzslides"]
                 selfcontain = if selfContained && htmlFormat
                                  then makeSelfContained datadir
                                  else return