File: template.R

package info (click to toggle)
r-cran-htmltools 0.5.8.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 736 kB
  • sloc: ansic: 189; sh: 15; makefile: 2
file content (139 lines) | stat: -rw-r--r-- 5,177 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#' Process an HTML template
#'
#' Process an HTML template and return a tagList object. If the template is a
#' complete HTML document, then the returned object will also have class
#' `html_document`, and can be passed to the function
#' [renderDocument()] to get the final HTML text.
#'
#' @param filename Path to an HTML template file. Incompatible with
#'   `text_`.
#' @param ... Variable values to use when processing the template.
#' @param text_ A string to use as the template, instead of a file. Incompatible
#'   with `filename`.
#' @param document_ Is this template a complete HTML document (`TRUE`), or
#'   a fragment of HTML that is to be inserted into an HTML document
#'   (`FALSE`)? With `"auto"` (the default), auto-detect by searching
#'   for the string `"<HTML>"` within the template.
#'
#' @seealso [renderDocument()]
#' @export
#' @useDynLib htmltools, .registration = TRUE
htmlTemplate <- function(filename = NULL, ..., text_ = NULL, document_ = "auto") {
  if (!xor(is.null(filename), is.null(text_))) {
    stop("htmlTemplate requires either `filename` or `text_`.")
  }

  if (!is.null(filename)) {
    html <- readChar(filename, file.info(filename)$size, useBytes = TRUE)
    Encoding(html) <- "UTF-8"
  } else if(!is.null(text_)) {
    text_ <- paste8(text_, collapse = "\n")
    html <- enc2utf8(text_)
  }

  pieces <- .Call(template_dfa, html)
  Encoding(pieces) <- "UTF-8"

  # Create environment to evaluate code, as a child of the global env. This
  # environment gets the ... arguments assigned as variables.
  vars <- dots_list(...)
  if ("headContent" %in% names(vars)) {
    stop("Can't use reserved argument name 'headContent'.")
  }
  vars$headContent <- function() HTML("<!-- HEAD_CONTENT -->")
  env <- list2env(vars, parent = globalenv())

  # All the odd-numbered pieces are HTML; all the even-numbered pieces are code
  pieces <- mapply(
    pieces,
    rep_len(c(FALSE, TRUE), length.out = length(pieces)),
    FUN = function(piece, isCode) {
      if (isCode) {
        eval(parse(text = piece), env)
      } else {
        HTML(piece, .noWS = "outside")
      }
    },
    SIMPLIFY = FALSE
  )


  result <- tagList(pieces)

  if (document_ == "auto") {
    document_ = grepl("<HTML(\\s[^<]*)?>", html, ignore.case = TRUE)
  }
  if (document_) {
    # The html.document class indicates that it's a complete document, and not
    # just a set of tags.
    class(result) <- c("html_document", class(result))
  }

  result
}


#' Render an html_document object
#'
#' This function renders `html_document` objects, and returns a string with
#' the final HTML content. It calls the [renderTags()] function to
#' convert any shiny.tag objects to HTML. It also finds any any web dependencies
#' (created by [htmlDependency()]) that are attached to the tags, and
#' inserts those. To do the insertion, this function finds the string
#' `"<!-- HEAD_CONTENT -->"` in the document, and replaces it with the web
#' dependencies.
#'
#' @param x An object of class `html_document`, typically generated by the
#'   [htmlTemplate()] function.
#' @param deps Any extra web dependencies to add to the html document. This can
#'   be an object created by [htmlDependency()], or a list of such
#'   objects. These dependencies will be added first, before other dependencies.
#' @param processDep A function that takes a "raw" html_dependency object and
#'   does further processing on it. For example, when `renderDocument` is
#'   called from Shiny, the function [shiny::createWebDependency()] is
#'   used; it modifies the href and tells Shiny to serve a particular path on
#'   the filesystem.
#'
#' @return An [HTML()] string, with UTF-8 encoding.
#'
#' @export
renderDocument <- function(x, deps = NULL, processDep = identity) {
  if (!inherits(x, "html_document")) {
    stop("Object must be an object of class html_document")
  }
  if (inherits(deps, "html_dependency")) {
    deps <- list(deps)
  }

  result <- renderTags(x)

  # Figure out dependencies
  deps <- c(deps, result$dependencies)
  deps <- resolveDependencies(deps)
  deps <- lapply(deps, processDep)
  depStr <- paste(sapply(deps, function(dep) {
    sprintf("%s[%s]", dep$name, dep$version)
  }), collapse = ";")
  depHtml <- renderDependencies(deps, "href")

  # Put content in the <head> section
  head_content <- paste0(
    '  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>\n',
    sprintf('  <script type="application/shiny-singletons">%s</script>\n',
            paste(result$singletons, collapse = ',')
    ),
    sprintf('  <script type="application/html-dependencies">%s</script>\n',
            depStr
    ),
    depHtml,
    c(result$head, recursive = TRUE)
  )
  # Need to mark result as UTF-8. If body is ASCII, it will be marked with
  # encoding "unknown". If the head has UTF-8 characters and is marked as
  # "UTF-8", the output string here will have the correct UTF-8 byte sequences,
  # but will be marked as "unknown", which causes the wrong text to be
  # displayed. See https://github.com/rstudio/shiny/issues/1395
  res <- sub("<!-- HEAD_CONTENT -->", head_content, result$html, fixed = TRUE)
  Encoding(res) <- "UTF-8"
  res
}