support speaker notes and timing comments on the title slide
Pandoc puts content written before the first heading into an otherwise
empty phantom slide right after the title slide. When such a slide
consists of nothing but ::: notes blocks, timing comments, and
whitespace, it is now merged into the title slide: the title gets the
data-timing attribute and the notes, the phantom slide disappears, and
slide_timing() reports the title time as "(title)".
Change-Id: I984e924c467c4e15905b97c128c380887132f09d
diff --git a/R/revealjs_presentation.R b/R/revealjs_presentation.R
index f641284..75d6c23 100644
--- a/R/revealjs_presentation.R
+++ b/R/revealjs_presentation.R
@@ -31,7 +31,8 @@
#' Content of speaker notes
#' :::
#' ```
-#' to create notes only viewable in presentation mode.
+#' to create notes only viewable in presentation mode. Notes placed before
+#' the first heading are attached to the title slide.
#'
#' On mobile browsers (touch devices), a button next to the fullscreen
#' toggle overlays the current slide with its speaker notes (plus the slide
@@ -55,7 +56,8 @@
#'
#' This means Marc Kupietz will need 30 seconds for that slide. For talks
#' presented by a single speaker, the speaker code can be omitted:
-#' `<!-- 00:30 -->`.
+#' `<!-- 00:30 -->`. Comments placed before the first heading are applied to
+#' the title slide.
#'
#' Typically you start from a known total time budget and adjust the
#' individual slides to it. You can set this budget yourself with the
@@ -408,6 +410,7 @@
# timer of the notes plugin) and reporting per-speaker totals
post_processor <- function(metadata, input_file, output_file, clean, verbose) {
lines <- readLines(output_file, warn = FALSE, encoding = "UTF-8")
+ lines <- merge_title_slide_notes(lines)
result <- apply_timing_attributes(lines)
comments <- attr(result, "comments")
if (nrow(comments) > 0) {
diff --git a/R/slide_timing.R b/R/slide_timing.R
index 560716d..7fbc976 100644
--- a/R/slide_timing.R
+++ b/R/slide_timing.R
@@ -138,6 +138,52 @@
ifelse(is.na(speaker), "(unnamed)", speaker)
}
+# Timing comments and ::: notes blocks written before the first heading do
+# not land on the title slide: pandoc puts them into an otherwise empty
+# phantom slide right after it. Merge such a phantom slide -- consisting of
+# nothing but notes, timing comments, and whitespace -- into the title
+# slide, so that notes and timing can be given for the title as well.
+merge_title_slide_notes <- function(lines) {
+ title_open <- grep('<section class="title-frame"', lines)
+ if (length(title_open) == 0) {
+ return(lines)
+ }
+ title_open <- title_open[1]
+ title_close <- which(lines == "</section>")
+ title_close <- title_close[title_close > title_open][1]
+
+ phantom_open <- grep("^<section", lines)
+ phantom_open <- phantom_open[phantom_open > title_close][1]
+ if (is.na(phantom_open)) {
+ return(lines)
+ }
+ phantom_close <- which(lines == "</section>")
+ phantom_close <- phantom_close[phantom_close > phantom_open][1]
+
+ inner <- lines[(phantom_open + 1):(phantom_close - 1)]
+
+ # the phantom slide is only merged if it has no visible content beyond
+ # notes, timing comments, and whitespace
+ aside <- grep('<aside class="notes">', inner)
+ aside_end <- grep("</aside>", inner)
+ in_aside <- if (length(aside) > 0) {
+ unlist(Map(":", aside, aside_end[aside_end > aside][1]))
+ }
+ content <- seq_along(inner)
+ content <- content[!inner %in% ""]
+ content <- setdiff(content, c(grep("<!--", inner), in_aside))
+ if (length(content) > 0) {
+ return(lines)
+ }
+
+ moved <- inner[nzchar(trimws(inner))]
+ append(
+ lines[-(phantom_open:phantom_close)],
+ moved,
+ after = title_close - 1
+ )
+}
+
# Convert timing comments into data-timing attributes on the enclosing
# <section> elements of a rendered reveal.js HTML document. Returns the
# modified lines invisibly along with the extracted comments as attributes.