diff --git a/CRAN-SUBMISSION b/CRAN-SUBMISSION
index 2b408937..c8861df0 100644
--- a/CRAN-SUBMISSION
+++ b/CRAN-SUBMISSION
@@ -1,3 +1,3 @@
-Version: 2.2.1
-Date: 2022-12-19 15:51:44 UTC
-SHA: 1119ebead95ee388e684a36791707ad63b6e2785
\ No newline at end of file
+Version: 2.3.0
+Date: 2023-06-15 20:15:12 UTC
+SHA: f0452c4292e86a20aeabe440ec587c263e1811b4
diff --git a/DESCRIPTION b/DESCRIPTION
index c087ebea..d77de64a 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,7 +1,7 @@
Package: bs4Dash
Type: Package
Title: A 'Bootstrap 4' Version of 'shinydashboard'
-Version: 2.3.0
+Version: 2.3.3
Authors@R: c(
person("David", "Granjon", email = "dgranjon@ymail.com", role = c("aut", "cre")),
person(family = "RinteRface", role = "cph"),
diff --git a/NEWS.md b/NEWS.md
index c9c6001b..37d54db3 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,3 +1,16 @@
+# bs4Dash 2.3.3
+
+## Breaking change (potential)
+
+- Fix #302: both `dashboardSidebar()` and `dashboardControlbar()` default __skin__ value is NULL.
+This allows them to inherit from the parent `dashboardPage()` __dark__ parameter and have either
+a full light or full dark skin. If not NULL, the corresponding skin is applied, regardless of the
+parent `dashboardPage()`.
+
+## Bug fixes
+- Remove unused `headTitles` parameter from `bs4Table()`.
+- Fix #315: alert title is not added to alert body and if Alert is not closable the header contains "undefined" key word. Thanks @MohammedFCIS.
+
# bs4Dash 2.3.0
## New feature
diff --git a/R/dashboardControlbar.R b/R/dashboardControlbar.R
index bad92aa4..fd4b8fca 100644
--- a/R/dashboardControlbar.R
+++ b/R/dashboardControlbar.R
@@ -10,7 +10,8 @@
#' in pixels, or a string that specifies the width in CSS units. 250 px by default.
#' @param collapsed Whether the control bar on the right side is collapsed or not at start. TRUE by default.
#' @param overlay Whether the sidebar covers the content when expanded. Default to TRUE.
-#' @param skin Controlbar skin. "dark" or "light".
+#' @param skin Controlbar skin. "dark" or "light". Matches the \link{dashboardPage} dark parameter
+#' value.
#' @param pinned Whether to block the controlbar state (TRUE or FALSE). Default to NULL.
#'
#' @author David Granjon, \email{dgranjon@@ymail.com}
@@ -19,9 +20,11 @@
#'
#' @export
bs4DashControlbar <- function(..., id = NULL, disable = FALSE, width = 250,
- collapsed = TRUE, overlay = TRUE, skin = "dark",
+ collapsed = TRUE, overlay = TRUE, skin = NULL,
pinned = NULL) {
if (is.null(id)) id <- "controlbarId"
+
+ skin <- set_sidebar_skin(skin)
controlbarTag <- shiny::tags$aside(
class = paste0("control-sidebar control-sidebar-", skin),
diff --git a/R/dashboardSidebar.R b/R/dashboardSidebar.R
index 99e6e5ef..f35b93f0 100644
--- a/R/dashboardSidebar.R
+++ b/R/dashboardSidebar.R
@@ -8,7 +8,8 @@
#' @param width The width of the sidebar. This must either be a number which
#' specifies the width in pixels, or a string that specifies the width in CSS
#' units.
-#' @param skin Sidebar skin. "dark" or "light".
+#' @param skin Sidebar skin. "dark" or "light". Matches the \link{dashboardPage} dark parameter
+#' value.
#' @param status Sidebar status. Valid statuses are defined as follows:
#' \itemize{
#' \item \code{primary}: \Sexpr[results=rd, stage=render]{bs4Dash:::rd_color_tag("#007bff")}.
@@ -47,10 +48,14 @@
#'
#' @export
bs4DashSidebar <- function(..., disable = FALSE, width = NULL,
- skin = "dark", status = "primary",
+ skin = NULL, status = "primary",
elevation = 4, collapsed = FALSE,
minified = TRUE, expandOnHover = TRUE,
fixed = TRUE, id = NULL, customArea = NULL) {
+ # When no skin is specified, sidebar color must match the dashboard skin color
+ # by default which is set in the dashboardPage function.
+ skin <- set_sidebar_skin(skin)
+
if (is.null(id)) id <- "sidebarId"
# If we're restoring a bookmarked app, this holds the value of whether or not the
# sidebar was collapsed. If this is not the case, the default is whatever the user
@@ -151,6 +156,14 @@ bs4DashSidebar <- function(..., disable = FALSE, width = NULL,
}
+#' @keywords internal
+set_sidebar_skin <- function(skin) {
+ is_dark_skin <- get_parent_args()$dark
+ if (is.null(skin)) {
+ skin <- if (!is_dark_skin) "light" else "dark"
+ }
+ skin
+}
#' Toggle sidebar state
diff --git a/R/useful-items.R b/R/useful-items.R
index c0dd8add..1798da3e 100644
--- a/R/useful-items.R
+++ b/R/useful-items.R
@@ -2667,8 +2667,6 @@ bs4Sortable <- function(..., width = 12) {
#'
#' @param data Expect dataframe, tibble or list of shiny tags... See examples.
#' @param cardWrap Whether to wrap the table in a card. FALSE by default.
-#' @param headTitles Table header names. Must have the same length as the number of
-#' \link{bs4TableItem} in \link{bs4TableItems}. Set "" to have an empty title field.
#' @param bordered Whether to display border between elements. FALSE by default.
#' @param striped Whether to displayed striped in elements. FALSE by default.
#' @param width Table width. 12 by default.
diff --git a/R/utils.R b/R/utils.R
index 464f7af2..256a4423 100644
--- a/R/utils.R
+++ b/R/utils.R
@@ -742,3 +742,11 @@ app_container <- function(url, deps = FALSE) {
}
}
}
+
+# Get parent function arguments
+get_parent_args <- function() {
+ cl <- sys.call(-3)
+ f <- get(as.character(cl[[1]]), mode = "function", sys.frame(-2))
+ cl <- match.call(definition = f, call = cl)
+ as.list(cl)[-1]
+}
diff --git a/_pkgdown.yml b/_pkgdown.yml
index f8641995..94b212f8 100644
--- a/_pkgdown.yml
+++ b/_pkgdown.yml
@@ -252,7 +252,7 @@ articles:
news:
releases:
- - text: "bs4Dash 2.3.0"
+ - text: "bs4Dash 2.4.0.9000"
- text: "bs4Dash 2.2.1"
- text: "bs4Dash 2.1.0"
- text: "bs4Dash 2.0.3"
diff --git a/cran-comments.md b/cran-comments.md
index 9eca9ef6..5c2e2fdb 100644
--- a/cran-comments.md
+++ b/cran-comments.md
@@ -1,5 +1,5 @@
## Test environments
-* local OS X install, R 4.1.3.
+* local OS X install, R 4.3.1.
* RStudio Server on linux Ubuntu 20.04.3 LTS R 4.2.1.
* `rhub::check_for_cran`.
* Github actions.
@@ -9,5 +9,4 @@
There were no ERRORs or WARNINGs or NOTEs.
## Note
-I remove the svg file from /man and replaced it by a smaller png, which fixes the NOTES
-on https://cran.r-project.org/web/checks/check_results_bs4Dash.html.
+This release will fixe the NOTES on https://cran.r-project.org/web/checks/check_results_bs4Dash.html.
diff --git a/inst/bs4Dash-2.3.0/bs4Dash.min.js b/inst/bs4Dash-2.3.0/bs4Dash.min.js
deleted file mode 100644
index 7b2c7bda..00000000
--- a/inst/bs4Dash-2.3.0/bs4Dash.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-var accordionBinding=new Shiny.InputBinding;$.extend(accordionBinding,{find:function(a){return $(a).find(".accordion")},getValue:function(a){var e=$(a).children(".card.active").index()+1;if(0!==e)return e},setValue:function(a,e){$(a).children(".card.active").removeClass("active"),$(a).children().eq(e-1).addClass("active"),$(a).children().eq(e-1).find('[data-toggle="collapse"]').click(),$(a).trigger("change")},receiveMessage:function(a,e){this.setValue(a,e)},subscribe:function(a,e){$(a).on("change",(function(a){e()})),$(a).find('[data-toggle="collapse"]').on("click",(function(t){$(this).closest(".card").hasClass("active")||$(a).children(".card.active").removeClass("active"),$(this).closest(".card").addClass("active"),e()}))},unsubscribe:function(a){$(a).off(".accordionBinding")}}),Shiny.inputBindings.register(accordionBinding,"accordion-input");const validStatuses=["primary","secondary","success","info","warning","danger"],validStatusesPlus=["dark","white","lightblue","navy","orange","fuchsia","purple","indigo","gray","gray-dark","pink","maroon","teal","lime","olive","green","yellow","red","blue"];var cardBinding=new Shiny.InputBinding;$.extend(cardBinding,{find:function(a){return $(a).find(".card.bs4Dash")},getValue:function(a){var e=$(a).parent().find("script[data-for='"+a.id+"']");e=JSON.parse(e.html());var t,i=$(a).hasClass("collapsed-card"),n=$(a).css("display"),s=$(a).hasClass("maximized-card");return t="none"!==n,s?$(a).find("[data-card-widget = 'collapse']").hide():$(a).find("[data-card-widget = 'collapse']").show(),{collapsible:e.collapsible,collapsed:i,closable:e.closable,visible:t,maximizable:e.maximizable,maximized:s,status:e.status,solidHeader:e.solidHeader,background:e.background,width:e.width,height:e.height}},_updateWidth:function(a,e,t){$(a).parent().toggleClass("col-sm-"+e),$(a).parent().addClass("col-sm-"+t),$(a).trigger("resize")},setValue:function(a,e){var t=$(a).parent().find("script[data-for='"+a.id+"']");if(t=JSON.parse(t.html()),"update"===e.action){var i=$(a).hasClass("user-card"),n=$(a).hasClass("social-card");if(e.options.hasOwnProperty("title")&&e.options.title!==t.title){var s;s="string"!=typeof e.options.title?$.parseHTML(e.options.title[0]):$.parseHTML(e.options.title);var r=$(a).find(".card-tools");n?$(a).find(".user-block").replaceWith($(s)):i?("string"==typeof e.options.title?(s=[s[0],s[2]],$(a).removeClass("widget-user-2").addClass("widget-user"),$(a).find(".widget-user-header").replaceWith($(s[0])),$(s[1]).insertAfter($(a).find(".widget-user-header"))):($(a).removeClass("widget-user").addClass("widget-user-2"),$(a).find(".widget-user-image").remove(),$(a).find(".widget-user-header").replaceWith($(s)),null!==e.options.status&&(e.options.gradient?$(a).find(".widget-user-header").addClass("bg-gradient-",c):$(a).find(".widget-user-header").addClass("bg-",c))),$(a).find(".widget-user-header").prepend($(r))):($(s).hasClass("card-title")||$(s).addClass("card-title"),$(a).find(".card-title").replaceWith($(s))),t.title=e.options.title}if(e.options.hasOwnProperty("collapsible")&&e.options.collapsible!==t.collapsible&&(e.options.collapsible?0===$(a).find('[data-card-widget = "collapse"]').length&&($(a).find(".card-tools.float-right").prepend($('')),t.collapsible=!0):($(a).find('[data-card-widget = "collapse"]').remove(),t.collapsible=!1)),e.options.hasOwnProperty("closable")&&e.options.closable!==t.closable&&(e.options.closable?0===$(a).find('[data-card-widget = "remove"]').length&&(0===$(a).find('[data-card-widget = "maximize"]').length?$(a).find(".card-tools.float-right").append($('')):$('').insertBefore($(a).find('[data-card-widget = "maximize"]')),t.closable=!0):($(a).find('[data-card-widget = "remove"]').remove(),t.closable=!1)),e.options.hasOwnProperty("maximizable")&&e.options.maximizable!==t.maximizable&&(e.options.maximizable?0===$(a).find('[data-card-widget = "maximize"]').length&&($(a).find(".card-tools.float-right").append($('')),t.maximizable=!0):($(a).find('[data-card-widget = "maximize"]').remove(),t.maximizable=!1)),e.options.hasOwnProperty("solidHeader")&&!n&&!i)if(e.options.solidHeader!==t.solidHeader&&$(a).hasClass("card-outline"))$(a).removeClass("card-outline"),t.solidHeader=!0;else if($(a).hasClass("card-outline")||e.options.solidHeader){if($(a).hasClass("card-outline")){o=t.status||e.options.status;e.options.background&&o?($(a).removeClass("card-outline"),t.solidHeader=!0):t.background&&o&&($(a).removeClass("card-outline"),t.solidHeader=!1)}}else{var o=t.status||e.options.status;e.options.background&&o&&(null!==e.options.background||t.background&&o)||($(a).addClass("card-outline"),t.solidHeader=!1)}if(e.options.hasOwnProperty("status")&&!n&&e.options.status!==t.status){var d,l,c;if(null===e.options.status&&null!==t.status){if(i||$(a).removeClass("card-"+t.status),$(a).hasClass("card-outline")&&!i&&$(a).addClass("card-outline"),e.options.background){var u=e.options.background;validStatusesPlus.indexOf(u)>-1?$(a).find(".btn-tool").addClass("bg-"+u):validStatuses.indexOf(u)>-1&&$(a).find(".btn-tool").addClass("btn-"+u)}}else e.options.status&&(i?(l="bg-",e.options.gradient&&(l+="gradient-"),l+=e.options.status,$(a).find(".widget-user-header").addClass(l)):(l="card-"+e.options.status,$(a).addClass(l)),t.status&&(i?(d="bg-",t.gradient&&(d+="gradient-"),d+=t.status,$(a).find(".widget-user-header").removeClass(d)):(d="card-"+t.status,$(a).removeClass(d))),$(a).hasClass("card-outline")&&!i||(validStatusesPlus.indexOf(e.options.status)>-1?$(a).find(".btn-tool").addClass("bg-"+e.options.status):validStatuses.indexOf(e.options.status)>-1&&$(a).find(".btn-tool").addClass("btn-"+e.options.status)));(t.status||t.background)&&(t.status?c=t.status:t.background&&(c=t.background),validStatusesPlus.indexOf(c)>-1?$(a).find(".btn-tool").removeClass("bg-"+c):validStatuses.indexOf(c)>-1&&$(a).find(".btn-tool").removeClass("btn-"+c)),t.status=e.options.status}if(e.options.hasOwnProperty("background")&&e.options.background!==t.background){var b="bg-";if(newBgClass=b,t.background){if(t.gradient&&(b+="gradient-"),b+=t.background,i&&!t.status&&!e.options.status){var h=$(a).find(".widget-user-header");$(h).removeClass(b)}$(a).removeClass(b)}if(e.options.background){if((t.gradient||e.options.gradient)&&(newBgClass+="gradient-"),newBgClass+=e.options.background,i&&!t.status&&!e.options.status){h=$(a).find(".widget-user-header");$(h).addClass(newBgClass)}$(a).addClass(newBgClass)}t.gradient!==e.options.gradient&&void 0!==e.options.gradient&&(t.gradient=e.options.gradient),t.background=e.options.background}e.options.hasOwnProperty("width")&&e.options.width!==t.width&&(this._updateWidth(a,t.width,e.options.width),t.width=e.options.width),e.options.hasOwnProperty("height")&&e.options.height!==t.height&&(null===e.options.height?$(a).find(".card-body").css("height",""):$(a).find(".card-body").css("height",e.options.height),t.height=e.options.height),$(a).parent().find("script[data-for='"+a.id+"']").replaceWith('\"\n );\n } else {\n if (value != \"restore\") {\n if ($(el).css(\"display\") != \"none\") {\n $(el).CardWidget(value);\n }\n } else {\n $(el).show();\n // this is needed so that the last event handler is considered\n // in the subscribe method.\n $(el).trigger(\"shown\");\n }\n }\n },\n receiveMessage: function(el, data) {\n this.setValue(el, data);\n $(el).trigger(\"change\");\n },\n\n subscribe: function(el, callback) {\n $(el).on(\"expanded.lte.cardwidget collapsed.lte.cardwidget\", function(e) {\n // set a delay so that SHiny get the input value when the collapse animation\n // is finished.\n setTimeout(function() {\n callback();\n }, 500);\n });\n\n $(el).on(\"maximized.lte.cardwidget minimized.lte.cardwidget\", function(e) {\n callback();\n });\n\n $(el).on(\"removed.lte.cardwidget\", function(e) {\n setTimeout(function() {\n callback();\n }, 500);\n });\n // we need to split removed and shown event since shown is immediate whereas close\n // takes some time\n $(el).on(\"shown.cardBinding\", function(e) {\n callback();\n });\n\n // handle change event triggered in the setValue method\n $(el).on(\"change.cardBinding\", function(event) {\n setTimeout(function() {\n callback();\n }, 500);\n });\n },\n\n unsubscribe: function(el) {\n $(el).off(\".cardBinding\");\n }\n});\n\nShiny.inputBindings.register(cardBinding);\n\n// Card sidebar input binding\nvar cardSidebarBinding = new Shiny.InputBinding();\n$.extend(cardSidebarBinding, {\n initialize: function(el) {\n // erase default to avoid seeing moving sidebars on initialization\n $(\".direct-chat-contacts, .direct-chat-messages\").css({\n transition: \"transform .0s ease-in-out\"\n });\n\n var background = $(el).attr(\"data-background\")\n ? $(el).attr(\"data-background\")\n : \"#343a40\";\n var width = $(el).attr(\"data-width\")\n ? parseInt($(el).attr(\"data-width\"))\n : 100;\n var closeTranslationRate = (100 * 100) / width;\n var contacts = $(el)\n .closest(\".direct-chat\")\n .find(\".direct-chat-contacts\");\n\n // apply width and background\n $(contacts).css({\n background: `${background}`,\n width: `${width}%`\n });\n\n // If start open, apply openTranslationRate else apply closeTranslationRate ...\n if ($(el).attr(\"data-start-open\") === \"true\") {\n var openTranslationRate = closeTranslationRate - 100;\n $(contacts).css({ transform: `translate(${openTranslationRate}%, 0)` });\n } else {\n $(contacts).css({ transform: `translate(${closeTranslationRate}%, 0)` });\n }\n\n // Restore for better transitions\n setTimeout(function() {\n $(\".direct-chat-contacts, .direct-chat-messages\").css({\n transition: \"transform .5s ease-in-out\"\n });\n }, 300);\n \n // Easyclose feature\n if ($(el).attr(\"data-easy-close\") === \"true\") {\n $(document).mouseup(function(e) {\n var container = $(\".direct-chat-contacts\");\n var openContainer = $(\".direct-chat-contacts-open\");\n // if the target of the click isn't the container nor a descendant of the container and also not if the filter symbol was clicke d\n if (!container.is(e.target) && \n container.has(e.target).length === 0 && \n $(e.target).parents('.card-tools').length !== 1) {\n openContainer\n .find(\"[data-widget='chat-pane-toggle']\")\n .click();\n }\n }); \n }\n },\n\n find: function(scope) {\n return $(scope).find('[data-widget=\"chat-pane-toggle\"]');\n },\n\n // Given the DOM element for the input, return the value\n getValue: function(el) {\n var cardWrapper = $(el).closest(\".card\");\n return $(cardWrapper).hasClass(\"direct-chat-contacts-open\");\n },\n\n // see updatebs4Card\n receiveMessage: function(el, data) {\n // In theory, adminLTE3 has a builtin function\n // we could use $(el).DirectChat('toggle');\n // However, it does not update the related input.\n // The toggled.lte.directchat event seems to be broken.\n $(el).trigger(\"click\");\n $(el).trigger(\"shown\");\n },\n\n subscribe: function(el, callback) {\n var self = this;\n $(el).on(\"click\", function(e) {\n var width = $(el).attr(\"data-width\")\n ? parseInt($(el).attr(\"data-width\"))\n : 100;\n var closeTranslationRate = (100 * 100) / width;\n var openTranslationRate = closeTranslationRate - 100;\n // set a delay so that Shiny get the input value when the collapse animation\n // is finished.\n var target = e.currentTarget;\n setTimeout(function(e = target) {\n // apply correct translation rate depending on current state\n var contacts = $(e)\n .closest(\".direct-chat\")\n .find(\".direct-chat-contacts\");\n if (self.getValue(el)) {\n $(contacts).css({\n transform: `translate(${openTranslationRate}%, 0)`\n });\n } else {\n $(contacts).css({\n transform: `translate(${closeTranslationRate}%, 0)`\n });\n }\n callback();\n }, 10);\n });\n },\n\n unsubscribe: function(el) {\n $(el).off(\".cardSidebarBinding\");\n }\n});\n\nShiny.inputBindings.register(cardSidebarBinding);\n","$(function() {\n \n // required to show a toast when the controlbar is pinned \n // for the first time. Show once since it may be annoying ...\n var showToast = true;\n const controlbarToast = () => {\n if (showToast) {\n $(document).Toasts('create', {\n title: 'Controlbar is pinned',\n close: false,\n autohide: true,\n delay: 2000\n });\n showToast = false; \n }\n };\n\n // This prevent box content from going outside their container \n // when the control-bar is on push mode\n $(\"#controlbar-toggle\").on(\"click\",\n function() {\n if ($(\"body\").hasClass(\"control-sidebar-push-slide\")) {\n $(window).trigger(\"resize\"); \n }\n });\n \n \n // The code below hande the click out of the right control bar\n $(window).click(function(e) { \n // There is a potential conflict. This function detect any click outside\n // the controlbar and close if if it is not pinned. Yet, if we click on an action // button controlling the controlbar state (see updatebs4Controlbar), it is also outside the controlbar so the toggle event will be triggered twice. The controlbar will never close as shown in https://github.com/RinteRface/bs4Dash/issues/110. Below we make sure to leave the function as soon as a click on a button holding the class action button. This is not really a fix but a reasonable workaround.\n var isActionButton = $(e.target).hasClass(\"action-button\");\n if (isActionButton) return null;\n \n if($(\"aside.control-sidebar\").find(e.target).length === 0) {\n var pinned = $(\".control-sidebar\").attr(\"data-pin\");\n if (pinned === \"false\" || pinned === undefined) {\n $(\"body\").removeClass(\"control-sidebar-slide-open\"); \n // don't forget to refresh the input binding\n $(\"#controlbar-toggle\").trigger('collapsed.lte.controlsidebar');\n }\n } \n });\n \n // handle the pin button: toggle data-pin state\n $(\"#controlbarPin\").on('click', function() {\n var $pinIcon = $(this).children();\n $pinIcon.toggleClass(\"fa-rotate-90 fa-lg\");\n \n $(\".control-sidebar\").attr(\"data-pin\",\n $(\".control-sidebar\").attr(\"data-pin\") == \"false\" ? \"true\" : \"false\");\n // toggle right sidebar control depending on the datapin\n if ($(\".control-sidebar\").attr(\"data-pin\") === \"true\") {\n $pinIcon.css(\"color\", \"#007bff\");\n $(\"#controlbar-toggle\").addClass(\"disabled\");\n controlbarToast();\n } else {\n $(\"#controlbar-toggle\").removeClass(\"disabled\");\n $pinIcon.css(\"color\", \"\");\n }\n });\n\n\nvar init = true;\n\n // Input binding\n var controlbarBinding = new Shiny.InputBinding();\n \n $.extend(controlbarBinding, {\n \n find: function(scope) {\n return $(scope).find(\".control-sidebar\");\n },\n \n // Given the DOM element for the input, return the value\n getValue: function(el) {\n // Handles the pin \n var controlbarOpen = $(\"body\").hasClass(\"control-sidebar-slide-open\");\n var pinned = $(el).attr(\"data-pin\") === \"true\";\n if (controlbarOpen && pinned && init) {\n $(\"#controlbar-toggle\").addClass(\"disabled\");\n $(\"#controlbarPin\")\n .children()\n .css(\"color\", \"#007bff\");\n controlbarToast();\n init = false;\n }\n \n // this handles the case where the controlbar is not collapsed at start\n var controlbarCollapsed = $(el).attr('data-collapsed');\n if (controlbarCollapsed === \"false\") {\n $(\"#controlbar-toggle\").ControlSidebar('toggle');\n $(el).attr('data-collapsed', \"true\");\n return true;\n } else {\n return $(\"body\").hasClass(\"control-sidebar-slide-open\");\n }\n },\n // see updatebs4Controlbar\n receiveMessage: function(el, data) {\n $(\"#controlbar-toggle\").ControlSidebar('toggle');\n },\n \n subscribe: function(el, callback) {\n $(\"#controlbar-toggle\").on(\"collapsed.lte.controlsidebar expanded.lte.controlsidebar\", function(e) {\n $(el).trigger('shown');\n // add a delay so that Shiny get the input value \n // after the AdminLTE3 animation is finished!\n setTimeout(\n function() {\n callback();\n }, 10);\n });\n },\n \n unsubscribe: function(el) {\n $(el).off(\".controlbarBinding\");\n }\n });\n \n Shiny.inputBindings.register(controlbarBinding, \"bs4Dash.controlbarBinding\");\n \n // handle controlbar overlay\n var controlbarOverlay = $('.control-sidebar').attr('data-overlay');\n if (controlbarOverlay === \"false\") {\n $('body').addClass('control-sidebar-push-slide');\n }\n\n});","$(function () {\n // handle tooltip from the server side\n Shiny.addCustomMessageHandler('create-tooltip', function (message) {\n var tooltipTarget;\n if (message.id) {\n tooltipTarget = '#' + message.id;\n } else {\n if (message.selector) {\n tooltipTarget = message.selector;\n }\n }\n $(tooltipTarget)\n .addClass('has-tooltip')\n .tooltip(message.options);\n console.log(`'Tooltip created for ${tooltipTarget}'`);\n });\n\n Shiny.addCustomMessageHandler('remove-tooltip', function (message) {\n var tooltipTarget = '#' + message;\n\n // only destroys if popover exists\n if ($(tooltipTarget).hasClass('has-tooltip')) {\n $(tooltipTarget)\n .removeClass('has-tooltip')\n .tooltip('dispose');\n console.log(`'Tooltip destroyed for ${tooltipTarget}'`);\n }\n });\n\n // handle popover from the server side\n Shiny.addCustomMessageHandler('create-popover', function (message) {\n var popoverTarget;\n if (message.id) {\n popoverTarget = '#' + message.id;\n } else {\n if (message.selector) {\n popoverTarget = message.selector;\n }\n }\n // indicate target has popover. This is for removePopover to know\n // whether the popover exists\n $(popoverTarget)\n .addClass('has-popover')\n .popover(message.options);\n console.log(`'Popover created for ${popoverTarget}'`);\n });\n\n\n Shiny.addCustomMessageHandler('remove-popover', function (message) {\n var popoverTarget = '#' + message;\n\n // only destroys if popover exists\n if ($(popoverTarget).hasClass('has-popover')) {\n $(popoverTarget)\n .removeClass('has-popover')\n .popover('dispose');\n console.log(`'Popover destroyed for ${popoverTarget}'`);\n }\n });\n\n\n // handle builtin toasts\n Shiny.addCustomMessageHandler('toast', function (message) {\n $(document).Toasts('create', message);\n });\n\n // Create an alert\n Shiny.addCustomMessageHandler('create-alert', function (message) {\n // setup target\n var alertTarget;\n if (message.id) {\n alertTarget = `#${message.id}`;\n } else {\n if (message.selector) {\n alertTarget = message.selector;\n }\n }\n\n // build the tag from options\n var config = message.options, alertCl, alertTag, iconType, closeButton, titleTag, contentTag;\n alertCl = 'alert alert-dismissible';\n if (config.status !== undefined) {\n alertCl = `${alertCl} alert-${config.status}`;\n }\n if (config.elevation !== undefined) {\n alertCl = `${alertCl} elevation-${config.elevation}`;\n }\n\n switch (config.status) {\n case 'primary': iconType = 'info';\n break;\n case 'danger': iconType = 'ban';\n break;\n case 'info': iconType = 'info';\n break;\n case 'warning': iconType = 'warning';\n break;\n case 'success': iconType = 'check';\n break;\n default: console.warn(`${config.status} does not belong to allowed statuses!`)\n }\n\n if (config.closable) {\n closeButton = ''\n }\n\n titleTag = `
`\n contentTag = config.content;\n\n alertTag = `
\n ${closeButton}${titleTag}${contentTag}\n
`\n if (config.width !== undefined) {\n alertTag = `
${alertTag}
`\n }\n\n // add it to the DOM if no existing alert is found in the anchor\n if ($(`#${message.id}-alert`).length === 0) {\n $(alertTarget).append(alertTag);\n Shiny.setInputValue(message.id, true, { priority: 'event' });\n\n // add events only after element is inserted\n\n // callback -> give ability to perform more actions on the Shiny side\n // once the alert is closed\n $(`#${message.id}-alert`).on('closed.bs.alert', function () {\n Shiny.setInputValue(message.id, false, { priority: 'event' });\n });\n // Clicking on close button does not trigger any event.\n // Trigger the closed.bs.alert event.\n $('[data-dismiss=\"alert\"]').on('click', function () {\n var alertId = $(this).parent.attr('id');\n $(`#${alertId}.`).trigger('closed.bs.alert');\n });\n\n } else {\n console.warn(`${alertTarget} already has an alert!`);\n }\n });\n\n\n Shiny.addCustomMessageHandler('close-alert', function (message) {\n // only closes if element exists\n if ($(`#${message}-alert`).length > 0) {\n $(`#${message}-alert`).alert('close');\n } else {\n console.warn('Nothing to delete!');\n }\n });\n});","// When document is ready, if there is a sidebar menu with no activated tabs,\n// activate the one specified by `data-start-selected`, or if that's not\n// present, the first one.\nvar ensureActivatedTab = function() {\n // get the selected tabs\n var $tablinks = $(\".sidebar-menu a[data-toggle='tab']\");\n\n // If there are no tabs, $startTab.length will be 0.\n var $startTab = $tablinks.filter(\"[data-start-selected='1']\");\n if ($startTab.length === 0) {\n // If no tab starts selected, use the first one, if present\n $startTab = $tablinks.first();\n }\n\n // If there's a `data-start-selected` attribute and we can find a tab with\n // that name, activate it.\n if ($startTab.length !== 0) {\n // This is just in case the user renders the tabs in a renderUI that does not\n // print immediately in the DOM. We need a bit of a delay before telling which\n // tab to show ...\n if ($(\".sidebar-menu\").hasClass(\"bs4Dash-menu-output\")) {\n setTimeout(function() {\n // we need to initialize any treeview elements that were not inserted\n // in the DOM when adminlte was first initialized!\n adminlte.Treeview._jQueryInterface.call($('[data-widget=\"treeview\"]'), 'init');\n\n $startTab.tab(\"show\");\n }, 10);\n } else {\n $startTab.tab(\"show\");\n }\n \n\n // This is indirectly setting the value of the Shiny input by setting\n // an attribute on the html element it is bound to. We cannot use the\n // inputBinding's setValue() method here because this is called too\n // early (before Shiny has fully initialized)\n $(\".sidebarMenuSelectedTabItem\").attr(\n \"data-value\",\n $startTab.attr(\"data-value\")\n );\n }\n};\n\n// This function handles a special case in the AdminLTE sidebar: when there\n// is a sidebar-menu with items, and one of those items has sub-items, and\n// they are used for tab navigation. Normally, if one of the items is\n// selected and then a sub-item is clicked, both the item and sub-item will\n// retain the \"active\" class, so they will both be highlighted. This happens\n// because they're not designed to be used together for tab panels. This\n// code ensures that only one item will have the \"active\" class.\nvar deactivateOtherTabs = function() {\n // Find all tab links under sidebar-menu even if they don't have a\n // tabName (which is why the second selector is necessary)\n var $tablinks = $(\n \".sidebar-menu a[data-toggle='tab'],\" + \".sidebar-menu li.has-treeview > a\"\n );\n\n // If any other items are active, deactivate them\n $tablinks.not($(this)).removeClass(\"active\");\n\n // also manually activate the parent link when the selected item\n // is part of a treeview. For some reason, this is not done by AdminLTE3...\n if ($(this).hasClass(\"treeview-link\")) {\n $(this)\n .parents(\".has-treeview\")\n .children()\n .eq(0)\n .addClass(\"active\");\n }\n\n // Trigger event for the tabItemInputBinding\n var $obj = $(\".sidebarMenuSelectedTabItem\");\n var inputBinding = $obj.data(\"shiny-input-binding\");\n if (typeof inputBinding !== \"undefined\") {\n inputBinding.setValue($obj, $(this).attr(\"data-value\"));\n $obj.trigger(\"change\");\n }\n};\n\n$(function() {\n // Whenever the sidebar finishes a transition (which it does every time it\n // changes from collapsed to expanded and vice versa), trigger resize,\n // so that all outputs are resized.\n $(\".main-sidebar\").on(\n \"webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend\",\n function() {\n $(window).trigger(\"resize\");\n }\n );\n\n $(document).on(\n \"shown.bs.tab\",\n '.sidebar-menu a[data-toggle=\"tab\"]',\n deactivateOtherTabs\n );\n\n ensureActivatedTab();\n\n // Whenever we expand a menuItem (to be expandable, it must have children),\n // update the value for the expandedItem's input binding (this is the\n // tabName of the fist subMenuItem inside the menuItem that is currently\n // expanded)\n $(document).on(\"click\", \".has-treeview\", function() {\n var $menu = $(this);\n // If this menuItem was already open, then clicking on it again,\n // should trigger the \"hidden\" event, so Shiny doesn't worry about\n // it while it's hidden (and vice versa).\n if ($menu.hasClass(\"menu-open\")) $menu.trigger(\"collapsed.lte.treeview\");\n else if ($menu.hasClass(\".has-treeview\"))\n $menu.trigger(\"expanded.lte.treeview\");\n\n // need to set timeout to account for the slideUp/slideDown animation\n var $obj = $(\".sidebar.shiny-bound-input\");\n setTimeout(function() {\n $obj.trigger(\"change\");\n }, 600);\n });\n\n //---------------------------------------------------------------------\n // tabItemInputBinding\n // ------------------------------------------------------------------\n // Based on Shiny.tabItemInputBinding, but customized for tabItems in\n // bs4Dash, which have a slightly different structure.\n var tabItemInputBinding = new Shiny.InputBinding();\n $.extend(tabItemInputBinding, {\n find: function(scope) {\n return $(scope).find(\".sidebarMenuSelectedTabItem\");\n },\n getValue: function(el) {\n var value = $(el).attr(\"data-value\");\n if (value === \"null\") return null;\n return value;\n },\n setValue: function(el, value) {\n var self = this;\n var anchors = $(el)\n .parent(\".sidebar-menu\")\n .find(\"li:not(.treeview)\")\n .children(\"a\");\n anchors.each(function() {\n // eslint-disable-line consistent-return\n if (self._getTabName($(this)) === value) {\n $(this).tab(\"show\");\n // this make sure that treeview items are open when we\n // use the updatebs4TabItems function on the server side\n if ($(this).hasClass(\"treeview-link\")) {\n if (\n !$(this)\n .parents(\".has-treeview\")\n .hasClass(\"menu-open\")\n ) {\n $(this)\n .parents(\".has-treeview\")\n .children()\n .eq(0)\n .trigger(\"click\");\n }\n }\n $(el).attr(\"data-value\", self._getTabName($(this)));\n return false;\n }\n });\n },\n receiveMessage: function(el, data) {\n if (data.hasOwnProperty(\"value\")) this.setValue(el, data.value);\n },\n subscribe: function(el, callback) {\n // This event is triggered by deactivateOtherTabs, which is triggered by\n // shown. The deactivation of other tabs must occur before Shiny gets the\n // input value.\n $(el).on(\"change.tabItemInputBinding\", function() {\n callback();\n });\n },\n unsubscribe: function(el) {\n $(el).off(\".tabItemInputBinding\");\n },\n _getTabName: function(anchor) {\n return anchor.attr(\"data-value\");\n }\n });\n\n Shiny.inputBindings.register(tabItemInputBinding, \"bs4Dash.tabItemInput\");\n\n //---------------------------------------------------------------------\n // sidebarInputBinding\n // ------------------------------------------------------------------\n // similar to controlbarInputBinding\n var sidebarBinding = new Shiny.InputBinding();\n\n $.extend(sidebarBinding, {\n find: function(scope) {\n return $(scope).find(\".main-sidebar\");\n },\n\n // Given the DOM element for the input, return the value\n getValue: function(el) {\n // Warning: we can't look for sidebar-open since this\n // class is only generated on mobile devices\n return !$(\"body\").hasClass(\"sidebar-collapse\");\n },\n\n // see updatebs4Controlbar\n receiveMessage: function(el, data) {\n $(\"[data-widget='pushmenu']\").PushMenu(\"toggle\");\n },\n\n subscribe: function(el, callback) {\n $(\"[data-widget='pushmenu']\").on(\n \"collapsed.lte.pushmenu.sidebarBinding shown.lte.pushmenu.sidebarBinding\",\n function(e) {\n callback();\n }\n );\n },\n\n unsubscribe: function(el) {\n $(el).off(\".sidebarBinding\");\n }\n });\n\n Shiny.inputBindings.register(sidebarBinding, \"bs4Dash.sidebarInput\");\n\n // sidebarmenuExpandedInputBinding\n // ------------------------------------------------------------------\n // This keeps tracks of what menuItem (if any) is expanded\n var sidebarmenuExpandedInputBinding = new Shiny.InputBinding();\n $.extend(sidebarmenuExpandedInputBinding, {\n find: function(scope) {\n // This will also have id=\"sidebarItemExpanded\"\n return $(scope).find(\".sidebar\");\n },\n getValue: function(el) {\n var $open = $(el)\n .find(\"li\")\n .filter(\".menu-open\")\n .find(\"ul\");\n if ($open.length === 1) return $open.attr(\"data-expanded\");\n else return null;\n },\n setValue: function(el, value) {\n // does not work (nothing is printed)\n var $menuItem = $(el).find(\"[data-expanded='\" + value + \"']\");\n // This will trigger actions defined by AdminLTE, as well as actions\n // defined in sidebar.js.\n $menuItem.prev().trigger(\"click\");\n },\n subscribe: function(el, callback) {\n $(el).on(\"change.sidebarmenuExpandedInputBinding\", function() {\n callback();\n });\n },\n unsubscribe: function(el) {\n $(el).off(\".sidebarmenuExpandedInputBinding\");\n }\n });\n Shiny.inputBindings.register(\n sidebarmenuExpandedInputBinding,\n \"bs4Dash.sidebarmenuExpandedInputBinding\"\n );\n\n // handle fixed sidebar\n if ($(\".main-sidebar\").attr(\"data-fixed\") === \"true\") {\n $(\"body\").addClass(\"layout-fixed\");\n //$('body').Layout('fixLayoutHeight');\n }\n});\n","$(function () {\n\n $navbar = $('.main-header.navbar');\n\n // Modify the shiny tabsetpanel binding to follow BS4 rules\n $(document).on('shiny:connected', function (event) {\n Shiny.unbindAll();\n $.extend(Shiny\n .inputBindings\n .bindingNames['shiny.bootstrapTabInput']\n .binding, {\n // do whathever you want to edit existing methods\n getValue: function (el) {\n var anchor = $(el).find('li:not(.dropdown)').children('a.active');\n if (anchor.length === 1)\n return this._getTabName(anchor);\n\n return null;\n }\n });\n Shiny.bindAll();\n });\n\n // footer has fixed layout?\n if ($(\".main-footer\").attr(\"data-fixed\") === \"true\") {\n $(\"body\").addClass(\"layout-footer-fixed\");\n }\n\n // add dropdown-menu-right class to correctly open the dropdown to all \n // navbar rightUi elements\n var navbarRight = $('.navbar-right').find('.dropdown-menu');\n $(navbarRight).each(function () {\n if (!$(this).hasClass('dropdown-menu-right')) {\n $(this).addClass('dropdown-menu-right');\n }\n });\n\n\n // data toggle collapse icon update\n $('.user-block [data-toggle=\"collapse\"]').on('click', function () {\n if ($(this).children('i').hasClass('fa-plus')) {\n $(this).children('i').attr('class', 'fa fa-minus');\n } else {\n $(this).children('i').attr('class', 'fa fa-plus');\n }\n });\n\n // fullscreen toggle\n if ($('body').attr('data-fullscreen') == 1) {\n var fullScreenToggle = `
`;\n $(fullScreenToggle).insertBefore($('[data-widget=\"control-sidebar\"]').parent());\n }\n\n // slide to top button\n if ($('body').attr('data-scrollToTop') == 1) {\n var $slideToTop = $('');\n\n $slideToTop.html('');\n\n $slideToTop.css({\n position: 'fixed',\n bottom: '20px',\n right: '25px',\n width: '40px',\n height: '40px',\n color: '#eee',\n 'font-size': '',\n 'line-height': '40px',\n 'text-align': 'center',\n 'background-color': '#222d32',\n cursor: 'pointer',\n 'border-radius': '5px',\n 'z-index': '99999',\n opacity: '.7',\n 'display': 'none'\n });\n\n $slideToTop.on('mouseenter', function () {\n $(this).css('opacity', '1');\n });\n\n $slideToTop.on('mouseout', function () {\n $(this).css('opacity', '.7');\n });\n\n $('.wrapper').append($slideToTop);\n\n $(window).scroll(function () {\n if ($(window).scrollTop() >= 150) {\n if (!$($slideToTop).is(':visible')) {\n $($slideToTop).fadeIn(500);\n }\n } else {\n $($slideToTop).fadeOut(500);\n }\n });\n\n $($slideToTop).click(function () {\n $('html, body').animate({\n scrollTop: 0\n }, 500);\n });\n }\n \n // nav item click also triggers scroll to top\n $('.main-sidebar .nav-item').on('click', function () {\n $('html, body').animate({\n scrollTop: 0\n }, 0);\n }); \n\n\n // tooltip/popover toggle\n if ($('body').attr('data-help') == 2 || \n $('body').attr('data-help') == 1) {\n var $help_switch_checkbox = $('', {\n type: 'checkbox',\n id: 'help_switch',\n class: 'custom-control-input'\n }).on('click', function () {\n if ($(this).is(':checked')) {\n $('[data-toggle=\"tooltip\"]').tooltip('enable');\n $('[data-toggle=\"popover\"]').popover({\n trigger: 'hover'\n });\n $('[data-toggle=\"popover\"]').popover('enable');\n } else {\n $('[data-toggle=\"tooltip\"]').tooltip('disable');\n $('[data-toggle=\"popover\"]').popover('disable');\n }\n });\n\n var $help_switch_container = $('', { class: 'custom-control custom-switch mx-2 mt-2' }).append($help_switch_checkbox).append(``);\n \n // insert before $('#controlbar-toggle') whenever possible ...\n if ($('.nav-item #controlbar-toggle')) {\n $help_switch_container.insertBefore($('#controlbar-toggle').parent());\n } else {\n $navbar.append($help_switch_container);\n }\n\n // trigger first click, if necessary\n $(document).on('shiny:connected', function() {\n if ($('body').attr('data-help') == 2 || $('body').attr('data-help') == 1) {\n $help_switch_checkbox.click(); \n // Click again if option is set to FALSE\n if ($('body').attr('data-help') == 1) {\n $help_switch_checkbox.click(); \n }\n }\n }); \n }\n\n // dark mode input\n $(document).one('shiny:connected', function () {\n if ($('body').hasClass('dark-mode')) {\n Shiny.setInputValue('dark_mode', true, { priority: 'event' });\n } else {\n Shiny.setInputValue('dark_mode', false, { priority: 'event' });\n }\n });\n\n\n // Navbar colors\n getNavbarColor = function () {\n for (let color of navbar_all_colors) {\n if ($('.main-header').attr('class').search(color) > -1) {\n return color;\n }\n }\n };\n\n var navbar_dark_skins = [\n 'navbar-primary',\n 'navbar-secondary',\n 'navbar-info',\n 'navbar-success',\n 'navbar-danger',\n 'navbar-indigo',\n 'navbar-purple',\n 'navbar-pink',\n 'navbar-maroon',\n 'navbar-fuchsia',\n 'navbar-navy',\n 'navbar-lightblue',\n 'navbar-lime',\n 'navbar-teal',\n 'navbar-olive',\n 'navbar-gray-dark',\n 'navbar-gray'\n ];\n\n var navbar_light_skins = [\n 'navbar-warning',\n 'navbar-white',\n 'navbar-orange'\n ];\n\n var navbar_all_colors = navbar_dark_skins.concat(navbar_light_skins);\n \n /**\n * Update color theme to navbar tag\n *\n * @param String color Color to apply.\n * @returns void\n */\n updateNavbarTheme = function (color) {\n var $main_header = $('.main-header');\n $main_header.removeClass('navbar-dark').removeClass('navbar-light');\n navbar_all_colors.forEach(function (color) {\n $main_header.removeClass(color);\n });\n\n if (navbar_dark_skins.indexOf(color) > -1) {\n $main_header.addClass('navbar-dark');\n } else {\n $main_header.addClass('navbar-light');\n }\n\n $main_header.addClass(color);\n };\n \n /**\n * Update icon color style based on navbar color.\n *\n * @param String color Current navbar color.\n * @returns void\n */\n updateNavbarIconColor = function(color) {\n var iconThemeColor = navbar_dark_skins.indexOf(color) > -1 ? \"white\" : \"rgba(0,0,0,.5)\";\n $(\".dark-theme-icon\").css(\"color\", iconThemeColor);\n $('[for=\"help_switch\"] i').css(\"color\", iconThemeColor);\n };\n \n // automatic global theme switcher\n if ($('body').attr('data-dark') == 2 || \n $('body').attr('data-dark') == 1) {\n var $dark_mode_checkbox = $('', {\n type: 'checkbox',\n id: 'theme_switch',\n class: 'custom-control-input'\n }).on('click', function () {\n\n // get any selected navbar skin in the navbar themer\n var newNavbarColor;\n // If there is not themer, we keep the navbar current color.\n // Otherwise, we replace it by the new color.\n if ($('.navbar-themer-chip').length > 0) {\n $('.navbar-themer-chip').filter(function () {\n if ($(this).css('border-style') === 'solid') {\n newNavbarColor = 'navbar-' +\n $(this)\n .attr('class')\n .split('elevation-2')[0]\n .trim()\n .replace('bg-', '');\n }\n });\n } else {\n newNavbarColor = getNavbarColor();\n }\n\n if ($(this).is(':checked')) {\n $('body').addClass('dark-mode');\n\n // use updateNavbarTheme to correctly setup the skin as depending\n // on the required color. If no color is chosen, we use gray-dark for dark mode\n if (newNavbarColor === undefined || newNavbarColor === 'navbar-white') {\n newNavbarColor = \"navbar-gray-dark\";\n }\n updateNavbarTheme(newNavbarColor);\n\n // sidebar update \n if ($('.main-sidebar').length > 0) {\n $('.main-sidebar').attr('class', $('.main-sidebar')\n .attr('class')\n .replace('light', 'dark'));\n $('#sidebar-skin').prop(\"checked\", true);\n\n $('.sidebar-themer-icon')\n .removeClass('fa-sun')\n .addClass('fa-moon');\n }\n\n // controlbar update\n if ($('.control-sidebar').length > 0) {\n $('.control-sidebar').attr('class', $('.control-sidebar')\n .attr('class')\n .replace('light', 'dark'));\n $('#controlbar-skin').prop(\"checked\", true);\n\n $('.controlbar-themer-icon')\n .removeClass('fa-sun')\n .addClass('fa-moon');\n }\n\n\n $('.dark-theme-icon')\n .removeClass('fa-sun')\n .addClass('fa-moon');\n\n // refresh shiny input value \n Shiny.setInputValue('dark_mode', true, { priority: 'event' });\n\n } else {\n $('body').removeClass('dark-mode');\n\n // use updateNavbarTheme to correctly setup the skin as depending\n // on the required color. If no color is chosen, we use white for light mode\n if (newNavbarColor === undefined || newNavbarColor === 'navbar-gray-dark') {\n newNavbarColor = \"navbar-white\";\n }\n updateNavbarTheme(newNavbarColor);\n\n // sidebar update\n if ($('.main-sidebar').length > 0) {\n $('.main-sidebar').attr('class', $('.main-sidebar')\n .attr('class')\n .replace('dark', 'light'));\n $('#sidebar-skin').prop(\"checked\", false);\n\n $('.sidebar-themer-icon')\n .removeClass('fa-moon')\n .addClass('fa-sun');\n }\n\n // controlbar update\n if ($('.control-sidebar').length > 0) {\n $('.control-sidebar').attr('class', $('.control-sidebar')\n .attr('class')\n .replace('dark', 'light'));\n $('#controlbar-skin').prop(\"checked\", false);\n\n $('.controlbar-themer-icon')\n .removeClass('fa-moon')\n .addClass('fa-sun');\n }\n\n $('.dark-theme-icon')\n .removeClass('fa-moon')\n .addClass('fa-sun');\n\n // refresh shiny input value \n Shiny.setInputValue('dark_mode', false, { priority: 'event' });\n }\n \n // update navbar icon colors\n updateNavbarIconColor(newNavbarColor);\n });\n\n var $dark_mode_icon = $('body').hasClass('dark-mode') ? '' : '';\n var $dark_mode_container = $('', { class: 'custom-control custom-switch mx-2 mt-2' }).append($dark_mode_checkbox).append(``);\n \n // insert before $('#controlbar-toggle') whenever possible ...\n if ($('.nav-item #controlbar-toggle')) {\n $dark_mode_container.insertBefore($('#controlbar-toggle').parent());\n } else {\n $navbar.append($dark_mode_container);\n }\n \n \n // Trigger dark mode\n if ($('body').attr('data-dark') == 2) {\n $(document).on('shiny:connected', function() {\n $dark_mode_checkbox.click();\n }); \n }\n }\n \n // apply correct navbar class depending on selected color\n if (getNavbarColor() !== undefined) {\n updateNavbarTheme(getNavbarColor());\n updateNavbarIconColor(getNavbarColor());\n }\n \n\n // Themer chips\n\n // Better style on hover\n $('.themer-chip').hover(function () {\n $(this).css({ opacity: 1 }).removeClass('elevation-2').addClass('elevation-4');\n }, function () {\n $(this).css({ opacity: 0.8 }).removeClass('elevation-4').addClass('elevation-2');\n });\n\n // \n $('.navbar-themer-chip').on('click', function () {\n $(this).css({ 'border-color': 'yellow', 'border-style': 'solid' });\n $('.navbar-themer-chip').not(this).css({ 'border-color': '', 'border-style': '' });\n });\n\n $('.accents-themer-chip').on('click', function () {\n $(this).css({ 'border-color': 'yellow', 'border-style': 'solid' });\n $('.accents-themer-chip').not(this).css({ 'border-color': '', 'border-style': '' });\n });\n\n $('.sidebar-themer-chip').on('click', function () {\n $(this).css({ 'border-color': 'yellow', 'border-style': 'solid' });\n $('.sidebar-themer-chip').not(this).css({ 'border-color': '', 'border-style': '' });\n });\n\n\n // Sidebar themer\n\n // detect global sidebar theme and select or not the toggle\n if ($('.main-sidebar').length > 0) {\n if ($('.main-sidebar').attr('class').match('dark')) {\n $('#sidebar-skin').prop(\"checked\", true);\n }\n }\n\n // clicking on dark/light switch changes:\n // - icon style\n // - sidebar class \n $('#sidebar-skin').on('click', function () {\n var sidebarCl;\n if ($(this).is(':checked')) {\n sidebarCl = $('.main-sidebar')\n .attr('class')\n .replace('light', 'dark');\n $('.main-sidebar').attr('class', sidebarCl);\n\n $('.sidebar-themer-icon')\n .removeClass('fa-sun')\n .addClass('fa-moon');\n } else {\n sidebarCl = $('.main-sidebar')\n .attr('class')\n .replace('dark', 'light');\n $('.main-sidebar').attr('class', sidebarCl);\n\n $('.sidebar-themer-icon')\n .removeClass('fa-moon')\n .addClass('fa-sun');\n }\n });\n\n var sidebar_colors = [\n 'bg-primary',\n 'bg-secondary',\n 'bg-info',\n 'bg-success',\n 'bg-danger',\n 'bg-indigo',\n 'bg-purple',\n 'bg-pink',\n 'bg-maroon',\n 'bg-fuchsia',\n 'bg-navy',\n 'bg-lightblue',\n 'bg-lime',\n 'bg-teal',\n 'bg-olive',\n 'bg-gray-dark',\n 'bg-gray',\n 'bg-light',\n 'bg-warning',\n 'bg-white',\n 'bg-orange'\n ];\n\n\n var sidebar_skins = [\n 'sidebar-dark-primary',\n 'sidebar-dark-secondary',\n 'sidebar-dark-info',\n 'sidebar-dark-success',\n 'sidebar-dark-danger',\n 'sidebar-dark-indigo',\n 'sidebar-dark-purple',\n 'sidebar-dark-pink',\n 'sidebar-dark-maroon',\n 'sidebar-dark-fuchsia',\n 'sidebar-dark-navy',\n 'sidebar-dark-lightblue',\n 'sidebar-dark-lime',\n 'sidebar-dark-teal',\n 'sidebar-dark-olive',\n 'sidebar-dark-gray-dark',\n 'sidebar-dark-gray',\n 'sidebar-dark-light',\n 'sidebar-dark-warning',\n 'sidebar-dark-white',\n 'sidebar-dark-orange',\n 'sidebar-light-primary',\n 'sidebar-light-secondary',\n 'sidebar-light-info',\n 'sidebar-light-success',\n 'sidebar-light-danger',\n 'sidebar-light-indigo',\n 'sidebar-light-purple',\n 'sidebar-light-pink',\n 'sidebar-light-maroon',\n 'sidebar-light-fuchsia',\n 'sidebar-light-navy',\n 'sidebar-light-lightblue',\n 'sidebar-light-lime',\n 'sidebar-light-teal',\n 'sidebar-light-olive',\n 'sidebar-light-gray-dark',\n 'sidebar-light-gray',\n 'sidebar-light-light',\n 'sidebar-light-warning',\n 'sidebar-light-white',\n 'sidebar-light-orange'\n ];\n\n\n updateSidebarTheme = function (color) {\n var sidebarCl;\n if ($('#sidebar-skin').is(':checked')) {\n sidebarCl = 'sidebar-dark-';\n } else {\n sidebarCl = 'sidebar-light-';\n }\n\n var sidebar_class = sidebarCl + color.replace('bg-', '');\n var $sidebar = $('.main-sidebar');\n sidebar_skins.forEach(function (skin) {\n $sidebar.removeClass(skin);\n });\n\n $sidebar.addClass(sidebar_class);\n };\n\n\n // Accents themer\n var accent_colors = [\n 'accent-primary',\n 'accent-secondary',\n 'accent-info',\n 'accent-success',\n 'accent-danger',\n 'accent-indigo',\n 'accent-purple',\n 'accent-pink',\n 'accent-maroon',\n 'accent-fuchsia',\n 'accent-navy',\n 'accent-lightblue',\n 'accent-lime',\n 'accent-teal',\n 'accent-olive',\n 'accent-gray-dark',\n 'accent-gray',\n 'accent-light',\n 'accent-warning',\n 'accent-white',\n 'accent-orange'\n ];\n\n\n updateAccentsTheme = function (color) {\n var accent_class = color;\n var $body = $('body');\n accent_colors.forEach(function (skin) {\n $body.removeClass(skin);\n });\n\n $body.addClass(accent_class);\n };\n\n\n // Controlbar themer\n\n // detect global controlbar theme and select or not the toggle\n if ($('.control-sidebar').length > 0) {\n if ($('.control-sidebar').attr('class').match('dark')) {\n $('#controlbar-skin').prop(\"checked\", true);\n }\n }\n\n // clicking on dark/light switch changes:\n // - icon style\n // - sidebar class \n $('#controlbar-skin').on('click', function () {\n var controlbarCl;\n if ($(this).is(':checked')) {\n controlbarCl = $('.control-sidebar')\n .attr('class')\n .replace('light', 'dark');\n $('.control-sidebar').attr('class', controlbarCl);\n\n $('.controlbar-themer-icon')\n .removeClass('fa-sun')\n .addClass('fa-moon');\n } else {\n controlbarCl = $('.control-sidebar')\n .attr('class')\n .replace('dark', 'light');\n $('.control-sidebar').attr('class', controlbarCl);\n\n $('.controlbar-themer-icon')\n .removeClass('fa-moon')\n .addClass('fa-sun');\n }\n });\n\n});","$(function () {\n // hide the right sidebar toggle \n // if no right sidebar is specified\n noControlbar = ($(\".control-sidebar\").length === 0);\n if (noControlbar) {\n $(\"#controlbar-toggle\").hide();\n }\n\n // hide the right sidebar toggle if the controlbar is disable\n disableControlbar = ($(\".control-sidebar\").attr(\"data-show\"));\n if (!disableControlbar) {\n $(\"#controlbar-toggle\").hide();\n }\n\n // controlbar slide\n controlbarSlide = ($(\".control-sidebar\").attr(\"data-slide\"));\n if (controlbarSlide) {\n $(\"#controlbar-toggle\").attr('data-controlsidebar-slide', controlbarSlide);\n }\n\n // when the sidebar is disabled, hide the sidebar toggle\n disableSidebar = ($(\".main-sidebar\").length === 0);\n if (disableSidebar) {\n $(\".nav-item > a[data-widget='pushmenu']\").css(\"visibility\", \"hidden\");\n }\n\n // handle fixed navbar\n if ($(\".navbar\").attr(\"data-fixed\") === \"true\") {\n $(\"body\").addClass(\"layout-navbar-fixed\");\n }\n\n});","var menuOutputBinding = new Shiny.OutputBinding();\n$.extend(menuOutputBinding, {\n find: function (scope) {\n return $(scope).find('.bs4Dash-menu-output');\n },\n onValueError: function (el, err) {\n Shiny.unbindAll(el);\n this.renderError(el, err);\n },\n renderValue: function (el, data) {\n Shiny.unbindAll(el);\n\n var html;\n var dependencies = [];\n if (data === null) {\n return;\n } else if (typeof (data) === 'string') {\n html = data;\n } else if (typeof (data) === 'object') {\n html = data.html;\n dependencies = data.deps;\n }\n\n var $html = $($.parseHTML(html));\n\n // Convert the inner contents to HTML, and pass to renderHtml\n Shiny.renderHtml($html.html(), el, dependencies);\n\n // Extract class of wrapper, and add them to the wrapper element\n el.className = 'bs4Dash-menu-output shiny-bound-output ' +\n $html.attr('class');\n \n // need this to activate adminLTE3 plugin for treeview \n $(el)\n .attr(\"data-widget\", \"treeview\")\n .attr(\"role\", \"menu\")\n .attr(\"data-accordion\", \"true\");\n\n Shiny.initializeInputs(el);\n Shiny.bindAll(el);\n if ($(el).hasClass(\"sidebar-menu\")) ensureActivatedTab(); // eslint-disable-line\n }\n});\nShiny.outputBindings.register(menuOutputBinding, \"bs4Dash.menuOutputBinding\");","$( document ).ready(function() {\n \n function findActivePage() {\n return $('ul.pagination').find('li.active');\n };\n \n function toggleNavigationItems(activeItem) {\n // hide pagination previous if we are at the first item\n if ($(activeItem).prev().find('a').hasClass('pagination-previous')) {\n $('.pagination-previous').parent('li').css('display', 'none');\n } else {\n $('.pagination-previous').parent('li').css('display', 'block');\n }\n \n // hide pagination next if we are at the last item\n if ($(activeItem).next().find('a').hasClass('pagination-next')) {\n $('.pagination-next').parent('li').css('display', 'none');\n } else {\n $('.pagination-next').parent('li').css('display', 'block');\n }\n }\n \n // Must run at start\n toggleNavigationItems(findActivePage());\n \n // Toggle active item button state\n $('ul.pagination a:not(.disabled, .pagination-previous, .pagination-next)')\n .on('click', function() {\n var activeItem = findActivePage();\n $(activeItem).removeClass('active');\n $(this).parent().addClass('active');\n activeItem = $(this).parent(); // store new active item\n // Also run dynamically\n toggleNavigationItems(activeItem);\n });\n \n // Previous click\n $('ul.pagination .pagination-previous').on('click', function() {\n var activeItem = findActivePage();\n \n var previousSibling = $(activeItem).prev();\n // jump back when we find a disabled sibling\n while ($(previousSibling).hasClass('disabled')) {\n previousSibling = $(previousSibling).prev();\n }\n // Only if active item has a previous sibling that is not \n // the pagination-previous itself.\n if ($(previousSibling).find('a').hasClass('pagination-previous') == false) {\n $(activeItem).removeClass('active');\n $(previousSibling).find('a').click(); \n }\n });\n \n // Next click\n $('ul.pagination .pagination-next').on('click', function() {\n var activeItem = findActivePage();\n \n var nextSibling = $(activeItem).next();\n // jump back when we find a disabled sibling\n while ($(nextSibling).hasClass('disabled')) {\n nextSibling = $(nextSibling).next();\n }\n // Only if active item has a previous sibling that is not \n // the pagination-previous itself.\n if ($(nextSibling).find('a').hasClass('pagination-next') == false) {\n $(activeItem).removeClass('active');\n $(nextSibling).find('a').click(); \n }\n });\n \n var paginationBinding = new Shiny.InputBinding();\n $.extend(paginationBinding, {\n \n initialize: function(el) {\n \n },\n \n find: function (scope) {\n return $(scope).find('ul.pagination');\n },\n \n // Given the DOM element for the input, return the value\n getValue: function (el) {\n return $(el).find('li.active a').attr('data-value');\n },\n \n setValue: function(el, value) {\n \n },\n // internal\n _disableTab: function(el, value) {\n $(el)\n .find('a[data-value=\"' + value + '\"]')\n .parent()\n .removeClass('active')\n .addClass('disabled')\n .attr('tabindex', '-1')\n },\n // see updatePagination\n receiveMessage: function (el, data) {\n // Activate new element\n if (data.hasOwnProperty('selected')) {\n // Disable active element\n $(el)\n .find('.active')\n .removeClass('active');\n // Activate new element\n var selectedItem = $(el)\n .find('a[data-value=\"' + data.selected + '\"]');\n // If the element was disabled before\n if ($(selectedItem).parent('li').hasClass('disabled')) {\n $(selectedItem)\n .parent('li')\n .removeClass('disabled')\n .removeAttr('tabindex');\n }\n $(selectedItem).click();\n }\n \n // Disable elements\n if (data.hasOwnProperty('disabled')) {\n // loop over all elements\n if (typeof data.disabled == 'string') {\n this._disableTab(el, data.disabled)\n } else {\n for (i of data.disabled) {\n // disable element \n this._disableTab(el, i)\n }\n }\n \n // Activate next or previous not disabled li\n var hasActiveItem = $(el).find('li.active').length\n if (hasActiveItem == 0) {\n // Activate first element found (maybe discussed ...)\n var newActive = $(el)\n .find('a:not(.pagination-previous, .pagination-next)')\n .parent('li:not(.disabled)');\n if (newActive.length > 0) {\n $(newActive[0]).find('a').click();\n }\n }\n }\n \n // Trigger the callback to update the input value \n // on the server side.\n $(el).trigger('change');\n },\n \n subscribe: function (el, callback) {\n $(el).find('a').on('click', function() {\n callback();\n });\n \n // Necessry for updatePagination\n $(el).on('change', function() {\n callback();\n })\n },\n \n unsubscribe: function (el) {\n $(el).off('.paginationBinding');\n }\n });\n \n Shiny.inputBindings.register(paginationBinding);\n});","var bootstrapTabInputBinding = new Shiny.InputBinding();\n$.extend(bootstrapTabInputBinding, {\n find: function(scope) {\n return $(scope).find('ul.nav.shiny-tab-input');\n },\n getValue: function(el) {\n // for Bootstrap 4, li element does not hold the active class anymore!\n // We need to look at a.active.\n var anchor = $(el).find('li:not(.dropdown)').children('a.active');\n if (anchor.length === 1)\n return this._getTabName(anchor);\n\n return null;\n },\n setValue: function(el, value) {\n let self = this;\n let success = false;\n if (value) {\n let anchors = $(el).find('li:not(.dropdown)').children('a');\n anchors.each(function() {\n if (self._getTabName($(this)) === value) {\n $(this).tab('show');\n success = true;\n return false; // Break out of each()\n }\n return true;\n });\n }\n if (!success) {\n // This is to handle the case where nothing is selected, e.g. the last tab\n // was removed using removeTab.\n $(el).trigger(\"change\");\n }\n },\n getState: function(el) {\n return { value: this.getValue(el) };\n },\n receiveMessage: function(el, data) {\n if (data.hasOwnProperty('value'))\n this.setValue(el, data.value);\n },\n subscribe: function(el, callback) {\n $(el).on('change shown.bootstrapTabInputBinding shown.bs.tab.bootstrapTabInputBinding', function(event) {\n callback();\n });\n },\n unsubscribe: function(el) {\n $(el).off('.bootstrapTabInputBinding');\n },\n _getTabName: function(anchor) {\n return anchor.attr('data-value') || anchor.text();\n }\n});\n\nShiny.inputBindings.register(bootstrapTabInputBinding, 'shiny.bootstrapTabInput');","// This code creates acustom handler for userMessages\nShiny.addCustomMessageHandler(\"user-messages\", function(message) {\n var id = message.id, action = message.action, content = message.body, index = message.index;\n \n // message text\n // We use Shiny.renderHtml to handle the case where the user pass input/outputs in the updated content that require a new dependency not available in the \n // page at startup. \n if (content.hasOwnProperty(\"text\")) {\n var text;\n if (content.text.html === undefined) {\n text = content.text;\n } else {\n text = Shiny.renderHtml(content.text.html, $([]), content.text.dependencies).html;\n } \n }\n \n // unbind all\n Shiny.unbindAll();\n \n if (action === \"remove\") {\n $(\"#\" + id).find(\".direct-chat-msg\").eq(index - 1).remove();\n } else if (action === \"add\") {\n var author = content.author, date = content.date, image = content.image, type = content.type;\n \n // build the new message \n var authorWrapper, dateWrapper;\n if (type === \"sent\") {\n authorWrapper = '' + author + '';\n dateWrapper = '' + date + '';\n } else {\n authorWrapper = '' + author + '';\n dateWrapper = '' + date + '';\n }\n\n var newMessage = `
${authorWrapper}${dateWrapper}
${text}
`;\n \n // build wrapper\n var newMessageWrapper;\n if (type === \"sent\") {\n newMessageWrapper = '
' + newMessage + '
';\n } else {\n newMessageWrapper = '
' + newMessage + '
';\n }\n \n // append message\n $(\"#\" + id).find(\".direct-chat-messages\").append(newMessageWrapper);\n } else if (action === \"update\") {\n \n // today's date\n var d = new Date();\n var month = d.getMonth() + 1;\n var day = d.getDate();\n var today = d.getFullYear() + '/' +\n ((''+month).length<2 ? '0' : '') + month + '/' +\n ((''+day).length<2 ? '0' : '') + day;\n \n // we assume only text may be updated. Does not make sense to modify author/date\n \n $(\"#\" + id)\n .find(\".direct-chat-text\")\n .eq(index - 1)\n .replaceWith('
(modified: ' + today +') ' + text + '
');\n }\n \n // Calls .initialize() for all of the input objects in all input bindings,\n // in the given scope (document)\n Shiny.initializeInputs();\n Shiny.bindAll(); // bind all inputs/outputs\n});"]}
diff --git a/inst/bs4Dash-2.3.0/bs4Dash.css b/inst/bs4Dash-2.3.3/bs4Dash.css
similarity index 100%
rename from inst/bs4Dash-2.3.0/bs4Dash.css
rename to inst/bs4Dash-2.3.3/bs4Dash.css
diff --git a/inst/bs4Dash-2.3.0/bs4Dash.js b/inst/bs4Dash-2.3.3/bs4Dash.js
similarity index 99%
rename from inst/bs4Dash-2.3.0/bs4Dash.js
rename to inst/bs4Dash-2.3.3/bs4Dash.js
index 1cfcc85b..251493ac 100644
--- a/inst/bs4Dash-2.3.0/bs4Dash.js
+++ b/inst/bs4Dash-2.3.3/bs4Dash.js
@@ -932,12 +932,14 @@ $(function () {
break;
default: console.warn(`${config.status} does not belong to allowed statuses!`)
}
+
+ closeButton = '';
if (config.closable) {
closeButton = ''
}
- titleTag = `
`
+ titleTag = `
${config.title}
`
contentTag = config.content;
alertTag = `
')),t.collapsible=!0):($(a).find('[data-card-widget = "collapse"]').remove(),t.collapsible=!1)),e.options.hasOwnProperty("closable")&&e.options.closable!==t.closable&&(e.options.closable?0===$(a).find('[data-card-widget = "remove"]').length&&(0===$(a).find('[data-card-widget = "maximize"]').length?$(a).find(".card-tools.float-right").append($('')):$('').insertBefore($(a).find('[data-card-widget = "maximize"]')),t.closable=!0):($(a).find('[data-card-widget = "remove"]').remove(),t.closable=!1)),e.options.hasOwnProperty("maximizable")&&e.options.maximizable!==t.maximizable&&(e.options.maximizable?0===$(a).find('[data-card-widget = "maximize"]').length&&($(a).find(".card-tools.float-right").append($('')),t.maximizable=!0):($(a).find('[data-card-widget = "maximize"]').remove(),t.maximizable=!1)),e.options.hasOwnProperty("solidHeader")&&!n&&!i)if(e.options.solidHeader!==t.solidHeader&&$(a).hasClass("card-outline"))$(a).removeClass("card-outline"),t.solidHeader=!0;else if($(a).hasClass("card-outline")||e.options.solidHeader){if($(a).hasClass("card-outline")){o=t.status||e.options.status;e.options.background&&o?($(a).removeClass("card-outline"),t.solidHeader=!0):t.background&&o&&($(a).removeClass("card-outline"),t.solidHeader=!1)}}else{var o=t.status||e.options.status;e.options.background&&o&&(null!==e.options.background||t.background&&o)||($(a).addClass("card-outline"),t.solidHeader=!1)}if(e.options.hasOwnProperty("status")&&!n&&e.options.status!==t.status){var d,l,c;if(null===e.options.status&&null!==t.status){if(i||$(a).removeClass("card-"+t.status),$(a).hasClass("card-outline")&&!i&&$(a).addClass("card-outline"),e.options.background){var u=e.options.background;validStatusesPlus.indexOf(u)>-1?$(a).find(".btn-tool").addClass("bg-"+u):validStatuses.indexOf(u)>-1&&$(a).find(".btn-tool").addClass("btn-"+u)}}else e.options.status&&(i?(l="bg-",e.options.gradient&&(l+="gradient-"),l+=e.options.status,$(a).find(".widget-user-header").addClass(l)):(l="card-"+e.options.status,$(a).addClass(l)),t.status&&(i?(d="bg-",t.gradient&&(d+="gradient-"),d+=t.status,$(a).find(".widget-user-header").removeClass(d)):(d="card-"+t.status,$(a).removeClass(d))),$(a).hasClass("card-outline")&&!i||(validStatusesPlus.indexOf(e.options.status)>-1?$(a).find(".btn-tool").addClass("bg-"+e.options.status):validStatuses.indexOf(e.options.status)>-1&&$(a).find(".btn-tool").addClass("btn-"+e.options.status)));(t.status||t.background)&&(t.status?c=t.status:t.background&&(c=t.background),validStatusesPlus.indexOf(c)>-1?$(a).find(".btn-tool").removeClass("bg-"+c):validStatuses.indexOf(c)>-1&&$(a).find(".btn-tool").removeClass("btn-"+c)),t.status=e.options.status}if(e.options.hasOwnProperty("background")&&e.options.background!==t.background){var b="bg-";if(newBgClass=b,t.background){if(t.gradient&&(b+="gradient-"),b+=t.background,i&&!t.status&&!e.options.status){var h=$(a).find(".widget-user-header");$(h).removeClass(b)}$(a).removeClass(b)}if(e.options.background){if((t.gradient||e.options.gradient)&&(newBgClass+="gradient-"),newBgClass+=e.options.background,i&&!t.status&&!e.options.status){h=$(a).find(".widget-user-header");$(h).addClass(newBgClass)}$(a).addClass(newBgClass)}t.gradient!==e.options.gradient&&void 0!==e.options.gradient&&(t.gradient=e.options.gradient),t.background=e.options.background}e.options.hasOwnProperty("width")&&e.options.width!==t.width&&(this._updateWidth(a,t.width,e.options.width),t.width=e.options.width),e.options.hasOwnProperty("height")&&e.options.height!==t.height&&(null===e.options.height?$(a).find(".card-body").css("height",""):$(a).find(".card-body").css("height",e.options.height),t.height=e.options.height),$(a).parent().find("script[data-for='"+a.id+"']").replaceWith('\"\n );\n } else {\n if (value != \"restore\") {\n if ($(el).css(\"display\") != \"none\") {\n $(el).CardWidget(value);\n }\n } else {\n $(el).show();\n // this is needed so that the last event handler is considered\n // in the subscribe method.\n $(el).trigger(\"shown\");\n }\n }\n },\n receiveMessage: function(el, data) {\n this.setValue(el, data);\n $(el).trigger(\"change\");\n },\n\n subscribe: function(el, callback) {\n $(el).on(\"expanded.lte.cardwidget collapsed.lte.cardwidget\", function(e) {\n // set a delay so that SHiny get the input value when the collapse animation\n // is finished.\n setTimeout(function() {\n callback();\n }, 500);\n });\n\n $(el).on(\"maximized.lte.cardwidget minimized.lte.cardwidget\", function(e) {\n callback();\n });\n\n $(el).on(\"removed.lte.cardwidget\", function(e) {\n setTimeout(function() {\n callback();\n }, 500);\n });\n // we need to split removed and shown event since shown is immediate whereas close\n // takes some time\n $(el).on(\"shown.cardBinding\", function(e) {\n callback();\n });\n\n // handle change event triggered in the setValue method\n $(el).on(\"change.cardBinding\", function(event) {\n setTimeout(function() {\n callback();\n }, 500);\n });\n },\n\n unsubscribe: function(el) {\n $(el).off(\".cardBinding\");\n }\n});\n\nShiny.inputBindings.register(cardBinding);\n\n// Card sidebar input binding\nvar cardSidebarBinding = new Shiny.InputBinding();\n$.extend(cardSidebarBinding, {\n initialize: function(el) {\n // erase default to avoid seeing moving sidebars on initialization\n $(\".direct-chat-contacts, .direct-chat-messages\").css({\n transition: \"transform .0s ease-in-out\"\n });\n\n var background = $(el).attr(\"data-background\")\n ? $(el).attr(\"data-background\")\n : \"#343a40\";\n var width = $(el).attr(\"data-width\")\n ? parseInt($(el).attr(\"data-width\"))\n : 100;\n var closeTranslationRate = (100 * 100) / width;\n var contacts = $(el)\n .closest(\".direct-chat\")\n .find(\".direct-chat-contacts\");\n\n // apply width and background\n $(contacts).css({\n background: `${background}`,\n width: `${width}%`\n });\n\n // If start open, apply openTranslationRate else apply closeTranslationRate ...\n if ($(el).attr(\"data-start-open\") === \"true\") {\n var openTranslationRate = closeTranslationRate - 100;\n $(contacts).css({ transform: `translate(${openTranslationRate}%, 0)` });\n } else {\n $(contacts).css({ transform: `translate(${closeTranslationRate}%, 0)` });\n }\n\n // Restore for better transitions\n setTimeout(function() {\n $(\".direct-chat-contacts, .direct-chat-messages\").css({\n transition: \"transform .5s ease-in-out\"\n });\n }, 300);\n \n // Easyclose feature\n if ($(el).attr(\"data-easy-close\") === \"true\") {\n $(document).mouseup(function(e) {\n var container = $(\".direct-chat-contacts\");\n var openContainer = $(\".direct-chat-contacts-open\");\n // if the target of the click isn't the container nor a descendant of the container and also not if the filter symbol was clicke d\n if (!container.is(e.target) && \n container.has(e.target).length === 0 && \n $(e.target).parents('.card-tools').length !== 1) {\n openContainer\n .find(\"[data-widget='chat-pane-toggle']\")\n .click();\n }\n }); \n }\n },\n\n find: function(scope) {\n return $(scope).find('[data-widget=\"chat-pane-toggle\"]');\n },\n\n // Given the DOM element for the input, return the value\n getValue: function(el) {\n var cardWrapper = $(el).closest(\".card\");\n return $(cardWrapper).hasClass(\"direct-chat-contacts-open\");\n },\n\n // see updatebs4Card\n receiveMessage: function(el, data) {\n // In theory, adminLTE3 has a builtin function\n // we could use $(el).DirectChat('toggle');\n // However, it does not update the related input.\n // The toggled.lte.directchat event seems to be broken.\n $(el).trigger(\"click\");\n $(el).trigger(\"shown\");\n },\n\n subscribe: function(el, callback) {\n var self = this;\n $(el).on(\"click\", function(e) {\n var width = $(el).attr(\"data-width\")\n ? parseInt($(el).attr(\"data-width\"))\n : 100;\n var closeTranslationRate = (100 * 100) / width;\n var openTranslationRate = closeTranslationRate - 100;\n // set a delay so that Shiny get the input value when the collapse animation\n // is finished.\n var target = e.currentTarget;\n setTimeout(function(e = target) {\n // apply correct translation rate depending on current state\n var contacts = $(e)\n .closest(\".direct-chat\")\n .find(\".direct-chat-contacts\");\n if (self.getValue(el)) {\n $(contacts).css({\n transform: `translate(${openTranslationRate}%, 0)`\n });\n } else {\n $(contacts).css({\n transform: `translate(${closeTranslationRate}%, 0)`\n });\n }\n callback();\n }, 10);\n });\n },\n\n unsubscribe: function(el) {\n $(el).off(\".cardSidebarBinding\");\n }\n});\n\nShiny.inputBindings.register(cardSidebarBinding);\n","$(function() {\n \n // required to show a toast when the controlbar is pinned \n // for the first time. Show once since it may be annoying ...\n var showToast = true;\n const controlbarToast = () => {\n if (showToast) {\n $(document).Toasts('create', {\n title: 'Controlbar is pinned',\n close: false,\n autohide: true,\n delay: 2000\n });\n showToast = false; \n }\n };\n\n // This prevent box content from going outside their container \n // when the control-bar is on push mode\n $(\"#controlbar-toggle\").on(\"click\",\n function() {\n if ($(\"body\").hasClass(\"control-sidebar-push-slide\")) {\n $(window).trigger(\"resize\"); \n }\n });\n \n \n // The code below hande the click out of the right control bar\n $(window).click(function(e) { \n // There is a potential conflict. This function detect any click outside\n // the controlbar and close if if it is not pinned. Yet, if we click on an action // button controlling the controlbar state (see updatebs4Controlbar), it is also outside the controlbar so the toggle event will be triggered twice. The controlbar will never close as shown in https://github.com/RinteRface/bs4Dash/issues/110. Below we make sure to leave the function as soon as a click on a button holding the class action button. This is not really a fix but a reasonable workaround.\n var isActionButton = $(e.target).hasClass(\"action-button\");\n if (isActionButton) return null;\n \n if($(\"aside.control-sidebar\").find(e.target).length === 0) {\n var pinned = $(\".control-sidebar\").attr(\"data-pin\");\n if (pinned === \"false\" || pinned === undefined) {\n $(\"body\").removeClass(\"control-sidebar-slide-open\"); \n // don't forget to refresh the input binding\n $(\"#controlbar-toggle\").trigger('collapsed.lte.controlsidebar');\n }\n } \n });\n \n // handle the pin button: toggle data-pin state\n $(\"#controlbarPin\").on('click', function() {\n var $pinIcon = $(this).children();\n $pinIcon.toggleClass(\"fa-rotate-90 fa-lg\");\n \n $(\".control-sidebar\").attr(\"data-pin\",\n $(\".control-sidebar\").attr(\"data-pin\") == \"false\" ? \"true\" : \"false\");\n // toggle right sidebar control depending on the datapin\n if ($(\".control-sidebar\").attr(\"data-pin\") === \"true\") {\n $pinIcon.css(\"color\", \"#007bff\");\n $(\"#controlbar-toggle\").addClass(\"disabled\");\n controlbarToast();\n } else {\n $(\"#controlbar-toggle\").removeClass(\"disabled\");\n $pinIcon.css(\"color\", \"\");\n }\n });\n\n\nvar init = true;\n\n // Input binding\n var controlbarBinding = new Shiny.InputBinding();\n \n $.extend(controlbarBinding, {\n \n find: function(scope) {\n return $(scope).find(\".control-sidebar\");\n },\n \n // Given the DOM element for the input, return the value\n getValue: function(el) {\n // Handles the pin \n var controlbarOpen = $(\"body\").hasClass(\"control-sidebar-slide-open\");\n var pinned = $(el).attr(\"data-pin\") === \"true\";\n if (controlbarOpen && pinned && init) {\n $(\"#controlbar-toggle\").addClass(\"disabled\");\n $(\"#controlbarPin\")\n .children()\n .css(\"color\", \"#007bff\");\n controlbarToast();\n init = false;\n }\n \n // this handles the case where the controlbar is not collapsed at start\n var controlbarCollapsed = $(el).attr('data-collapsed');\n if (controlbarCollapsed === \"false\") {\n $(\"#controlbar-toggle\").ControlSidebar('toggle');\n $(el).attr('data-collapsed', \"true\");\n return true;\n } else {\n return $(\"body\").hasClass(\"control-sidebar-slide-open\");\n }\n },\n // see updatebs4Controlbar\n receiveMessage: function(el, data) {\n $(\"#controlbar-toggle\").ControlSidebar('toggle');\n },\n \n subscribe: function(el, callback) {\n $(\"#controlbar-toggle\").on(\"collapsed.lte.controlsidebar expanded.lte.controlsidebar\", function(e) {\n $(el).trigger('shown');\n // add a delay so that Shiny get the input value \n // after the AdminLTE3 animation is finished!\n setTimeout(\n function() {\n callback();\n }, 10);\n });\n },\n \n unsubscribe: function(el) {\n $(el).off(\".controlbarBinding\");\n }\n });\n \n Shiny.inputBindings.register(controlbarBinding, \"bs4Dash.controlbarBinding\");\n \n // handle controlbar overlay\n var controlbarOverlay = $('.control-sidebar').attr('data-overlay');\n if (controlbarOverlay === \"false\") {\n $('body').addClass('control-sidebar-push-slide');\n }\n\n});","$(function () {\n // handle tooltip from the server side\n Shiny.addCustomMessageHandler('create-tooltip', function (message) {\n var tooltipTarget;\n if (message.id) {\n tooltipTarget = '#' + message.id;\n } else {\n if (message.selector) {\n tooltipTarget = message.selector;\n }\n }\n $(tooltipTarget)\n .addClass('has-tooltip')\n .tooltip(message.options);\n console.log(`'Tooltip created for ${tooltipTarget}'`);\n });\n\n Shiny.addCustomMessageHandler('remove-tooltip', function (message) {\n var tooltipTarget = '#' + message;\n\n // only destroys if popover exists\n if ($(tooltipTarget).hasClass('has-tooltip')) {\n $(tooltipTarget)\n .removeClass('has-tooltip')\n .tooltip('dispose');\n console.log(`'Tooltip destroyed for ${tooltipTarget}'`);\n }\n });\n\n // handle popover from the server side\n Shiny.addCustomMessageHandler('create-popover', function (message) {\n var popoverTarget;\n if (message.id) {\n popoverTarget = '#' + message.id;\n } else {\n if (message.selector) {\n popoverTarget = message.selector;\n }\n }\n // indicate target has popover. This is for removePopover to know\n // whether the popover exists\n $(popoverTarget)\n .addClass('has-popover')\n .popover(message.options);\n console.log(`'Popover created for ${popoverTarget}'`);\n });\n\n\n Shiny.addCustomMessageHandler('remove-popover', function (message) {\n var popoverTarget = '#' + message;\n\n // only destroys if popover exists\n if ($(popoverTarget).hasClass('has-popover')) {\n $(popoverTarget)\n .removeClass('has-popover')\n .popover('dispose');\n console.log(`'Popover destroyed for ${popoverTarget}'`);\n }\n });\n\n\n // handle builtin toasts\n Shiny.addCustomMessageHandler('toast', function (message) {\n $(document).Toasts('create', message);\n });\n\n // Create an alert\n Shiny.addCustomMessageHandler('create-alert', function (message) {\n // setup target\n var alertTarget;\n if (message.id) {\n alertTarget = `#${message.id}`;\n } else {\n if (message.selector) {\n alertTarget = message.selector;\n }\n }\n\n // build the tag from options\n var config = message.options, alertCl, alertTag, iconType, closeButton, titleTag, contentTag;\n alertCl = 'alert alert-dismissible';\n if (config.status !== undefined) {\n alertCl = `${alertCl} alert-${config.status}`;\n }\n if (config.elevation !== undefined) {\n alertCl = `${alertCl} elevation-${config.elevation}`;\n }\n\n switch (config.status) {\n case 'primary': iconType = 'info';\n break;\n case 'danger': iconType = 'ban';\n break;\n case 'info': iconType = 'info';\n break;\n case 'warning': iconType = 'warning';\n break;\n case 'success': iconType = 'check';\n break;\n default: console.warn(`${config.status} does not belong to allowed statuses!`)\n }\n\n closeButton = '';\n\n if (config.closable) {\n closeButton = ''\n }\n\n titleTag = `
${config.title}
`\n contentTag = config.content;\n\n alertTag = `
\n ${closeButton}${titleTag}${contentTag}\n
`\n if (config.width !== undefined) {\n alertTag = `
${alertTag}
`\n }\n\n // add it to the DOM if no existing alert is found in the anchor\n if ($(`#${message.id}-alert`).length === 0) {\n $(alertTarget).append(alertTag);\n Shiny.setInputValue(message.id, true, { priority: 'event' });\n\n // add events only after element is inserted\n\n // callback -> give ability to perform more actions on the Shiny side\n // once the alert is closed\n $(`#${message.id}-alert`).on('closed.bs.alert', function () {\n Shiny.setInputValue(message.id, false, { priority: 'event' });\n });\n // Clicking on close button does not trigger any event.\n // Trigger the closed.bs.alert event.\n $('[data-dismiss=\"alert\"]').on('click', function () {\n var alertId = $(this).parent.attr('id');\n $(`#${alertId}.`).trigger('closed.bs.alert');\n });\n\n } else {\n console.warn(`${alertTarget} already has an alert!`);\n }\n });\n\n\n Shiny.addCustomMessageHandler('close-alert', function (message) {\n // only closes if element exists\n if ($(`#${message}-alert`).length > 0) {\n $(`#${message}-alert`).alert('close');\n } else {\n console.warn('Nothing to delete!');\n }\n });\n});","// When document is ready, if there is a sidebar menu with no activated tabs,\n// activate the one specified by `data-start-selected`, or if that's not\n// present, the first one.\nvar ensureActivatedTab = function() {\n // get the selected tabs\n var $tablinks = $(\".sidebar-menu a[data-toggle='tab']\");\n\n // If there are no tabs, $startTab.length will be 0.\n var $startTab = $tablinks.filter(\"[data-start-selected='1']\");\n if ($startTab.length === 0) {\n // If no tab starts selected, use the first one, if present\n $startTab = $tablinks.first();\n }\n\n // If there's a `data-start-selected` attribute and we can find a tab with\n // that name, activate it.\n if ($startTab.length !== 0) {\n // This is just in case the user renders the tabs in a renderUI that does not\n // print immediately in the DOM. We need a bit of a delay before telling which\n // tab to show ...\n if ($(\".sidebar-menu\").hasClass(\"bs4Dash-menu-output\")) {\n setTimeout(function() {\n // we need to initialize any treeview elements that were not inserted\n // in the DOM when adminlte was first initialized!\n adminlte.Treeview._jQueryInterface.call($('[data-widget=\"treeview\"]'), 'init');\n\n $startTab.tab(\"show\");\n }, 10);\n } else {\n $startTab.tab(\"show\");\n }\n \n\n // This is indirectly setting the value of the Shiny input by setting\n // an attribute on the html element it is bound to. We cannot use the\n // inputBinding's setValue() method here because this is called too\n // early (before Shiny has fully initialized)\n $(\".sidebarMenuSelectedTabItem\").attr(\n \"data-value\",\n $startTab.attr(\"data-value\")\n );\n }\n};\n\n// This function handles a special case in the AdminLTE sidebar: when there\n// is a sidebar-menu with items, and one of those items has sub-items, and\n// they are used for tab navigation. Normally, if one of the items is\n// selected and then a sub-item is clicked, both the item and sub-item will\n// retain the \"active\" class, so they will both be highlighted. This happens\n// because they're not designed to be used together for tab panels. This\n// code ensures that only one item will have the \"active\" class.\nvar deactivateOtherTabs = function() {\n // Find all tab links under sidebar-menu even if they don't have a\n // tabName (which is why the second selector is necessary)\n var $tablinks = $(\n \".sidebar-menu a[data-toggle='tab'],\" + \".sidebar-menu li.has-treeview > a\"\n );\n\n // If any other items are active, deactivate them\n $tablinks.not($(this)).removeClass(\"active\");\n\n // also manually activate the parent link when the selected item\n // is part of a treeview. For some reason, this is not done by AdminLTE3...\n if ($(this).hasClass(\"treeview-link\")) {\n $(this)\n .parents(\".has-treeview\")\n .children()\n .eq(0)\n .addClass(\"active\");\n }\n\n // Trigger event for the tabItemInputBinding\n var $obj = $(\".sidebarMenuSelectedTabItem\");\n var inputBinding = $obj.data(\"shiny-input-binding\");\n if (typeof inputBinding !== \"undefined\") {\n inputBinding.setValue($obj, $(this).attr(\"data-value\"));\n $obj.trigger(\"change\");\n }\n};\n\n$(function() {\n // Whenever the sidebar finishes a transition (which it does every time it\n // changes from collapsed to expanded and vice versa), trigger resize,\n // so that all outputs are resized.\n $(\".main-sidebar\").on(\n \"webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend\",\n function() {\n $(window).trigger(\"resize\");\n }\n );\n\n $(document).on(\n \"shown.bs.tab\",\n '.sidebar-menu a[data-toggle=\"tab\"]',\n deactivateOtherTabs\n );\n\n ensureActivatedTab();\n\n // Whenever we expand a menuItem (to be expandable, it must have children),\n // update the value for the expandedItem's input binding (this is the\n // tabName of the fist subMenuItem inside the menuItem that is currently\n // expanded)\n $(document).on(\"click\", \".has-treeview\", function() {\n var $menu = $(this);\n // If this menuItem was already open, then clicking on it again,\n // should trigger the \"hidden\" event, so Shiny doesn't worry about\n // it while it's hidden (and vice versa).\n if ($menu.hasClass(\"menu-open\")) $menu.trigger(\"collapsed.lte.treeview\");\n else if ($menu.hasClass(\".has-treeview\"))\n $menu.trigger(\"expanded.lte.treeview\");\n\n // need to set timeout to account for the slideUp/slideDown animation\n var $obj = $(\".sidebar.shiny-bound-input\");\n setTimeout(function() {\n $obj.trigger(\"change\");\n }, 600);\n });\n\n //---------------------------------------------------------------------\n // tabItemInputBinding\n // ------------------------------------------------------------------\n // Based on Shiny.tabItemInputBinding, but customized for tabItems in\n // bs4Dash, which have a slightly different structure.\n var tabItemInputBinding = new Shiny.InputBinding();\n $.extend(tabItemInputBinding, {\n find: function(scope) {\n return $(scope).find(\".sidebarMenuSelectedTabItem\");\n },\n getValue: function(el) {\n var value = $(el).attr(\"data-value\");\n if (value === \"null\") return null;\n return value;\n },\n setValue: function(el, value) {\n var self = this;\n var anchors = $(el)\n .parent(\".sidebar-menu\")\n .find(\"li:not(.treeview)\")\n .children(\"a\");\n anchors.each(function() {\n // eslint-disable-line consistent-return\n if (self._getTabName($(this)) === value) {\n $(this).tab(\"show\");\n // this make sure that treeview items are open when we\n // use the updatebs4TabItems function on the server side\n if ($(this).hasClass(\"treeview-link\")) {\n if (\n !$(this)\n .parents(\".has-treeview\")\n .hasClass(\"menu-open\")\n ) {\n $(this)\n .parents(\".has-treeview\")\n .children()\n .eq(0)\n .trigger(\"click\");\n }\n }\n $(el).attr(\"data-value\", self._getTabName($(this)));\n return false;\n }\n });\n },\n receiveMessage: function(el, data) {\n if (data.hasOwnProperty(\"value\")) this.setValue(el, data.value);\n },\n subscribe: function(el, callback) {\n // This event is triggered by deactivateOtherTabs, which is triggered by\n // shown. The deactivation of other tabs must occur before Shiny gets the\n // input value.\n $(el).on(\"change.tabItemInputBinding\", function() {\n callback();\n });\n },\n unsubscribe: function(el) {\n $(el).off(\".tabItemInputBinding\");\n },\n _getTabName: function(anchor) {\n return anchor.attr(\"data-value\");\n }\n });\n\n Shiny.inputBindings.register(tabItemInputBinding, \"bs4Dash.tabItemInput\");\n\n //---------------------------------------------------------------------\n // sidebarInputBinding\n // ------------------------------------------------------------------\n // similar to controlbarInputBinding\n var sidebarBinding = new Shiny.InputBinding();\n\n $.extend(sidebarBinding, {\n find: function(scope) {\n return $(scope).find(\".main-sidebar\");\n },\n\n // Given the DOM element for the input, return the value\n getValue: function(el) {\n // Warning: we can't look for sidebar-open since this\n // class is only generated on mobile devices\n return !$(\"body\").hasClass(\"sidebar-collapse\");\n },\n\n // see updatebs4Controlbar\n receiveMessage: function(el, data) {\n $(\"[data-widget='pushmenu']\").PushMenu(\"toggle\");\n },\n\n subscribe: function(el, callback) {\n $(\"[data-widget='pushmenu']\").on(\n \"collapsed.lte.pushmenu.sidebarBinding shown.lte.pushmenu.sidebarBinding\",\n function(e) {\n callback();\n }\n );\n },\n\n unsubscribe: function(el) {\n $(el).off(\".sidebarBinding\");\n }\n });\n\n Shiny.inputBindings.register(sidebarBinding, \"bs4Dash.sidebarInput\");\n\n // sidebarmenuExpandedInputBinding\n // ------------------------------------------------------------------\n // This keeps tracks of what menuItem (if any) is expanded\n var sidebarmenuExpandedInputBinding = new Shiny.InputBinding();\n $.extend(sidebarmenuExpandedInputBinding, {\n find: function(scope) {\n // This will also have id=\"sidebarItemExpanded\"\n return $(scope).find(\".sidebar\");\n },\n getValue: function(el) {\n var $open = $(el)\n .find(\"li\")\n .filter(\".menu-open\")\n .find(\"ul\");\n if ($open.length === 1) return $open.attr(\"data-expanded\");\n else return null;\n },\n setValue: function(el, value) {\n // does not work (nothing is printed)\n var $menuItem = $(el).find(\"[data-expanded='\" + value + \"']\");\n // This will trigger actions defined by AdminLTE, as well as actions\n // defined in sidebar.js.\n $menuItem.prev().trigger(\"click\");\n },\n subscribe: function(el, callback) {\n $(el).on(\"change.sidebarmenuExpandedInputBinding\", function() {\n callback();\n });\n },\n unsubscribe: function(el) {\n $(el).off(\".sidebarmenuExpandedInputBinding\");\n }\n });\n Shiny.inputBindings.register(\n sidebarmenuExpandedInputBinding,\n \"bs4Dash.sidebarmenuExpandedInputBinding\"\n );\n\n // handle fixed sidebar\n if ($(\".main-sidebar\").attr(\"data-fixed\") === \"true\") {\n $(\"body\").addClass(\"layout-fixed\");\n //$('body').Layout('fixLayoutHeight');\n }\n});\n","$(function () {\n\n $navbar = $('.main-header.navbar');\n\n // Modify the shiny tabsetpanel binding to follow BS4 rules\n $(document).on('shiny:connected', function (event) {\n Shiny.unbindAll();\n $.extend(Shiny\n .inputBindings\n .bindingNames['shiny.bootstrapTabInput']\n .binding, {\n // do whathever you want to edit existing methods\n getValue: function (el) {\n var anchor = $(el).find('li:not(.dropdown)').children('a.active');\n if (anchor.length === 1)\n return this._getTabName(anchor);\n\n return null;\n }\n });\n Shiny.bindAll();\n });\n\n // footer has fixed layout?\n if ($(\".main-footer\").attr(\"data-fixed\") === \"true\") {\n $(\"body\").addClass(\"layout-footer-fixed\");\n }\n\n // add dropdown-menu-right class to correctly open the dropdown to all \n // navbar rightUi elements\n var navbarRight = $('.navbar-right').find('.dropdown-menu');\n $(navbarRight).each(function () {\n if (!$(this).hasClass('dropdown-menu-right')) {\n $(this).addClass('dropdown-menu-right');\n }\n });\n\n\n // data toggle collapse icon update\n $('.user-block [data-toggle=\"collapse\"]').on('click', function () {\n if ($(this).children('i').hasClass('fa-plus')) {\n $(this).children('i').attr('class', 'fa fa-minus');\n } else {\n $(this).children('i').attr('class', 'fa fa-plus');\n }\n });\n\n // fullscreen toggle\n if ($('body').attr('data-fullscreen') == 1) {\n var fullScreenToggle = `
`;\n $(fullScreenToggle).insertBefore($('[data-widget=\"control-sidebar\"]').parent());\n }\n\n // slide to top button\n if ($('body').attr('data-scrollToTop') == 1) {\n var $slideToTop = $('');\n\n $slideToTop.html('');\n\n $slideToTop.css({\n position: 'fixed',\n bottom: '20px',\n right: '25px',\n width: '40px',\n height: '40px',\n color: '#eee',\n 'font-size': '',\n 'line-height': '40px',\n 'text-align': 'center',\n 'background-color': '#222d32',\n cursor: 'pointer',\n 'border-radius': '5px',\n 'z-index': '99999',\n opacity: '.7',\n 'display': 'none'\n });\n\n $slideToTop.on('mouseenter', function () {\n $(this).css('opacity', '1');\n });\n\n $slideToTop.on('mouseout', function () {\n $(this).css('opacity', '.7');\n });\n\n $('.wrapper').append($slideToTop);\n\n $(window).scroll(function () {\n if ($(window).scrollTop() >= 150) {\n if (!$($slideToTop).is(':visible')) {\n $($slideToTop).fadeIn(500);\n }\n } else {\n $($slideToTop).fadeOut(500);\n }\n });\n\n $($slideToTop).click(function () {\n $('html, body').animate({\n scrollTop: 0\n }, 500);\n });\n }\n \n // nav item click also triggers scroll to top\n $('.main-sidebar .nav-item').on('click', function () {\n $('html, body').animate({\n scrollTop: 0\n }, 0);\n }); \n\n\n // tooltip/popover toggle\n if ($('body').attr('data-help') == 2 || \n $('body').attr('data-help') == 1) {\n var $help_switch_checkbox = $('', {\n type: 'checkbox',\n id: 'help_switch',\n class: 'custom-control-input'\n }).on('click', function () {\n if ($(this).is(':checked')) {\n $('[data-toggle=\"tooltip\"]').tooltip('enable');\n $('[data-toggle=\"popover\"]').popover({\n trigger: 'hover'\n });\n $('[data-toggle=\"popover\"]').popover('enable');\n } else {\n $('[data-toggle=\"tooltip\"]').tooltip('disable');\n $('[data-toggle=\"popover\"]').popover('disable');\n }\n });\n\n var $help_switch_container = $('', { class: 'custom-control custom-switch mx-2 mt-2' }).append($help_switch_checkbox).append(``);\n \n // insert before $('#controlbar-toggle') whenever possible ...\n if ($('.nav-item #controlbar-toggle')) {\n $help_switch_container.insertBefore($('#controlbar-toggle').parent());\n } else {\n $navbar.append($help_switch_container);\n }\n\n // trigger first click, if necessary\n $(document).on('shiny:connected', function() {\n if ($('body').attr('data-help') == 2 || $('body').attr('data-help') == 1) {\n $help_switch_checkbox.click(); \n // Click again if option is set to FALSE\n if ($('body').attr('data-help') == 1) {\n $help_switch_checkbox.click(); \n }\n }\n }); \n }\n\n // dark mode input\n $(document).one('shiny:connected', function () {\n if ($('body').hasClass('dark-mode')) {\n Shiny.setInputValue('dark_mode', true, { priority: 'event' });\n } else {\n Shiny.setInputValue('dark_mode', false, { priority: 'event' });\n }\n });\n\n\n // Navbar colors\n getNavbarColor = function () {\n for (let color of navbar_all_colors) {\n if ($('.main-header').attr('class').search(color) > -1) {\n return color;\n }\n }\n };\n\n var navbar_dark_skins = [\n 'navbar-primary',\n 'navbar-secondary',\n 'navbar-info',\n 'navbar-success',\n 'navbar-danger',\n 'navbar-indigo',\n 'navbar-purple',\n 'navbar-pink',\n 'navbar-maroon',\n 'navbar-fuchsia',\n 'navbar-navy',\n 'navbar-lightblue',\n 'navbar-lime',\n 'navbar-teal',\n 'navbar-olive',\n 'navbar-gray-dark',\n 'navbar-gray'\n ];\n\n var navbar_light_skins = [\n 'navbar-warning',\n 'navbar-white',\n 'navbar-orange'\n ];\n\n var navbar_all_colors = navbar_dark_skins.concat(navbar_light_skins);\n \n /**\n * Update color theme to navbar tag\n *\n * @param String color Color to apply.\n * @returns void\n */\n updateNavbarTheme = function (color) {\n var $main_header = $('.main-header');\n $main_header.removeClass('navbar-dark').removeClass('navbar-light');\n navbar_all_colors.forEach(function (color) {\n $main_header.removeClass(color);\n });\n\n if (navbar_dark_skins.indexOf(color) > -1) {\n $main_header.addClass('navbar-dark');\n } else {\n $main_header.addClass('navbar-light');\n }\n\n $main_header.addClass(color);\n };\n \n /**\n * Update icon color style based on navbar color.\n *\n * @param String color Current navbar color.\n * @returns void\n */\n updateNavbarIconColor = function(color) {\n var iconThemeColor = navbar_dark_skins.indexOf(color) > -1 ? \"white\" : \"rgba(0,0,0,.5)\";\n $(\".dark-theme-icon\").css(\"color\", iconThemeColor);\n $('[for=\"help_switch\"] i').css(\"color\", iconThemeColor);\n };\n \n // automatic global theme switcher\n if ($('body').attr('data-dark') == 2 || \n $('body').attr('data-dark') == 1) {\n var $dark_mode_checkbox = $('', {\n type: 'checkbox',\n id: 'theme_switch',\n class: 'custom-control-input'\n }).on('click', function () {\n\n // get any selected navbar skin in the navbar themer\n var newNavbarColor;\n // If there is not themer, we keep the navbar current color.\n // Otherwise, we replace it by the new color.\n if ($('.navbar-themer-chip').length > 0) {\n $('.navbar-themer-chip').filter(function () {\n if ($(this).css('border-style') === 'solid') {\n newNavbarColor = 'navbar-' +\n $(this)\n .attr('class')\n .split('elevation-2')[0]\n .trim()\n .replace('bg-', '');\n }\n });\n } else {\n newNavbarColor = getNavbarColor();\n }\n\n if ($(this).is(':checked')) {\n $('body').addClass('dark-mode');\n\n // use updateNavbarTheme to correctly setup the skin as depending\n // on the required color. If no color is chosen, we use gray-dark for dark mode\n if (newNavbarColor === undefined || newNavbarColor === 'navbar-white') {\n newNavbarColor = \"navbar-gray-dark\";\n }\n updateNavbarTheme(newNavbarColor);\n\n // sidebar update \n if ($('.main-sidebar').length > 0) {\n $('.main-sidebar').attr('class', $('.main-sidebar')\n .attr('class')\n .replace('light', 'dark'));\n $('#sidebar-skin').prop(\"checked\", true);\n\n $('.sidebar-themer-icon')\n .removeClass('fa-sun')\n .addClass('fa-moon');\n }\n\n // controlbar update\n if ($('.control-sidebar').length > 0) {\n $('.control-sidebar').attr('class', $('.control-sidebar')\n .attr('class')\n .replace('light', 'dark'));\n $('#controlbar-skin').prop(\"checked\", true);\n\n $('.controlbar-themer-icon')\n .removeClass('fa-sun')\n .addClass('fa-moon');\n }\n\n\n $('.dark-theme-icon')\n .removeClass('fa-sun')\n .addClass('fa-moon');\n\n // refresh shiny input value \n Shiny.setInputValue('dark_mode', true, { priority: 'event' });\n\n } else {\n $('body').removeClass('dark-mode');\n\n // use updateNavbarTheme to correctly setup the skin as depending\n // on the required color. If no color is chosen, we use white for light mode\n if (newNavbarColor === undefined || newNavbarColor === 'navbar-gray-dark') {\n newNavbarColor = \"navbar-white\";\n }\n updateNavbarTheme(newNavbarColor);\n\n // sidebar update\n if ($('.main-sidebar').length > 0) {\n $('.main-sidebar').attr('class', $('.main-sidebar')\n .attr('class')\n .replace('dark', 'light'));\n $('#sidebar-skin').prop(\"checked\", false);\n\n $('.sidebar-themer-icon')\n .removeClass('fa-moon')\n .addClass('fa-sun');\n }\n\n // controlbar update\n if ($('.control-sidebar').length > 0) {\n $('.control-sidebar').attr('class', $('.control-sidebar')\n .attr('class')\n .replace('dark', 'light'));\n $('#controlbar-skin').prop(\"checked\", false);\n\n $('.controlbar-themer-icon')\n .removeClass('fa-moon')\n .addClass('fa-sun');\n }\n\n $('.dark-theme-icon')\n .removeClass('fa-moon')\n .addClass('fa-sun');\n\n // refresh shiny input value \n Shiny.setInputValue('dark_mode', false, { priority: 'event' });\n }\n \n // update navbar icon colors\n updateNavbarIconColor(newNavbarColor);\n });\n\n var $dark_mode_icon = $('body').hasClass('dark-mode') ? '' : '';\n var $dark_mode_container = $('', { class: 'custom-control custom-switch mx-2 mt-2' }).append($dark_mode_checkbox).append(``);\n \n // insert before $('#controlbar-toggle') whenever possible ...\n if ($('.nav-item #controlbar-toggle')) {\n $dark_mode_container.insertBefore($('#controlbar-toggle').parent());\n } else {\n $navbar.append($dark_mode_container);\n }\n \n \n // Trigger dark mode\n if ($('body').attr('data-dark') == 2) {\n $(document).on('shiny:connected', function() {\n $dark_mode_checkbox.click();\n }); \n }\n }\n \n // apply correct navbar class depending on selected color\n if (getNavbarColor() !== undefined) {\n updateNavbarTheme(getNavbarColor());\n updateNavbarIconColor(getNavbarColor());\n }\n \n\n // Themer chips\n\n // Better style on hover\n $('.themer-chip').hover(function () {\n $(this).css({ opacity: 1 }).removeClass('elevation-2').addClass('elevation-4');\n }, function () {\n $(this).css({ opacity: 0.8 }).removeClass('elevation-4').addClass('elevation-2');\n });\n\n // \n $('.navbar-themer-chip').on('click', function () {\n $(this).css({ 'border-color': 'yellow', 'border-style': 'solid' });\n $('.navbar-themer-chip').not(this).css({ 'border-color': '', 'border-style': '' });\n });\n\n $('.accents-themer-chip').on('click', function () {\n $(this).css({ 'border-color': 'yellow', 'border-style': 'solid' });\n $('.accents-themer-chip').not(this).css({ 'border-color': '', 'border-style': '' });\n });\n\n $('.sidebar-themer-chip').on('click', function () {\n $(this).css({ 'border-color': 'yellow', 'border-style': 'solid' });\n $('.sidebar-themer-chip').not(this).css({ 'border-color': '', 'border-style': '' });\n });\n\n\n // Sidebar themer\n\n // detect global sidebar theme and select or not the toggle\n if ($('.main-sidebar').length > 0) {\n if ($('.main-sidebar').attr('class').match('dark')) {\n $('#sidebar-skin').prop(\"checked\", true);\n }\n }\n\n // clicking on dark/light switch changes:\n // - icon style\n // - sidebar class \n $('#sidebar-skin').on('click', function () {\n var sidebarCl;\n if ($(this).is(':checked')) {\n sidebarCl = $('.main-sidebar')\n .attr('class')\n .replace('light', 'dark');\n $('.main-sidebar').attr('class', sidebarCl);\n\n $('.sidebar-themer-icon')\n .removeClass('fa-sun')\n .addClass('fa-moon');\n } else {\n sidebarCl = $('.main-sidebar')\n .attr('class')\n .replace('dark', 'light');\n $('.main-sidebar').attr('class', sidebarCl);\n\n $('.sidebar-themer-icon')\n .removeClass('fa-moon')\n .addClass('fa-sun');\n }\n });\n\n var sidebar_colors = [\n 'bg-primary',\n 'bg-secondary',\n 'bg-info',\n 'bg-success',\n 'bg-danger',\n 'bg-indigo',\n 'bg-purple',\n 'bg-pink',\n 'bg-maroon',\n 'bg-fuchsia',\n 'bg-navy',\n 'bg-lightblue',\n 'bg-lime',\n 'bg-teal',\n 'bg-olive',\n 'bg-gray-dark',\n 'bg-gray',\n 'bg-light',\n 'bg-warning',\n 'bg-white',\n 'bg-orange'\n ];\n\n\n var sidebar_skins = [\n 'sidebar-dark-primary',\n 'sidebar-dark-secondary',\n 'sidebar-dark-info',\n 'sidebar-dark-success',\n 'sidebar-dark-danger',\n 'sidebar-dark-indigo',\n 'sidebar-dark-purple',\n 'sidebar-dark-pink',\n 'sidebar-dark-maroon',\n 'sidebar-dark-fuchsia',\n 'sidebar-dark-navy',\n 'sidebar-dark-lightblue',\n 'sidebar-dark-lime',\n 'sidebar-dark-teal',\n 'sidebar-dark-olive',\n 'sidebar-dark-gray-dark',\n 'sidebar-dark-gray',\n 'sidebar-dark-light',\n 'sidebar-dark-warning',\n 'sidebar-dark-white',\n 'sidebar-dark-orange',\n 'sidebar-light-primary',\n 'sidebar-light-secondary',\n 'sidebar-light-info',\n 'sidebar-light-success',\n 'sidebar-light-danger',\n 'sidebar-light-indigo',\n 'sidebar-light-purple',\n 'sidebar-light-pink',\n 'sidebar-light-maroon',\n 'sidebar-light-fuchsia',\n 'sidebar-light-navy',\n 'sidebar-light-lightblue',\n 'sidebar-light-lime',\n 'sidebar-light-teal',\n 'sidebar-light-olive',\n 'sidebar-light-gray-dark',\n 'sidebar-light-gray',\n 'sidebar-light-light',\n 'sidebar-light-warning',\n 'sidebar-light-white',\n 'sidebar-light-orange'\n ];\n\n\n updateSidebarTheme = function (color) {\n var sidebarCl;\n if ($('#sidebar-skin').is(':checked')) {\n sidebarCl = 'sidebar-dark-';\n } else {\n sidebarCl = 'sidebar-light-';\n }\n\n var sidebar_class = sidebarCl + color.replace('bg-', '');\n var $sidebar = $('.main-sidebar');\n sidebar_skins.forEach(function (skin) {\n $sidebar.removeClass(skin);\n });\n\n $sidebar.addClass(sidebar_class);\n };\n\n\n // Accents themer\n var accent_colors = [\n 'accent-primary',\n 'accent-secondary',\n 'accent-info',\n 'accent-success',\n 'accent-danger',\n 'accent-indigo',\n 'accent-purple',\n 'accent-pink',\n 'accent-maroon',\n 'accent-fuchsia',\n 'accent-navy',\n 'accent-lightblue',\n 'accent-lime',\n 'accent-teal',\n 'accent-olive',\n 'accent-gray-dark',\n 'accent-gray',\n 'accent-light',\n 'accent-warning',\n 'accent-white',\n 'accent-orange'\n ];\n\n\n updateAccentsTheme = function (color) {\n var accent_class = color;\n var $body = $('body');\n accent_colors.forEach(function (skin) {\n $body.removeClass(skin);\n });\n\n $body.addClass(accent_class);\n };\n\n\n // Controlbar themer\n\n // detect global controlbar theme and select or not the toggle\n if ($('.control-sidebar').length > 0) {\n if ($('.control-sidebar').attr('class').match('dark')) {\n $('#controlbar-skin').prop(\"checked\", true);\n }\n }\n\n // clicking on dark/light switch changes:\n // - icon style\n // - sidebar class \n $('#controlbar-skin').on('click', function () {\n var controlbarCl;\n if ($(this).is(':checked')) {\n controlbarCl = $('.control-sidebar')\n .attr('class')\n .replace('light', 'dark');\n $('.control-sidebar').attr('class', controlbarCl);\n\n $('.controlbar-themer-icon')\n .removeClass('fa-sun')\n .addClass('fa-moon');\n } else {\n controlbarCl = $('.control-sidebar')\n .attr('class')\n .replace('dark', 'light');\n $('.control-sidebar').attr('class', controlbarCl);\n\n $('.controlbar-themer-icon')\n .removeClass('fa-moon')\n .addClass('fa-sun');\n }\n });\n\n});","$(function () {\n // hide the right sidebar toggle \n // if no right sidebar is specified\n noControlbar = ($(\".control-sidebar\").length === 0);\n if (noControlbar) {\n $(\"#controlbar-toggle\").hide();\n }\n\n // hide the right sidebar toggle if the controlbar is disable\n disableControlbar = ($(\".control-sidebar\").attr(\"data-show\"));\n if (!disableControlbar) {\n $(\"#controlbar-toggle\").hide();\n }\n\n // controlbar slide\n controlbarSlide = ($(\".control-sidebar\").attr(\"data-slide\"));\n if (controlbarSlide) {\n $(\"#controlbar-toggle\").attr('data-controlsidebar-slide', controlbarSlide);\n }\n\n // when the sidebar is disabled, hide the sidebar toggle\n disableSidebar = ($(\".main-sidebar\").length === 0);\n if (disableSidebar) {\n $(\".nav-item > a[data-widget='pushmenu']\").css(\"visibility\", \"hidden\");\n }\n\n // handle fixed navbar\n if ($(\".navbar\").attr(\"data-fixed\") === \"true\") {\n $(\"body\").addClass(\"layout-navbar-fixed\");\n }\n\n});","var menuOutputBinding = new Shiny.OutputBinding();\n$.extend(menuOutputBinding, {\n find: function (scope) {\n return $(scope).find('.bs4Dash-menu-output');\n },\n onValueError: function (el, err) {\n Shiny.unbindAll(el);\n this.renderError(el, err);\n },\n renderValue: function (el, data) {\n Shiny.unbindAll(el);\n\n var html;\n var dependencies = [];\n if (data === null) {\n return;\n } else if (typeof (data) === 'string') {\n html = data;\n } else if (typeof (data) === 'object') {\n html = data.html;\n dependencies = data.deps;\n }\n\n var $html = $($.parseHTML(html));\n\n // Convert the inner contents to HTML, and pass to renderHtml\n Shiny.renderHtml($html.html(), el, dependencies);\n\n // Extract class of wrapper, and add them to the wrapper element\n el.className = 'bs4Dash-menu-output shiny-bound-output ' +\n $html.attr('class');\n \n // need this to activate adminLTE3 plugin for treeview \n $(el)\n .attr(\"data-widget\", \"treeview\")\n .attr(\"role\", \"menu\")\n .attr(\"data-accordion\", \"true\");\n\n Shiny.initializeInputs(el);\n Shiny.bindAll(el);\n if ($(el).hasClass(\"sidebar-menu\")) ensureActivatedTab(); // eslint-disable-line\n }\n});\nShiny.outputBindings.register(menuOutputBinding, \"bs4Dash.menuOutputBinding\");","$( document ).ready(function() {\n \n function findActivePage() {\n return $('ul.pagination').find('li.active');\n };\n \n function toggleNavigationItems(activeItem) {\n // hide pagination previous if we are at the first item\n if ($(activeItem).prev().find('a').hasClass('pagination-previous')) {\n $('.pagination-previous').parent('li').css('display', 'none');\n } else {\n $('.pagination-previous').parent('li').css('display', 'block');\n }\n \n // hide pagination next if we are at the last item\n if ($(activeItem).next().find('a').hasClass('pagination-next')) {\n $('.pagination-next').parent('li').css('display', 'none');\n } else {\n $('.pagination-next').parent('li').css('display', 'block');\n }\n }\n \n // Must run at start\n toggleNavigationItems(findActivePage());\n \n // Toggle active item button state\n $('ul.pagination a:not(.disabled, .pagination-previous, .pagination-next)')\n .on('click', function() {\n var activeItem = findActivePage();\n $(activeItem).removeClass('active');\n $(this).parent().addClass('active');\n activeItem = $(this).parent(); // store new active item\n // Also run dynamically\n toggleNavigationItems(activeItem);\n });\n \n // Previous click\n $('ul.pagination .pagination-previous').on('click', function() {\n var activeItem = findActivePage();\n \n var previousSibling = $(activeItem).prev();\n // jump back when we find a disabled sibling\n while ($(previousSibling).hasClass('disabled')) {\n previousSibling = $(previousSibling).prev();\n }\n // Only if active item has a previous sibling that is not \n // the pagination-previous itself.\n if ($(previousSibling).find('a').hasClass('pagination-previous') == false) {\n $(activeItem).removeClass('active');\n $(previousSibling).find('a').click(); \n }\n });\n \n // Next click\n $('ul.pagination .pagination-next').on('click', function() {\n var activeItem = findActivePage();\n \n var nextSibling = $(activeItem).next();\n // jump back when we find a disabled sibling\n while ($(nextSibling).hasClass('disabled')) {\n nextSibling = $(nextSibling).next();\n }\n // Only if active item has a previous sibling that is not \n // the pagination-previous itself.\n if ($(nextSibling).find('a').hasClass('pagination-next') == false) {\n $(activeItem).removeClass('active');\n $(nextSibling).find('a').click(); \n }\n });\n \n var paginationBinding = new Shiny.InputBinding();\n $.extend(paginationBinding, {\n \n initialize: function(el) {\n \n },\n \n find: function (scope) {\n return $(scope).find('ul.pagination');\n },\n \n // Given the DOM element for the input, return the value\n getValue: function (el) {\n return $(el).find('li.active a').attr('data-value');\n },\n \n setValue: function(el, value) {\n \n },\n // internal\n _disableTab: function(el, value) {\n $(el)\n .find('a[data-value=\"' + value + '\"]')\n .parent()\n .removeClass('active')\n .addClass('disabled')\n .attr('tabindex', '-1')\n },\n // see updatePagination\n receiveMessage: function (el, data) {\n // Activate new element\n if (data.hasOwnProperty('selected')) {\n // Disable active element\n $(el)\n .find('.active')\n .removeClass('active');\n // Activate new element\n var selectedItem = $(el)\n .find('a[data-value=\"' + data.selected + '\"]');\n // If the element was disabled before\n if ($(selectedItem).parent('li').hasClass('disabled')) {\n $(selectedItem)\n .parent('li')\n .removeClass('disabled')\n .removeAttr('tabindex');\n }\n $(selectedItem).click();\n }\n \n // Disable elements\n if (data.hasOwnProperty('disabled')) {\n // loop over all elements\n if (typeof data.disabled == 'string') {\n this._disableTab(el, data.disabled)\n } else {\n for (i of data.disabled) {\n // disable element \n this._disableTab(el, i)\n }\n }\n \n // Activate next or previous not disabled li\n var hasActiveItem = $(el).find('li.active').length\n if (hasActiveItem == 0) {\n // Activate first element found (maybe discussed ...)\n var newActive = $(el)\n .find('a:not(.pagination-previous, .pagination-next)')\n .parent('li:not(.disabled)');\n if (newActive.length > 0) {\n $(newActive[0]).find('a').click();\n }\n }\n }\n \n // Trigger the callback to update the input value \n // on the server side.\n $(el).trigger('change');\n },\n \n subscribe: function (el, callback) {\n $(el).find('a').on('click', function() {\n callback();\n });\n \n // Necessry for updatePagination\n $(el).on('change', function() {\n callback();\n })\n },\n \n unsubscribe: function (el) {\n $(el).off('.paginationBinding');\n }\n });\n \n Shiny.inputBindings.register(paginationBinding);\n});","var bootstrapTabInputBinding = new Shiny.InputBinding();\n$.extend(bootstrapTabInputBinding, {\n find: function(scope) {\n return $(scope).find('ul.nav.shiny-tab-input');\n },\n getValue: function(el) {\n // for Bootstrap 4, li element does not hold the active class anymore!\n // We need to look at a.active.\n var anchor = $(el).find('li:not(.dropdown)').children('a.active');\n if (anchor.length === 1)\n return this._getTabName(anchor);\n\n return null;\n },\n setValue: function(el, value) {\n let self = this;\n let success = false;\n if (value) {\n let anchors = $(el).find('li:not(.dropdown)').children('a');\n anchors.each(function() {\n if (self._getTabName($(this)) === value) {\n $(this).tab('show');\n success = true;\n return false; // Break out of each()\n }\n return true;\n });\n }\n if (!success) {\n // This is to handle the case where nothing is selected, e.g. the last tab\n // was removed using removeTab.\n $(el).trigger(\"change\");\n }\n },\n getState: function(el) {\n return { value: this.getValue(el) };\n },\n receiveMessage: function(el, data) {\n if (data.hasOwnProperty('value'))\n this.setValue(el, data.value);\n },\n subscribe: function(el, callback) {\n $(el).on('change shown.bootstrapTabInputBinding shown.bs.tab.bootstrapTabInputBinding', function(event) {\n callback();\n });\n },\n unsubscribe: function(el) {\n $(el).off('.bootstrapTabInputBinding');\n },\n _getTabName: function(anchor) {\n return anchor.attr('data-value') || anchor.text();\n }\n});\n\nShiny.inputBindings.register(bootstrapTabInputBinding, 'shiny.bootstrapTabInput');","// This code creates acustom handler for userMessages\nShiny.addCustomMessageHandler(\"user-messages\", function(message) {\n var id = message.id, action = message.action, content = message.body, index = message.index;\n \n // message text\n // We use Shiny.renderHtml to handle the case where the user pass input/outputs in the updated content that require a new dependency not available in the \n // page at startup. \n if (content.hasOwnProperty(\"text\")) {\n var text;\n if (content.text.html === undefined) {\n text = content.text;\n } else {\n text = Shiny.renderHtml(content.text.html, $([]), content.text.dependencies).html;\n } \n }\n \n // unbind all\n Shiny.unbindAll();\n \n if (action === \"remove\") {\n $(\"#\" + id).find(\".direct-chat-msg\").eq(index - 1).remove();\n } else if (action === \"add\") {\n var author = content.author, date = content.date, image = content.image, type = content.type;\n \n // build the new message \n var authorWrapper, dateWrapper;\n if (type === \"sent\") {\n authorWrapper = '' + author + '';\n dateWrapper = '' + date + '';\n } else {\n authorWrapper = '' + author + '';\n dateWrapper = '' + date + '';\n }\n\n var newMessage = `
${authorWrapper}${dateWrapper}
${text}
`;\n \n // build wrapper\n var newMessageWrapper;\n if (type === \"sent\") {\n newMessageWrapper = '
' + newMessage + '
';\n } else {\n newMessageWrapper = '
' + newMessage + '
';\n }\n \n // append message\n $(\"#\" + id).find(\".direct-chat-messages\").append(newMessageWrapper);\n } else if (action === \"update\") {\n \n // today's date\n var d = new Date();\n var month = d.getMonth() + 1;\n var day = d.getDate();\n var today = d.getFullYear() + '/' +\n ((''+month).length<2 ? '0' : '') + month + '/' +\n ((''+day).length<2 ? '0' : '') + day;\n \n // we assume only text may be updated. Does not make sense to modify author/date\n \n $(\"#\" + id)\n .find(\".direct-chat-text\")\n .eq(index - 1)\n .replaceWith('
(modified: ' + today +') ' + text + '
');\n }\n \n // Calls .initialize() for all of the input objects in all input bindings,\n // in the given scope (document)\n Shiny.initializeInputs();\n Shiny.bindAll(); // bind all inputs/outputs\n});"]}
diff --git a/man/dashboardControlbar.Rd b/man/dashboardControlbar.Rd
index 7dcf52ed..c91a0b4a 100644
--- a/man/dashboardControlbar.Rd
+++ b/man/dashboardControlbar.Rd
@@ -16,7 +16,7 @@ bs4DashControlbar(
width = 250,
collapsed = TRUE,
overlay = TRUE,
- skin = "dark",
+ skin = NULL,
pinned = NULL
)
@@ -47,7 +47,7 @@ dashboardControlbar(
width = 250,
collapsed = TRUE,
overlay = TRUE,
- skin = "dark",
+ skin = NULL,
pinned = NULL
)
}
@@ -65,7 +65,8 @@ in pixels, or a string that specifies the width in CSS units. 250 px by default.
\item{overlay}{Whether the sidebar covers the content when expanded. Default to TRUE.}
-\item{skin}{Controlbar skin. "dark" or "light".}
+\item{skin}{Controlbar skin. "dark" or "light". Matches the \link{dashboardPage} dark parameter
+value.}
\item{pinned}{Whether to block the controlbar state (TRUE or FALSE). Default to NULL.}
diff --git a/man/dashboardSidebar.Rd b/man/dashboardSidebar.Rd
index 95bf14ba..9db3a285 100644
--- a/man/dashboardSidebar.Rd
+++ b/man/dashboardSidebar.Rd
@@ -23,7 +23,7 @@ bs4DashSidebar(
...,
disable = FALSE,
width = NULL,
- skin = "dark",
+ skin = NULL,
status = "primary",
elevation = 4,
collapsed = FALSE,
@@ -85,7 +85,7 @@ dashboardSidebar(
...,
disable = FALSE,
width = NULL,
- skin = "dark",
+ skin = NULL,
status = "primary",
elevation = 4,
collapsed = FALSE,
@@ -152,7 +152,8 @@ updateTabItems(
specifies the width in pixels, or a string that specifies the width in CSS
units.}
-\item{skin}{Sidebar skin. "dark" or "light".}
+\item{skin}{Sidebar skin. "dark" or "light". Matches the \link{dashboardPage} dark parameter
+value.}
\item{status}{Sidebar status. Valid statuses are defined as follows:
\itemize{
diff --git a/man/table.Rd b/man/table.Rd
index 32e12e27..8c19c0c0 100644
--- a/man/table.Rd
+++ b/man/table.Rd
@@ -26,9 +26,6 @@ bs4TableItem(..., dataCell = FALSE)
\item{...}{Any HTML element.}
\item{dataCell}{Whether the cell should be contain data or text.
by default.}
-
-\item{headTitles}{Table header names. Must have the same length as the number of
-\link{bs4TableItem} in \link{bs4TableItems}. Set "" to have an empty title field.}
}
\description{
Build an Bootstrap 4 table container
diff --git a/srcjs/bs4Dash-2.3.0/accordions-binding.js b/srcjs/bs4Dash-2.4.0.9000/accordions-binding.js
similarity index 100%
rename from srcjs/bs4Dash-2.3.0/accordions-binding.js
rename to srcjs/bs4Dash-2.4.0.9000/accordions-binding.js
diff --git a/srcjs/bs4Dash-2.3.0/cards.js b/srcjs/bs4Dash-2.4.0.9000/cards.js
similarity index 100%
rename from srcjs/bs4Dash-2.3.0/cards.js
rename to srcjs/bs4Dash-2.4.0.9000/cards.js
diff --git a/srcjs/bs4Dash-2.3.0/controlbar.js b/srcjs/bs4Dash-2.4.0.9000/controlbar.js
similarity index 100%
rename from srcjs/bs4Dash-2.3.0/controlbar.js
rename to srcjs/bs4Dash-2.4.0.9000/controlbar.js
diff --git a/srcjs/bs4Dash-2.3.0/feedbacks.js b/srcjs/bs4Dash-2.4.0.9000/feedbacks.js
similarity index 97%
rename from srcjs/bs4Dash-2.3.0/feedbacks.js
rename to srcjs/bs4Dash-2.4.0.9000/feedbacks.js
index 921b8a2d..5d72084a 100644
--- a/srcjs/bs4Dash-2.3.0/feedbacks.js
+++ b/srcjs/bs4Dash-2.4.0.9000/feedbacks.js
@@ -100,11 +100,13 @@ $(function () {
default: console.warn(`${config.status} does not belong to allowed statuses!`)
}
+ closeButton = '';
+
if (config.closable) {
closeButton = ''
}
- titleTag = `