# `Kotoba.Renderer`
[🔗](https://github.com/hattoriai/kotoba/blob/v0.5.0/lib/kotoba/renderer.ex#L1)

Renders a `Kotoba.Document` as HTML, as plain text and as Markdown.

`to_html/2` returns safe iodata from `Phoenix.HTML`. Every string in the
document is escaped, and Kotoba never passes raw HTML through. Before it
renders a node, the renderer calls `Kotoba.Sanitizer.check/3`. A node
that fails the check renders as an unknown node:
`<span class="kotoba-unknown" data-type="...">` with no content.

## HTML of the built-in nodes

| Node               | HTML                                                   |
| ------------------ | ------------------------------------------------------ |
| paragraph          | `p`                                                    |
| heading            | `h1` to `h4` (`h5` and `h6` become `h4`)               |
| quote              | `blockquote`                                           |
| list               | `ul`, `ol` (with `start` when it is not 1), `ul.kotoba-check` |
| list item          | `li`; in a check list `li.kotoba-checked` or `li.kotoba-unchecked` |
| code               | `pre` with `code.language-<language>`, the text escaped |
| text               | the text in `strong`, `em`, `s`, `u`, `code`, `sub`, `sup`, `mark` (`mark.kotoba-highlight-<name>` for a highlight color), and `span.kotoba-lowercase`, `span.kotoba-uppercase`, `span.kotoba-capitalize`; a text color is a `span.kotoba-color-<name>` around it |
| line break         | `br`                                                   |
| horizontal rule    | `hr`                                                   |
| table              | `table.kotoba-table` in a `div.kotoba-table-scroll`, with a `thead` for a first row of header cells and a `tbody` |
| table row          | `tr`                                                   |
| table cell         | `td`, or `th` with `scope="col"` (header row) or `scope="row"` (header column); `colspan` and `rowspan` when they are more than 1 |
| link, autolink     | `a` with `rel="noopener nofollow"`, or the text when the URL is not safe |
| attachment         | `figure.kotoba-attachment` with an `img`, an `object` (PDF), a `video`, or a download link, and a `figcaption` |
| gallery            | `div.kotoba-gallery` with the `figure` of each attachment |
| mention            | `span.kotoba-mention` with `data-kind` and `data-id`   |
| tab                | a tab character, escaped                               |
| unknown            | `span.kotoba-unknown` with `data-type`                 |

## Options

  * `:policy` - `:default` or `:untrusted`. See `Kotoba.Sanitizer`. The
    default is `:default`.
  * `:schemes` - the allowed link schemes. See `Kotoba.Sanitizer.link_url/2`.
  * `:blank_lines` - `:keep` (the default) or `:collapse`: whether the
    blank lines that a person typed (empty paragraphs between blocks) are
    in the output. See `Kotoba.Nodes.Paragraph`.

## The options of the render callbacks

The renderer gives the options above to each render callback of a node,
with these changes:

  * `:policy` - the policy map from `Kotoba.Sanitizer.policy/1`.
  * `:parent` - the parent node, or `nil` for the root node.
  * `:index` - the position of the node among the children of its
    parent, or `nil` for the root node.

A node gives the same `opts` to `html_children/2`, `text_children/2` and
`markdown_children/3`.

# `attribute`

```elixir
@type attribute() :: {atom() | String.t(), String.t() | integer() | boolean() | nil}
```

An attribute of an HTML tag. A `nil` or `false` value is left out.

# `escape_markdown`

```elixir
@spec escape_markdown(String.t()) :: String.t()
```

Escapes the Markdown characters in a plain string, for a
`c:Kotoba.Node.render_markdown/2` callback.

It escapes the inline syntax everywhere (`` ` ``, `*`, `_`, `[`, `]`,
`~`, `<`, `&` and `\`), and the block syntax at the start of a line: `#`
headings, `>` quotes, `-` and `+` bullets, `1.` and `1)` list markers,
and `---` or `===` lines. So a node that renders its text at the start
of a block cannot make a heading or a list.

## Examples

    iex> Kotoba.Renderer.escape_markdown("*not* [a link]")
    "\\*not\\* \\[a link\\]"

    iex> Kotoba.Renderer.escape_markdown("# not a heading")
    "\\# not a heading"

    iex> Kotoba.Renderer.escape_markdown("1. not a list")
    "1\\. not a list"

# `escape_markdown_url`

```elixir
@spec escape_markdown_url(String.t()) :: String.t()
```

Returns a URL for the destination of a Markdown link, for a
`c:Kotoba.Node.render_markdown/2` callback. `\`, `&`, `<` and `>` get a
backslash, so that a CommonMark renderer decodes no entity, and a URL
with spaces or parentheses is in angle brackets, as it is. Check the URL
with `Kotoba.Sanitizer.link_url/2` first.

## Examples

    iex> Kotoba.Renderer.escape_markdown_url("/docs/a page (draft)")
    "</docs/a page (draft)>"

    iex> Kotoba.Renderer.escape_markdown_url("/search?q=a&b")
    "/search?q=a\\&b"

# `html_children`

```elixir
@spec html_children(Kotoba.Node.t(), keyword()) :: Phoenix.HTML.safe()
```

Renders the children of an element node as safe HTML.

# `markdown_children`

```elixir
@spec markdown_children(Kotoba.Node.t(), keyword(), String.t()) :: String.t()
```

Renders the children of an element node as Markdown.

Consecutive inline nodes are joined. The blocks are separated by
`separator` (the default is a blank line). Empty blocks are left out.

# `markdown_each`

```elixir
@spec markdown_each(Kotoba.Node.t(), keyword()) :: [{Kotoba.Node.t(), String.t()}]
```

Renders each child of an element node as Markdown. Returns a list of
`{child, markdown}` tuples, in order.

# `policy`

```elixir
@spec policy(keyword()) :: Kotoba.Sanitizer.policy()
```

Returns the policy map of the render options. See `Kotoba.Sanitizer.policy/1`.

# `tag`

```elixir
@spec tag(String.t(), [attribute()], Phoenix.HTML.safe() | String.t() | :void) ::
  Phoenix.HTML.safe()
```

Returns an HTML tag as safe HTML.

The attribute values and a string `content` are escaped. `content` can
also be safe HTML. A `nil` or `false` attribute is left out, and a `true`
attribute has no value. When `content` is `:void`, the tag has no end tag.

## Examples

    iex> Kotoba.Renderer.tag("a", [href: "/x?a=1&b=2", title: nil], "A & B") |> Phoenix.HTML.safe_to_string()
    ~s(<a href="/x?a=1&amp;b=2">A &amp; B</a>)

    iex> Kotoba.Renderer.tag("br", [], :void) |> Phoenix.HTML.safe_to_string()
    "<br>"

# `text_children`

```elixir
@spec text_children(Kotoba.Node.t(), keyword()) :: String.t()
```

Renders the children of an element node as plain text.

Consecutive inline nodes are joined. Each block child gives one line, and
the lines are separated by `"\n"`. A decorator in the root node is a
block; it gives no line when it has no text.

# `text_each`

```elixir
@spec text_each(Kotoba.Node.t(), keyword()) :: [{Kotoba.Node.t(), String.t()}]
```

Renders each child of an element node as plain text. Returns a list of
`{child, text}` tuples, in order.

# `to_html`

```elixir
@spec to_html(Kotoba.Document.t(), keyword()) :: Phoenix.HTML.safe()
```

Renders a document as safe HTML.

## Examples

    iex> {:ok, doc} =
    ...>   Kotoba.Document.parse(%{
    ...>     "kotoba" => 1,
    ...>     "lexical" => "0.51",
    ...>     "root" => %{
    ...>       "type" => "root",
    ...>       "children" => [
    ...>         %{"type" => "paragraph", "children" => [%{"type" => "text", "text" => "<b>hi</b>"}]}
    ...>       ]
    ...>     }
    ...>   })
    iex> doc |> Kotoba.Renderer.to_html() |> Phoenix.HTML.safe_to_string()
    "<p>&lt;b&gt;hi&lt;/b&gt;</p>"

# `to_markdown`

```elixir
@spec to_markdown(Kotoba.Document.t(), keyword()) :: String.t()
```

Renders a document as Markdown, as well as Markdown can show it.

Headings, lists, code blocks, bold, italic, strikethrough, inline code,
links and images become Markdown. The other nodes and formats give their
plain text. Blocks are separated by a blank line.

# `to_text`

```elixir
@spec to_text(Kotoba.Document.t(), keyword()) :: String.t()
```

Renders a document as plain text. Blocks are separated by `"\n"`.

# `unknown_html`

```elixir
@spec unknown_html(String.t()) :: Phoenix.HTML.safe()
```

Returns the safe HTML of an unknown node of `type`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
