html_fragment <- function(...,
toc = TRUE,
number_sections = TRUE,
fig_width = 5.67,
fig_height = fig_width,
fig_align = "right",
fig_crop = TRUE,
theme = NULL,
highlight = NULL,
navlinks = TRUE,
navlinks_top = TRUE,
navlinks_level = 3) {
## enable BiocStyle macros
require(BiocStyle, quietly = TRUE)
knitr <- knitr_options(opts_knit = list(width = 80L),
# remove figure margins
opts_chunk = list(crop = isTRUE(fig_crop),
fig.align = fig_align),
knit_hooks = list(crop = hook_pdfcrop))
post_processor <- function(metadata, input, output, clean, verbose) {
lines <- readUTF8(output)
## add author affiliations
lines <- modifyLines(lines, from='<!-- AUTH AFFIL -->', insert=auth_affil_html(metadata))
## append "References" section header
if ( !is.null(metadata$bibliography) )
lines <- add_refs_header(lines)
## move all headers one level down (for proper formatting when embedded in the website)
lines <- process_headers(lines)
writeUTF8(lines, output)
output
}
config <- output_format(
knitr = knitr,
pandoc = NULL,
post_processor = post_processor,
base_format = bookdown::html_document2(
toc = toc,
number_sections = number_sections,
fig_width = fig_width,
fig_height = fig_height,
theme = theme,
highlight = highlight,
...
))
## swap template afterwards in order to retain original mathjax functionality
## and to include widget-enabling header scripts in document body
template <- system.file("resources", "fragment.html", package="BiocStyle")
idx <- which(config$pandoc$args=="--template") + 1L
config$pandoc$args[idx] <- template
## one needs to run the post processor processing headers before bookdown, but
## process captions only after bookdown
post = config$post_processor
config$post_processor <- function(metadata, input, output, clean, verbose) {
if (is.function(post)) output = post(metadata, input, output, clean, verbose)
lines <- readUTF8(output)
## replace footnotes with sidenotes
lines = process_footnotes(lines)
## set captions as sidenotes
lines = process_captions(lines)
## add navigation
if (isTRUE(navlinks))
lines <- add_navigation(lines, navlinks_top, navlinks_level)
writeUTF8(lines, output)
output
}
config
}
add_refs_header = function(lines) {
refs <- grep('^<div id="refs" class="references">$', lines)
if ( !isTRUE(grepl('<h1>.*</h1>', lines[refs-1L])) )
lines <- c(lines[1:refs],
'<h1>References</h1>',
lines[(refs+1L):length(lines)])
lines
}
process_headers = function(lines) {
template <- '<h%s>%s</h%s>'
pattern <- sprintf(template, '([1-5])', '(.*)', '\\1')
headers <- grep(pattern, lines, value=TRUE)
levels <- as.integer(gsub(pattern, '\\1', headers))
matches <- regexpr(pattern, lines)
f <- function(header, level) {
sub(pattern, sprintf(template, level, '\\2', level), header)
}
regmatches(lines, matches) <- mapply(f, headers, levels+1L, USE.NAMES = FALSE)
lines
}
add_navigation = function(lines, top, level) {
## match to all section div's
pattern <- sprintf('^<div id="(.*)" class="section level([1-%d]).*">$', level)
idx <- grep(pattern, lines)
sections <- lines[idx]
sections_length <- length(sections)
## extract section metadata
section_ids <- sub(pattern, '#\\1', sections)
section_levels <- as.integer(sub(pattern, '\\2', sections))
section_names <- sub('<h([1-6])>(?:<span class="header-section-number">[0-9.]*</span> )?(.*)</h\\1>', '\\2', lines[idx+1L])
seq_indices = seq_along(sections)
## add virtual top section at level 0
if (isTRUE(top)) {
section_ids <- c("#top", section_ids)
section_levels <- c(0L, section_levels)
section_names <- c("Top", section_names)
seq_indices <- seq_indices + 1L
}
## index of previous section on the same level
section_prev <- sapply(seq_indices, function(i) {
level = section_levels[i]
neighbor <- which(section_levels==level)
level_up <- which(section_levels==level-1L)
level_up <- max(level_up[level_up<i], 0L)
neighbor <- neighbor[neighbor<i & neighbor>level_up]
neighbor[length(neighbor)][1L]
})
## index of next section on the same level
section_next <- sapply(seq_indices, function(i) {
level = section_levels[i]
neighbor <- which(section_levels==level)
level_up <- which(section_levels==level-1L)
level_up <- min(level_up[level_up>i], sections_length+1L)
neighbor[neighbor>i & neighbor<level_up][1L]
})
## index of parent section
section_up <- sapply(seq_indices, function(i) {
level = section_levels[i]
level_up <- which(section_levels[1:i]==level-1L)
level_up[length(level_up)][1L]
})
## join navigation for sections and their immediate subsections
merged <- c(FALSE, idx[-length(idx)]+2L == idx[-1L])
merged_ids <- which(merged) + isTRUE(top)
section_ids[merged_ids] <- section_ids[merged_ids - 1L]
idx <- idx[!merged]
## move links to next subsections from fused sections up to their parents
section_down <- rep(NA_integer_, sections_length)
section_down[c(merged[-1L], FALSE)] <- section_next[merged]
## create links
create_link <- function(v=0L, id=section_ids[v], name=section_names[v], icon)
ifelse(is.na(v), "", sprintf('<span class="nav-icon">%s</span> <a href="%s">%s</a><br/>', icon, id, name))
links <- paste0(
create_link(id="#TOC", name="Table of Contents", icon="≣"),
create_link(section_next, icon="▸"),
create_link(section_prev, icon="◂"),
create_link(section_up, icon="▴"),
create_link(section_down, icon="▾")
)[!merged]
## preallocate the results vector and populate it with original lines
idx_length <- length(idx)
res <- vector(mode = "character", length = length(lines)+idx_length)
idx <- idx + seq_len(idx_length)
res[-idx] <- lines
## insert links
res[idx] <- sprintf('<p class="sidenote">%s</p>', links)
res
}
process_captions = function(lines) {
## convert table captions enlosed in <caption> tags into <p class="caption">
pattern <- '(^<caption>)(.*)(</caption>$)'
idx <- grep('^<table', lines)
idx <- idx[grepl(pattern, lines[idx+1L])]
captions <- lines[idx+1L]
lines[idx+1L] <- lines[idx]
lines[idx] <- gsub(pattern, '<p class="caption">\\2</p>', captions)
## append 'sidenote' CSS class to figure and table captions
lines = gsub('(?<=^<p class="caption)(?=">)', ' sidenote', lines, perl=TRUE)
lines
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.