#' Convert to an HTML document
#'
#' Format for converting from R Markdown to an HTML document.
#'
#' See the [online documentation](https://bookdown.org/yihui/rmarkdown/html-document.html) for additional details on using the `html_document`
#' format.
#'
#' R Markdown documents can have optional metadata that is used to generate a
#' document header that includes the title, author, and date. For more details
#' see the documentation on R Markdown \link[=rmd_metadata]{metadata}.
#'
#' R Markdown documents also support citations. You can find more information on
#' the markdown syntax for citations in the
#' [Bibliographies and Citations](https://pandoc.org/MANUAL.html#citations) article in the online documentation.
#'
#' @inheritParams output_format
#' @param toc `TRUE` to include a table of contents in the output
#' @param toc_depth Depth of headers to include in table of contents
#' @param toc_float `TRUE` to float the table of contents to the left of the
#'  main document content. Rather than `TRUE` you may also pass a list of
#'  options that control the behavior of the floating table of contents. See the
#'  *Floating Table of Contents* section below for details.
#' @param number_sections `TRUE` to number section headings
#' @param anchor_sections `TRUE` to show section anchors when mouse hovers
#'  for all headers. A list can also be passed with `style` and/or
#'  `depth` to customize the behavior. See
#'  \link[rmarkdown:html_document]{Anchor Sections Customization section}.
#' @param fig_width Default width (in inches) for figures
#' @param fig_height Default height (in inches) for figures
#' @param fig_retina Scaling to perform for retina displays (defaults to 2, which
#'  currently works for all widely used retina displays). Set to `NULL` to
#'  prevent retina scaling. Note that this will always be `NULL` when
#'  `keep_md` is specified (this is because `fig_retina` relies on
#'  outputting HTML directly into the markdown document).
#' @param fig_caption `TRUE` to render figures with captions
#' @param dev Graphics device to use for figure output (defaults to png)
#' @param code_folding Enable document readers to toggle the display of R code
#'  chunks. Specify `"none"` to display all code chunks. Specify
#'  `"hide"` or `"show"` to hide or show all R code chunks by
#'  default, and let readers toggle the states on browsers. See the
#'  *Code folding*
#' @param code_download Embed the Rmd source code within the document and provide
#'  a link that can be used by readers to download the code.
#' @param self_contained Produce a standalone HTML file with no external
#'  dependencies, using data: URIs to incorporate the contents of linked
#'  scripts, stylesheets, images, and videos. Note that even for self contained
#'  documents MathJax is still loaded externally (this is necessary because of
#'  its size).
#' @param theme One of the following:
#'  * A [bslib::bs_theme()] object (or a list of [bslib::bs_theme()] argument values)
#'    * Use this option for custom themes using Bootstrap 4 or 3.
#'    * In this case, any `.scss`/`.sass` files provided to the `css`
#'      parameter may utilize the `theme`'s underlying Sass utilities
#'      (e.g., variables, mixins, etc).
#'  * `NULL` for no theme (i.e., no [html_dependency_bootstrap()]).
#'  * A character string specifying a [Bootswatch 3](https://bootswatch.com/3/)
#'    theme name (for backwards-compatibility).
#' @param highlight Syntax highlight engine and style. See the
#'  *Highlighting* section below for details.
#'
#'  "default" (and "textmate") will use highlightjs as syntax highlighting
#'  engine instead of Pandoc.
#'
#'  Any other value will be passed as Pandoc's highlighting style. Pandoc's
#'  built-in styles include "tango", "pygments", "kate", "monochrome",
#'  "espresso", "zenburn", "haddock" and "breezedark".
#'
#'  Two custom styles are also included, "arrow", an accessible color scheme,
#'  and "rstudio", which mimics the default IDE theme. Alternatively, supply a
#'  path to a \samp{.theme} to use
#'  [a custom Pandoc
#'  style](https://pandoc.org/MANUAL.html#syntax-highlighting). Note that custom theme requires Pandoc 2.0+.
#'
#'  Pass `NULL` to prevent syntax highlighting.
#'
#' @param highlight_downlit `TRUE` to use the \pkg{downlit} package as
#'  syntax highlight engine to highlight inline code and R code chunks
#'  (including providing hyperlinks to function documentation). The package
#'  needs to be installed to use this feature.
#'
#'  Only Pandoc color schemes are supported with this engine. With
#'  `highlight = "default"`, it will use the accessible theme called
#'  "arrow". To learn more about \pkg{downlit} highlighting engine, see
#'  <https://downlit.r-lib.org/>.
#' @param mathjax Include mathjax. The "default" option uses an https URL from a
#'  MathJax CDN. The "local" option uses a local version of MathJax (which is
#'  copied into the output directory). You can pass an alternate URL or pass
#'  `NULL` to exclude MathJax entirely.
#' @param math_method Math rendering engine to use. This will define the math method to use with Pandoc.
#'
#'  - It can be a string for the engine, one of `r knitr::combine_words(c(pandoc_math_engines(), "r-katex"), and = "or ", before = '"')`
#'  or "default" for `mathjax`.
#'  - It can be a list of
#'    - `engine`:  one of
#'      `r knitr::combine_words(pandoc_math_engines(), and = "or ", before = '"')`.
#'    - `url`: A specific url to use with `mathjax`, `katex` or `webtex`.
#'      Note that for `engine = "mathjax"`, `url = "local"` will use a local version of MathJax (which is
#'  copied into the output directory).
#'
#'  For example,
#'  ```yaml
#'  output:
#'    html_document:
#'      math_method:
#'        engine: katex
#'        url: https://cdn.jsdelivr.net/npm/katex@0.11.1/dist
#'  ```
#'
#'  See [Pandoc's Manual about Math in
#'  HTML](https://pandoc.org/MANUAL.html#math-rendering-in-html) for the details
#'  about Pandoc supported methods.
#'
#'  Using `math_method = "r-katex"` will opt-in server side rendering using
#'  KaTeX thanks to [katex](https://docs.ropensci.org/katex/) R package. This is
#'  useful compared to `math_method = "katex"` to have no JS dependency, only a
#'  CSS dependency for styling equation.
#' @param section_divs Wrap sections in `<div>` tags, and attach identifiers to the
#'  enclosing `<div>` rather than the header itself.
#' @param template Pandoc template to use for rendering. Pass "default" to use
#'  the rmarkdown package default template; pass `NULL` to use pandoc's
#'  built-in template; pass a path to use a custom template that you've created.
#'  Note that if you don't use the "default" template then some features of
#'  `html_document` won't be available (see the Templates section below for
#'  more details).
#' @param extra_dependencies Extra dependencies as a list of the
#'   `html_dependency` class objects typically generated by
#'   [htmltools::htmlDependency()].
#' @param css CSS and/or Sass files to include. Files with an extension of .sass
#'  or .scss are compiled to CSS via `sass::sass()`. Also, if `theme` is a
#'  [bslib::bs_theme()] object, Sass code may reference the relevant Bootstrap
#'  Sass variables, functions, mixins, etc.
#' @param includes Named list of additional content to include within the
#'  document (typically created using the [`includes`] function).
#' @param keep_md Keep the markdown file generated by knitting.
#' @param lib_dir Directory to copy dependent HTML libraries (e.g. jquery,
#'  bootstrap, etc.) into. By default this will be the name of the document with
#'  `_files` appended to it.
#' @param md_extensions Markdown extensions to be added or removed from the
#'  default definition of R Markdown. See the [`rmarkdown_format`] for
#'  additional details.
#' @param pandoc_args Additional command line options to pass to pandoc
#' @param ... Additional function arguments to pass to the base R Markdown HTML
#'   output formatter [`html_document_base`]
#' @return R Markdown output format to pass to [`render`]
#'
#' @section Highlighting:
#'
#'  There are three highlighting engines available to HTML documents:
#'
#'  \describe{
#'  \item{highlightjs}{It does highlighting in the browser, using javascript It
#'  can only be used with the default template (i.e `template = "default"`)
#'  and it has two styles ("default" and "textmate"). When activated, it adds
#'  two additional dependencies to the output file: a JS script and a CSS file.
#'  For now, this is the default engine for the default template - this could
#'  change in the future.}
#'
#'  \item{Pandoc}{Pandoc's built-in highlighting.engine works with any template,
#'  default or custom, and style can be chosen among the built-in ones ("tango",
#'  "pygments", "kate", "monochrome", "espresso", "zenburn", "haddock" and
#'  "breezedark") or a path to a custom theme ".theme" file (see Details in the
#'  [Pandoc Manual](https://pandoc.org/MANUAL.html#syntax-highlighting)).
#'  \pkg{rmarkdown} includes two custom themes to select with `highlight`
#'  parameter:
#'  - "arrow", an accessible style using colors [optimized for accessibility and color contrast](https://www.a11yproject.com/)
#'  - "rstudio", a color scheme close to RStudio's default highlighting and
#'  highglightjs's textmate.
#'
#'  Custom themes are only available for Pandoc 2.0 and above.}
#'
#'  \item{downlit}{[\pkg{downlit}](https://downlit.r-lib.org/) is an R package that
#'  provides a syntax highlighting engine in R. It will also do automatic
#'  linking of R code (requires internet connectivity). It is activated only if
#'  `highlight_downlit = TRUE` and only affects R code, leaving
#'  highlighting for other languages unchanged. The default color scheme is
#'  the accessible theme "arrow".
#'
#'  It requires some CSS in the template to correctly style links. This is included
#'  in the default template, but if you want to use with a custom template, you will
#'  need to add this to your template:
#'
#' ```
#' $if(highlight-downlit)$
#' <style type="text/css">
#'   code a:any-link {
#'    color: inherit; /* use colour from syntax highlighting */
#'    text-decoration: underline;
#'    text-decoration-color: #ccc;
#'   }
#' </style>
#' $endif$
#' ```
#'  }}
#'
#' @section Anchor Sections Customization:
#'
#'  This will be the default to activate anchor sections link on header
#'  ```yaml
#'  output:
#'    html_document:
#'      anchor_sections: TRUE
#'  ````
#'  There are currently two options to modify the default behavior
#'
#'  \describe{
#'  \item{`style`}{Select a predefined visual style:
#'   * `style = "dash"`, the default, uses \samp{#}, a minimalist choice that evokes the id selector from HTML and CSS.
#'   * `style = "symbol"` will use a [link symbol](https://codepoints.net/U+1F517) \if{html}{\out{(&#x1F517;&#xFE0E;)}}
#'   * `style = "icon"` will use an svg icon. \if{html}{(\figure{link-black-18dp.svg}{options: alt="icon link"})}
#'
#'  You can also customize using a css rule in your
#'  document. For example, to get a pictogram \if{html}{\out{(&#x1F517;)}}:
#'  ```css
#'  a.anchor-section::before {
#'    content: '\\01F517';
#'  }
#'  ```
#'  About how to apply custom CSS in R Markdown document, see
#'  <https://bookdown.org/yihui/rmarkdown-cookbook/html-css.html>
#'  }
#'  \item{`depth`}{Select the maximum header level to add the
#'  anchor link to. For example, this yaml will use the symbol style and
#'  only with level 1 and 2 headings:
#'  ```yaml
#'  output:
#'    html_document:
#'      anchor_sections:
#'        style: icon
#'        depth: 2
#'  ```
#'  If omitted, anchor will be added to all headers (equivalent of
#'  `depth=6`). You can also set anchors manually with `depth = 0` using this syntax
#'  ```markdown
#'  # my header {.hasAnchor}
#'  ```
#'  }
#'
#'  Using anchor sections will add some CSS to your document output for the
#'  styling, and a JS script if `section_divs = TRUE`. The anchor link itself
#'  is added using a Lua filter, and hence requires Pandoc 2.0+
#'  }
#'
#' @section Navigation Bars:
#'
#'  If you have a set of html documents which you'd like to provide a common
#'  global navigation bar for, you can include a "_navbar.yml" or "_navbar.html"
#'  file within the same directory as your html document and it will automatically
#'  be included at the top of the document.
#'
#'  The "_navbar.yml" file includes `title`, `type`, `left`, and
#'  `right` fields (to define menu items for the left and right of the navbar
#'  respectively). Menu items include `text` and `href` fields. For example:
#'
#'  ```yaml
#'  title: "My Website"
#'  type: default
#'  left:
#'    - text: "Home"
#'      href: index.html
#'    - text: "Other"
#'      href: other.html
#'  right:
#'    - text: GitHub
#'      href: https://github.com
#'  ```
#'  The `type` field is optional and can take the value "default" or "inverse" (which
#'  provides a different color scheme for the navigation bar).
#'
#'  Alternatively, you can include a "_navbar.html" file which is a full HTML definition
#'  of a bootstrap navigation bar. For a simple example of including a navigation bar see
#'  <https://github.com/rstudio/rmarkdown-website/blob/master/_navbar.html>.
#'   For additional documentation on creating Bootstrap navigation bars see
#'  <https://getbootstrap.com/docs/4.5/components/navbar/>.
#'
#'
#' @section Floating Table of Contents:
#'
#'  You may specify a list of options for the `toc_float` parameter which
#'  control the behavior of the floating table of contents. Options include:
#'
#'  - `collapsed` (defaults to `TRUE`) controls whether the table of contents appears with only the top-level (H2) headers. When collapsed the table of contents is automatically expanded inline when necessary.
#'  - `smooth_scroll` (defaults to `TRUE`) controls whether page scrolls are animated when table of contents items are navigated to via mouse clicks.
#'  - `print` (defaults to `TRUE`) controls whether the table of contents appears when user prints out the HTML page.
#'
#' @section Code folding:
#'
#'  Code blocks become foldable by specifying "show" or "hide" to the
#'  `code_folding` parameter. The state can be toggled individually on
#'  browsers. The document-wide toggle button is also provided for
#'  `html_document` and some of its extensions such as
#'  `html_notebook`. Note that this feature applies not only to source
#'  codes of chunks, but also markdown code blocks.
#'
#'  Supported languages are R, Python, Bash, SQL, C++, Stan, and Julia. To
#'  support code blocks with other languages, add `foldable` class to them
#'  (i.e., `class.source = "foldable"` as a chunk option).
#'
#'  The default initial state of code folding respects the value given to the
#'  `code_folding` parameter. To override the behavior individually, add
#'  `fold-none` to disable, `fold-hide` to initially hide,
#'  `fold-show` to initially show.
#'
#' @section Tabbed Sections:
#'
#'  You can organize content using tabs by applying the `.tabset` class
#'  attribute to headers within a document. This will cause all sub-headers of
#'  the header with the `.tabset` attribute to appear within tabs rather
#'  than as standalone sections. For example:
#'
#'  ```markdown
#'  ## Quarterly Results {.tabset}
#'
#'  ### By Product
#'
#'  ### By Region
#' ```
#'
#'  With [html_document()], you can also specify two additional attributes to
#'  control the appearance and behavior of the tabs. The `.tabset-fade`
#'  attributes causes the tabs to fade in and out when switching. The
#'  `.tabset-pills` attribute causes the visual appearance of the tabs to
#'  be "pill" rather than traditional tabs. For example:
#'
#'  ```markdown
#'  ## Quarterly Results {.tabset .tabset-fade .tabset-pills}
#'  ```
#'
#'  If tabbed sections relies on [html_dependency_tabset()], for example by
#'  [html_vignette()], these two attributes are not supported.
#'
#' @section Templates:
#'
#'  You can provide a custom HTML template to be used for rendering. The syntax
#'  for templates is described in the
#'  [pandoc documentation](https://pandoc.org/MANUAL.html). You can also use
#'  the basic pandoc template by passing `template = NULL`.
#'
#'  Note however that if you choose not to use the "default" HTML template then
#'  several aspects of HTML document rendering will behave differently:
#'
#'  - The `theme` parameter does not work (you can still provide styles using the `css` parameter).
#'  - For the `highlight` parameter, the default highlighting engine will resolve to Pandoc instead of highlightjs and highlighting style will default to "pygments". "textmate" style is not available as related to highlightjs
#'  - The `toc_float` parameter will not work.
#'  - The `code_folding` parameter will not work.
#'  - Tabbed sections (as described above) will not work.
#'  - Navigation bars (as described above) will not work.
#'  - MathJax will not work if `self_contained` is `TRUE` (these two options can't be used together in normal pandoc templates).
#'
#'  Due to the above restrictions, you might consider using the `includes`
#'  parameter as an alternative to providing a fully custom template.
#'
#' @examples
#' \dontrun{
#' library(rmarkdown)
#'
#' render("input.Rmd", html_document())
#'
#' render("input.Rmd", html_document(toc = TRUE))
#' }
#' @md
#' @export
html_document <- function(
  toc = FALSE,
  toc_depth = 3,
  toc_float = FALSE,
  number_sections = FALSE,
  anchor_sections = FALSE,
  section_divs = TRUE,
  fig_width = 7,
  fig_height = 5,
  fig_retina = 2,
  fig_caption = TRUE,
  dev = 'png',
  df_print = "default",
  code_folding = c("none", "show", "hide"),
  code_download = FALSE,
  self_contained = TRUE,
  theme = "default",
  highlight = "default",
  highlight_downlit = FALSE,
  math_method = "default",
  mathjax = "default",
  template = "default",
  extra_dependencies = NULL,
  css = NULL,
  includes = NULL,
  keep_md = FALSE,
  lib_dir = NULL,
  md_extensions = NULL,
  pandoc_args = NULL,
  ...
) {
  # self_contained = TRUE already uses --standalone
  args <- if (!self_contained) c("--standalone")

  # to add lua_filters
  lua_filters <- c()

  # use section divs
  if (section_divs) {
    args <- c(args, "--section-divs")
  }

  # table of contents
  args <- c(args, pandoc_toc_args(toc, toc_depth))

  # makes downstream logic easier to reason about
  theme <- resolve_theme(theme)

  # toc_float
  if (toc && !identical(toc_float, FALSE)) {
    # must have a theme
    if (is.null(theme)) {
      stop("You must use a theme when specifying the 'toc_float' option")
    }

    # resolve options
    toc_float_options <- list(
      collapsed = TRUE,
      smooth_scroll = TRUE,
      print = TRUE
    )
    if (is.list(toc_float)) {
      toc_float_options <- merge_lists(toc_float_options, toc_float)
      toc_float <- TRUE
    } else if (!isTRUE(toc_float)) {
      stop("toc_float must be a logical or a list with options")
    }

    # dependencies
    extra_dependencies <- append(
      extra_dependencies,
      list(
        html_dependency_jquery(),
        html_dependency_jqueryui(),
        html_dependency_tocify()
      )
    )

    # flag for template
    args <- c(args, pandoc_variable_arg("toc_float", "1"))

    # selectors
    selectors <- paste0("h", seq(1, toc_depth), collapse = ",")
    args <- c(args, pandoc_variable_arg("toc_selectors", selectors))

    # options
    if (toc_float_options$collapsed) {
      args <- c(args, pandoc_variable_arg("toc_collapsed", "1"))
    }
    if (toc_float_options$smooth_scroll) {
      args <- c(args, pandoc_variable_arg("toc_smooth_scroll", "1"))
    }
    if (toc_float_options$print) {
      args <- c(args, pandoc_variable_arg("toc_print", "1"))
    }
  }

  # template path and assets
  template_file <- if (identical(template, "default")) {
    pkg_file("rmd/h/default.html")
  } else {
    template
  }
  if (!is.null(template_file)) {
    args <- c(args, "--template", pandoc_path_arg(template_file))
  }

  # validate code_folding
  code_folding <- match.arg(code_folding)

  # navigation dependencies
  if (!is.null(theme)) {
    code_menu <- !identical(code_folding, "none") || code_download
    source_embed <- code_download
    extra_dependencies <- append(
      extra_dependencies,
      list(
        html_dependency_jquery(),
        html_dependency_navigation(
          code_menu = code_menu,
          source_embed = source_embed
        )
      )
    )
  }

  # highlighting ---------
  if (highlight_downlit && !xfun::loadable("downlit")) {
    stop(
      "highlight_downlit=TRUE requires the downlit package to be installed.",
      call. = FALSE
    )
  }
  args <- c(
    args,
    pandoc_html_highlight_args(template, highlight, highlight_downlit)
  )

  # add highlight.js html_dependency if required
  extra_dependencies <- append(
    extra_dependencies,
    if (identical(template, "default") && is_highlightjs(highlight)) {
      list(html_dependency_highlightjs(highlight))
    } else if (!is.null(highlight)) {
      # for screen-reader accessibility improvement
      list(html_dependency_accessible_code_block())
    }
  )

  # numbered sections
  if (number_sections) {
    args <- c(args, "--number-sections")
  }

  # manage list of exit_actions (backing out changes to knitr options)
  exit_actions <- list()
  on_exit <- function() {
    for (action in exit_actions) {
      try(action())
    }
  }

  # capture the source code if requested
  source_code <- NULL
  source_file <- NULL
  pre_knit <- function(input, ...) {
    if (code_download) {
      source_file <<- basename(input)
      source_code <<- paste0(
        '<div id="rmd-source-code">',
        xfun::base64_encode(input),
        '</div>'
      )
    }
  }

  # pagedtable
  if (identical(df_print, "paged")) {
    extra_dependencies <- append(
      extra_dependencies,
      list(html_dependency_pagedtable())
    )
  }

  # anchor-sections
  components <- add_anchor_sections(anchor_sections, section_divs)
  args <- c(args, components$args)
  lua_filters <- c(lua_filters, components$lua_filters)
  extra_dependencies <- append(
    extra_dependencies,
    components$extra_dependencies
  )

  # pre-processor for arguments that may depend on the name of the
  # the input file AND which need to inject html dependencies
  # (otherwise we could just call the pre_processor)
  post_knit <- function(metadata, input_file, runtime, ...) {
    # extra args
    args <- c()

    # navbar (requires theme)
    if (!is.null(theme)) {
      # add navbar to includes if necessary
      navbar <- file.path(normalize_path(dirname(input_file)), "_navbar.html")

      # if there is no _navbar.html look for a _navbar.yml
      if (!file.exists(navbar)) {
        navbar_yaml <- file.path(dirname(navbar), "_navbar.yml")
        if (file.exists(navbar_yaml)) {
          navbar <- navbar_html_from_yaml(navbar_yaml)
        }
        # if there is no _navbar.yml then look in site config (if we have it)
        config <- site_config(input_file)
        if (!is.null(config) && !is.null(config$navbar)) {
          navbar <- navbar_html(config$navbar)
        }
      }

      if (file.exists(navbar)) {
        # include the navbar html
        includes <- list(before_body = navbar)
        args <- c(
          args,
          includes_to_pandoc_args(
            includes,
            filter = if (is_shiny_classic(runtime)) {
              function(x) normalize_path(x, must_work = FALSE)
            } else {
              identity
            }
          )
        )

        # flag indicating we need extra navbar css and js
        args <- c(args, pandoc_variable_arg("navbar", "1"))

        # navbar icon dependencies
        iconDeps <- navbar_icon_dependencies(navbar)
        if (length(iconDeps) > 0) {
          knitr::knit_meta_add(list(iconDeps))
        }
      }
    }

    args
  }

  # pre-processor for arguments that may depend on the name of the
  # the input file (e.g. ones that need to copy supporting files)
  pre_processor <- function(
    metadata,
    input_file,
    runtime,
    knit_meta,
    files_dir,
    output_dir
  ) {
    # use files_dir as lib_dir if not explicitly specified
    if (is.null(lib_dir)) {
      lib_dir <- files_dir
    }

    # extra args
    args <- c()

    # track whether we have a code menu
    code_menu <- FALSE

    # code_folding
    if (code_folding %in% c("show", "hide")) {
      # must have a theme
      if (is.null(theme)) {
        stop("You must use a theme when specifying the 'code_folding' option")
      }
      args <- c(args, pandoc_variable_arg("code_folding", code_folding))
      code_menu <- TRUE
    }

    # source_embed
    if (code_download) {
      if (is.null(theme)) {
        stop("You must use a theme when specifying the 'code_download' option")
      }
      args <- c(args, pandoc_variable_arg("source_embed", source_file))
      sourceCodeFile <- tempfile(fileext = ".html")
      write_utf8(source_code, sourceCodeFile)
      args <- c(args, pandoc_include_args(after_body = sourceCodeFile))
      code_menu <- TRUE
    }

    # code menu
    if (code_menu) {
      args <- c(args, pandoc_variable_arg("code_menu", "1"))
    }

    # content includes (we do this here so that user include-in-header content
    # goes after dependency generated content). make the paths absolute if
    # making a Shiny document so we can resolve them even if rendering
    # elsewhere.
    args <- c(
      args,
      includes_to_pandoc_args(
        includes,
        filter = if (is_shiny_classic(runtime)) {
          function(x) normalize_path(x, must_work = FALSE)
        } else {
          identity
        }
      )
    )

    # return additional args
    args
  }

  # post-processor that uses the output file from pandoc
  post_processor <- function(
    metadata,
    input_file,
    output_file,
    clean,
    verbose
  ) {
    # add a post processor for syntax highlighting with downlit if requested
    if (highlight_downlit) {
      output_file <- downlit::downlit_html_path(
        output_file,
        output_file,
        classes = downlit::classes_pandoc()
      )
    }

    output_file
  }

  # return format
  output_format(
    knitr = knitr_options_html(fig_width, fig_height, fig_retina, keep_md, dev),
    pandoc = pandoc_options(
      to = "html",
      from = from_rmarkdown(fig_caption, md_extensions),
      args = args,
      lua_filters = lua_filters
    ),
    keep_md = keep_md,
    clean_supporting = self_contained,
    df_print = df_print,
    pre_knit = pre_knit,
    post_knit = post_knit,
    pre_processor = pre_processor,
    post_processor = post_processor,
    on_exit = on_exit,
    base_format = html_document_base(
      theme = theme,
      self_contained = self_contained,
      lib_dir = lib_dir,
      math_method = math_method,
      mathjax = mathjax,
      template = template,
      pandoc_args = pandoc_args,
      extra_dependencies = extra_dependencies,
      css = css,
      ...
    )
  )
}


#' Knitr options for an HTML output format
#'
#' Define knitr options for an R Markdown output format that creates
#' HTML output.
#'
#' @inheritParams html_document
#' @return An list that can be passed as the \code{knitr} argument of the
#'   \code{\link{output_format}} function.
#' @seealso \link{knitr_options}, \link{output_format}
#' @export
knitr_options_html <- function(
  fig_width,
  fig_height,
  fig_retina,
  keep_md,
  dev = 'png'
) {
  opts_chunk <- list(
    dev = dev,
    dpi = 96,
    fig.width = fig_width,
    fig.height = fig_height,
    fig.retina = fig_retina
  )

  if (keep_md) {
    opts_chunk$fig.retina <- NULL
  }

  knitr_options(opts_chunk = opts_chunk)
}

# CSS files in inst/rmd/h/bootstrap/css
themes <- function() {
  c(
    "default", # keep for backward compatibility reason, changed to 'bootstrap' internally
    "bootstrap",
    "cerulean",
    "cosmo",
    "darkly",
    "flatly",
    "journal",
    "lumen",
    "paper",
    "readable",
    "sandstone",
    "simplex",
    "spacelab",
    "united",
    "yeti"
  )
}

html_highlighters <- function() {
  c(highlighters(), "textmate")
}

navbar_html_from_yaml <- function(navbar_yaml) {
  # parse the yaml
  navbar <- yaml_load_file(navbar_yaml)

  # generate the html
  navbar_html(navbar)
}


#' Create a navbar HTML file from a navbar definition
#'
#' @param navbar Navbar definition
#' @param links List of navbar links
#' @return Path to temporary file with navbar definition
#' @keywords internal
#' @export
navbar_html <- function(navbar) {
  # title and type
  if (is.null(navbar$title)) {
    navbar$title <- ""
  }
  if (is.null(navbar$type)) {
    navbar$type <- "default"
  }

  # menu entries
  left <- navbar_links_html(navbar$left)
  right <- navbar_links_html(navbar$right)

  # build the navigation bar and return it as a temp file
  template <- file_string(pkg_file("rmd/h/_navbar.html"))
  navbar_html <- sprintf(template, navbar$type, navbar$title, left, right)
  as_tmpfile(navbar_html)
}

#' @keywords internal
#' @name navbar_html
#' @export
navbar_links_html <- function(links) {
  as.character(navbar_links_tags(links))
}

navbar_links_tags <- function(links, depth = 0L) {
  if (!is.null(links)) {
    tags <- lapply(links, function(x) {
      if (!is.null(x$menu)) {
        # sub-menu
        is_submenu <- depth > 0L

        if (is_submenu) {
          menu_class <- "dropdown-submenu"
          link_text <- navbar_link_text(x)
        } else {
          menu_class <- "dropdown"
          link_text <- navbar_link_text(x, " ", tags$span(class = "caret"))
        }

        submenuLinks <- navbar_links_tags(x$menu, depth = depth + 1L)

        tags$li(
          class = menu_class,
          tags$a(
            href = "#",
            class = "dropdown-toggle",
            `data-toggle` = "dropdown",
            role = "button",
            `data-bs-toggle` = "dropdown", # BS5
            `aria-expanded` = "false",
            link_text
          ),
          tags$ul(class = "dropdown-menu", role = "menu", submenuLinks)
        )
      } else if (!is.null(x$text) && grepl("^\\s*-{3,}\\s*$", x$text)) {
        # divider
        tags$li(class = "divider")
      } else if (!is.null(x$text) && is.null(x$href)) {
        # header
        tags$li(class = "dropdown-header", x$text)
      } else {
        # standard menu item
        textTags <- navbar_link_text(x)
        tags$li(tags$a(href = x$href, textTags))
      }
    })
    tagList(tags)
  } else {
    tagList()
  }
}

navbar_link_text <- function(x, ...) {
  if (!is.null(x$icon)) {
    # find the iconset
    split <- strsplit(x$icon, "-")
    if (length(split[[1]]) > 1) {
      iconset <- split[[1]][[1]]
    } else {
      iconset <- ""
    }
    # check if a full class is passed for fontawesome = V5
    # Add fa deprecated fa prefix otherwise = V4 compatibility
    # https://github.com/rstudio/rmarkdown/issues/1554
    class = if (grepl("^fa\\w? fa", iconset)) {
      # Fontawesome 5 - full new prefix + name must be passed
      # if old fa prefix is passed - keep it for compatibility
      x$icon
    } else if (iconset == "fa") {
      # Fontawesome 4 compatibility - Add deprecated fa prefix
      paste("fa", x$icon)
    } else {
      # Other Icon sets
      paste(iconset, x$icon)
    }
    tagList(tags$span(class = class), " ", x$text, ...)
  } else {
    tagList(x$text, ...)
  }
}

add_anchor_sections <- function(anchor_sections, section_divs = FALSE) {
  # expected output object
  res <- list(args = NULL, lua_filters = NULL, extra_dependencies = NULL)
  # Do nothing
  if (identical(anchor_sections, FALSE)) {
    return(res)
  }
  # Requires Pandoc 2.0 because using a Lua filter
  if (!pandoc2.0()) {
    stop("Using anchor_sections requires Pandoc 2.0+", call. = FALSE)
  }

  allowed_args <- c("style", "depth")
  default_style <- "hash"

  if (isTRUE(anchor_sections)) {
    style <- default_style
    depth <- NULL
  } else if (is.list(anchor_sections)) {
    # check list elements
    all_allowed <- all(names(anchor_sections) %in% allowed_args)
    if (!all_allowed) {
      stop(
        "`anchor_sections` could be a list with only names in [",
        paste(allowed_args, collapse = ", "),
        "]",
        call. = FALSE
      )
    }
    style <- anchor_sections[["style"]] %||% default_style
    depth <- anchor_sections[["depth"]]
  } else {
    stop(
      "`anchor_sections` should be FALSE, TRUE or a list with names [",
      paste(allowed_args, collapse = ", "),
      "]",
      call. = FALSE
    )
  }

  res$args <- if (!is.null(depth)) {
    pandoc_metadata_arg("rmd_anchor_depth", depth)
  }
  res$lua_filters <- pkg_file_lua("anchor-sections.lua")
  res$extra_dependencies <- list(html_dependency_anchor_sections(
    style,
    section_divs
  ))

  res
}
