Dashboard merupakan salah satu perangkat visualisasi yang dapat digunakan untuk manayangkan ringkasan data, analisa,atau indikator tertentu. Penyusunan dashboard menggunakan R software dapat dilakukan dengan beberapa cara, di antaranya adalah dengan menggunakan:
flexdashboard
R shinydashboard
R shiny
Modul ini akan membahas secara ringkas tentang penyusunan dashboard dengan beberapa pendekatan tersebut dengan menggunakan R Studio.
flexdashboard
Package flexdashboard
memungkinkan kita menyusun dashboard menggunakan file berbasis R markdown. Berikut adalah langkah-langkahnya. Pertama, kita perlu install terlebih dulu package flexdashboard
tersebut.
install.packages("flexdashboard")
Setelah itu, buatlah file baru dengan cara mengakses menu File > New File > R Markdown > From Template > Flex Dashboard , seperti pada gambar di bawah ini.

Selanjutnya, layout dashboard dapat diatur dengan ketentuan sebagai berikut:
Gunakan #
untuk membuat page
Gunakan ##
untuk membuat tampilan kolom
Gunakan ###
untuk membuat tampilan baris
Ilustrasi

Program di atas akan menghasilkan tampilan dengan 2 page, dimana page pertama akan terdiri dari 2 kolom. Kolom pertama hanya berisi Chart A, sedangkan kolom kedua berisi Chart B dan Chart C yang berurut menurut baris. Page 2 akan kosong, karena belum diberikan isi apapun.

Di dalam setiap subheader Chart A, B, dan C, kita dapat menuliskan R code chunk yang akan muncul pada bagian tersebut masing-masing. Anda dapat mencoba menuliskan beberapa contoh syntax pada modul https://rpubs.com/r_anisa/interactive-plot-using-r ke dalam code chunk di Chart A, B, atau C, contohnya seperti hasil berikut ini.

Penjelasan lengkap tentang flexdashboard
dapat dipelajari lebih lanjut di Grolemund (2016).
Pengenalan R Shiny
Struktur R shiny terdiri dari tiga komponen utama, yaitu:
user interface (UI), ini merupakan bagian dimana kita bisa mengatur tampilan dari app yang kita bangun.
server, ini merupakan bagian untuk menjalankan fungsi-fungsi yang ada di dalam app.
shinyApp, ini merupakan bagian yang menghubungkan antara UI dengan server.
R Shiny biasa digunakan untuk membangun aplikasi web, berikut adalah contoh sederhananya.
install.packages("shiny")
library(shiny)
runExample("01_hello")

Pada bagian bawah tampilan contoh tersebut, Anda dapat melihat code yang digunakan untuk membangun app tersebut. Objek ui
berikut berisi pengaturan layout yang ingin ditampilkan. Terlihat bahwa interface terdiri dari tiga panel, yaitu:
Judul, diisi dengan tulisan “Hello Shiny!”
Side bar, diisi dengan slide bar untuk menginput lebar kelas histogram (bins), dengan batas bawah 1 dan batas nilai maksimum 50, serta diset dengan nilai awal 30.
Main panel, digunakan untuk menampilkan output berupa histogram.
# Define UI for app that draws a histogram ----
ui <- fluidPage(
# App title ----
titlePanel("Hello Shiny!"),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
# Input: Slider for the number of bins ----
sliderInput(inputId = "bins",
label = "Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Main panel for displaying outputs ----
mainPanel(
# Output: Histogram ----
plotOutput(outputId = "distPlot")
)
)
)
Server menjalankan fungsi untuk menghasilkan output histogram yang ditampilkan pada ui
. Fungsi renderPlot()
digunakan untuk menghasilkan plot berdasarkan input yang diberikan pada menu slidebar di bagian ui
.
# Define server logic required to draw a histogram ----
server <- function(input, output) {
# Histogram of the Old Faithful Geyser Data ----
# with requested number of bins
# This expression that generates a histogram is wrapped in a call
# to renderPlot to indicate that:
#
# 1. It is "reactive" and therefore should be automatically
# re-executed when inputs (input$bins) change
# 2. Its output type is a plot
output$distPlot <- renderPlot({
x <- faithful$waiting
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x, breaks = bins, col = "#75AADB", border = "white",
xlab = "Waiting time to next eruption (in mins)",
main = "Histogram of waiting times")
})
}
Pada bagian akhir, fungsi shinyApp()
akan membuat object Shiny app dari pasangan object ui
dan server
yang telah dibuat sebelumnya.
# Create Shiny app ----
shinyApp(ui = ui, server = server)
shinydashboard
Package shinydashboard
dapat digunakan untuk menyusun dashboard menggunakan R shiny. Berikut adalah contohnya.
install.packages("shinydashboard")
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(title = "Basic dashboard"),
dashboardSidebar(),
dashboardBody(
# Boxes need to be put in a row (or column)
fluidRow(
box(plotOutput("plot1", height = 250)),
box(
title = "Controls",
sliderInput("slider", "Number of observations:", 1, 100, 50)
)
)
)
)
server <- function(input, output) {
set.seed(122)
histdata <- rnorm(500)
output$plot1 <- renderPlot({
data <- histdata[seq_len(input$slider)]
hist(data)
})
}
shinyApp(ui, server)
Program di atas akan menghasilkan dashboard sederhana seperti berikut ini.

Beberapa contoh dashboard dapat dilihat di: https://rstudio.github.io/shinydashboard/examples.html .
Modifikasi flexdashboard dengan shiny
Kita dapat memodifikasi dashboard yang disusun menggunakan flexdashboard
dengan menambahkan argumen runtime: shiny
pada bagian header YAML. Hal ini memungkinkan kita menjalankan konten reaktif pada dashboard menggunakan package shiny
.
Perhatikan contoh berikut ini (Sumber: Berishvili, 2020).
Template dashboard yang digunakan dibuat oleh Berishvili (2020) dan dapat diakses di link ini.
Sebagai ilustrasi, akan digunakan data kartu kredit yang diambil dari Kaggle, data tersedia di link ini.
Tuliskan code berikut setelah code chunk dengan argumen {r data}
.
data<-read.csv("https://github.com/raoy/data/raw/master/BankChurners.csv")
Categorical.Variables = c("Gender", "Education_Level", "Marital_Status")
Numeric.Variables = c("Customer_Age", "Total_Trans_Ct", "Credit_Limit")
- Membuat user input (widgets)
Untuk membuat widgets yang berfungsi sebagai user input, silahkan Anda mencoba menuliskan code chunk berikut pada bagian column pertama.
selectInput("categorical_variable", label = "Select Categorical Variable:", choices = Categorical.Variables)
selectInput("numeric_variable", label = "Select Numeric Variable:", choices = Numeric.Variables)
Syntax tersebut akan menghasilkan tampilan seperti di bawah ini.

Terdapat berbagai jenis input yang tersedia dalam package shiny
, di antaranya adalah:
actionButton()
checkboxInput()
dataInput()
numericInput()
radioButtons()
selectInput()
sliderInput()
textInput()
Membuat Output Reactive
Kegunaan shiny
adalah rectivity yang memungkinkan pengguna memperoleh output yang updated sesuai dengan input yang dapat diubah-ubah. Caranya adalah dengan menggunakan berbagai fungsi render
yang tersedia pada package shiny
, diantaranya adalah:
renderPrint()
renderText()
renderTable()
renderDataTable()
renderPlot()
renderImage()
renderUI()
Pada ilustrasi ini, kita akan membuat dashboard menggunakan grafik dari package plotly
yang terdiri dari boxplot, bar chart, dan histogram. Dengan menggunakan fungsi renderPlotly()
, grafik yang dihasilkan akan secara otomatis diupdate sesuai dengan input dari user.
Silahkan tuliskan r code chunk berikut pada bagian Chart A.
renderPlotly({
plot_ly(data,
x = ~data[[input$numeric_variable]],
color = ~data[[input$categorical_variable]],
colors = "Paired",
type = "box") %>%
layout(title = "",
xaxis = list(title = "" ,
zeroline = FALSE))
})
Selanjutnya, tuliskan R code chunk berikut pada bagian Chart B.
renderPlotly({
data %>%
count(var = data[[input$categorical_variable]], name = "count") %>%
plot_ly( x = ~var, y = ~ count, type = "bar", marker = list(color = '#008ae6',
line = list(color = '#008ae6', width = 2)), hoverinfo = "x+y") %>%
add_text(text = ~paste0( " (", scales::percent(count/sum(count)),")"),
textposition = "bottom",
textfont = list(size = 12, color = "white"),
showlegend = FALSE) %>%
layout(xaxis = list(title = ""), yaxis = list(title = ""))
})
Terakhir, silahkan tuliskan code berikut pada bagian Chart C.
renderPlotly({
plot_ly(x = data[[input$numeric_variable]], type = "histogram", marker = list(color = "#008ae6",
line = list(color = "darkgray",
width = 1)))
})
Program di atas akan menghasilkan dashboard seperti di bawah ini.

Sebagai perbandingan, dengan mengikuti tahapan pada ilustrasi di atas, seharusnya Anda memiliki code akhir seperti pada contoh ini, dan dashboard seperti pada link ini.
LS0tDQp0aXRsZTogIk1lbnl1c3VuIERhc2hib2FyZCBNZW5nZ3VuYWthbiBSIg0KYXV0aG9yOiBSYWhtYSBBbmlzYQ0KZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIgJVknKWAiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRoZW1lOiBsdW1lbg0KLS0tDQoNCkRhc2hib2FyZCBtZXJ1cGFrYW4gc2FsYWggc2F0dSBwZXJhbmdrYXQgdmlzdWFsaXNhc2kgeWFuZyBkYXBhdCBkaWd1bmFrYW4gdW50dWsgbWFuYXlhbmdrYW4gcmluZ2thc2FuIGRhdGEsIGFuYWxpc2EsYXRhdSBpbmRpa2F0b3IgdGVydGVudHUuIFBlbnl1c3VuYW4gZGFzaGJvYXJkIG1lbmdndW5ha2FuIFIgc29mdHdhcmUgZGFwYXQgZGlsYWt1a2FuIGRlbmdhbiBiZWJlcmFwYSBjYXJhLCBkaSBhbnRhcmFueWEgYWRhbGFoIGRlbmdhbiBtZW5nZ3VuYWthbjoNCg0KLSBmbGV4ZGFzaGJvYXJkDQoNCi0gUiBzaGlueWRhc2hib2FyZA0KDQotIFIgc2hpbnkNCg0KTW9kdWwgaW5pIGFrYW4gbWVtYmFoYXMgc2VjYXJhIHJpbmdrYXMgdGVudGFuZyBwZW55dXN1bmFuIGRhc2hib2FyZCBkZW5nYW4gYmViZXJhcGEgcGVuZGVrYXRhbiB0ZXJzZWJ1dCBkZW5nYW4gbWVuZ2d1bmFrYW4gUiBTdHVkaW8uDQoNCiMgZmxleGRhc2hib2FyZA0KDQpQYWNrYWdlIGBmbGV4ZGFzaGJvYXJkYCBtZW11bmdraW5rYW4ga2l0YSBtZW55dXN1biBkYXNoYm9hcmQgbWVuZ2d1bmFrYW4gZmlsZSBiZXJiYXNpcyBSIG1hcmtkb3duLiBCZXJpa3V0IGFkYWxhaCBsYW5na2FoLWxhbmdrYWhueWEuIFBlcnRhbWEsIGtpdGEgcGVybHUgaW5zdGFsbCB0ZXJsZWJpaCBkdWx1IHBhY2thZ2UgYGZsZXhkYXNoYm9hcmRgIHRlcnNlYnV0Lg0KDQpgYGB7ciBldmFsPUZBTFNFfQ0KaW5zdGFsbC5wYWNrYWdlcygiZmxleGRhc2hib2FyZCIpDQpgYGANCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCnNldHdkKCJEOi9EZXB0LlNUSy9Db3Vyc2VzL0Vrc3Bsb3Jhc2kgZGFuIHZpc3VhbGlzYXNpIGRhdGEvUHJha3Rpa3VtL1AxMiIpDQpgYGANCg0KDQpTZXRlbGFoIGl0dSwgYnVhdGxhaCBmaWxlIGJhcnUgZGVuZ2FuIGNhcmEgbWVuZ2Frc2VzIG1lbnUgKipGaWxlID4gTmV3IEZpbGUgPiBSIE1hcmtkb3duID4gRnJvbSBUZW1wbGF0ZSA+IEZsZXggRGFzaGJvYXJkKiogLCBzZXBlcnRpIHBhZGEgZ2FtYmFyIGRpIGJhd2FoIGluaS4NCg0KIVtdKDEtdGVtcGxhdGUtZmxleGRhc2hib2FyZC5wbmcpe3dpZHRoPTUwMH0NCg0KU2VsYW5qdXRueWEsIGxheW91dCBkYXNoYm9hcmQgZGFwYXQgZGlhdHVyIGRlbmdhbiBrZXRlbnR1YW4gc2ViYWdhaSBiZXJpa3V0Og0KDQoqIEd1bmFrYW4gYCNgIHVudHVrIG1lbWJ1YXQgcGFnZQ0KDQoqIEd1bmFrYW4gYCMjYCB1bnR1ayBtZW1idWF0IHRhbXBpbGFuIGtvbG9tDQoNCiogR3VuYWthbiBgIyMjYCB1bnR1ayBtZW1idWF0IHRhbXBpbGFuIGJhcmlzDQoNCioqSWx1c3RyYXNpKioNCg0KIVtdKDEtY29udG9oLXN5bnRheC1mbGV4ZGFzaGJvYXJkLnBuZyl7d2lkdGg9NzUwfQ0KDQpQcm9ncmFtIGRpIGF0YXMgYWthbiBtZW5naGFzaWxrYW4gdGFtcGlsYW4gZGVuZ2FuIDIgcGFnZSwgZGltYW5hIHBhZ2UgcGVydGFtYSBha2FuIHRlcmRpcmkgZGFyaSAyIGtvbG9tLiBLb2xvbSBwZXJ0YW1hIGhhbnlhIGJlcmlzaSBDaGFydCBBLCBzZWRhbmdrYW4ga29sb20ga2VkdWEgYmVyaXNpIENoYXJ0IEIgZGFuIENoYXJ0IEMgeWFuZyBiZXJ1cnV0IG1lbnVydXQgYmFyaXMuIFBhZ2UgMiBha2FuIGtvc29uZywga2FyZW5hIGJlbHVtIGRpYmVyaWthbiBpc2kgYXBhcHVuLg0KDQohW10oMy1jb250b2gtbGF5b3V0LWZsZXhkYXNoYm9hcmQucG5nKQ0KDQpEaSBkYWxhbSBzZXRpYXAgc3ViaGVhZGVyIENoYXJ0IEEsIEIsIGRhbiBDLCBraXRhIGRhcGF0IG1lbnVsaXNrYW4gUiBjb2RlIGNodW5rIHlhbmcgYWthbiBtdW5jdWwgcGFkYSBiYWdpYW4gdGVyc2VidXQgbWFzaW5nLW1hc2luZy4gQW5kYSBkYXBhdCBtZW5jb2JhIG1lbnVsaXNrYW4gYmViZXJhcGEgY29udG9oIHN5bnRheCBwYWRhIG1vZHVsIDxodHRwczovL3JwdWJzLmNvbS9yX2FuaXNhL2ludGVyYWN0aXZlLXBsb3QtdXNpbmctcj4ga2UgZGFsYW0gY29kZSBjaHVuayBkaSBDaGFydCBBLCBCLCBhdGF1IEMsIGNvbnRvaG55YSBzZXBlcnRpIGhhc2lsIGJlcmlrdXQgaW5pLg0KDQohW10oNC1jb250b2gtZmxleGRhc2hib2FyZC5wbmcpDQoNClBlbmplbGFzYW4gbGVuZ2thcCB0ZW50YW5nIGBmbGV4ZGFzaGJvYXJkYCBkYXBhdCBkaXBlbGFqYXJpIGxlYmloIGxhbmp1dCBkaSBHcm9sZW11bmQgKDIwMTYpLg0KDQoNCiMgUGVuZ2VuYWxhbiBSIFNoaW55DQoNClN0cnVrdHVyIFIgc2hpbnkgdGVyZGlyaSBkYXJpIHRpZ2Ega29tcG9uZW4gdXRhbWEsIHlhaXR1Og0KDQoqICoqdXNlciBpbnRlcmZhY2UgKFVJKSoqLCBpbmkgbWVydXBha2FuIGJhZ2lhbiBkaW1hbmEga2l0YSBiaXNhIG1lbmdhdHVyIHRhbXBpbGFuIGRhcmkgYXBwIHlhbmcga2l0YSBiYW5ndW4uDQoNCiogKipzZXJ2ZXIqKiwgaW5pIG1lcnVwYWthbiBiYWdpYW4gdW50dWsgbWVuamFsYW5rYW4gZnVuZ3NpLWZ1bmdzaSB5YW5nIGFkYSBkaSBkYWxhbSBhcHAuDQoNCiogKipzaGlueUFwcCoqLCBpbmkgbWVydXBha2FuIGJhZ2lhbiB5YW5nIG1lbmdodWJ1bmdrYW4gYW50YXJhIFVJIGRlbmdhbiBzZXJ2ZXIuDQoNClIgU2hpbnkgYmlhc2EgZGlndW5ha2FuIHVudHVrIG1lbWJhbmd1biBhcGxpa2FzaSB3ZWIsIGJlcmlrdXQgYWRhbGFoIGNvbnRvaCBzZWRlcmhhbmFueWEuDQoNCmBgYHtyIGV2YWw9RkFMU0V9DQppbnN0YWxsLnBhY2thZ2VzKCJzaGlueSIpDQpsaWJyYXJ5KHNoaW55KQ0KcnVuRXhhbXBsZSgiMDFfaGVsbG8iKQ0KYGBgDQoNCiFbXSg1LWNvbnRvaC1kZW1vLXNoaW55LnBuZykNCg0KUGFkYSBiYWdpYW4gYmF3YWggdGFtcGlsYW4gY29udG9oIHRlcnNlYnV0LCBBbmRhIGRhcGF0IG1lbGloYXQgY29kZSB5YW5nIGRpZ3VuYWthbiB1bnR1ayBtZW1iYW5ndW4gYXBwIHRlcnNlYnV0LiBPYmplayBgdWlgIGJlcmlrdXQgYmVyaXNpIHBlbmdhdHVyYW4gbGF5b3V0IHlhbmcgaW5naW4gZGl0YW1waWxrYW4uIFRlcmxpaGF0IGJhaHdhIGludGVyZmFjZSB0ZXJkaXJpIGRhcmkgdGlnYSBwYW5lbCwgeWFpdHU6DQoNCiogKipKdWR1bCoqLCBkaWlzaSBkZW5nYW4gdHVsaXNhbiAiSGVsbG8gU2hpbnkhIg0KDQoqICoqU2lkZSBiYXIqKiwgZGlpc2kgZGVuZ2FuIHNsaWRlIGJhciB1bnR1ayBtZW5naW5wdXQgbGViYXIga2VsYXMgaGlzdG9ncmFtICgqYmlucyopLCBkZW5nYW4gYmF0YXMgYmF3YWggMSBkYW4gYmF0YXMgbmlsYWkgbWFrc2ltdW0gNTAsIHNlcnRhIGRpc2V0IGRlbmdhbiBuaWxhaSBhd2FsIDMwLg0KDQoqICoqTWFpbiBwYW5lbCoqLCBkaWd1bmFrYW4gdW50dWsgbWVuYW1waWxrYW4gb3V0cHV0IGJlcnVwYSBoaXN0b2dyYW0uDQoNCmBgYHtyIGV2YWw9RkFMU0V9DQojIERlZmluZSBVSSBmb3IgYXBwIHRoYXQgZHJhd3MgYSBoaXN0b2dyYW0gLS0tLQ0KdWkgPC0gZmx1aWRQYWdlKA0KDQogICMgQXBwIHRpdGxlIC0tLS0NCiAgdGl0bGVQYW5lbCgiSGVsbG8gU2hpbnkhIiksDQoNCiAgIyBTaWRlYmFyIGxheW91dCB3aXRoIGlucHV0IGFuZCBvdXRwdXQgZGVmaW5pdGlvbnMgLS0tLQ0KICBzaWRlYmFyTGF5b3V0KA0KDQogICAgIyBTaWRlYmFyIHBhbmVsIGZvciBpbnB1dHMgLS0tLQ0KICAgIHNpZGViYXJQYW5lbCgNCg0KICAgICAgIyBJbnB1dDogU2xpZGVyIGZvciB0aGUgbnVtYmVyIG9mIGJpbnMgLS0tLQ0KICAgICAgc2xpZGVySW5wdXQoaW5wdXRJZCA9ICJiaW5zIiwNCiAgICAgICAgICAgICAgICAgIGxhYmVsID0gIk51bWJlciBvZiBiaW5zOiIsDQogICAgICAgICAgICAgICAgICBtaW4gPSAxLA0KICAgICAgICAgICAgICAgICAgbWF4ID0gNTAsDQogICAgICAgICAgICAgICAgICB2YWx1ZSA9IDMwKQ0KDQogICAgKSwNCg0KICAgICMgTWFpbiBwYW5lbCBmb3IgZGlzcGxheWluZyBvdXRwdXRzIC0tLS0NCiAgICBtYWluUGFuZWwoDQoNCiAgICAgICMgT3V0cHV0OiBIaXN0b2dyYW0gLS0tLQ0KICAgICAgcGxvdE91dHB1dChvdXRwdXRJZCA9ICJkaXN0UGxvdCIpDQoNCiAgICApDQogICkNCikNCmBgYA0KDQpTZXJ2ZXIgbWVuamFsYW5rYW4gZnVuZ3NpIHVudHVrIG1lbmdoYXNpbGthbiBvdXRwdXQgaGlzdG9ncmFtIHlhbmcgZGl0YW1waWxrYW4gcGFkYSBgdWlgLiBGdW5nc2kgYHJlbmRlclBsb3QoKWAgZGlndW5ha2FuIHVudHVrIG1lbmdoYXNpbGthbiBwbG90IGJlcmRhc2Fya2FuIGlucHV0IHlhbmcgZGliZXJpa2FuIHBhZGEgbWVudSBzbGlkZWJhciBkaSBiYWdpYW4gYHVpYC4gDQoNCmBgYHtyIGV2YWw9RkFMU0V9DQojIERlZmluZSBzZXJ2ZXIgbG9naWMgcmVxdWlyZWQgdG8gZHJhdyBhIGhpc3RvZ3JhbSAtLS0tDQpzZXJ2ZXIgPC0gZnVuY3Rpb24oaW5wdXQsIG91dHB1dCkgew0KDQogICMgSGlzdG9ncmFtIG9mIHRoZSBPbGQgRmFpdGhmdWwgR2V5c2VyIERhdGEgLS0tLQ0KICAjIHdpdGggcmVxdWVzdGVkIG51bWJlciBvZiBiaW5zDQogICMgVGhpcyBleHByZXNzaW9uIHRoYXQgZ2VuZXJhdGVzIGEgaGlzdG9ncmFtIGlzIHdyYXBwZWQgaW4gYSBjYWxsDQogICMgdG8gcmVuZGVyUGxvdCB0byBpbmRpY2F0ZSB0aGF0Og0KICAjDQogICMgMS4gSXQgaXMgInJlYWN0aXZlIiBhbmQgdGhlcmVmb3JlIHNob3VsZCBiZSBhdXRvbWF0aWNhbGx5DQogICMgICAgcmUtZXhlY3V0ZWQgd2hlbiBpbnB1dHMgKGlucHV0JGJpbnMpIGNoYW5nZQ0KICAjIDIuIEl0cyBvdXRwdXQgdHlwZSBpcyBhIHBsb3QNCiAgb3V0cHV0JGRpc3RQbG90IDwtIHJlbmRlclBsb3Qoew0KDQogICAgeCAgICA8LSBmYWl0aGZ1bCR3YWl0aW5nDQogICAgYmlucyA8LSBzZXEobWluKHgpLCBtYXgoeCksIGxlbmd0aC5vdXQgPSBpbnB1dCRiaW5zICsgMSkNCg0KICAgIGhpc3QoeCwgYnJlYWtzID0gYmlucywgY29sID0gIiM3NUFBREIiLCBib3JkZXIgPSAid2hpdGUiLA0KICAgICAgICAgeGxhYiA9ICJXYWl0aW5nIHRpbWUgdG8gbmV4dCBlcnVwdGlvbiAoaW4gbWlucykiLA0KICAgICAgICAgbWFpbiA9ICJIaXN0b2dyYW0gb2Ygd2FpdGluZyB0aW1lcyIpDQoNCiAgICB9KQ0KDQp9DQoNCmBgYA0KDQpQYWRhIGJhZ2lhbiBha2hpciwgZnVuZ3NpIGBzaGlueUFwcCgpYCBha2FuIG1lbWJ1YXQgb2JqZWN0IFNoaW55IGFwcCBkYXJpIHBhc2FuZ2FuIG9iamVjdCBgdWlgIGRhbiBgc2VydmVyYCB5YW5nIHRlbGFoIGRpYnVhdCBzZWJlbHVtbnlhLg0KDQpgYGB7ciBldmFsPUZBTFNFfQ0KIyBDcmVhdGUgU2hpbnkgYXBwIC0tLS0NCnNoaW55QXBwKHVpID0gdWksIHNlcnZlciA9IHNlcnZlcikNCmBgYA0KDQojIHNoaW55ZGFzaGJvYXJkDQoNClBhY2thZ2UgYHNoaW55ZGFzaGJvYXJkYCBkYXBhdCBkaWd1bmFrYW4gdW50dWsgbWVueXVzdW4gZGFzaGJvYXJkIG1lbmdndW5ha2FuIFIgc2hpbnkuIEJlcmlrdXQgYWRhbGFoIGNvbnRvaG55YS4NCg0KYGBge3IgZXZhbD1GQUxTRX0NCmluc3RhbGwucGFja2FnZXMoInNoaW55ZGFzaGJvYXJkIikNCmxpYnJhcnkoc2hpbnlkYXNoYm9hcmQpDQoNCnVpIDwtIGRhc2hib2FyZFBhZ2UoDQogIGRhc2hib2FyZEhlYWRlcih0aXRsZSA9ICJCYXNpYyBkYXNoYm9hcmQiKSwNCiAgZGFzaGJvYXJkU2lkZWJhcigpLA0KICBkYXNoYm9hcmRCb2R5KA0KICAgICMgQm94ZXMgbmVlZCB0byBiZSBwdXQgaW4gYSByb3cgKG9yIGNvbHVtbikNCiAgICBmbHVpZFJvdygNCiAgICAgIGJveChwbG90T3V0cHV0KCJwbG90MSIsIGhlaWdodCA9IDI1MCkpLA0KDQogICAgICBib3goDQogICAgICAgIHRpdGxlID0gIkNvbnRyb2xzIiwNCiAgICAgICAgc2xpZGVySW5wdXQoInNsaWRlciIsICJOdW1iZXIgb2Ygb2JzZXJ2YXRpb25zOiIsIDEsIDEwMCwgNTApDQogICAgICApDQogICAgKQ0KICApDQopDQoNCnNlcnZlciA8LSBmdW5jdGlvbihpbnB1dCwgb3V0cHV0KSB7DQogIHNldC5zZWVkKDEyMikNCiAgaGlzdGRhdGEgPC0gcm5vcm0oNTAwKQ0KDQogIG91dHB1dCRwbG90MSA8LSByZW5kZXJQbG90KHsNCiAgICBkYXRhIDwtIGhpc3RkYXRhW3NlcV9sZW4oaW5wdXQkc2xpZGVyKV0NCiAgICBoaXN0KGRhdGEpDQogIH0pDQp9DQoNCnNoaW55QXBwKHVpLCBzZXJ2ZXIpDQpgYGANCg0KUHJvZ3JhbSBkaSBhdGFzIGFrYW4gbWVuZ2hhc2lsa2FuIGRhc2hib2FyZCBzZWRlcmhhbmEgc2VwZXJ0aSBiZXJpa3V0IGluaS4NCg0KIVtdKDYtY29udG9oLXNoaW55ZGFzaGJvYXJkLnBuZykNCg0KQmViZXJhcGEgY29udG9oIGRhc2hib2FyZCBkYXBhdCBkaWxpaGF0IGRpOiA8aHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9zaGlueWRhc2hib2FyZC9leGFtcGxlcy5odG1sPiAuDQoNCiMgTW9kaWZpa2FzaSBmbGV4ZGFzaGJvYXJkIGRlbmdhbiBzaGlueQ0KDQpLaXRhIGRhcGF0IG1lbW9kaWZpa2FzaSBkYXNoYm9hcmQgeWFuZyBkaXN1c3VuIG1lbmdndW5ha2FuIGBmbGV4ZGFzaGJvYXJkYCBkZW5nYW4gbWVuYW1iYWhrYW4gYXJndW1lbiBgcnVudGltZTogc2hpbnlgIHBhZGEgYmFnaWFuIGhlYWRlciBZQU1MLiBIYWwgaW5pIG1lbXVuZ2tpbmthbiBraXRhIG1lbmphbGFua2FuIGtvbnRlbiByZWFrdGlmIHBhZGEgZGFzaGJvYXJkIG1lbmdndW5ha2FuIHBhY2thZ2UgYHNoaW55YC4NCg0KUGVyaGF0aWthbiBjb250b2ggYmVyaWt1dCBpbmkgKFN1bWJlcjogQmVyaXNodmlsaSwgMjAyMCkuIA0KDQohW10oNy1jdXN0b21pemluZy1mbGV4ZGFzaGJvYXJkLXdpdGgtc2hpbnkucG5nKXt3aWR0aD02MDB9DQpUZW1wbGF0ZSBkYXNoYm9hcmQgeWFuZyBkaWd1bmFrYW4gZGlidWF0IG9sZWggQmVyaXNodmlsaSAoMjAyMCkgZGFuIGRhcGF0IGRpYWtzZXMgZGkgW2xpbmsgaW5pXShodHRwczovL2dpc3QuZ2l0aHViLmNvbS9uYXRhYmVyaXNodmlsaS8xMjdlMjg0OWNmMzI0NjNiZWU5ZTE3Mjg2NDc2MzJmNSkuDQoNClNlYmFnYWkgaWx1c3RyYXNpLCBha2FuIGRpZ3VuYWthbiBkYXRhIGthcnR1IGtyZWRpdCB5YW5nIGRpYW1iaWwgZGFyaSBLYWdnbGUsIGRhdGEgdGVyc2VkaWEgZGkgW2xpbmsgaW5pXShodHRwczovL3d3dy5rYWdnbGUuY29tL3Nha3NoaWdveWFsNy9jcmVkaXQtY2FyZC1jdXN0b21lcnMpLiANCg0KKiAqKk1lbmdpbXBvciBkYXRhKioNCg0KVHVsaXNrYW4gY29kZSBiZXJpa3V0IHNldGVsYWggY29kZSBjaHVuayBkZW5nYW4gYXJndW1lbiBge3IgZGF0YX1gLg0KDQpgYGB7ciBldmFsPUZBTFNFfQ0KDQpkYXRhPC1yZWFkLmNzdigiaHR0cHM6Ly9naXRodWIuY29tL3Jhb3kvZGF0YS9yYXcvbWFzdGVyL0JhbmtDaHVybmVycy5jc3YiKQ0KDQpDYXRlZ29yaWNhbC5WYXJpYWJsZXMgPSBjKCJHZW5kZXIiLCAiRWR1Y2F0aW9uX0xldmVsIiwgIk1hcml0YWxfU3RhdHVzIikNCg0KTnVtZXJpYy5WYXJpYWJsZXMgPSBjKCJDdXN0b21lcl9BZ2UiLCAiVG90YWxfVHJhbnNfQ3QiLCAiQ3JlZGl0X0xpbWl0IikNCg0KYGBgDQoNCiogKipNZW1idWF0IHVzZXIgaW5wdXQgKHdpZGdldHMpKioNCg0KVW50dWsgbWVtYnVhdCB3aWRnZXRzIHlhbmcgYmVyZnVuZ3NpIHNlYmFnYWkgdXNlciBpbnB1dCwgc2lsYWhrYW4gQW5kYSBtZW5jb2JhIG1lbnVsaXNrYW4gY29kZSBjaHVuayBiZXJpa3V0IHBhZGEgYmFnaWFuIGNvbHVtbiBwZXJ0YW1hLg0KDQpgYGB7ciBldmFsPUZBTFNFfQ0KDQpzZWxlY3RJbnB1dCgiY2F0ZWdvcmljYWxfdmFyaWFibGUiLCBsYWJlbCA9ICJTZWxlY3QgQ2F0ZWdvcmljYWwgVmFyaWFibGU6IiwgY2hvaWNlcyA9IENhdGVnb3JpY2FsLlZhcmlhYmxlcykNCg0Kc2VsZWN0SW5wdXQoIm51bWVyaWNfdmFyaWFibGUiLCBsYWJlbCA9ICJTZWxlY3QgTnVtZXJpYyBWYXJpYWJsZToiLCBjaG9pY2VzID0gTnVtZXJpYy5WYXJpYWJsZXMpDQoNCmBgYA0KDQpTeW50YXggdGVyc2VidXQgYWthbiBtZW5naGFzaWxrYW4gdGFtcGlsYW4gc2VwZXJ0aSBkaSBiYXdhaCBpbmkuDQoNCiFbXSg4LWZsZXhkYXNoYm9hcmQtd2lkZ2V0cy5wbmcpDQoNClRlcmRhcGF0IGJlcmJhZ2FpIGplbmlzIGlucHV0IHlhbmcgdGVyc2VkaWEgZGFsYW0gcGFja2FnZSBgc2hpbnlgLCBkaSBhbnRhcmFueWEgYWRhbGFoOg0KDQorIGBhY3Rpb25CdXR0b24oKWANCg0KKyBgY2hlY2tib3hJbnB1dCgpYA0KDQorIGBkYXRhSW5wdXQoKWANCg0KKyBgbnVtZXJpY0lucHV0KClgDQoNCisgYHJhZGlvQnV0dG9ucygpYA0KDQorIGBzZWxlY3RJbnB1dCgpYA0KDQorIGBzbGlkZXJJbnB1dCgpYA0KDQorIGB0ZXh0SW5wdXQoKWANCg0KDQoqICoqTWVtYnVhdCBPdXRwdXQgUmVhY3RpdmUgKioNCg0KS2VndW5hYW4gYHNoaW55YCBhZGFsYWggcmVjdGl2aXR5IHlhbmcgbWVtdW5na2lua2FuIHBlbmdndW5hIG1lbXBlcm9sZWggb3V0cHV0IHlhbmcgdXBkYXRlZCBzZXN1YWkgZGVuZ2FuIGlucHV0IHlhbmcgZGFwYXQgZGl1YmFoLXViYWguIENhcmFueWEgYWRhbGFoIGRlbmdhbiBtZW5nZ3VuYWthbiBiZXJiYWdhaSBmdW5nc2kgYHJlbmRlcmAgeWFuZyB0ZXJzZWRpYSBwYWRhIHBhY2thZ2UgYHNoaW55YCwgZGlhbnRhcmFueWEgYWRhbGFoOg0KDQorIGByZW5kZXJQcmludCgpYA0KDQorIGByZW5kZXJUZXh0KClgDQoNCisgYHJlbmRlclRhYmxlKClgDQoNCisgYHJlbmRlckRhdGFUYWJsZSgpYA0KDQorIGByZW5kZXJQbG90KClgDQoNCisgYHJlbmRlckltYWdlKClgDQoNCisgYHJlbmRlclVJKClgDQoNClBhZGEgaWx1c3RyYXNpIGluaSwga2l0YSBha2FuIG1lbWJ1YXQgZGFzaGJvYXJkIG1lbmdndW5ha2FuIGdyYWZpayBkYXJpIHBhY2thZ2UgYHBsb3RseWAgeWFuZyB0ZXJkaXJpIGRhcmkgYm94cGxvdCwgYmFyIGNoYXJ0LCBkYW4gaGlzdG9ncmFtLiBEZW5nYW4gbWVuZ2d1bmFrYW4gZnVuZ3NpIGByZW5kZXJQbG90bHkoKWAsIGdyYWZpayB5YW5nIGRpaGFzaWxrYW4gYWthbiBzZWNhcmEgb3RvbWF0aXMgZGl1cGRhdGUgc2VzdWFpIGRlbmdhbiBpbnB1dCBkYXJpIHVzZXIuDQoNClNpbGFoa2FuIHR1bGlza2FuIHIgY29kZSBjaHVuayBiZXJpa3V0IHBhZGEgYmFnaWFuIENoYXJ0IEEuDQoNCmBgYHtyIGV2YWw9RkFMU0V9DQpyZW5kZXJQbG90bHkoew0KICAgcGxvdF9seShkYXRhLA0KICAgICAgICAgICAgICB4ID0gfmRhdGFbW2lucHV0JG51bWVyaWNfdmFyaWFibGVdXSwNCiAgICAgICAgICAgICAgY29sb3IgPSB+ZGF0YVtbaW5wdXQkY2F0ZWdvcmljYWxfdmFyaWFibGVdXSwNCiAgICAgICAgICAgICAgY29sb3JzID0gIlBhaXJlZCIsDQogICAgICAgICAgICAgIHR5cGUgPSAiYm94IikgJT4lDQogIGxheW91dCh0aXRsZSA9ICIiLA0KICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIiIgLA0KICAgICAgICAgICAgICAgICAgICAgIHplcm9saW5lID0gRkFMU0UpKQ0KfSkNCmBgYA0KDQpTZWxhbmp1dG55YSwgdHVsaXNrYW4gUiBjb2RlIGNodW5rIGJlcmlrdXQgcGFkYSBiYWdpYW4gQ2hhcnQgQi4NCg0KYGBge3IgZXZhbD1GQUxTRX0NCnJlbmRlclBsb3RseSh7DQogIGRhdGEgJT4lDQogICAgY291bnQodmFyID0gZGF0YVtbaW5wdXQkY2F0ZWdvcmljYWxfdmFyaWFibGVdXSwgbmFtZSA9ICJjb3VudCIpICU+JQ0KICAgIHBsb3RfbHkoIHggPSB+dmFyLCB5ID0gfiBjb3VudCwgdHlwZSA9ICJiYXIiLCBtYXJrZXIgPSBsaXN0KGNvbG9yID0gJyMwMDhhZTYnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAnIzAwOGFlNicsIHdpZHRoID0gMikpLCBob3ZlcmluZm8gPSAieCt5IikgJT4lDQogICAgYWRkX3RleHQodGV4dCA9IH5wYXN0ZTAoICIgKCIsICAgc2NhbGVzOjpwZXJjZW50KGNvdW50L3N1bShjb3VudCkpLCIpIiksIA0KICAgICAgICAgICB0ZXh0cG9zaXRpb24gPSAiYm90dG9tIiwgDQogICAgICAgICAgIHRleHRmb250ID0gbGlzdChzaXplID0gMTIsIGNvbG9yID0gIndoaXRlIiksIA0KICAgICAgICAgICBzaG93bGVnZW5kID0gRkFMU0UpICU+JQ0KICAgIGxheW91dCh4YXhpcyA9IGxpc3QodGl0bGUgPSAiIiksIHlheGlzID0gbGlzdCh0aXRsZSA9ICIiKSkNCiAgICANCn0pDQpgYGANCg0KVGVyYWtoaXIsIHNpbGFoa2FuIHR1bGlza2FuIGNvZGUgYmVyaWt1dCBwYWRhIGJhZ2lhbiBDaGFydCBDLg0KDQpgYGB7ciBldmFsPUZBTFNFfQ0KcmVuZGVyUGxvdGx5KHsNCiAgcGxvdF9seSh4ID0gZGF0YVtbaW5wdXQkbnVtZXJpY192YXJpYWJsZV1dLCB0eXBlID0gImhpc3RvZ3JhbSIsICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gIiMwMDhhZTYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gImRhcmtncmF5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aWR0aCA9IDEpKSkNCn0pDQpgYGANCg0KUHJvZ3JhbSBkaSBhdGFzIGFrYW4gbWVuZ2hhc2lsa2FuIGRhc2hib2FyZCBzZXBlcnRpIGRpIGJhd2FoIGluaS4NCg0KIVtdKDktZmluYWwtZmxleGRhc2hib2FyZC1zaGlueS11aS5wbmcpDQoNClNlYmFnYWkgcGVyYmFuZGluZ2FuLCBkZW5nYW4gbWVuZ2lrdXRpIHRhaGFwYW4gcGFkYSBpbHVzdHJhc2kgZGkgYXRhcywgc2VoYXJ1c255YSBBbmRhIG1lbWlsaWtpIGNvZGUgYWtoaXIgc2VwZXJ0aSBwYWRhIFtjb250b2ggaW5pXShodHRwczovL2dpc3QuZ2l0aHViLmNvbS9uYXRhYmVyaXNodmlsaS8zMWQ2MTU2ZTYzNWQ2NTJkNDBhMzhiODgwMWVjYTRiZSksIGRhbiBkYXNoYm9hcmQgc2VwZXJ0aSBwYWRhIFtsaW5rIGluaV0oaHR0cHM6Ly9uYXRhYmVyaXNodmlsaS5zaGlueWFwcHMuaW8vRURBZGFzaGJvYXJkLykuDQoNCiMgUkVGRVJFTkNFUw0KDQpCZXJpc2h2aWxpLCBOLiAoMjAyMCwgRGVjZW1iZXIgMzEpLiBDcmVhdGUgYW4gaW50ZXJhY3RpdmUgZGFzaGJvYXJkIHdpdGggc2hpbnksIEZsZXhkYXNoYm9hcmQsIGFuZCBQbG90bHkuIE1lZGl1bS4gaHR0cHM6Ly90b3dhcmRzZGF0YXNjaWVuY2UuY29tL2NyZWF0ZS1hbi1pbnRlcmFjdGl2ZS1kYXNoYm9hcmQtd2l0aC1zaGlueS1mbGV4ZGFzaGJvYXJkLWFuZC1wbG90bHktYjFmMDI1YWViYzljDQoNCkdyb2xlbXVuZCwgRy4gKDIwMTYsIEp1bmUgOCkuIEludHJvZHVjaW5nIGZsZXhkYXNoYm9hcmRzLiBSU3R1ZGlvIHwgT3BlbiBzb3VyY2UgJiBwcm9mZXNzaW9uYWwgc29mdHdhcmUgZm9yIGRhdGEgc2NpZW5jZSB0ZWFtcyAtIFJTdHVkaW8uIGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Jlc291cmNlcy93ZWJpbmFycy9pbnRyb2R1Y2luZy1mbGV4ZGFzaGJvYXJkcy8NCg0KUiBTdHVkaW8gaW5jLiAoMjAyMCkuIFRoZSBiYXNpYyBwYXJ0cyBvZiBhIHNoaW55IGFwcC4gU2hpbnkuIGh0dHBzOi8vc2hpbnkucnN0dWRpby5jb20vYXJ0aWNsZXMvYmFzaWNzLmh0bWwNCg==