Shiny Source Code Explained: Rendering the UI

Ever wondered what goes on behind the shinyApp function from Shiny? There’s actually a lot happening to turn your UI and server into something that displays and works in the web browser! In this series, you will dive into the Shiny source code to better understand what happens when you click on the “Run App” button. Let’s start with the UI, or HTML, side of your Shiny application. 

The Shiny App Lifecycle

To explain what exactly happens when running your Shiny application, we take the following simple (and classic) Shiny application:

				
					library(shiny)

ui <- fluidPage(
  titlePanel("Basic Shiny App"),
  sidebarLayout(
    sidebarPanel(
      sliderInput("slider", "Slider", min = 1, max = 100, value = 50)
    ),
    mainPanel(
      plotOutput("plot")
    )
  )
)

server <- function(input, output) {
  output$plot <- renderPlot({
    hist(rnorm(input$slider))
  })
}

shinyApp(ui = ui, server = server)
				
			

Saving a file with the above code will give you the beloved and magic “Run App” button in RStudio. When you access a Shiny app in your browser, a series of events occurs. This series of event we call the Shiny app lifecycle.

Roughly, this lifecycle consists of the following steps (if you run your app locally):

1. You call the runApp() function by either hitting the “Run App” button in RStudio or calling runApp("path/to/app.R") directly in your console.

2. runApp() finds a Shiny app object in your file, generated by shinyApp(). This shinyApp() function contains ui and server objects, which are called appParts.

3. The appParts are given to a function called startApp(), which creates HTTP and WebSocket handlers. These handlers are responsible for controlling the WebSocket behaviour.

4. startApp() calls startServer() from the httpuv package, which starts the HTTP server and opens the server WebSocket connection.

5. If the R code is error-free, the HTTP server returns the given UI in HTML code.

6. The client, or web browser, receives that HTML code and interprets it.

7. The HTML is rendered and displayed in the web browser.

8. The web browser makes sure that the WebSocket connection is opened with the necessary JavaScript.

Since we’re focussing on the UI part of the application in this article, let’s take a closer look at this step:

5. If the R code is error-free, the HTTP server returns the given UI in HTML code.

Looking at the ui of our simple demo application, it returns the following HTML when printing it right on the R console:

				
					<div class="container-fluid">
  <h2>Basic Shiny App</h2>
  <div class="row">
    <div class="col-sm-4">
      <form class="well" role="complementary">
        <div class="form-group shiny-input-container">
          <label class="control-label" id="slider-label" for="slider">Slider</label>
          <input class="js-range-slider" id="slider" data-skin="shiny" data-min="1" data-max="100" data-from="50" data-step="1" data-grid="true" data-grid-num="9.9" data-grid-snap="false" data-prettify-separator="," data-prettify-enabled="true" data-keyboard="true" data-data-type="number"/>
        </div>
      </form>
    </div>
    <div class="col-sm-8" role="main">
      <div class="shiny-plot-output html-fill-item" id="plot" style="width:100%;height:400px;"></div>
    </div>
  </div>
</div>
				
			

However, this is not a valid HTML template yet. A valid HTML template contains tags like <!DOCTYPE html>, <html>, <head>, and <body>. The above only provides some simple <div> tags with a title and some inputs. 

If we would provide the above HTML as is to a browser, the browser will not understand. So the question is: what happens to create that valid HTML template?

Shiny and htmltools

Long story short: Shiny handles R to HTML conversion with the htmltools package. More specifically, via this call stack:

				
					shinyApp()
└── shiny:::uiHttpHandler()
    └── shiny:::renderPage()
        ├── tags$body(ui)
        ├── htmltools::htmlTemplate()
        └── htmltools::renderDocument()
				
			

Where tags$body(ui) wraps the ui in already one important HTML tag: <body>. Two functions of htmltools take care of the rest: htmltools::htmlTemplate() and htmltools::renderDocument()

The first one, htmlTemplate() fills a boilerplate that looks like this:

				
					<!DOCTYPE html>
<html{{ if (isTRUE(nzchar(lang))) paste0(" lang=\"", lang, "\"") }}>
<head>
{{ headContent() }}
</head>
{{ body }}
</html>
				
			

Applying that to our demo app we can run the following:

				
					htmlTemplate(
  system.file("template", "default.html", package = "shiny"), 
  lang = "en", 
  body = tags$body(ui), 
  document_ = TRUE
)
				
			

Which leads to the following result:

				
					<!DOCTYPE html>
<html lang="en">
<head>
<!-- HEAD_CONTENT -->
</head>
<body>
  <div class="container-fluid">
    <h2>Basic Shiny App</h2>
    <div class="row">
      <div class="col-sm-4">
        <form class="well" role="complementary">
          <div class="form-group shiny-input-container">
            <label class="control-label" id="slider-label" for="slider">Slider</label>
            <input class="js-range-slider" id="slider" data-skin="shiny" data-min="1" data-max="100" data-from="50" data-step="1" data-grid="true" data-grid-num="9.9" data-grid-snap="false" data-prettify-separator="," data-prettify-enabled="true" data-keyboard="true" data-data-type="number"/>
          </div>
        </form>
      </div>
      <div class="col-sm-8" role="main">
        <div class="shiny-plot-output html-fill-item" id="plot" style="width:100%;height:400px;"></div>
      </div>
    </div>
  </div>
</body>
</html>
				
			

As you can see, there’s a placeholder for the head content. After htmltools::htmlTemplate(), htmltools::renderDocument() gets called. This one takes care of replacing the head content. 

Shiny dependencies

Looking at the Shiny source code, we see that htmltools::renderDocument() gets called as the final step of shiny:::renderPage()

				
					html <- renderDocument(ui, shiny_deps, processDep = createWebDependency)
				
			

And it is here, that the Shiny dependencies get attached to the HTML. These dependencies are: 

				
					  shiny_deps <- c(
    list(jqueryDependency()),
    shinyDependencies()
  )
				
			

The first one, jqueryDependency(), speaks for itself: it takes care of the jQuery dependency. The second one, shinyDependencies(), attaches something called “shiny-busy-indicators”, which is a CSS file containing styles for outputs that are recalculating, and main JavaScript that is needed for Shiny: shiny.min.js.

If you put your app in test mode or in dev mode, additional dependencies will be attached as well.

The result of htmltools::renderDocument() is an object called html, and it gets encoded to UTF-8. This is eventually returned by shiny:::renderPage().

If all went well, Shiny sends of a 200 HTTP response (meaning success) to the web browser with the full and valid HTML document. In the browser, that eventually looks like this: 

				
					<!DOCTYPE html>
<html class="">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <script type="application/shiny-singletons"></script>
  <script type="application/html-dependencies">jquery[3.6.0];shiny-css[1.8.1.9001];shiny-busy-indicators[1.8.1.9001];shiny-javascript[1.8.1.9001];ionrangeslider-javascript[2.3.1];strftime[0.9.2];ionrangeslider-css[2.3.1];htmltools-fill[0.5.8.1];bootstrap[3.4.1]</script>
  <script src="jquery-3.6.0/jquery.min.js"></script>
  <link href="shiny-css-1.8.1.9001/shiny.min.css" rel="stylesheet">
  <link href="shiny-busy-indicators-1.8.1.9001/busy-indicators.css" rel="stylesheet">
  <script src="shiny-javascript-1.8.1.9001/shiny.min.js"></script>
  <script src="ionrangeslider-javascript-2.3.1/js/ion.rangeSlider.min.js"></script>
  <script src="strftime-0.9.2/strftime-min.js"></script>
  <link href="ionrangeslider-css-2.3.1/css/ion.rangeSlider.css" rel="stylesheet">
  <link href="htmltools-fill-0.5.8.1/fill.css" rel="stylesheet">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="bootstrap-3.4.1/css/bootstrap.min.css" rel="stylesheet">
  <link href="bootstrap-3.4.1/accessibility/css/bootstrap-accessibility.min.css" rel="stylesheet">
  <script src="bootstrap-3.4.1/js/bootstrap.min.js"></script>
  <script src="bootstrap-3.4.1/accessibility/js/bootstrap-accessibility.min.js"></script>  <title>Basic Shiny App</title>
</head>
<body>
  <div class="container-fluid">
    <h2>Basic Shiny App</h2>
    <div class="row">
      <div class="col-sm-4">
        <form class="well" role="complementary">
          <div class="form-group shiny-input-container">
            <label class="control-label" id="slider-label" for="slider">Slider</label>
            <span class="irs irs--shiny js-irs-0 irs-with-grid"><span class="irs"><span class="irs-line" tabindex="0"></span><span class="irs-min" style="visibility: visible;">1</span><span class="irs-max" style="visibility: visible;">100</span><span class="irs-from" style="visibility: hidden;">0</span><span class="irs-to" style="visibility: hidden;">0</span><span class="irs-single" style="left: 47.304794%;">50</span></span><span class="irs-grid" style="width: 94.653793%; left: 2.573104%;"><span class="irs-grid-pol" style="left: 0%"></span><span class="irs-grid-text js-grid-text-0" style="left: 0%; margin-left: -1.446069%;">1</span><span class="irs-grid-pol small" style="left: 6.7340067340067336%"></span><span class="irs-grid-pol small" style="left: 3.3670033670033668%"></span><span class="irs-grid-pol" style="left: 10.1010101010101%"></span><span class="irs-grid-text js-grid-text-1" style="left: 10.10101%; visibility: visible; margin-left: -2.16128%;">11</span><span class="irs-grid-pol small" style="left: 16.835016835016834%"></span><span class="irs-grid-pol small" style="left: 13.468013468013467%"></span><span class="irs-grid-pol" style="left: 20.2020202020202%"></span><span class="irs-grid-text js-grid-text-2" style="left: 20.20202%; visibility: visible; margin-left: -2.16128%;">21</span><span class="irs-grid-pol small" style="left: 26.936026936026934%"></span><span class="irs-grid-pol small" style="left: 23.569023569023567%"></span><span class="irs-grid-pol" style="left: 30.3030303030303%"></span><span class="irs-grid-text js-grid-text-3" style="left: 30.30303%; visibility: visible; margin-left: -2.16128%;">31</span><span class="irs-grid-pol small" style="left: 37.03703703703704%"></span><span class="irs-grid-pol small" style="left: 33.67003367003367%"></span><span class="irs-grid-pol" style="left: 40.4040404040404%"></span><span class="irs-grid-text js-grid-text-4" style="left: 40.40404%; margin-left: -2.16128%;">41</span><span class="irs-grid-pol small" style="left: 47.138047138047135%"></span><span class="irs-grid-pol small" style="left: 43.77104377104377%"></span><span class="irs-grid-pol" style="left: 50.505050505050505%"></span><span class="irs-grid-text js-grid-text-5" style="left: 50.505051%; visibility: visible; margin-left: -2.16128%;">51</span><span class="irs-grid-pol small" style="left: 57.23905723905724%"></span><span class="irs-grid-pol small" style="left: 53.87205387205387%"></span><span class="irs-grid-pol" style="left: 60.6060606060606%"></span><span class="irs-grid-text js-grid-text-6" style="left: 60.606061%; visibility: visible; margin-left: -2.16128%;">61</span><span class="irs-grid-pol small" style="left: 67.34006734006734%"></span><span class="irs-grid-pol small" style="left: 63.973063973063965%"></span><span class="irs-grid-pol" style="left: 70.7070707070707%"></span><span class="irs-grid-text js-grid-text-7" style="left: 70.707071%; visibility: visible; margin-left: -2.16128%;">71</span><span class="irs-grid-pol small" style="left: 77.44107744107744%"></span><span class="irs-grid-pol small" style="left: 74.07407407407406%"></span><span class="irs-grid-pol" style="left: 80.8080808080808%"></span><span class="irs-grid-text js-grid-text-8" style="left: 80.808081%; margin-left: -2.16128%;">81</span><span class="irs-grid-pol small" style="left: 87.54208754208754%"></span><span class="irs-grid-pol small" style="left: 84.17508417508417%"></span><span class="irs-grid-pol" style="left: 90.9090909090909%"></span><span class="irs-grid-text js-grid-text-9" style="left: 90.909091%; visibility: visible; margin-left: -2.16128%;">91</span><span class="irs-grid-pol small" style="left: 96.96969696969697%"></span><span class="irs-grid-pol small" style="left: 93.93939393939394%"></span><span class="irs-grid-pol" style="left: 100%"></span><span class="irs-grid-text js-grid-text-10" style="left: 100%; visibility: visible; margin-left: -2.876492%;">100</span></span><span class="irs-bar irs-bar--single" style="left: 0px; width: 49.521951%;"></span><span class="irs-shadow shadow-single" style="display: none;"></span><span class="irs-handle single" style="left: 46.848847%;"><i></i><i></i><i></i></span></span><input class="js-range-slider irs-hidden-input shiny-bound-input" id="slider" data-skin="shiny" data-min="1" data-max="100" data-from="50" data-step="1" data-grid="true" data-grid-num="9.9" data-grid-snap="false" data-prettify-separator="," data-prettify-enabled="true" data-keyboard="true" data-data-type="number" tabindex="-1" readonly="">
          </div>
        </form>
      </div>
      <div class="col-sm-8" role="main">
        <div class="shiny-plot-output html-fill-item shiny-bound-output" id="plot" style="width:100%;height:400px;" aria-live="polite"><img decoding="async" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAB0oAAAMfCAIAAADXIsN8AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAABYlAAAWJQFJUiTwAAAgAElEQVR4nOzdeXxU1f038JsEwhKCrCKbiLK44UYFlUoVUdEqKgooLhV9oO4bagVt+VFR0PqzClpcKBUQFRdc0NYtLlVRUQRRKptLlEXAyL4mmXn+mMd5pkmAISQZ7vB+/3Vy7rn3fucw1xd+ODk3IxqNBgAAAAAAhE1mqgsAAAAAAKA8xLsAAAAAAKEk3gUAAAAACCXxLgAAAABAKIl3AQAAAABCSbwLAAAAABBK4l0AAAAAgFAS7wIAAAAAhJJ4FwAAAAAglMS7AAAAAAChJN4FAAAAAAgl8S4AAAAAQCiJdwEAAAAAQkm8CwAAAAAQSuJdAAAAAIBQEu8CAAAAAISSeBcAAAAAIJTEuwAAAAAAoSTeBQAAAAAIJfEuAAAAAEAoiXcBAAAAAEJJvAsAAAAAEEriXQAAAACAUBLvAgAAAACEkngXAAAAACCUxLsAAAAAAKEk3gUAAAAACCXxLgAAAABAKIl3AQAAAABCSbwLAAAAABBK4l0AAAAAgFAS7wIAAAAAhJJ4FwAAAAAglMS7AAAAAAChJN4FAAAAAAgl8S4AAAAAQCiJdwEAAAAAQkm8CwAAAAAQSuJdAAAAAIBQEu8CAAAAAISSeBcAAAAAIJTEuwAAAAAAoSTeBQAAAAAIJfEuAAAAAEAoiXcBAAAAAEJJvAsAAITSqlWrCgsLU10FAEAqiXcBAIBQOv744z/44INUVwEAkEriXQAAIGTWrVs3f/78n3/++bPPPvv5559TXU4ZioqKNv4iEomkuhwAIG2JdwGAXdShhx6a/Ytvv/22zDHHH398fMx//vOfeP/GjRvj/SeffPLOlLFly5b1v4hGoztzKZK0cePGe++9t0uXLg0aNMjOzj7ttNNSXVEIzJo1K/aFHzZsWKynAp+CqrTdJ27x4sUnn3xybm5u+/btv//++0GDBjVs2HCfffZ55plndvIJ3bJlS3zGevXqFe8v30yOGzeu9i9mzJixM4VtwyeffBIrbMSIEZV0CwBgFyfeBQB2UUVFRYW/2FpqU5igxJjEQztTxsiRI+v8Yvny5TtzKZJRXFx8+OGHDxo0aNq0aStXriwsLCwqKkp1Ubu6wsLC0047rbCwMCMj48Ybb0zsr5CnoCpt+4n78ssvW7Vq9frrr5foz8/P79OnzwUXXLCTCW98ukp863bZmTzyyCO7dOlSWFg4ZMiQBQsWpLocACAFxLsAAOxCZsyYMW/evPiPHTt2bNu2bQrrCYX77rtv8eLFQRDccccdOTk5qS6nshQWFp544onFxcWJnSeddFKbNm1i7SeeeOIf//hHKkpLpVGjRsUaO59uAwBhJN4FAGAXMmfOnHj7hRde+PTTT0ePHp3CenZ9K1asuOWWW2Lt3//+96ktplK99dZbP/74Y6w9cODAb7/9tn379oMHD543b1737t1j/YMGDdrdIs4OHTp07NgxCILp06e/8sorqS4HAKhq1VJdAABAxatVq9buFvGkjQ0bNsTbrVu3TmElYXHzzTfH3tx10UUX5ebmxvvT7yl4//33Y42GDRs+9NBDGRkZtWrVCoIgMzPzpptuevPNN4MgWLVq1U8//dS4ceMKvO+uP5N//OMfzzzzzCAILr744mXLlmVlZaW6IgCg6li9CwCkoW2/Cqm4uPjll1/u27dvq1atatSo0bRp0x49ejz00EM///xz4rArr7wyOzt76NCh8Z7mzZtnZ2ffcccdpW/31FNP9e3bt2XLlrVq1WrRokXv3r0ff/zx9evXb63CmTNnXnLJJS1atIgXuXr16ueee67Mt8ktW7Ys3v/cc88FQfDZZ5/16dOnfv36eXl58WGFhYUvvvjieeedt99++9WqVatx48bdunX73//93++//77E3VevXn3LLz777LNoNDp16tQePXrUrVu3adOmV1xxxcKFC2MjCwoKhg0bdvDBB+fk5HTu3Hns2LElfjU+GUnOT2zCr7rqqnjP4Ycfnp2dfcABB2z7+tudn82bN//5Fx9++GEQBJ988sn555/fqFGjunXrdu3adezYsZs3b97J+pOppMJnfsWKFY899lisfdlll5Uou8ynoByzsXDhwvil8vLyotHoa6+9dsopp9StW7dJkyb9+vX74IMPSteWeNb1119f4mjfvn3jR9euXRsk8cQtW7Ys1ujcuXNGRkbi1Y4++ujnfxHLfOOSfN63YbuvVvv8888HDBjQsmXLnJycrl27/uMf/0hmw+i5c+fefPPNhx56aK1atZo2bXr++ec/++yzpff2TebxP+mkk2KNgoKCf/7zn0l+LgAgTUQBAHZJBx54YPxvLF9//XWZY7p06RIf8+WXX8b7E1eAHn/88YmnrFixIr5TZ2kTJ06MjywRlsUNGzYs8YLvvfde4pLJRLVr13799ddL1ByJRK677rrSg1u1anXPPfeU+ZHjv5AeBMHTTz/90ksvxX984403YmPy8/NbtGixtc919dVXRyKR+AV/+OGH+KFx48ZdffXVpU9ZsGDBf/7zn+zs7BL9Rx999JYtW5L/c0x+frY24W3atNn2LbY7P2vWrIn33H333Q8++GDpu+y1114//fTTztSfTCUVPvOJ35nNmzcnHtraU1CO2Zg/f3780EsvvXTRRReVPuWss84qUUDiWVdffXWJynv16hU/umbNmm18AeJP3J133hnrqV279qZNm6LR6GGHHfb2229v47uR/PMejUYTQ+3TTz99uzMZjUaLi4sT/0EirmPHjjfccEP8x+nTpyeetWXLlksuuaTMkpo2bbpo0aLEwck8/tFo9LjjjovfehsTAgCkH6t3AYDdSHFx8THHHBNfIFnahRdeOHv27OQv+OGHHx577LGxtYelbdiw4aSTTor9znjcPffcc99995UenJ+f/4c//GG7d3z11Vdjv4WdqKio6Kijjlq0aNHWzho9evSUKVPKPDRs2LAyN7c944wzjjrqqC1btpTo//DDD8usv0zlmJ+dVOb8JHr00UevvPLK0v0//vjjBRdcUKJzZ+rfbiUVMvPx12p17dq1dCK8XTs0GzG///3vJ0yYULr/+eef79Onz44WsENOO+20WGPDhg09e/b87rvviouLt7HsusKf99KGDBnywAMPlO6fMWPGvffeW+Yp0Wi0Z8+e48aNK/Po0qVL27dvv3z58jKPbuNL1bdv3/itCwoKtl86AJA2Up0vAwCULXH1bmZmZvWyJP6tJpnVu4lJ3B/+8Ie1a9dGIpF169Ylpma9evVKLCPxV8WXLFmSeGjz5s0NGzaMHx0+fPjq1asjkcjq1asTN3DIzc3dsGFD7JQSscuECRM2btxYXFz8xRdftGrVKvHQ1lbvxjVs2PCggw569913o9HoO++8E+8/55xzfv7550gksmnTpqeffjref/LJJ8cvmLiGNAiCG264YcmSJatWrXrmmWdK3GXixIlr165dtWpV4i/X16tXL5k/wXLMTzQajW81EARBbPuC7dru/CSuVw2C4MADD1ywYEEk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V155ZWtBVfXq1Z955plzzjknsXPChAmJ+4TGdezYcejQoUl+kBImT54cX8P7/PPPn3XWWdddd91333337LPPxu++cuXKEr8JXgXKMT+7lF28/rp163bo0CHWfvPNN6vmpsOGDTv22GNL95933nljxowp0Xn33Xcn7iARd9lll/3hD38o8/rbeOIaN2788ccf5+bmJg546qmnXnvttVj73nvvja/wDcr1vO+oRx55pF+/fqX7u3fvXubmDEEQZGZm/vvf/77ooou2dvTvf/974sYgSZoxY0a8nbhtNACQ9sS7AMDu5eSTT161atXYsWN/+9vfNm/ePDMzs2bNmh06dBg4cGBeXt6cOXMSNwwNguCQQw6ZOnXqNuKSU089dcWKFePHjz/jjDP23HPPzMzMRo0anX766Y8++uhPP/1UOvurXr36Cy+88Pbbb/fu3bthw4bZ2dmdOnV64IEHpk2blrheL/5+qmTsu+++S5cuHTJkSOvWrbOysvbff/+hQ4cuXbr07LPP7ty5c2xMJBJJXMy4k5JPxHZ0fnY1u1r9JWb+2muvjTXeeeedil2gvTV77bXXW2+9NX78+OOOOy47O7tRo0bnnXfetGnTnnjiidLvl6tTp86XX345evToo48+Oisrq27duj169Pj3v/89ZsyYGjVqlHn9bT9xhxxyyIoVK1566aVLLrmkdevWQRBUr169S5cujzzyyPLly6+//voS43f0ed9RWVlZkyZNeu+9984555y6devWrFnzhBNOGD9+/Kuvvrrnnntu7awaNWqMHz/+66+/vu222w4//PCsrKycnJzjjz/+vvvuW758eXxF8A55/vnnY42TTz65RAIOAKS3jMRXrAIAsDXRaDQSicR+ubuSbnH55Zc/9NBDsfaKFSsaNWpUSTcibaxatap+/fqx9vTp04888sjKuMuCBQviG9Q+/PDDiVsuVJ5knrjDDz98xIgRPXr0qIJ6dmWbNm2qVatWrP3aa6+ddNJJqa0HAKhKJf+BHQCAMmVkZGzjt7yTNGPGjPnz58faHTt2THynUyQSeeGFF+I/xjM72IZ69epddNFFEyZMCILgoYceqqR4NyUq5InbTbzxxhuxRqNGjZJ8bR0AkDZszgAAUHXmzp3b7xcnnHDC/PnzI5FINBpdvnz57373ux9//DE2rHfv3oItkjR8+PBYY9y4catX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AgAAAACEkngXAAAAACCUxLsAAAAAAKEk3gUAAAAACCXxLgAAAABAKIl3AQAAAABCSbwLAAAAABBKScW748ePX7ZsWWWXAgAAAABA8pKKdwcMGLDXXnsdcMABDzzwwPfff1/ZNQEAAAAAsF1Jxbu9e/fOysqaO3fu1Vdf3apVqxYtWowYMWLevHnRaLSy6wMAAAAAoExJxbuTJk3atGnTjBkzbrvttr322mvx4sVDhgzZf//9GzZsOHjw4NmzZ0cikcouFAAAAACARMm+Wq1atWpHHHHE7bffvnTp0kWLFo0bN65r164rV64cOXLkoYceWr9+/T/96U8//PBDpdYKAAAAAEBcsvFuoubNm/fv3//dd99dt25dXl7ewIEDi4qKbr/99r333rtLly7//ve/bdoAAAAAAFDZyhPvxmVlZVWvXr1mzZrVqlWL9UybNu03v/nN3nvvPXXq1IooDwAAAACAspUn3l26dOnjjz9+4okn1qpVq2vXrqNGjVqzZs3ee+995513zpkz55NPPgmCoGfPnuPGjavoagEAAAAA+H+qJTmuqKjoiy++ePHFF8eOHbt48eJ4f+fOnQcOHNijR49mzZrFOz///POGDRsOHz78kksuqeB6AQAAAAAIgiDJePf888+fPHlycXFx7MfMzMyzzz77oosu6tq1a926dUuPb9CgQRAE33//fQUWCgAAAABAoqTi3Weeeaa4uDg3N/fSSy/t27fvEUcckZ2dvY3xhYWFd955Z8OGDSuoSAAAAAAASkoq3h0xYsRpp53Wrl27jIyMZMZXr1598ODBO1cYAAAAAADbktSr1QYNGtS+ffuMjIx169bNnDkz8dCXX365ZcuWyqkNAAAAAICtSireDYKgoKDgjDPOyM3NPeKIIxL7jzjiiFq1ag0ePFjICwAAAABQlZKKd9evX9++ffuXXnopMzPzoosuSjzUu3fvSCQycuTILl26xN+9tquJRCJLly79+uuvV61alepaAAAAAAAqRlLx7t/+9reCgoJTTz11zZo148ePTzw0adKk5cuXH3bYYZ9++umzzz5bOUUmKz8/f8SIEddee+1TTz21fv36WOfzzz/fqFGjZs2atWnTpn79+vvvv//bb7+d2joBAAAAAHZeUvHuxIkTgyAYN25cTk5O6aONGzeOBbsPPfRQxRa3QyZNmrTPPvsMGTJk1KhR5513XrNmzZYtW/bkk0/26tVr5cqVQRDk5uYGQTBv3rxu3brdfvvtKSwVAAAAAGDnJRXvLliwIAiCPffcc2sD9ttvvyAIPvroo4oqa0d9/fXXF1xwQRAEPXv2HDduXGyhcbdu3S688MIgCCZNmrR58+Y1a9YUFRXl5eXVrFnzT3/60xdffJGqagEAAAAAdl5S8e4+++wTBMGaNWu2NqCwsDAIglq1alVQVTvsvvvuC4JgxIgRL774Yv/+/V955ZURI0b85z//KS4ufuSRR/r165ednR0EQVZWVrdu3Z588sn4KQAAAAAAIZVUvBtbGPvnP/95awMmT54cBMHpp59eUWXtqFdeeSUIgiuuuCLec+WVV8Yaffv2LTG4R48eQRC89tprVVUdAAAAAEDFq5bMoGuuuebuu+++9957Fy5cePvtt7dr165mzZpBEEQikUWLFo0bN27YsGFBENx6662VW+zWLV68OPhld92YeDuxMyZW/NKlS8t9u0MOOWT+/PnJjCwsLKxXr165bwQAwC6osLBw1KhRDz74YKoLoSJdeOGFjz76aKqrAADYMUnFu7m5uZ988smRRx750ksvvfTSS7GenJycFStWFBcXx8Y8/fTT7dq1q8RKt6l169bz5s3bsGFD/OVv8a0k1q1bVyLh3bBhQxAEO1Pt1Vdf/fXXXycz8oUXXti0aVO5bwQAwC6ouLj4gAMO6Ny5c6oLocK8/vrrs2bNSnUVAAA7LKl4NwiCdu3aLVmyZPTo0X/961+XL1++du3atWvXBkGQmZnZr1+/O+64Y++9967MOrfjjDPOuPvuu//+979fc801sZ6//e1vscbYsWOvv/76xMGPP/547JRy327AgAFJjly4cOHcuXPLfSMAAHZNHTt2LPGXTEJtwYIFGzduTHUVAAA7LNl4NwiCnJycW2655ZZbblm9evVPP/20efPmPfbYY88996xevXrl1Zek66677u6777722mtfeumlnj17/utf/3r11Ve7dOkyZ86cG264obi4uH///nXr1i0oKPj73/9+2223Bf+9US8AAAAAQOjsQLwbt8cee+yxxx4VXsrOaNq06bvvvnviiSfm5eXl5eUFQZCbm/vss8/m5+cfddRRN91000033ZQ4/p577kntcmMAAAAAgJ2UbLwbiUQ++OCDadOmzZ07dxubyT755JMVVNgO69q1a0FBwcsvvzxv3rzWrVv36tWrTp06e+211/Tp0wcOHBjfSKt9+/YPPPBA9+7dU1UnAAAAAECFSCre3bx5c7du3aZNm7bdkSmMd4MgqFOnzrnnnlui88gjj5w5c+aGDRvWrl1bp06d+LvXAAAAAABCLal4969//eu0adMyMzMHDx583HHH1a1bNyMjo7Irq1i1a9euXbt2qqsAAAAAAKgwScW7jz32WBAE77777q9//evKLQcAAAAAgORkJjPohx9+CILgmGOOqeRiAAAAAABIVlLxbufOnYMgKC4uruRiAAAAAABIVlLx7pVXXhkEwRtvvFHJxQAAAAAAkKyk4t1evXqde+65Z5999vTp0yu7IAAAAAAAkpHUq9WeeOKJY4455rnnnuvcufMhhxzym9/8pl69etWqlXHun/70p4quEAAAAACAMiQV7/bv37+wsDDWnj179uzZs7c2UrwLAAAAAFA1kop3n3/++Wg0WtmlAAAAAACQvKTi3d/+9reVXQcAAAAAADskqXg3USQSWbly5fr167ds2dKmTZsgCKLRaEZGRiXUBgAAAADAVmUmP/Sbb77p379/9erVGzVq1KpVq7Zt28b6W7Zs+fTTT1dOeQAAAAAAlC3ZeDcvL2+//fZ77LHHIpHInnvumXho+fLlffv2HTRoUCWUBwAAAABA2ZKKd1esWNG9e/cgCO6///7Vq1cvW7asevXq8aP5+fktWrS49957Z8+eXVllAgAAAADw35KKd0ePHh0EwaOPPnrNNdfUrVu3xNGmTZtOnjw5CIJ77rmnwusDAAAAAKBMScW7Tz75ZBAE/fr129qAI488MgiCN998s6LKAgAAAABg25KKd5cuXRoEQa1atbY2ILZXw/LlyyuqLAAAAAAAti2peLdTp05BECxevHhrA9atWxcEQZs2bSqqLAAAAAAAti2peHfgwIFBEPTv37+oqKjMAaNGjQqC4OKLL664wgAAAAAA2Jak4t3evXt37NjxzTff7NChw2uvvbZ8+fJIJBIEwdq1a2fPnt2/f/9bb721fv36V199dSVXCwAAAADA/1MtmUFZWVnvvPNOjx49Pvjggx49esT769atG2vUr1//008/zcnJqZQaAQAAAAAoJanVu0EQ1KlT57333svLy+vZs2dm5v8/q3379g888MDSpUv33XffyqkQAAAAAIAyJLV6NyYjI6Nbt27dunWLRqMbNmwoLi6uXbt2tWo7cAUAAAAAACpKecLZjIwM+zAAAAAAAKRWspszAAAAAACwS0lq9e7o0aMjkUgyI6+99tqdqwcAAAAAgKQkFe8OGjSosLAwmZHiXQAAAACAqpFUvDtx4sQSq3ej0eimTZu+/fbbt956a9q0aTk5OY899ljTpk0rp0gAAAAAAEpKKt7t27fv1g7dfvvt33zzzfHHH3/bbbd9/vnnFVcYAAAAAADbUgGvVtt3332nTp06b968++67b+evBgAAAABAMiog3g2CoEOHDkEQjBkzpkKuBgAAAADAdlVMvBvbmXfRokUVcjUAAAAAALarYuLdCRMmBEHwq1/9qkKuBgAAAADAdiX1arU77rijuLi4zEMbNmx4++23p0+fHgTBtddeW5GlAQAAAACwdUnFu8OGDSssLNz2mEGDBp177rkVURIAAAAAANuXVLw7ceLE2O66pWVmZjZr1uyggw5q0KBBhRYGAAAAAMC2JBXv9u3bt7LrAAAAAABgh1TMq9UAAAAAAKhiSa3evfDCC4uKispx9SeffLIcZwEAAAAAsF1JxbuTJ0/e7qvVyiTeBQAAAACoJEnFu/n5+e+9916/fv0aNmx46623du7cec899ywuLl6yZMnHH3981113FRQUDB8+/JRTTqnscgEAAAAAiEkq3o1GoxdccEGXLl1ef/31GjVqxPvbtGnTtWvXq6+++pRTTvnzn/988cUXN2/evNJKBQAAAADg/0vq1Wp33XVXYWHh+PHjE7PduJo1a/7f9u48PMrq0B/4mUwSQkKQJYIIiChKcaFuqID2US5V3FBacF/QVqzFWq371S5uVEUQcbebUhdAi1hXVJRWEUW8Lq2IimBkExDQRCBkmJnfH3N/uWkgEHFgeMPn84fPzJmT9/1O5kkm8/Vw3r/+9a/V1dW/+93vspwOAAAAAIB6NKjefeSRR0IIO+20U30TOnToEEJ44oknshULAAAAAIANa1C9m5eXF0JYvXp1fROqq6tDCFVVVdmKBQAAAADAhjWo3j3xxBNDCDfddFN9Ex599NEQwnHHHZetWAAAAAAAbFiD6t1rrrmmoKDghhtuOP/88+fNm5dOpzPj6XT6yy+/vP766wcPHhxC+M1vfrP5ggIAAAAAUFt+Qya1bdt2xowZvXr1uvfee++99968vLyOHTsWFBTMnTs3mUxm5tx3333dunXbnFEBAAAAAPg/DVq9G0Lo3r374sWLb7311nbt2qVSqfLy8tmzZ2e63aOPPvq9994bMmTI5swJAAAAAMB/aNDq3YySkpJLLrnkkksuqaioWLZsWSKRaNasWVlZWWFh4ebLBwAAAADAen2LejcjlUolEol4PJ5MJnfccccQQjqdjsVimyEbAAAAAAD1aujmDCGEOXPmnH322QUFBWVlZZ06ddptt90y4x07dhw/fvzmiQcAAAAAwPo1tN6dPHnyrrvu+sADD6RSqTZt2tR+aMmSJSeddNIll1yyGeIBAAAAALB+Dap3ly5d2rdv3xDC7bff/vXXXy9evLigoKDm0fLy8g4dOowcOfL999/fXDEBAAAAAPhPDap377jjjhDCH/7whwsvvLB58+Z1Hm3Xrt24ceNCCLfeemvW8wEAAAAAsF4NqncfffTREMKpp55a34QePXqEEF566aVsxQIAAAAAYMMaVO8uWrQohNC0adP6JmT2aliyZEm2YgEAAAAAsGENqncPPPDAEMKCBQvqm/DNN9+EELp06ZKtWAAAAAAAbFiD6t0hQ4aEEM4+++y1a9eud8Lo0aNDCIMHD85esKxZu3bt3Llz33zzzffff/+rr77KdRwAAAAAgOxoUL07aNCg/fff/6WXXtp7770nTZq0ZMmSVCoVQqisrHz//ffPPvvsq6++umXLlr/4xS82c9oNWb169W9+85vaV35LpVIjR44sLS3dZZddDj744O9///stW7Y85JBDZs6cmcOcAAAAAABZkd+QSfF4fMqUKf369Zs6dWq/fv1qxmu61JYtW86YMaOkpGSzZGyAqqqqbt26lZeX1x487bTTxo4dm7m9//77J5PJd999d+rUqXvuueeDDz545pln5iIpAAAAAEB2NGj1bgihWbNmr7766uTJk/v375+X939f1bVr1zvvvHPRokW77LLL5knYIPfff395efm+++47f/78zMiUKVPGjh1bXFz8zDPPVFdXz5gx45133qmqqpowYUJhYeFZZ501e/bsHAYGAAAAAPiOGrR6NyMWi/Xp06dPnz7pdHrVqlXJZLK4uDg//1scYfPJrNIdO3Zs+/btMyMjR44MITz11FN9+vSpmdakSZMBAwY8++yzffv2HT58+H333ZeTtAAAAAAA312DytmLL744mUxmrp8WQojFYjnch2G9/v3vf4cQunTpUjPyj3/8I4RwyCGHrDv5sMMOCyE888wzm3y6t99+e+7cuQ2ZuWDBgkQiscknAtiwhQsXvv7667lOQTa99957uY4AAABAZDSo3p0wYcLnn38+cuTIrWSt7roywZLJZM3GEaWlpRUVFesNnJnzxRdfbPLp+vbt+9VXXzVwcmlp6SafCGDDLr300kcffTTXKQAAAIDcaFBde+mll1544YVvvfVWz549N3egTXP44YdPmDDhtddeO/zwwzMj/fv3v+eee8rLyzt37lxn8rx580IIBx100Caf7uOPP168eHFDZv7qV7+q2Q4YIOtWr17dqVOnUaNG5ToIWXPTTTe9+eabuU4BAABANDSo3v35z3/+97///eijj548efJ+++23uTNtgksvvXTChAnHHXfc9OnT99hjjxDCVVdddf/99w8ePHjy5Mm11/CuWbPm1FNPDSEMHTp0k0+3/fbbb7/99g2Z2bx589pXogPIuqKiotpb0xB1W9v2RwAAAGzNGlTv3n333T179nzllVf233//Dh069OvXr2nTpuudWbM/7xbWs2fP//7v/x42bNiee+75ox/9aMiQIXvttddjjz02cODArl27XnPNNXvttdfatWunTZs2bNiwZcuW7bvvvieddFJOogIAAAAAZEWD6t1LLrmk5vpg8+fP/+Mf/1jfzFzVuyGEG2+88eCDD/7pT386YcKECRMm1IzPmTPnnHPOqT2zV69ezz//fDwe3+IZAQAAAACypkH17ssvv5xOpzd3lO/uuOOOW7hw4dtvv/3000+/9dZbb7311rJly2oe7dKlS//+/U8++eQePXrkMCQAAAAAQFY0qN495JBDNneObInH4wceeOCBBx6YuZtMJpPJZAihoKAgFovlNBoAAAAAQDY1qN6Nrng8bhMGAAAAAKBRyqvvgZKSksLCwi0ZBQAAAACAhqu33k0kEjWXU6vt2GOP7dev3+aMBAAAAADAxn3rzRleeOGF9da+AAAAAABsSfWu3gUAAAAAYGum3gUAAAAAiCT1LgAAAABAJKl3AQAAAAAiSb0LAAAAABBJ6l0AAAAAgEjK3/DDb7zxRjwerz2STCbXO57Ro0ePLIYDAAAAAKA+G6l3e/bs+a3G0+n0d00EAAAAAEAD1FvvlpSUJBKJLRkFAAAAAICGq7feXbFixZbM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MH/+/I4dO2508vLly0MIu+222yaf7tNPP62srGzg5NatW2/yiXKoXbt2X3755apVq3IdhGxKJBL5+fkN/L8gREIqlUqlUvn5jeSXORmJRMI6o0bGa9r4eEttfLylNkp+/TY+XtPGx2vaKEW0CouuRvLny09+8pNf/epXp5xyyosvvti0adMNzKyurh48eHAI4eyzz97k0xUXF9fZ27dRKi0tLS0tzXUKAAAAAGD9Gsm/a1izZk23bt3mzp3bvHnzyy+/vF+/fh07dmzZsmU8Ho/FYqlUqrKyctGiRf/4xz9++9vfLlmypF27dnPmzLGtIQAAAAAQXY2k3g0hVFRUnHrqqc8888xGZx5wwAEvvvhiixYttkAqAAAAAIDNpPHUuxkffPDBmDFjJkyYMHv27DoPtW7d+sc//vFPfvKTHj162CgNAAAAAIi6xlbv1kilUl999dXq1atTqVRRUVGzZs02vCcvAAAAAEC0NNp6FwAAAACgccvLdQAAAAAAADaFehcAAAAAIJLUuwAAAAAAkaTeBQAAAACIJPUuAAAAAEAkqXcBAAAAACJJvQsAAAAAEEnqXQAAAACASFLvAgAAAABEknoXAAAAACCS1LsAAAAAAJGUn+sAwJZz1llnLV++vFWrVrkOAtRr5cqV5eXle+yxR66DABvyySeflJWVtWzZMtdBgHp988038+bN69atW66DABsye/bsK6+88rjjjst1EIiwWDqdznUGYAspKipq0qSJehe2ZhUVFcuXL99pp53y8vwLG9h6ff75582aNfOWCluzr7/+esWKFZ06dYrFYrnOAtSrvLz85JNPfuSRR3IdBCLM6l3YhhQXF59++umjR4/OdRCgXqNGjbr44ovfe++9Fi1a5DoLUK/S0tKf/vSnI0aMyHUQoF633HLLFVdcMXPmzOLi4lxnAerVpEmTTp065ToFRJuVQQAAAAAAkaTeBQAAAACIJPUuAAAAAEAkqXcBAAAAACJJvQsAAAAAEEnqXQAAAACASFLvAgAAAABEknoXAAAAACCS1LsAAAAAAJGk3gUAAAAAiCT1LgAAAABAJKl3AQAAAAAiSb0L25CWLVu2bNky1ymADWnZsmXTpk0LCwtzHQTYEG+psPVr2bJlSUlJfn5+roMAG+ItFb67WDqdznUGYAtZuHDhdtttV1JSkusgQL0SicSCBQt23nnnXAcBNmTBggUtW7YsLi7OdRCgXtXV1YsWLerUqVOugwAbMn/+/NatWzdt2jTXQSDC1LsAAAAAAJFkcwYAAAAAgEhS7wIAAAAARJJ6FwAAAAAgktS7AAAAAACRpN4FAAAAAIgk9S4AAAAAQCSpdwEAAAAAIkm9CwAAAAAQSepdAAAAAIBIUu8CAAAAAESSehcAAAAAIJLUuwAAAAAAkaTeBQAAAACIJPUuAAAAAEAkqXcBAAAAACJJvQsAAAAAEEnqXQAAAACASFLvwralsrJyxIgRvXr1Kikp2X777Y8//vhnnnkmnU7nOhewfjNnziwsLPzb3/6W6yBACCGsWbNm9OjR3bt3b9q0affu3W+66aaVK1fmOhRQL2+jsNXyyRSyKOaHB7Yds2fP3meffdb9IHr00Uf//e9/j8fjOUkFbMBJJ500fvz48ePHDxo0KNdZYFtXWVm5zz77zJkzp/ZgaWnpzJkzO3TokKtUwAZ4G4Wtk0+mkF1W78K2IpFIHHzwwStXrjzllFMWLlyYSqWqqqqeffbZoqKiZ5999s4778x1QOD/fPPNN++888555503fvz4XGcB/tcZZ5wxZ86cnj17lpeXp1Kpr7/++pJLLqmsrDzwwAMTiUSu0wH/x9sobM18MoWss3oXthUvvPDCkUceecABB0yfPj0Wi9WMT5o0qV+/fkVFRZWVlfn5+TlMCGRceOGFd9xxR+0Ry44g52bNmtWtW7eSkpLFixeXlJRkBtPp9KGHHjp16lQ/pLD18DYKWzmfTCHrrN6FbcXkyZNDCBdddFHtd9AQQp8+fUIIVVVV5eXluUkGAFu9hx56KIRw6aWX1nS7IYRYLHb55ZeHEO6+++6cJQOASPHJFLJOvQvbinfffTeEsOeee9YZLygoyNxwcRjYSowePTr9/51++um5jgOEEELm0kxHH310nfF99903hDBlypRUKpWDWMA6vI3CVs4nU8g6y91hWzFp0qT1jtdcImbHHXfcgnGABqmzqAHIiVQqNWvWrBDCTjvtVOeh7bffPnNj+fLlZWVlWzoZsEHeRmEr5JMpZJ3Vu7BNSyaTQ4YMCSEcdNBBPpQCwHpVVlZmbrRs2bLOQ0VFRZnVRhUVFVs6FgA0Fj6Zwneh3oVt14oVK3r37j158uSCgoLHHnss13EAYCu1atWqzI2afzdaW7NmzUIIVVVVWzQTADQWPpnCd6TehW3UxIkTd9xxxzfffLN9+/azZs3q2LFjrhMBwFZqw/vq5uXl1fwXAPhWfDKF786fodDYFP6nHj161JmwfPnyfv36DRgwoKqq6owzzvjkk0922WWXnESFbdlGf1SBrUdRUVHmxnp73hUrVoQQSktLt2gmAIg4n0whW1xaDRqbRCJR++7atWtr3502bVqfPn2qqqratWv32GOP9e7de8umA/7Xhn9Uga1KZvuFEEJFRUWrVq1qP5RIJDKdb4sWLXKQDACiySdTyCL1LjQ26XS6vofeeOONXr16hRAuuOCCESNGFBYWbsFcwH/YwI8qsLVp0qTJDjvs8MUXXyxatKhOvbt06dIQQosWLUpKSnKUDgAixidTyC6bM8C2YtWqVUcccUQI4d57773jjju8gwJAww0cODCE8PLLL9cZf/3110MIp556ag4yAUAE+WQKWafehW3F2LFjKysrBwwYcN555+U6CwBEzODBg0MI11577Zo1a2oGk8nkb3/725pHAYCN8skUsk69C9uKBx98MIQwYsSIXAcBgOjZb7/9evbsuWzZshNOOGHZsmUhhGXLlg0aNGjmzJn/9V//5eqIANBAPplC1tl7F7YJyWTyn//8Zwhht912y8tb///XmT179k477bRlcwFANMRiseeee27PPfd8/vnny8rKasY7d+48ceLEHAYDgAjxyRQ2B6t3YZuwYsWKzI1kMpmohws9AcAGbLfddp9++umtt9665557FhQUdO/effTo0R999FGzZs1yHQ0AosEnU9gcYn5sAAAAAACiyOpdAAAAAIBIUu8CAAAAAESSehcAAAAAIJLUuwAAAAAAkaTeBQAAAACIJPUuAAAAAEAkqXcBAAAAACJJvQsAAAAAEEnqXQAAAACASFLvAgAAAABEknoXAAAAACCS1LsAAAAAAJGk3gUAAAAAiCT1LgAAAABAJKl3AQAAAAAiSb0LAAAAABBJ6l0AAAAAgEhS7wIAAAAARJJ6FwAAAAAgktS7AAAAAACRpN4FAAAAAIgk9S4AAAAAQCSpdwEAAAAAIkm9CwAAAAAQSepdAAAAAIBIUu8CAAAAAESSehcAAAAAIJLUuwAAAAAAkaTeBQAAAACIJPUuAAAAAEAkqXcBAAAAACJJvQsAAAAAEEnqXQAAAACASFLvAgAAAABEknoXAAAAACCS1LsAAAAAAJGk3gUAAAAAiCT1LgAAbBXatm1bWVmZ6xQAAESJehcAALYKiUQi1xEAAIgY9S4AAI3N7373u0MOOSRze9KkSYWFhZdffnluI23A1KlTBw0aVFZWtmLFil122eWss856++23v+1B3njjjcLCwqFDh2buNvxZL1iwoLCw8KSTTvrWudcnlUrtvvvu48aNy8rRAADYKPUuAACNyrx586699tpbb701czedTicSiWQymdtU6XS6sLCwsLBwzZo1tccvv/zyQw455PHHH1+2bFkI4csvvxwzZswBBxxwxRVXfNvjJxKJVCpV+25DnnVm5tq1a7/V6eqTl5c3atSoc84555tvvsnKAQEA2DD1LgAAjcrPfvaz3Xbb7aCDDsrc7devXzqdHjFiRG5ThRASiUSd7Rdef/314cOHN2/efOrUqYlEori4+Isvvpg6dWpJScktt9zyz3/+c5PPlcNnfeSRRxYXF2/Ny6UBABoT9S4AAI3HO++88+yzzw4fPjwWi+U6y8Zllhg/9NBDvXr1ys/Pj8VixcXFvXr1evLJJ0MIv//973MdcFPE4/Gbb775nnvu+fTTT3OdBQCg8VPvAgDwvzL7t44cOTKZTN55550777xzZi/XzEauDz74YDqdnjhxYt++fUtKSnbfffdhw4bV2WoghLBq1arbb7+9R48eTZs23X333a+++uqlS5d+qxPdeOON6XR63LhxBx10ULNmzY466qhp06aFENLp9MMPP3zwwQeXlJT06NFj7Nix6XS6zpGHDBlSXFx8zDHH1IzU2YW2gc9l5MiRhYWFkydPTiQS9957b/fu3Zs1a3bkkUdOmTKlzhn/9re/FRYW/u53v1vv+MiRIzOpCgoKMuMlJSU1WzS8++67IYSahcY1Dj300BDC5MmTa0ZSqdS4ceMOP/zw5s2bl5WVnXDCCS+++GKoX31775aXl5977rlt27Zt1arV2WefPWfOnPV+eTKZHD9+fN++fZs1a7brrrtec801ixcvrjOnvhcxhHDyySeHEC677LINJAQAICvycx0AAICtRWYb1mXLlh1zzDGTJk0KIWT2cs2ML1++/Mwzz3zooYcykz/55JOrr776ySefnDZtWl7e/y4a+Oyzz3r06PHll1/WzBk2bNjNN9/89NNP9+vXr4EnqqysHDx48JgxYzKTn3/++eeff/7VV1+9++67H3300czgjBkzTjnllLlz51511VU1h/34449nzJhx8cUX5+fn1zlXzS60DXwuqVQqkUh8/vnnvXr1mjFjRmbaCy+88MILL1x22WW33HJLzfEzM9fd5TYznnleyWSyZkLt2926dZs7d+7bb7991FFH1f7awsLC2s11KpXq27fvK6+8UjPy5JNPPvnkk+ecc86f/vSn9byQ9ey9O2XKlMMPP7zm7gMPPDBmzJjRo0fX+dqVK1f26dNn+vTpmbtz5sy58cYbb7zxxueff/7II4+sc4p1X8QQQnFx8YABA5544okVK1a0bNlyvQkBAMgKq3cBAPgPN91007Rp01577bVEInHPPffUjF9xxRXjx49/6qmn1qxZk0wmX3/99Xg8Pn369JotYquqqg444IAvv/zyoosuWr58eSqVqqysvPfee5PJ5FFHHfXxxx838ES33nrrM88889prr61du3blypWnn356COHQQw999NFHH3zwwdWrVycSifHjx4cQ/vu//3v16tU1X/j444+HEPr377/R57jR55Jx7rnnLl269L333ksmk1VVVRMmTIjH48OHD3/uuee+1bf0T3/6U03TumrVqnQ63aRJkxDCL3/5yxDCwIEDx44dW/uJ1PHII4+88sor/fr1W7x4cSqVqq6u/p//+Z+ddtrpz3/+c031vFFfffVV3759QwgXXnjhihUrUqnUwoULjzjiiAsuuKD2tHQ6feyxx06fPv3www///PPPU6nUypUrMyVyv379Zs+eXeew9b2IP/7xj0MIzz77bAPjAQCwadS7AAD8h1Qq9dJLL/Xu3bv2GtgQQiKRmDJlyrHHHltYWJiXl9ezZ89f//rXIYTXX389M+Gee+5ZtmzZoEGDbrvttpYtW8ZisWbNmp133nl33nlnCOHnP/95A0+UTCZfffXV3r17x+Px4uLiYcOGZcbvvffeM888s6ioKD8/f9CgQXvssUcI4bPPPqv5wocffjiEsNtuu230OW70udRO0r1797y8vCZNmgwYMOCvf/1rCOH888/f+PfxP9XsBVyz0jmEcMQRR1x33XWrVq065ZRTiouLV65cud6vzTyvESNGtGnTJhaLFRQU7Lvvvn/5y19CCBMnTmxggFGjRiWTyTPOOOP2229v0aJFLBZr167d008/3aVLl9rT/vnPf06ZMqVLly4vvPBCx44dM9sBn3POOXfddVcI4dprr61z2PpexB49eoQQxo0b18B4AABsGvUuAAD/YYcddjjggAPWHd9jjz169uxZe+QHP/hBCOHDDz/M3B0+fHhYXwN49tlnhxAmT55cp76s70RdunTp1q1bzd2ysrLMjR/96Ee1p+2zzz4hhC+++CJzd+3atTNnzqw9fwM2+lwy+vfv37Fjx9ojgwYNisfj5eXlS5Ys2ehZGuLXv/71woULf//733ft2jWE0Lt377vvvnvZsmW151RWVob/3Io3hNCnT590On3DDTc05CzpdPq2224LIVx33XW1x+PxeJ0jZPZqGDVqVJ269owzzgghPPTQQ3U2fKjvRWzbtm0IYdKkSevujwwAQBapdwEA+A9HHHFEzVLT2jIFaG3NmzcPIaxatSqEUFFRsWjRohDC7rvvXmdacXFxhw4dQggLFixoyIkOO+yw2nfj8XjmRp1dXDPjNW3jihUrQgglJSWZfQ82bMPPpUbtrWYz8vPzM1dCmzdv3kb52gV6AAAI+0lEQVTP0kDt2rW78sorZ82aVVxc/K9//Wvo0KFlZWWnnXZaVVVVZsLgwYNDCBdeeOGgQYNeeumlr7/++tueYtmyZRUVFXl5eZ06darzUGaZbUY6nX7yySdDCPvtt1+daaWlpe3btw8hLF++vPZ4fS9iaWlpCKG6urrOtxQAgOxS7wIA8B+Ki4vXO96iRYs6I7V7vczl1Lp27VrTxtaW2TAh08Bu9ERt2rRZ73jtbQ3WlSk927Vrt4E5NTb8XGrsuuuu6w5+73vfqzndBmzCqtVYLDZnzpyJEye2b9/+kUceOfbYYzPjP/nJT66++uoQwuOPP/7DH/6wRYsWu++++9133/3VV1818MhLly4NIXTq1Gndp1n7W1FVVZWpyzt27Fi4jkw7X2cJdn0vYl5eXlFRUfj/S48BANhM1LsAADRInX+tX8fatWtDCPWtnM10gg1ZVxs2VuPWp7q6OoTQtGnThkze8HOpsd7AmWdaUFCw4a9ds2ZNQ05RR1lZ2fHHHz9z5syioqLJkyeXl5eHEGKx2A033FBRUTF27NiBAwcWFBR88sknQ4cObd269fTp0xty2EzmVCq17kO1a+iapdDJZDKxjsxDme9zQ5SUlHyr+QAAbAL1LgAAWbDddtuFED799NN1H0qlUm+//Xaof1luVmQa2/quTrZpMkuS6/joo49CCJmdCjZgzpw5G56waNGiwsLCzFYPdTRv3vzUU0+tc5DS0tKTTjrpscceW7169XvvvXfMMcekUqk6+xHXp1WrViGEefPmrdvw1n6ONeX4V199la7Huptv1CezucRGe3AAAL4L9S4AAFlQVlYWj8dXrlxZc62zGvPnz08mk0VFRTvssMPmC5DplxcvXpzFY06dOrXOyJo1a958881QaxeIzFrjdXeYnTJlyoYP3rJly0QiMWPGjPUuqs3ILICtIx6Pd+/e/YknnojH4wsWLFi9evVGnkYIbdu2zcvLS6VS624Z/Oqrr9Y+8t577x3qqem/lXQ6nanaM5vwAgCwmah3AQDIgng8ftppp4UQRo0aVeehO++8M4QwdOjQTdt1oYFat24dQli5cmUWdwO4995762wd+5e//CWE0K9fv5qFrjvuuGMI4eWXX6497bPPPquv3q3ZDKGoqKhz586pVGrGjBl15lRXVz/++OMhhC5dulRWVhYWFq53zWxmL4WGbHmRn5+fWQ48YsSI2uOJROI3v/lN7ZELLrhg3WkhhCVLljRp0qRdu3YN3FO4oqIihBCPx9fbUAMAkC3qXQAAsuOGG24IIdx888233nprZuXm6tWr77zzzuHDh8fj8auuumqznj0/P793794hhGXLlmXrmFVVVX369Jk7d246nU4kEmPGjDn//PNDCDfffHPNnG7duoUQ3n333T/+8Y/JZDKdTv/rX/867LDD6tSasVgsc9G5zz77rGbw+uuvDyH07dv3lVdeyWxum0gkysvL+/XrV1FRcdxxx7Vq1aq0tHSvvfb65JNPfv3rX2eeWjqdXrhw4VlnnRVCOPfccxtYmg8bNiyEcMcddwwfPjyz1njp0qXHHXdcne0sBg8eXFZW9sgjj1x33XXffPNNJtIbb7zRo0eP6urqESNGrPcadOvKLOLu3bt3A+cDALBp1LsAAGRHx44dJ06cGEK47LLLmjVrFovFiouLf/GLX8Tj8VdeeSWzunazOvHEE0MI77//frYOeMUVV8yfP3+XXXbJy8srLCzMNKo33XRT9+7da+a0aNHiyiuvDCGce+65+fn5eXl53bt3LygoeOCBB+oc7dhjjw0hdOvWLR6PZy68duqpp/785z+vrKzs06dPYWHhypUrW7duvfPOO7/yyiu77bbbI488kvnC8ePHx+PxG264oaysLBaL5eXltW/f/tFHH+3UqdPIkSMb+FxqXp3LL7+8pKQkFou1adPm9ddff+mll2pPKywsnD59euvWrX/729+WlpbGYrHCwsKePXt+/vnnF110UWYJcEN8+OGHIYRTTjmlgfMBANg06l0AALLm+OOPLy8vv+CCC9q3bx+Px/fcc89rrrlm4cKFhx566BY4+4ABA0IIf//737N1wP333//DDz/81a9+1aZNm+Li4v79+0+fPv2KK66oM23YsGFjx47t2bNnPB7v1KnTsGHD/vWvf3Xo0KHOtLvuuuvQQw/N7IGbGYnFYnfdddeMGTN+9rOfZTYmbt269amnnvr000/PnDmzWbNmmWldunRZuHDh1VdfvffeexcUFBQVFR100EF33XXXRx99VDOnIY4//viPPvroxBNPLC4u3mGHHS688MLZs2fvtddedaZ17ty5vLz8lltu+d73vhePx9u1a3f66ae/9957t912W8PP9be//S2EcMwxxzT8SwAA2ASxBm6eBQAAW7+DDjro/fffr6yszM/P/y7HufXWWy+77LLx48cPGjQoW9k2qlWrVuXl5Y3gWmRVVVUlJSWHH354naXBAABkndW7AAA0HrfffntVVdVzzz2X6yDbtHHjxqVSqbvuuivXQQAAGj/1LgAAjcfBBx98zDHHXHLJJVH8N2rLly9vBEt3U6nUlVdeecopp3Tt2jXXWQAAGj/1LgAAjcqf//znTz/99I033sh1kG3Uyy+/vGTJklGjRuU6CADANkG9CwBAo9KmTZvbbrtt6NChUVzAG3XpdPqCCy4YPnx4mzZtcp0FAGCb4NJqAAAAAACRZPUuAAAAAEAkqXcBAAAAACJJvQsAAAAAEEnqXQAAAACASFLvAgAAAABEknoXAAAAACCS1LsAAAAAAJGk3gUAAAAAiCT1LgAAAABAJKl3AQAAAAAiSb0LAAAAABBJ6l0AAAAAgEhS7wIAAAAARJJ6FwAAAAAgktS7AAAAAACRpN4FAAAAAIgk9S4AAAAAQCSpdwEAAAAAIkm9CwAAAAAQSepdAAAAAIBIUu8CAAAAAESSehcAAAAAIJLUuwAAAAAAkaTeBQAAAACIJPUuAAAAAEAkqXcBAAAAACJJvQsAAAAAEEnqXQAAAACASFLvAgAAAABEknoXAAAAACCS1LsAAAAAAJH0/wDjJafNxCk9UgAAAABJRU5ErkJggg==" width="933.1249389648438" height="399.9999694824219" alt="Plot object"></div>
      </div>
    </div>
  </div>
</body></html>
				
			

Bootstrap and other dependencies

But wait a minute… Where are the Bootstrap dependencies coming from? Or things like “ionrangeslider”? These dependencies are actually attached by the functions in the ui itself. For example, taking a look at fluidPage(), we see that this function itself also contains some dependencies: 

				
					htmltools::renderTags(fluidPage())$dependencies 
				
			

Again jQuery, and Bootstrap:

				
					[[1]]
List of 9
 $ name      : chr "jquery"
 $ version   : chr "3.6.0"
 $ src       :List of 1
  ..$ file: chr "/shiny/inst/www/shared"
 $ meta      : NULL
 $ script    : chr "jquery.min.js"
 $ stylesheet: NULL
 $ head      : NULL
 $ attachment: NULL
 $ all_files : logi FALSE
 - attr(*, "class")= chr "html_dependency"

[[2]]
List of 9
 $ name      : chr "bootstrap"
 $ version   : chr "3.4.1"
 $ src       :List of 1
  ..$ file: chr "/shiny/inst/www/shared/bootstrap"
 $ meta      :List of 1
  ..$ viewport: chr "width=device-width, initial-scale=1"
 $ script    : chr [1:2] "js/bootstrap.min.js" "accessibility/js/bootstrap-accessibility.min.js"
 $ stylesheet: chr [1:2] "css/bootstrap.min.css" "accessibility/css/bootstrap-accessibility.min.css"
 $ head      : NULL
 $ attachment: NULL
 $ all_files : logi TRUE
 - attr(*, "class")= chr "html_dependency"

				
			

The fact that these dependencies also end up in the <head> tag is because htmltools::renderDocument() also processes the dependencies that are already present in the ui. The shiny_deps object are extra dependencies added on top of the ones found in any of your ui elements. 

And what about “ionrangeslider”? That is coming from the sliderInput():

				
					htmltools::renderTags(sliderInput("slider", "Slider", min = 1, max = 100, value = 50))$dependencies 
				
			
				
					[[1]]
List of 9
 $ name      : chr "ionrangeslider-javascript"
 $ version   : chr "2.3.1"
 $ src       :List of 1
  ..$ file: chr "/shiny/inst/www/shared/ionrangeslider"
 $ meta      : NULL
 $ script    : chr "js/ion.rangeSlider.min.js"
 $ stylesheet: NULL
 $ head      : NULL
 $ attachment: NULL
 $ all_files : logi TRUE
 - attr(*, "class")= chr "html_dependency"

[[2]]
List of 9
 $ name      : chr "strftime"
 $ version   : chr "0.9.2"
 $ src       :List of 1
  ..$ file: chr "/shiny/inst/www/shared/strftime"
 $ meta      : NULL
 $ script    : chr "strftime-min.js"
 $ stylesheet: NULL
 $ head      : NULL
 $ attachment: NULL
 $ all_files : logi TRUE
 - attr(*, "class")= chr "html_dependency"

[[3]]
List of 9
 $ name      : chr "ionrangeslider-css"
 $ version   : chr "2.3.1"
 $ src       :List of 1
  ..$ file: chr "/shiny/inst/www/shared/ionrangeslider"
 $ meta      : NULL
 $ script    : NULL
 $ stylesheet: chr "css/ion.rangeSlider.css"
 $ head      : NULL
 $ attachment: NULL
 $ all_files : logi TRUE
 - attr(*, "class")= chr "html_dependency"
				
			

And that’s how it’s done!

Of course this doesn’t cover the server part- we’ll have to leave that to the next part of this series.

Resources

I cover this topic extensively in one of my courses: CustomiZing WidgetZ. It’s an advanced Shiny course where you will learn to build your own Shiny packages. And to be able to do that, you need to understand Shiny on a deeper level! 

The course is based on the brilliant book of David Granjon: Outstanding User Interfaces with Shiny. Specifically, the chapter about web application concepts was used to explain the Shiny App Lifecycle. I definitely recommend diving into this chapter (and whole book really!) to learn more about Shiny. The book is available online for free, but if you like a hard copy and want to support David in the process, you can purchase it on (amongst others) Amazon.

Wrap up

Hopefully this article uncovered some of the Shiny magic!

☕️ Was this useful to you and would you like to support me? You can buy me a coffee!

I provide R and Shiny consultancy. Do you need help with a project? Reach out and let’s have a chat!

Leave a Reply

Your email address will not be published. Required fields are marked *