---
title: "Literate JavaScript Programming"
output: js4shiny::html_document_js
vignette: >
  %\VignetteIndexEntry{Literate JavaScript Programming}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{css echo=FALSE, eval=identical(Sys.getenv("IN_PKGDOWN"), "true")}
div > pre:empty {
  display: none;
}

h1 > code, h2 > code, h3 > code {
  background: none;
  color: currentColor;
}

pre.r, pre.js, pre.html {
  position: relative;
}

pre.r::after, pre.js::after, pre.html::after {
  position: absolute;
  bottom: 0;
  right: 0;
  color: #888;
  text-align: right;
  padding-right: 5px;
  width: 50px;
}

pre.r::after { content: "r"; }
pre.js::after { content: "js"; }
pre.html::after { content: "html"; }

.btn-copy-ex { z-index: 100; }
```

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
js4shiny::register_knitr_js_engine()
htmltools::tagList(js4shiny::html_dependency_js4shiny(stylize = "none"))
```


**js4shiny** includes several components
that enable the use of R Markdown
for literate programming with JavaScript. 
The first of these is the R Markdown HTML format, 
`js4shiny::html_document_js()`.

````markdown
---
output: js4shiny::html_document_js
---
````

Alternatively, 
you can override the `js` knitr chunk engine
of any HTML-based R Markdown format.
This function also registers [json](#json-knitr-engine) and [html](#html-knitr-engine) knitr engines.

```{r eval=FALSE}
js4shiny::register_knitr_js_engine()
```

## Introducing `html_document_js`

The `html_document_js` renders R Markdown to HTML
and uses a new JavaScript knitr engine that
runs each JavaScript chunk in the browser
and writes the output of any `console.log()`, 
or the return value of the chunk,
into the HTML document, interactively in the browser.

In other words, it renders JavaScript chunks that kind of like R chunks.
Here's a standard R chunk.

```{r}
x <- 10
message("multiplying x times 20...")
x * 20
```

And here's a JavaScript chunk.

```{js}
const x = 10
console.log("multiplying x times 20...")
x * 20
```

When the document is viewed in a browser, 
each JavaScript chunk is evaluated in its own block scope
and any `console.log()` statements
and the return value of the block 
are automatically printed to the chunk's output `<div>`,
unless that value is `undefined`.

The last statement in a chunk doesn't need `console.log()` to be output.
This makes it easier to show the results of code
without requiring numerous `console.log()` statements.

```{js}
true && false
```

```{js}
let x = 12
x = x * 4 - 6
```

There are a few differences between R chunks and JavaScript chunks.
The first is that JavaScript results are always grouped together in the output code block,
whereas output from R chunks is printed after the expression causing the output.

The second difference is that R chunks build upon previous chunks;
each R chunk is executed in the same environment
and the results from each chunk are available to subsequent chunks.
The JavaScript chunks, 
on the other hand,
are each executed in a separate block scope,
so the result of a chunk isn't necessarily available for later code.
I'll explain and provide methods for working around this limitation
in the next section.

## Scoping JavaScript Chunks

Because each chunk is block-scoped,
variables created in one block may not be available to other chunks.
Notice that if I try to access `x` from the previous JavaScript chunk,
where `x` is eventually assigned `42`,
I get an error that `x` is not defined.

```{js}
x
```

Because variables in JavaScript need to be declared
with `let`, `const`, or `var`,
this may be preferred to executing everything in a shared environment.
For example, 
in the first JavaScript chunk in this vignette,
I declared `const x = 10`, 
but in a later chunk I declared `const x = 12`.
If all chunks were evaluated to build upon previous chunks,
then you would frequently run into "identifier already declared" errors.

```{js}
const x = 42
const x = 64
```

When you want a variable to exist beyond the scope of the current block,
two methods are available to create global variables.
First, you can assign your variable as a property of
[globalThis](https://javascript.info/global-object),
a recent addition to JavaScript that's available in about
[90% of browsers](https://caniuse.com/#search=globalThis).
(You could also assign to `window` instead of `globalThis`.)

```{js}
globalThis.x = 12
x = x * 4 - 6
```

If you try to access `x` in another chunk, you'll get the global `x`

```{js}
x
```

but you can still create local variables that mask global variables.

```{js}
const x = 12
x
```

Alternatively,
the entire chunk can be evaluated globally
by temporarily disabling the console redirect.
Add `js_redirect = FALSE` to the chunk
that you would like to be evaluated in the global scope.
These chunks use the standard `js` knitr engine,
that simply embeds the `js` code in a `<script>` tag in your output.
With this option,
`console.log()` output will not appear *in the document*,
but logged statements will still be available
in the browser's developer tools console.
Helpfully, though, 
variables created in these chunks
are assigned in the global scope are thereafter available to all chunks.

````
```{js, js_redirect = FALSE}`r ''`
let globalVariable = 'this is a global variable'
console.log(globalVariable) // goes to browser console
```

```{js}`r ''`
console.log(globalVariable) // outputs in document
```
````

## Using the Output `<div>`

The live JavaScript chunks are written in to the document as a `<div>` and `<script>` pair.
For a chunk like the following

````
```{js hello-world}`r ''`
document.getElementById('out-simple-redirect').innerHTML = 'Hello, world!'
```
````

the following HTML is included in the output.

````html
<div id="out-hello-world">
  <pre><!-- output statements will appear here --></pre>
</div>
<script type="text/javascript">
// the javascript from the current chunk
</script>
````

This means that if your JavaScript chunk needs a dedicated element on the page
to use for writing the output, you can look for the element with id
`out-<chunk-name-here>` (non-alphanumeric characters in the chunk name are replaced with `_`).
This element exists _before_ the JavaScript is called —
so you can always know that it's available for use —
but after the static code chunk is printed.
This is 
[particularly useful](https://livefreeordichotomize.com/2017/01/24/custom-javascript-visualizations-in-rmarkdown/)
if your JavaScript chunks are demonstrating 
[d3.js visualizations](https://d3js.org/).

```{js hello-world}
document.getElementById('out-hello-world').innerHTML = '<p><em>Hello, world!</em></p>'
```

## Interactive Documents

There are a lot of interesting things you can do
when you blend your JavaScript and HTML together.
For instance,
you've clicked the button below
<span id="n_btn_clicks">zero times</span>.
Go ahead and click it<span id="click-again">!</span>

```{=html}
<p><button type="button" id="click-me" value=0 class="btn btn-outline-primary">Click Me</button></p>
```

```{js}
const btn = document.getElementById('click-me')
btn.addEventListener('click', function() {
  const val = ++btn.value
  btn.value = val
  if (val > 9) console.log("Okay, that's enough!")
  
  const text = document.getElementById('n_btn_clicks')
  text.textContent = val === 1 ? '1 time' : `${val} times`
  
  const textCTA = document.getElementById('click-again')
  if (val === 1) {
    textCTA.textContent = ' again.'
  } else if (val >= 10) {
    textCTA.textContent = ` again. Okay, that's enough, thank you!` 
  }
})
```

## Other `console` methods

You can also use other JavaScript `console` methods, like `console.table()`.

```{js people-js}
const p1 = {first: 'Colin', last: 'Fay', country: 'France', twitter: '@_ColinFay'}
const p2 = {first: 'Garrick', last: 'Aden-Buie', country: 'USA', twitter: '@grrrck'}

console.table(p1)
console.table(p2)
```

```{js echo=FALSE}
[...document.querySelectorAll('#out-people-js table')]
  .forEach(el => el.classList.add('table'))
```

## JSON Chunks {#json-knitr-engine}

To visualize or use JSON-formatted data in your document,
**js4shiny** also provides a `json` knitr engine.
This next chunk is a JSON chunk named `people` that renders as an interactive list.

````
```{json people}`r ''`
[
  {
    "first":"Colin",
    "last":"Fay",
    "country":"France",
    "twitter":"@_ColinFay"
  },
  {
    "first":"Garrick",
    "last":"Aden-Buie",
    "country":"USA",
    "twitter":"@grrrck"
  }
] 
```
````

```{json people, echo=FALSE}
[
  {
    "first":"Colin",
    "last":"Fay",
    "country":"France",
    "twitter":"@_ColinFay"
  },
  {
    "first":"Garrick",
    "last":"Aden-Buie",
    "country":"USA",
    "twitter":"@grrrck"
  }
] 
```

And the data from the JSON becomes a global variable named `data_<chunk_name>`,
or in this case `data_people`.

```{js}
data_people.forEach(function ({first, last, twitter}) {
  console.log(`- ${first} ${last} (${twitter})`)
})
```

## HTML Chunks {#html-knitr-engine}

Finally, `js4shiny::html_document_js` also provides an HTML knitr engine.
This is useful when you want the code of the HTML itself to appear in the document,
in addition to the HTML output.

```{html}
<p style="color: #338D70">This text is green, right?</p>
```
