mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-11 16:08:19 -05:00
Compare commits
54 Commits
v1.3-patch
...
disable-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
349de7060f | ||
|
|
38d2809131 | ||
|
|
d7718991a6 | ||
|
|
32c2bff6eb | ||
|
|
555ede03ed | ||
|
|
2a6f218700 | ||
|
|
b087c19b52 | ||
|
|
6fed1c60ac | ||
|
|
b10f2a5291 | ||
|
|
a4a49a354e | ||
|
|
ead23528ca | ||
|
|
b8644949cc | ||
|
|
b88e3a64f2 | ||
|
|
2871b423fd | ||
|
|
562fafbc39 | ||
|
|
191e0874f8 | ||
|
|
fa5ff7bfa5 | ||
|
|
82e80ccdeb | ||
|
|
ff84cf5a18 | ||
|
|
44843a7768 | ||
|
|
68eeb338da | ||
|
|
ea54c17902 | ||
|
|
d5ad7eed40 | ||
|
|
c2430cd3f4 | ||
|
|
8a0731493f | ||
|
|
07e2b80b5d | ||
|
|
1311e1fca2 | ||
|
|
e6c2133520 | ||
|
|
3d6f734ff2 | ||
|
|
e0eaa58779 | ||
|
|
ced6622b25 | ||
|
|
2d2cf96f5e | ||
|
|
370f1b51ee | ||
|
|
67d3a504ae | ||
|
|
34ee48ef93 | ||
|
|
c61a585e79 | ||
|
|
09388c9f07 | ||
|
|
b1bc78dad3 | ||
|
|
a5a0f23c3a | ||
|
|
4c50c064d3 | ||
|
|
a63f271300 | ||
|
|
08b22ff550 | ||
|
|
b04133bf65 | ||
|
|
3602358d2c | ||
|
|
67b0416eba | ||
|
|
f8d69ecb1f | ||
|
|
5e8bc204c1 | ||
|
|
938332d646 | ||
|
|
386078d441 | ||
|
|
bc8fbd60d7 | ||
|
|
4c332eac9a | ||
|
|
583a8d1001 | ||
|
|
36a808add0 | ||
|
|
f651d4a274 |
@@ -1,7 +1,7 @@
|
||||
Package: shiny
|
||||
Type: Package
|
||||
Title: Web Application Framework for R
|
||||
Version: 1.3.2
|
||||
Version: 1.3.2.9000
|
||||
Authors@R: c(
|
||||
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@rstudio.com"),
|
||||
person("Joe", "Cheng", role = "aut", email = "joe@rstudio.com"),
|
||||
@@ -77,17 +77,20 @@ Imports:
|
||||
promises (>= 1.0.1),
|
||||
tools,
|
||||
crayon,
|
||||
rlang
|
||||
rlang,
|
||||
fastmap
|
||||
Suggests:
|
||||
datasets,
|
||||
Cairo (>= 1.5-5),
|
||||
testthat,
|
||||
testthat (>= 2.2.1),
|
||||
knitr (>= 1.6),
|
||||
markdown,
|
||||
rmarkdown,
|
||||
ggplot2,
|
||||
reactlog (>= 1.0.0),
|
||||
magrittr
|
||||
Remotes:
|
||||
wch/fastmap
|
||||
URL: http://shiny.rstudio.com
|
||||
BugReports: https://github.com/rstudio/shiny/issues
|
||||
Collate:
|
||||
|
||||
@@ -301,5 +301,6 @@ import(httpuv)
|
||||
import(methods)
|
||||
import(mime)
|
||||
import(xtable)
|
||||
importFrom(fastmap,fastmap)
|
||||
importFrom(grDevices,dev.cur)
|
||||
importFrom(grDevices,dev.set)
|
||||
|
||||
22
NEWS.md
22
NEWS.md
@@ -1,3 +1,23 @@
|
||||
shiny 1.3.2.9000
|
||||
=======
|
||||
|
||||
### Improvements
|
||||
|
||||
* Resolved ([#2402](https://github.com/rstudio/shiny/issues/2402)): An informative warning is now thrown for mis-specified (date) strings in `dateInput()`, `updateDateInput()`, `dateRangeInput()`, and `updateDateRangeInput()`. ([#2403](https://github.com/rstudio/shiny/pull/2403))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed [#2387](https://github.com/rstudio/shiny/issues/2387): Updating a `sliderInput()`'s type from numeric to date no longer changes the rate policy from debounced to immediate. More generally, updating an input binding with a new type should (no longer) incorrectly alter the input rate policy. ([#2404](https://github.com/rstudio/shiny/pull/2404))
|
||||
|
||||
* Fixed [#868](https://github.com/rstudio/shiny/issues/868): If an input is initialized with a `NULL` label, it can now be updated with a string. Moreover, if an input label is initialized with a string, it can now be removed by updating with `label=character(0)` (similar to how `choices` and `selected` can be cleared in `updateSelectInput()`). ([#2406](https://github.com/rstudio/shiny/pull/2406))
|
||||
|
||||
* Fixed [#2250](https://github.com/rstudio/shiny/issues/2250): `updateSliderInput()` now works with un-specified (or zero-length) `min`, `max`, and `value`. ([#2416](https://github.com/rstudio/shiny/pull/2416))
|
||||
|
||||
* Fixed [#2233](https://github.com/rstudio/shiny/issues/2233): `verbatimTextOutput()` produced wrapped text on Safari, but the text should not be wrapped. ([#2353](https://github.com/rstudio/shiny/pull/2353))
|
||||
* Fixed [rstudio/reactlog#36](https://github.com/rstudio/reactlog/issues/36): Changes to reactive values not displaying accurately in reactlog. ([#2424](https://github.com/rstudio/shiny/pull/2424))
|
||||
|
||||
* Fixed [#2329](https://github.com/rstudio/shiny/issues/2329), [#1817](https://github.com/rstudio/shiny/issues/1817): These bugs were reported as fixed in Shiny 1.3.0 but were not actually fixed because some JavaScript changes were accidentally not included in the release. The fix resolves issues that occur when `withProgressBar()` or bookmarking are combined with the [networkD3](https://christophergandrud.github.io/networkD3/) package's Sankey plot.
|
||||
|
||||
shiny 1.3.2
|
||||
===========
|
||||
|
||||
@@ -11,6 +31,8 @@ shiny 1.3.2
|
||||
shiny 1.3.1
|
||||
===========
|
||||
|
||||
## Full changelog
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed a performance issue introduced in v1.3.0 when using large nested lists within Shiny. ([#2377](https://github.com/rstudio/shiny/pull/2377))
|
||||
|
||||
@@ -179,7 +179,7 @@ MemoryCache <- R6Class("MemoryCache",
|
||||
if (!is.numeric(max_size)) stop("max_size must be a number. Use `Inf` for no limit.")
|
||||
if (!is.numeric(max_age)) stop("max_age must be a number. Use `Inf` for no limit.")
|
||||
if (!is.numeric(max_n)) stop("max_n must be a number. Use `Inf` for no limit.")
|
||||
private$cache <- new.env(parent = emptyenv())
|
||||
private$cache <- fastmap()
|
||||
private$max_size <- max_size
|
||||
private$max_age <- max_age
|
||||
private$max_n <- max_n
|
||||
@@ -208,7 +208,7 @@ MemoryCache <- R6Class("MemoryCache",
|
||||
}
|
||||
|
||||
private$log(paste0('get: key "', key, '" found'))
|
||||
value <- private$cache[[key]]$value
|
||||
value <- private$cache$get(key)$value
|
||||
value
|
||||
},
|
||||
|
||||
@@ -226,37 +226,36 @@ MemoryCache <- R6Class("MemoryCache",
|
||||
size <- NULL
|
||||
}
|
||||
|
||||
private$cache[[key]] <- list(
|
||||
private$cache$set(key, list(
|
||||
key = key,
|
||||
value = value,
|
||||
size = size,
|
||||
mtime = time,
|
||||
atime = time
|
||||
)
|
||||
))
|
||||
self$prune()
|
||||
invisible(self)
|
||||
},
|
||||
|
||||
exists = function(key) {
|
||||
validate_key(key)
|
||||
# Faster than `exists(key, envir = private$cache, inherits = FALSE)
|
||||
!is.null(private$cache[[key]])
|
||||
private$cache$exists(key)
|
||||
},
|
||||
|
||||
keys = function() {
|
||||
ls(private$cache, sorted = FALSE) # Faster with sorted=FALSE
|
||||
private$cache$keys()
|
||||
},
|
||||
|
||||
remove = function(key) {
|
||||
private$log(paste0('remove: key "', key, '"'))
|
||||
validate_key(key)
|
||||
rm(list = key, envir = private$cache)
|
||||
private$cache$remove(key)
|
||||
invisible(self)
|
||||
},
|
||||
|
||||
reset = function() {
|
||||
private$log(paste0('reset'))
|
||||
rm(list = self$keys(), envir = private$cache)
|
||||
private$cache$reset()
|
||||
invisible(self)
|
||||
},
|
||||
|
||||
@@ -271,7 +270,7 @@ MemoryCache <- R6Class("MemoryCache",
|
||||
rm_idx <- timediff > private$max_age
|
||||
if (any(rm_idx)) {
|
||||
private$log(paste0("prune max_age: Removing ", paste(info$key[rm_idx], collapse = ", ")))
|
||||
rm(list = info$key[rm_idx], envir = private$cache)
|
||||
private$cache$remove(info$key[rm_idx])
|
||||
info <- info[!rm_idx, ]
|
||||
}
|
||||
}
|
||||
@@ -298,7 +297,7 @@ MemoryCache <- R6Class("MemoryCache",
|
||||
ensure_info_is_sorted()
|
||||
rm_idx <- seq_len(nrow(info)) > private$max_n
|
||||
private$log(paste0("prune max_n: Removing ", paste(info$key[rm_idx], collapse = ", ")))
|
||||
rm(list = info$key[rm_idx], envir = private$cache)
|
||||
private$cache$remove(info$key[rm_idx])
|
||||
info <- info[!rm_idx, ]
|
||||
}
|
||||
|
||||
@@ -308,7 +307,7 @@ MemoryCache <- R6Class("MemoryCache",
|
||||
cum_size <- cumsum(info$size)
|
||||
rm_idx <- cum_size > private$max_size
|
||||
private$log(paste0("prune max_size: Removing ", paste(info$key[rm_idx], collapse = ", ")))
|
||||
rm(list = info$key[rm_idx], envir = private$cache)
|
||||
private$cache$remove(info$key[rm_idx])
|
||||
info <- info[!rm_idx, ]
|
||||
}
|
||||
|
||||
@@ -335,23 +334,23 @@ MemoryCache <- R6Class("MemoryCache",
|
||||
maybe_prune_single = function(key) {
|
||||
if (!is.finite(private$max_age)) return()
|
||||
|
||||
obj <- private$cache[[key]]
|
||||
obj <- private$cache$get(key)
|
||||
if (is.null(obj)) return()
|
||||
|
||||
timediff <- as.numeric(Sys.time()) - obj$mtime
|
||||
if (timediff > private$max_age) {
|
||||
private$log(paste0("pruning single object exceeding max_age: Removing ", key))
|
||||
rm(list = key, envir = private$cache)
|
||||
private$cache$remove(key)
|
||||
}
|
||||
},
|
||||
|
||||
object_info = function() {
|
||||
keys <- ls(private$cache, sorted = FALSE)
|
||||
keys <- private$cache$keys()
|
||||
data.frame(
|
||||
key = keys,
|
||||
size = vapply(keys, function(key) private$cache[[key]]$size, 0),
|
||||
mtime = vapply(keys, function(key) private$cache[[key]]$mtime, 0),
|
||||
atime = vapply(keys, function(key) private$cache[[key]]$atime, 0),
|
||||
size = vapply(keys, function(key) private$cache$get(key)$size, 0),
|
||||
mtime = vapply(keys, function(key) private$cache$get(key)$mtime, 0),
|
||||
atime = vapply(keys, function(key) private$cache$get(key)$atime, 0),
|
||||
stringsAsFactors = FALSE
|
||||
)
|
||||
},
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
# package itself, making our PRNG completely deterministic. This line resets
|
||||
# the private seed during load.
|
||||
withPrivateSeed(set.seed(NULL))
|
||||
|
||||
appsByToken <<- Map$new()
|
||||
appsNeedingFlush <<- Map$new()
|
||||
timerCallbacks <<- TimerCallbacks$new()
|
||||
initializeInputHandlers()
|
||||
.globals$onStopCallbacks <<- Callbacks$new()
|
||||
}
|
||||
|
||||
.onAttach <- function(libname, pkgname) {
|
||||
|
||||
@@ -94,7 +94,7 @@ checkboxGroupInput <- function(inputId, label, choices = NULL, selected = NULL,
|
||||
tags$div(id = inputId,
|
||||
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
|
||||
class = divClass,
|
||||
controlLabel(inputId, label),
|
||||
shinyInputLabel(inputId, label),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
#'
|
||||
#' # Disable Mondays and Tuesdays.
|
||||
#' dateInput("date7", "Date:", daysofweekdisabled = c(1,2)),
|
||||
#'
|
||||
#'
|
||||
#' # Disable specific dates.
|
||||
#' dateInput("date8", "Date:", value = "2012-02-29",
|
||||
#' datesdisabled = c("2012-03-01", "2012-03-02"))
|
||||
@@ -92,14 +92,10 @@ dateInput <- function(inputId, label, value = NULL, min = NULL, max = NULL,
|
||||
language = "en", width = NULL, autoclose = TRUE,
|
||||
datesdisabled = NULL, daysofweekdisabled = NULL) {
|
||||
|
||||
# If value is a date object, convert it to a string with yyyy-mm-dd format
|
||||
# Same for min and max
|
||||
if (inherits(value, "Date")) value <- format(value, "%Y-%m-%d")
|
||||
if (inherits(min, "Date")) min <- format(min, "%Y-%m-%d")
|
||||
if (inherits(max, "Date")) max <- format(max, "%Y-%m-%d")
|
||||
if (inherits(datesdisabled, "Date")) {
|
||||
datesdisabled <- format(datesdisabled, "%Y-%m-%d")
|
||||
}
|
||||
value <- dateYMD(value, "value")
|
||||
min <- dateYMD(min, "min")
|
||||
max <- dateYMD(max, "max")
|
||||
datesdisabled <- dateYMD(datesdisabled, "datesdisabled")
|
||||
|
||||
value <- restoreInput(id = inputId, default = value)
|
||||
|
||||
@@ -107,7 +103,7 @@ dateInput <- function(inputId, label, value = NULL, min = NULL, max = NULL,
|
||||
class = "shiny-date-input form-group shiny-input-container",
|
||||
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
|
||||
|
||||
controlLabel(inputId, label),
|
||||
shinyInputLabel(inputId, label),
|
||||
tags$input(type = "text",
|
||||
class = "form-control",
|
||||
`data-date-language` = language,
|
||||
|
||||
@@ -76,12 +76,10 @@ dateRangeInput <- function(inputId, label, start = NULL, end = NULL,
|
||||
weekstart = 0, language = "en", separator = " to ", width = NULL,
|
||||
autoclose = TRUE) {
|
||||
|
||||
# If start and end are date objects, convert to a string with yyyy-mm-dd format
|
||||
# Same for min and max
|
||||
if (inherits(start, "Date")) start <- format(start, "%Y-%m-%d")
|
||||
if (inherits(end, "Date")) end <- format(end, "%Y-%m-%d")
|
||||
if (inherits(min, "Date")) min <- format(min, "%Y-%m-%d")
|
||||
if (inherits(max, "Date")) max <- format(max, "%Y-%m-%d")
|
||||
start <- dateYMD(start, "start")
|
||||
end <- dateYMD(end, "end")
|
||||
min <- dateYMD(min, "min")
|
||||
max <- dateYMD(max, "max")
|
||||
|
||||
restored <- restoreInput(id = inputId, default = list(start, end))
|
||||
start <- restored[[1]]
|
||||
@@ -92,7 +90,7 @@ dateRangeInput <- function(inputId, label, start = NULL, end = NULL,
|
||||
class = "shiny-date-range-input form-group shiny-input-container",
|
||||
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
|
||||
|
||||
controlLabel(inputId, label),
|
||||
shinyInputLabel(inputId, label),
|
||||
# input-daterange class is needed for dropdown behavior
|
||||
div(class = "input-daterange input-group",
|
||||
tags$input(
|
||||
|
||||
@@ -103,7 +103,7 @@ fileInput <- function(inputId, label, multiple = FALSE, accept = NULL,
|
||||
|
||||
div(class = "form-group shiny-input-container",
|
||||
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
|
||||
label %AND% tags$label(label),
|
||||
shinyInputLabel(inputId, label),
|
||||
|
||||
div(class = "input-group",
|
||||
tags$label(class = "input-group-btn",
|
||||
|
||||
@@ -42,7 +42,7 @@ numericInput <- function(inputId, label, value, min = NA, max = NA, step = NA,
|
||||
|
||||
div(class = "form-group shiny-input-container",
|
||||
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
|
||||
label %AND% tags$label(label, `for` = inputId),
|
||||
shinyInputLabel(inputId, label),
|
||||
inputTag
|
||||
)
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ passwordInput <- function(inputId, label, value = "", width = NULL,
|
||||
placeholder = NULL) {
|
||||
div(class = "form-group shiny-input-container",
|
||||
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
|
||||
label %AND% tags$label(label, `for` = inputId),
|
||||
shinyInputLabel(inputId, label),
|
||||
tags$input(id = inputId, type="password", class="form-control", value=value,
|
||||
placeholder = placeholder)
|
||||
)
|
||||
|
||||
@@ -102,7 +102,7 @@ radioButtons <- function(inputId, label, choices = NULL, selected = NULL,
|
||||
tags$div(id = inputId,
|
||||
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
|
||||
class = divClass,
|
||||
controlLabel(inputId, label),
|
||||
shinyInputLabel(inputId, label),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ selectInput <- function(inputId, label, choices, selected = NULL,
|
||||
res <- div(
|
||||
class = "form-group shiny-input-container",
|
||||
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
|
||||
controlLabel(inputId, label),
|
||||
shinyInputLabel(inputId, label),
|
||||
div(selectTag)
|
||||
)
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ sliderInput <- function(inputId, label, min, max, value, step = NULL,
|
||||
|
||||
sliderTag <- div(class = "form-group shiny-input-container",
|
||||
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
|
||||
if (!is.null(label)) controlLabel(inputId, label),
|
||||
shinyInputLabel(inputId, label),
|
||||
do.call(tags$input, sliderProps)
|
||||
)
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ textInput <- function(inputId, label, value = "", width = NULL,
|
||||
|
||||
div(class = "form-group shiny-input-container",
|
||||
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
|
||||
label %AND% tags$label(label, `for` = inputId),
|
||||
shinyInputLabel(inputId, label),
|
||||
tags$input(id = inputId, type="text", class="form-control", value=value,
|
||||
placeholder = placeholder)
|
||||
)
|
||||
|
||||
@@ -55,7 +55,7 @@ textAreaInput <- function(inputId, label, value = "", width = NULL, height = NUL
|
||||
if (length(style) == 0) style <- NULL
|
||||
|
||||
div(class = "form-group shiny-input-container",
|
||||
label %AND% tags$label(label, `for` = inputId),
|
||||
shinyInputLabel(inputId, label),
|
||||
tags$textarea(
|
||||
id = inputId,
|
||||
class = "form-control",
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
controlLabel <- function(controlName, label) {
|
||||
label %AND% tags$label(class = "control-label", `for` = controlName, label)
|
||||
shinyInputLabel <- function(inputId, label = NULL) {
|
||||
tags$label(
|
||||
label,
|
||||
class = "control-label",
|
||||
class = if (is.null(label)) "shiny-label-null",
|
||||
`for` = inputId
|
||||
)
|
||||
}
|
||||
|
||||
# This function takes in either a list or vector for `choices` (and
|
||||
|
||||
43
R/map.R
43
R/map.R
@@ -9,63 +9,58 @@
|
||||
# Remove of unknown key does nothing
|
||||
# Setting a key twice always results in last-one-wins
|
||||
# /TESTS
|
||||
|
||||
# Note that Map objects can't be saved in one R session and restored in
|
||||
# another, because they are based on fastmap, which uses an external pointer,
|
||||
# and external pointers can't be saved and restored in another session.
|
||||
#' @importFrom fastmap fastmap
|
||||
Map <- R6Class(
|
||||
'Map',
|
||||
portable = FALSE,
|
||||
public = list(
|
||||
initialize = function() {
|
||||
private$env <- new.env(parent=emptyenv())
|
||||
private$map <<- fastmap()
|
||||
},
|
||||
get = function(key) {
|
||||
env[[key]]
|
||||
map$get(key)
|
||||
},
|
||||
set = function(key, value) {
|
||||
env[[key]] <- value
|
||||
map$set(key, value)
|
||||
value
|
||||
},
|
||||
mget = function(keys) {
|
||||
base::mget(keys, env)
|
||||
map$mget(keys)
|
||||
},
|
||||
mset = function(...) {
|
||||
args <- list(...)
|
||||
if (length(args) == 0)
|
||||
return()
|
||||
|
||||
arg_names <- names(args)
|
||||
if (is.null(arg_names) || any(!nzchar(arg_names)))
|
||||
stop("All elements must be named")
|
||||
|
||||
list2env(args, envir = env)
|
||||
map$mset(...)
|
||||
},
|
||||
remove = function(key) {
|
||||
if (!self$containsKey(key))
|
||||
if (!map$exists(key))
|
||||
return(NULL)
|
||||
|
||||
result <- env[[key]]
|
||||
rm(list=key, envir=env, inherits=FALSE)
|
||||
result <- map$get(key)
|
||||
map$remove(key)
|
||||
result
|
||||
},
|
||||
containsKey = function(key) {
|
||||
exists(key, envir=env, inherits=FALSE)
|
||||
map$exists(key)
|
||||
},
|
||||
keys = function() {
|
||||
# Sadly, this is much faster than ls(), because it doesn't sort the keys.
|
||||
names(as.list(env, all.names=TRUE))
|
||||
map$keys()
|
||||
},
|
||||
values = function() {
|
||||
as.list(env, all.names=TRUE)
|
||||
map$as_list()
|
||||
},
|
||||
clear = function() {
|
||||
private$env <- new.env(parent=emptyenv())
|
||||
invisible(NULL)
|
||||
map$reset()
|
||||
},
|
||||
size = function() {
|
||||
length(env)
|
||||
map$size()
|
||||
}
|
||||
),
|
||||
|
||||
private = list(
|
||||
env = 'environment'
|
||||
map = NULL
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
144
R/reactives.R
144
R/reactives.R
@@ -21,28 +21,28 @@ Dependents <- R6Class(
|
||||
# must wrap in if statement as ctx react id could be NULL
|
||||
# if options(shiny.suppressMissingContextError = TRUE)
|
||||
if (is.character(.reactId) && is.character(ctx$.reactId)) {
|
||||
rLog$dependsOn(ctx$.reactId, .reactId, ctx$id, ctx$.domain)
|
||||
# rLog$dependsOn(ctx$.reactId, .reactId, ctx$id, ctx$.domain)
|
||||
}
|
||||
|
||||
.dependents$set(ctx$id, ctx)
|
||||
|
||||
ctx$onInvalidate(function() {
|
||||
rLog$dependsOnRemove(ctx$.reactId, .reactId, ctx$id, ctx$.domain)
|
||||
# rLog$dependsOnRemove(ctx$.reactId, .reactId, ctx$id, ctx$.domain)
|
||||
.dependents$remove(ctx$id)
|
||||
})
|
||||
}
|
||||
},
|
||||
# at times, the context is run in a ctx$onInvalidate(...) which has no runtime context
|
||||
invalidate = function(log = TRUE) {
|
||||
if (isTRUE(log)) {
|
||||
# if (isTRUE(log)) {
|
||||
|
||||
domain <- getDefaultReactiveDomain()
|
||||
rLog$invalidateStart(.reactId, NULL, "other", domain)
|
||||
on.exit(
|
||||
rLog$invalidateEnd(.reactId, NULL, "other", domain),
|
||||
add = TRUE
|
||||
)
|
||||
}
|
||||
# domain <- getDefaultReactiveDomain()
|
||||
# rLog$invalidateStart(.reactId, NULL, "other", domain)
|
||||
# on.exit(
|
||||
# rLog$invalidateEnd(.reactId, NULL, "other", domain),
|
||||
# add = TRUE
|
||||
# )
|
||||
# }
|
||||
lapply(
|
||||
.dependents$values(),
|
||||
function(ctx) {
|
||||
@@ -74,7 +74,7 @@ ReactiveVal <- R6Class(
|
||||
private$value <- value
|
||||
private$label <- label
|
||||
private$dependents <- Dependents$new(reactId = private$reactId)
|
||||
rLog$define(private$reactId, value, private$label, type = "reactiveVal", getDefaultReactiveDomain())
|
||||
# rLog$define(private$reactId, value, private$label, type = "reactiveVal", getDefaultReactiveDomain())
|
||||
},
|
||||
get = function() {
|
||||
private$dependents$register()
|
||||
@@ -88,7 +88,7 @@ ReactiveVal <- R6Class(
|
||||
if (identical(private$value, value)) {
|
||||
return(invisible(FALSE))
|
||||
}
|
||||
rLog$valueChange(private$reactId, value, getDefaultReactiveDomain())
|
||||
# rLog$valueChange(private$reactId, value, getDefaultReactiveDomain())
|
||||
private$value <- value
|
||||
private$dependents$invalidate()
|
||||
invisible(TRUE)
|
||||
@@ -97,14 +97,14 @@ ReactiveVal <- R6Class(
|
||||
if (is.null(session)) {
|
||||
stop("Can't freeze a reactiveVal without a reactive domain")
|
||||
}
|
||||
rLog$freezeReactiveVal(private$reactId, session)
|
||||
# rLog$freezeReactiveVal(private$reactId, session)
|
||||
session$onFlushed(function() {
|
||||
self$thaw(session)
|
||||
})
|
||||
private$frozen <- TRUE
|
||||
},
|
||||
thaw = function(session = getDefaultReactiveDomain()) {
|
||||
rLog$thawReactiveVal(private$reactId, session)
|
||||
# rLog$thawReactiveVal(private$reactId, session)
|
||||
private$frozen <- FALSE
|
||||
},
|
||||
isFrozen = function() {
|
||||
@@ -292,9 +292,9 @@ ReactiveValues <- R6Class(
|
||||
# For debug purposes
|
||||
.reactId = character(0),
|
||||
.label = character(0),
|
||||
.values = 'environment',
|
||||
.metadata = 'environment',
|
||||
.dependents = 'environment',
|
||||
.values = 'Map',
|
||||
.metadata = 'Map',
|
||||
.dependents = 'Map',
|
||||
# Dependents for the list of all names, including hidden
|
||||
.namesDeps = 'Dependents',
|
||||
# Dependents for all values, including hidden
|
||||
@@ -312,9 +312,9 @@ ReactiveValues <- R6Class(
|
||||
) {
|
||||
.reactId <<- nextGlobalReactId()
|
||||
.label <<- label
|
||||
.values <<- new.env(parent=emptyenv())
|
||||
.metadata <<- new.env(parent=emptyenv())
|
||||
.dependents <<- new.env(parent=emptyenv())
|
||||
.values <<- Map$new()
|
||||
.metadata <<- Map$new()
|
||||
.dependents <<- Map$new()
|
||||
.hasRetrieved <<- list(names = FALSE, asListAll = FALSE, asList = FALSE, keys = list())
|
||||
.namesDeps <<- Dependents$new(reactId = rLog$namesIdStr(.reactId))
|
||||
.allValuesDeps <<- Dependents$new(reactId = rLog$asListAllIdStr(.reactId))
|
||||
@@ -324,27 +324,24 @@ ReactiveValues <- R6Class(
|
||||
|
||||
get = function(key) {
|
||||
# get value right away to use for logging
|
||||
if (!exists(key, envir=.values, inherits=FALSE))
|
||||
keyValue <- NULL
|
||||
else
|
||||
keyValue <- .values[[key]]
|
||||
keyValue <- .values$get(key)
|
||||
|
||||
# Register the "downstream" reactive which is accessing this value, so
|
||||
# that we know to invalidate them when this value changes.
|
||||
ctx <- getCurrentContext()
|
||||
dep.key <- paste(key, ':', ctx$id, sep='')
|
||||
if (!exists(dep.key, envir=.dependents, inherits=FALSE)) {
|
||||
reactKeyId <- rLog$keyIdStr(.reactId, key)
|
||||
if (!.dependents$containsKey(dep.key)) {
|
||||
# reactKeyId <- rLog$keyIdStr(.reactId, key)
|
||||
|
||||
if (!isTRUE(.hasRetrieved$keys[[key]])) {
|
||||
rLog$defineKey(.reactId, keyValue, key, .label, ctx$.domain)
|
||||
# rLog$defineKey(.reactId, keyValue, key, .label, ctx$.domain)
|
||||
.hasRetrieved$keys[[key]] <<- TRUE
|
||||
}
|
||||
rLog$dependsOnKey(ctx$.reactId, .reactId, key, ctx$id, ctx$.domain)
|
||||
.dependents[[dep.key]] <- ctx
|
||||
# rLog$dependsOnKey(ctx$.reactId, .reactId, key, ctx$id, ctx$.domain)
|
||||
.dependents$set(dep.key, ctx)
|
||||
ctx$onInvalidate(function() {
|
||||
rLog$dependsOnKeyRemove(ctx$.reactId, .reactId, key, ctx$id, ctx$.domain)
|
||||
rm(list=dep.key, envir=.dependents, inherits=FALSE)
|
||||
# rLog$dependsOnKeyRemove(ctx$.reactId, .reactId, key, ctx$id, ctx$.domain)
|
||||
.dependents$remove(dep.key)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -384,57 +381,53 @@ ReactiveValues <- R6Class(
|
||||
domain <- getDefaultReactiveDomain()
|
||||
hidden <- substr(key, 1, 1) == "."
|
||||
|
||||
key_exists <- exists(key, envir=.values, inherits=FALSE)
|
||||
key_exists <- .values$containsKey(key)
|
||||
|
||||
if (key_exists) {
|
||||
if (.dedupe && identical(.values[[key]], value)) {
|
||||
if (.dedupe && identical(.values$get(key), value)) {
|
||||
return(invisible())
|
||||
}
|
||||
}
|
||||
|
||||
# set the value for better logging
|
||||
.values[[key]] <- value
|
||||
.values$set(key, value)
|
||||
|
||||
if (key_exists) {
|
||||
# key has been depended upon (can not happen if the key is being set)
|
||||
if (isTRUE(.hasRetrieved$keys[[key]])) {
|
||||
rLog$valueChangeKey(.reactId, key, value, domain)
|
||||
keyReactId <- rLog$keyIdStr(.reactId, key)
|
||||
rLog$invalidateStart(keyReactId, NULL, "other", domain)
|
||||
on.exit(
|
||||
rLog$invalidateEnd(keyReactId, NULL, "other", domain),
|
||||
add = TRUE
|
||||
)
|
||||
}
|
||||
# key has been depended upon
|
||||
# if (isTRUE(.hasRetrieved$keys[[key]])) {
|
||||
# rLog$valueChangeKey(.reactId, key, value, domain)
|
||||
# keyReactId <- rLog$keyIdStr(.reactId, key)
|
||||
# rLog$invalidateStart(keyReactId, NULL, "other", domain)
|
||||
# on.exit(
|
||||
# rLog$invalidateEnd(keyReactId, NULL, "other", domain),
|
||||
# add = TRUE
|
||||
# )
|
||||
# }
|
||||
|
||||
} else {
|
||||
# only invalidate if there are deps
|
||||
if (isTRUE(.hasRetrieved$names)) {
|
||||
rLog$valueChangeNames(.reactId, ls(.values, all.names = TRUE), domain)
|
||||
.namesDeps$invalidate()
|
||||
}
|
||||
# only invalidate if there are deps
|
||||
if (!key_exists && isTRUE(.hasRetrieved$names)) {
|
||||
# rLog$valueChangeNames(.reactId, .values$keys(), domain)
|
||||
.namesDeps$invalidate()
|
||||
}
|
||||
|
||||
if (hidden) {
|
||||
if (isTRUE(.hasRetrieved$asListAll)) {
|
||||
rLog$valueChangeAsListAll(.reactId, as.list(.values, all.names = TRUE), domain)
|
||||
# rLog$valueChangeAsListAll(.reactId, .values$values(), domain)
|
||||
.allValuesDeps$invalidate()
|
||||
}
|
||||
} else {
|
||||
if (isTRUE(.hasRetrieved$asList)) {
|
||||
react_vals <- .values$values()
|
||||
react_vals <- react_vals[!grepl("^\\.", base::names(react_vals))]
|
||||
# leave as is. both object would be registered to the listening object
|
||||
rLog$valueChangeAsList(.reactId, as.list(.values, all.names = FALSE), domain)
|
||||
# rLog$valueChangeAsList(.reactId, react_vals, domain)
|
||||
.valuesDeps$invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
dep.keys <- objects(
|
||||
envir=.dependents,
|
||||
pattern=paste('^\\Q', key, ':', '\\E', '\\d+$', sep=''),
|
||||
all.names=TRUE
|
||||
)
|
||||
dep.keys <- .dependents$keys()
|
||||
dep.keys <- dep.keys[grepl(paste('^\\Q', key, ':', '\\E', '\\d+$', sep=''), dep.keys)]
|
||||
lapply(
|
||||
mget(dep.keys, envir=.dependents),
|
||||
.dependents$mget(dep.keys),
|
||||
function(ctx) {
|
||||
ctx$invalidate()
|
||||
NULL
|
||||
@@ -451,10 +444,10 @@ ReactiveValues <- R6Class(
|
||||
},
|
||||
|
||||
names = function() {
|
||||
nameValues <- ls(.values, all.names=TRUE)
|
||||
nameValues <- .values$keys()
|
||||
if (!isTRUE(.hasRetrieved$names)) {
|
||||
domain <- getDefaultReactiveDomain()
|
||||
rLog$defineNames(.reactId, nameValues, .label, domain)
|
||||
# rLog$defineNames(.reactId, nameValues, .label, domain)
|
||||
.hasRetrieved$names <<- TRUE
|
||||
}
|
||||
.namesDeps$register()
|
||||
@@ -465,7 +458,7 @@ ReactiveValues <- R6Class(
|
||||
getMeta = function(key, metaKey) {
|
||||
# Make sure to use named (not numeric) indexing into list.
|
||||
metaKey <- as.character(metaKey)
|
||||
.metadata[[key]][[metaKey]]
|
||||
.metadata$get("key")[[metaKey]]
|
||||
},
|
||||
|
||||
# Set a metadata value. Does not trigger reactivity.
|
||||
@@ -473,24 +466,26 @@ ReactiveValues <- R6Class(
|
||||
# Make sure to use named (not numeric) indexing into list.
|
||||
metaKey <- as.character(metaKey)
|
||||
|
||||
if (!exists(key, envir = .metadata, inherits = FALSE)) {
|
||||
.metadata[[key]] <<- list()
|
||||
if (!.metadata$containsKey(key)) {
|
||||
.metadata$set(key, list())
|
||||
}
|
||||
|
||||
.metadata[[key]][[metaKey]] <<- value
|
||||
m <- .metadata$get(key)
|
||||
m[[metaKey]] <- value
|
||||
.metadata$set(key, m)
|
||||
},
|
||||
|
||||
# Mark a value as frozen If accessed while frozen, a shiny.silent.error will
|
||||
# be thrown.
|
||||
freeze = function(key) {
|
||||
domain <- getDefaultReactiveDomain()
|
||||
rLog$freezeReactiveKey(.reactId, key, domain)
|
||||
# rLog$freezeReactiveKey(.reactId, key, domain)
|
||||
setMeta(key, "frozen", TRUE)
|
||||
},
|
||||
|
||||
thaw = function(key) {
|
||||
domain <- getDefaultReactiveDomain()
|
||||
rLog$thawReactiveKey(.reactId, key, domain)
|
||||
# rLog$thawReactiveKey(.reactId, key, domain)
|
||||
setMeta(key, "frozen", NULL)
|
||||
},
|
||||
|
||||
@@ -499,11 +494,14 @@ ReactiveValues <- R6Class(
|
||||
},
|
||||
|
||||
toList = function(all.names=FALSE) {
|
||||
listValue <- as.list(.values, all.names=all.names)
|
||||
listValue <- .values$values()
|
||||
if (!all.names) {
|
||||
listValue <- listValue[!grepl("^\\.", base::names(listValue))]
|
||||
}
|
||||
if (all.names) {
|
||||
if (!isTRUE(.hasRetrieved$asListAll)) {
|
||||
domain <- getDefaultReactiveDomain()
|
||||
rLog$defineAsListAll(.reactId, listValue, .label, domain)
|
||||
# rLog$defineAsListAll(.reactId, listValue, .label, domain)
|
||||
.hasRetrieved$asListAll <<- TRUE
|
||||
}
|
||||
.allValuesDeps$register()
|
||||
@@ -512,7 +510,7 @@ ReactiveValues <- R6Class(
|
||||
if (!isTRUE(.hasRetrieved$asList)) {
|
||||
domain <- getDefaultReactiveDomain()
|
||||
# making sure the value being recorded is with `all.names = FALSE`
|
||||
rLog$defineAsList(.reactId, as.list(.values, all.names=FALSE), .label, domain)
|
||||
# rLog$defineAsList(.reactId, listValue[!grepl("^\\.", base::names(listValue))], .label, domain)
|
||||
.hasRetrieved$asList <<- TRUE
|
||||
}
|
||||
.valuesDeps$register()
|
||||
@@ -832,7 +830,7 @@ Observable <- R6Class(
|
||||
.running <<- FALSE
|
||||
.execCount <<- 0L
|
||||
.mostRecentCtxId <<- ""
|
||||
rLog$define(.reactId, .value, .label, type = "observable", .domain)
|
||||
# rLog$define(.reactId, .value, .label, type = "observable", .domain)
|
||||
},
|
||||
getValue = function() {
|
||||
.dependents$register()
|
||||
@@ -1082,7 +1080,7 @@ Observer <- R6Class(
|
||||
setAutoDestroy(autoDestroy)
|
||||
|
||||
.reactId <<- nextGlobalReactId()
|
||||
rLog$defineObserver(.reactId, .label, .domain)
|
||||
# rLog$defineObserver(.reactId, .label, .domain)
|
||||
|
||||
# Defer the first running of this until flushReact is called
|
||||
.createContext()$invalidate()
|
||||
@@ -1607,7 +1605,7 @@ invalidateLater <- function(millis, session = getDefaultReactiveDomain()) {
|
||||
force(session)
|
||||
|
||||
ctx <- getCurrentContext()
|
||||
rLog$invalidateLater(ctx$.reactId, ctx$id, millis, session)
|
||||
# rLog$invalidateLater(ctx$.reactId, ctx$id, millis, session)
|
||||
|
||||
timerHandle <- scheduleTask(millis, function() {
|
||||
if (is.null(session)) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Create a map for input handlers and register the defaults.
|
||||
inputHandlers <- Map$new()
|
||||
# Create a map for input handlers and register the defaults. The Map object is
|
||||
# initialized in initializeInputHandlers, which is called from .onLoad().
|
||||
inputHandlers <- NULL
|
||||
|
||||
#' Register an Input Handler
|
||||
#'
|
||||
@@ -128,114 +129,118 @@ applyInputHandlers <- function(inputs, shinysession = getDefaultReactiveDomain()
|
||||
}
|
||||
|
||||
|
||||
# Takes a list-of-lists and returns a matrix. The lists
|
||||
# must all be the same length. NULL is replaced by NA.
|
||||
registerInputHandler("shiny.matrix", function(data, ...) {
|
||||
if (length(data) == 0)
|
||||
return(matrix(nrow=0, ncol=0))
|
||||
initializeInputHandlers <- function() {
|
||||
inputHandlers <<- Map$new()
|
||||
|
||||
m <- matrix(unlist(lapply(data, function(x) {
|
||||
sapply(x, function(y) {
|
||||
ifelse(is.null(y), NA, y)
|
||||
})
|
||||
})), nrow = length(data[[1]]), ncol = length(data))
|
||||
return(m)
|
||||
})
|
||||
# Takes a list-of-lists and returns a matrix. The lists
|
||||
# must all be the same length. NULL is replaced by NA.
|
||||
registerInputHandler("shiny.matrix", function(data, ...) {
|
||||
if (length(data) == 0)
|
||||
return(matrix(nrow=0, ncol=0))
|
||||
|
||||
|
||||
registerInputHandler("shiny.number", function(val, ...){
|
||||
ifelse(is.null(val), NA, val)
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.password", function(val, shinysession, name) {
|
||||
# Mark passwords as not serializable
|
||||
setSerializer(name, serializerUnserializable)
|
||||
val
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.date", function(val, ...){
|
||||
# First replace NULLs with NA, then convert to Date vector
|
||||
datelist <- ifelse(lapply(val, is.null), NA, val)
|
||||
|
||||
res <- NULL
|
||||
tryCatch({
|
||||
res <- as.Date(unlist(datelist))
|
||||
},
|
||||
error = function(e) {
|
||||
# It's possible for client to send a string like "99999-01-01", which
|
||||
# as.Date can't handle.
|
||||
warning(e$message)
|
||||
res <<- as.Date(rep(NA, length(datelist)))
|
||||
}
|
||||
)
|
||||
|
||||
res
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.datetime", function(val, ...){
|
||||
# First replace NULLs with NA, then convert to POSIXct vector
|
||||
times <- lapply(val, function(x) {
|
||||
if (is.null(x)) NA
|
||||
else x
|
||||
m <- matrix(unlist(lapply(data, function(x) {
|
||||
sapply(x, function(y) {
|
||||
ifelse(is.null(y), NA, y)
|
||||
})
|
||||
})), nrow = length(data[[1]]), ncol = length(data))
|
||||
return(m)
|
||||
})
|
||||
as.POSIXct(unlist(times), origin = "1970-01-01", tz = "UTC")
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.action", function(val, shinysession, name) {
|
||||
# mark up the action button value with a special class so we can recognize it later
|
||||
class(val) <- c(class(val), "shinyActionButtonValue")
|
||||
val
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.file", function(val, shinysession, name) {
|
||||
# This function is only used when restoring a Shiny fileInput. When a file is
|
||||
# uploaded the usual way, it takes a different code path and won't hit this
|
||||
# function.
|
||||
if (is.null(val))
|
||||
return(NULL)
|
||||
|
||||
# The data will be a named list of lists; convert to a data frame.
|
||||
val <- as.data.frame(lapply(val, unlist), stringsAsFactors = FALSE)
|
||||
|
||||
# `val$datapath` should be a filename without a path, for security reasons.
|
||||
if (basename(val$datapath) != val$datapath) {
|
||||
stop("Invalid '/' found in file input path.")
|
||||
}
|
||||
|
||||
# Prepend the persistent dir
|
||||
oldfile <- file.path(getCurrentRestoreContext()$dir, val$datapath)
|
||||
|
||||
# Copy the original file to a new temp dir, so that a restored session can't
|
||||
# modify the original.
|
||||
newdir <- file.path(tempdir(), createUniqueId(12))
|
||||
dir.create(newdir)
|
||||
val$datapath <- file.path(newdir, val$datapath)
|
||||
file.copy(oldfile, val$datapath)
|
||||
|
||||
# Need to mark this input value with the correct serializer. When a file is
|
||||
# uploaded the usual way (instead of being restored), this occurs in
|
||||
# session$`@uploadEnd`.
|
||||
setSerializer(name, serializerFileInput)
|
||||
|
||||
snapshotPreprocessInput(name, snapshotPreprocessorFileInput)
|
||||
|
||||
val
|
||||
})
|
||||
|
||||
|
||||
# to be used with !!!answer
|
||||
registerInputHandler("shiny.symbolList", function(val, ...) {
|
||||
if (is.null(val)) {
|
||||
list()
|
||||
} else {
|
||||
lapply(val, as.symbol)
|
||||
}
|
||||
})
|
||||
# to be used with !!answer
|
||||
registerInputHandler("shiny.symbol", function(val, ...) {
|
||||
if (is.null(val) || identical(val, "")) {
|
||||
NULL
|
||||
} else {
|
||||
as.symbol(val)
|
||||
}
|
||||
})
|
||||
registerInputHandler("shiny.number", function(val, ...){
|
||||
ifelse(is.null(val), NA, val)
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.password", function(val, shinysession, name) {
|
||||
# Mark passwords as not serializable
|
||||
setSerializer(name, serializerUnserializable)
|
||||
val
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.date", function(val, ...){
|
||||
# First replace NULLs with NA, then convert to Date vector
|
||||
datelist <- ifelse(lapply(val, is.null), NA, val)
|
||||
|
||||
res <- NULL
|
||||
tryCatch({
|
||||
res <- as.Date(unlist(datelist))
|
||||
},
|
||||
error = function(e) {
|
||||
# It's possible for client to send a string like "99999-01-01", which
|
||||
# as.Date can't handle.
|
||||
warning(e$message)
|
||||
res <<- as.Date(rep(NA, length(datelist)))
|
||||
}
|
||||
)
|
||||
|
||||
res
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.datetime", function(val, ...){
|
||||
# First replace NULLs with NA, then convert to POSIXct vector
|
||||
times <- lapply(val, function(x) {
|
||||
if (is.null(x)) NA
|
||||
else x
|
||||
})
|
||||
as.POSIXct(unlist(times), origin = "1970-01-01", tz = "UTC")
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.action", function(val, shinysession, name) {
|
||||
# mark up the action button value with a special class so we can recognize it later
|
||||
class(val) <- c(class(val), "shinyActionButtonValue")
|
||||
val
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.file", function(val, shinysession, name) {
|
||||
# This function is only used when restoring a Shiny fileInput. When a file is
|
||||
# uploaded the usual way, it takes a different code path and won't hit this
|
||||
# function.
|
||||
if (is.null(val))
|
||||
return(NULL)
|
||||
|
||||
# The data will be a named list of lists; convert to a data frame.
|
||||
val <- as.data.frame(lapply(val, unlist), stringsAsFactors = FALSE)
|
||||
|
||||
# `val$datapath` should be a filename without a path, for security reasons.
|
||||
if (basename(val$datapath) != val$datapath) {
|
||||
stop("Invalid '/' found in file input path.")
|
||||
}
|
||||
|
||||
# Prepend the persistent dir
|
||||
oldfile <- file.path(getCurrentRestoreContext()$dir, val$datapath)
|
||||
|
||||
# Copy the original file to a new temp dir, so that a restored session can't
|
||||
# modify the original.
|
||||
newdir <- file.path(tempdir(), createUniqueId(12))
|
||||
dir.create(newdir)
|
||||
val$datapath <- file.path(newdir, val$datapath)
|
||||
file.copy(oldfile, val$datapath)
|
||||
|
||||
# Need to mark this input value with the correct serializer. When a file is
|
||||
# uploaded the usual way (instead of being restored), this occurs in
|
||||
# session$`@uploadEnd`.
|
||||
setSerializer(name, serializerFileInput)
|
||||
|
||||
snapshotPreprocessInput(name, snapshotPreprocessorFileInput)
|
||||
|
||||
val
|
||||
})
|
||||
|
||||
|
||||
# to be used with !!!answer
|
||||
registerInputHandler("shiny.symbolList", function(val, ...) {
|
||||
if (is.null(val)) {
|
||||
list()
|
||||
} else {
|
||||
lapply(val, as.symbol)
|
||||
}
|
||||
})
|
||||
# to be used with !!answer
|
||||
registerInputHandler("shiny.symbol", function(val, ...) {
|
||||
if (is.null(val) || identical(val, "")) {
|
||||
NULL
|
||||
} else {
|
||||
as.symbol(val)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#' @include server-input-handlers.R
|
||||
|
||||
appsByToken <- Map$new()
|
||||
appsNeedingFlush <- Map$new()
|
||||
# These Map objects are initialized in .onLoad() because Maps based on fastmap
|
||||
# can't be saved in one R session and loaded in another.
|
||||
appsByToken <- NULL
|
||||
appsNeedingFlush <- NULL
|
||||
|
||||
# Provide a character representation of the WS that can be used
|
||||
# as a key in a Map.
|
||||
|
||||
@@ -513,8 +513,7 @@ ShinySession <- R6Class(
|
||||
# in the web page; in these cases, there's no output_foo_hidden flag,
|
||||
# and hidden should be TRUE. In other words, NULL and TRUE should map to
|
||||
# TRUE, FALSE should map to FALSE.
|
||||
hidden <- private$.clientData$.values[[paste("output_", name, "_hidden",
|
||||
sep="")]]
|
||||
hidden <- private$.clientData$.values$get(paste0("output_", name, "_hidden"))
|
||||
if (is.null(hidden)) hidden <- TRUE
|
||||
|
||||
return(hidden && private$getOutputOption(name, 'suspendWhenHidden', TRUE))
|
||||
@@ -1917,7 +1916,7 @@ ShinySession <- R6Class(
|
||||
|
||||
fileData <- readBin(file, 'raw', n=bytes)
|
||||
|
||||
if (isTRUE(private$.clientData$.values$allowDataUriScheme)) {
|
||||
if (isTRUE(private$.clientData$.values$get("allowDataUriScheme"))) {
|
||||
b64 <- rawToBase64(fileData)
|
||||
return(paste('data:', contentType, ';base64,', b64, sep=''))
|
||||
} else {
|
||||
@@ -2220,7 +2219,8 @@ flushPendingSessions <- function() {
|
||||
})
|
||||
}
|
||||
|
||||
.globals$onStopCallbacks <- Callbacks$new()
|
||||
# Initialized in .onLoad
|
||||
.globals$onStopCallbacks <- NULL
|
||||
|
||||
#' Run code after an application or session ends
|
||||
#'
|
||||
|
||||
@@ -86,7 +86,8 @@ TimerCallbacks <- R6Class(
|
||||
)
|
||||
)
|
||||
|
||||
timerCallbacks <- TimerCallbacks$new()
|
||||
# Initialized in onLoad() because TimerCallbacks uses Map.
|
||||
timerCallbacks <- NULL
|
||||
|
||||
scheduleTask <- function(millis, callback) {
|
||||
cancelled <- FALSE
|
||||
|
||||
@@ -205,18 +205,9 @@ updateActionButton <- function(session, inputId, label = NULL, icon = NULL) {
|
||||
updateDateInput <- function(session, inputId, label = NULL, value = NULL,
|
||||
min = NULL, max = NULL) {
|
||||
|
||||
# Make sure values are NULL or Date objects. This is so we can ensure that
|
||||
# they will be formatted correctly. For example, the string "2016-08-9" is not
|
||||
# correctly formatted, but the conversion to Date and back to string will fix
|
||||
# it.
|
||||
formatDate <- function(x) {
|
||||
if (is.null(x))
|
||||
return(NULL)
|
||||
format(as.Date(x), "%Y-%m-%d")
|
||||
}
|
||||
value <- formatDate(value)
|
||||
min <- formatDate(min)
|
||||
max <- formatDate(max)
|
||||
value <- dateYMD(value, "value")
|
||||
min <- dateYMD(min, "min")
|
||||
max <- dateYMD(max, "max")
|
||||
|
||||
message <- dropNulls(list(label=label, value=value, min=min, max=max))
|
||||
session$sendInputMessage(inputId, message)
|
||||
@@ -266,12 +257,11 @@ updateDateInput <- function(session, inputId, label = NULL, value = NULL,
|
||||
updateDateRangeInput <- function(session, inputId, label = NULL,
|
||||
start = NULL, end = NULL, min = NULL,
|
||||
max = NULL) {
|
||||
# Make sure start and end are strings, not date objects. This is for
|
||||
# consistency across different locales.
|
||||
if (inherits(start, "Date")) start <- format(start, '%Y-%m-%d')
|
||||
if (inherits(end, "Date")) end <- format(end, '%Y-%m-%d')
|
||||
if (inherits(min, "Date")) min <- format(min, '%Y-%m-%d')
|
||||
if (inherits(max, "Date")) max <- format(max, '%Y-%m-%d')
|
||||
|
||||
start <- dateYMD(start, "start")
|
||||
end <- dateYMD(end, "end")
|
||||
min <- dateYMD(min, "min")
|
||||
max <- dateYMD(max, "max")
|
||||
|
||||
message <- dropNulls(list(
|
||||
label = label,
|
||||
@@ -428,13 +418,15 @@ updateNumericInput <- function(session, inputId, label = NULL, value = NULL,
|
||||
updateSliderInput <- function(session, inputId, label = NULL, value = NULL,
|
||||
min = NULL, max = NULL, step = NULL, timeFormat = NULL, timezone = NULL)
|
||||
{
|
||||
# If no min/max/value is provided, we won't know the
|
||||
# type, and this will return an empty string
|
||||
dataType <- getSliderType(min, max, value)
|
||||
|
||||
if (is.null(timeFormat)) {
|
||||
timeFormat <- switch(dataType, date = "%F", datetime = "%F %T", number = NULL)
|
||||
}
|
||||
|
||||
if (dataType == "date" || dataType == "datetime") {
|
||||
if (isTRUE(dataType %in% c("date", "datetime"))) {
|
||||
to_ms <- function(x) 1000 * as.numeric(as.POSIXct(x))
|
||||
if (!is.null(min)) min <- to_ms(min)
|
||||
if (!is.null(max)) max <- to_ms(max)
|
||||
|
||||
20
R/utils.R
20
R/utils.R
@@ -1560,6 +1560,25 @@ URLencode <- function(value, reserved = FALSE) {
|
||||
if (reserved) encodeURIComponent(value) else encodeURI(value)
|
||||
}
|
||||
|
||||
# Make user-supplied dates are either NULL or can be coerced
|
||||
# to a yyyy-mm-dd formatted string. If a date is specified, this
|
||||
# function returns a string for consistency across locales.
|
||||
# Also, `as.Date()` is used to coerce strings to date objects
|
||||
# so that strings like "2016-08-9" are expanded to "2016-08-09"
|
||||
dateYMD <- function(date = NULL, argName = "value") {
|
||||
if (!length(date)) return(NULL)
|
||||
if (length(date) > 1) warning("Expected `", argName, "` to be of length 1.")
|
||||
tryCatch(date <- format(as.Date(date), "%Y-%m-%d"),
|
||||
error = function(e) {
|
||||
warning(
|
||||
"Couldn't coerce the `", argName,
|
||||
"` argument to a date string with format yyyy-mm-dd",
|
||||
call. = FALSE
|
||||
)
|
||||
}
|
||||
)
|
||||
date
|
||||
}
|
||||
|
||||
# This function takes a name and function, and it wraps that function in a new
|
||||
# function which calls the original function using the specified name. This can
|
||||
@@ -1730,6 +1749,7 @@ createVarPromiseDomain <- function(env, name, value) {
|
||||
|
||||
getSliderType <- function(min, max, value) {
|
||||
vals <- dropNulls(list(value, min, max))
|
||||
if (length(vals) == 0) return("")
|
||||
type <- unique(lapply(vals, function(x) {
|
||||
if (inherits(x, "Date")) "date"
|
||||
else if (inherits(x, "POSIXt")) "datetime"
|
||||
|
||||
@@ -12,6 +12,13 @@ pre.shiny-text-output.noplaceholder:empty {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/* Some browsers (like Safari) will wrap text in <pre> tags with Bootstrap's
|
||||
CSS. This changes the behavior to not wrap.
|
||||
*/
|
||||
pre.shiny-text-output {
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
.shiny-image-output img.shiny-scalable, .shiny-plot-output img.shiny-scalable {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
@@ -209,6 +216,10 @@ pre.shiny-text-output.noplaceholder:empty {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
.shiny-label-null {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.crosshair {
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
|
||||
var exports = window.Shiny = window.Shiny || {};
|
||||
|
||||
exports.version = "1.3.2"; // Version number inserted by Grunt
|
||||
exports.version = "1.3.2.9000"; // Version number inserted by Grunt
|
||||
|
||||
var origPushState = window.history.pushState;
|
||||
window.history.pushState = function () {
|
||||
@@ -321,6 +321,24 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
if (op === "==") return diff === 0;else if (op === ">=") return diff >= 0;else if (op === ">") return diff > 0;else if (op === "<=") return diff <= 0;else if (op === "<") return diff < 0;else throw "Unknown operator: " + op;
|
||||
};
|
||||
|
||||
function updateLabel(labelTxt, labelNode) {
|
||||
// Only update if label was specified in the update method
|
||||
if (typeof labelTxt === "undefined") return;
|
||||
if (labelNode.length !== 1) {
|
||||
throw new Error("labelNode must be of length 1");
|
||||
}
|
||||
|
||||
// Should the label be empty?
|
||||
var emptyLabel = $.isArray(labelTxt) && labelTxt.length === 0;
|
||||
|
||||
if (emptyLabel) {
|
||||
labelNode.addClass("shiny-label-null");
|
||||
} else {
|
||||
labelNode.text(labelTxt);
|
||||
labelNode.removeClass("shiny-label-null");
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Source file: ../srcjs/browser.js
|
||||
|
||||
@@ -545,8 +563,8 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
this.lastChanceCallback = [];
|
||||
};
|
||||
(function () {
|
||||
this.setInput = function (name, value, opts) {
|
||||
this.pendingData[name] = value;
|
||||
this.setInput = function (nameType, value, opts) {
|
||||
this.pendingData[nameType] = value;
|
||||
|
||||
if (!this.reentrant) {
|
||||
if (opts.priority === "event") {
|
||||
@@ -582,8 +600,8 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
this.lastSentValues = this.reset(initialValues);
|
||||
};
|
||||
(function () {
|
||||
this.setInput = function (name, value, opts) {
|
||||
var _splitInputNameType = splitInputNameType(name);
|
||||
this.setInput = function (nameType, value, opts) {
|
||||
var _splitInputNameType = splitInputNameType(nameType);
|
||||
|
||||
var inputName = _splitInputNameType.name;
|
||||
var inputType = _splitInputNameType.inputType;
|
||||
@@ -610,10 +628,10 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
if (values.hasOwnProperty(inputName)) {
|
||||
var _splitInputNameType2 = splitInputNameType(inputName);
|
||||
|
||||
var name = _splitInputNameType2.name;
|
||||
var _name = _splitInputNameType2.name;
|
||||
var inputType = _splitInputNameType2.inputType;
|
||||
|
||||
cacheValues[name] = {
|
||||
cacheValues[_name] = {
|
||||
jsonValue: JSON.stringify(values[inputName]),
|
||||
inputType: inputType
|
||||
};
|
||||
@@ -628,10 +646,10 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
this.target = target;
|
||||
};
|
||||
(function () {
|
||||
this.setInput = function (name, value, opts) {
|
||||
this.setInput = function (nameType, value, opts) {
|
||||
var evt = jQuery.Event("shiny:inputchanged");
|
||||
|
||||
var input = splitInputNameType(name);
|
||||
var input = splitInputNameType(nameType);
|
||||
evt.name = input.name;
|
||||
evt.inputType = input.inputType;
|
||||
evt.value = value;
|
||||
@@ -657,25 +675,41 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
this.inputRatePolicies = {};
|
||||
};
|
||||
(function () {
|
||||
this.setInput = function (name, value, opts) {
|
||||
this.$ensureInit(name);
|
||||
// Note that the first argument of setInput() and setRatePolicy()
|
||||
// are passed both the input name (i.e., inputId) and type.
|
||||
// https://github.com/rstudio/shiny/blob/67d3a/srcjs/init_shiny.js#L111-L126
|
||||
// However, $ensureInit() and $doSetInput() are meant to be passed just
|
||||
// the input name (i.e., inputId), which is why we distinguish between
|
||||
// nameType and name.
|
||||
this.setInput = function (nameType, value, opts) {
|
||||
var _splitInputNameType3 = splitInputNameType(nameType);
|
||||
|
||||
if (opts.priority !== "deferred") this.inputRatePolicies[name].immediateCall(name, value, opts);else this.inputRatePolicies[name].normalCall(name, value, opts);
|
||||
var inputName = _splitInputNameType3.name;
|
||||
|
||||
|
||||
this.$ensureInit(inputName);
|
||||
|
||||
if (opts.priority !== "deferred") this.inputRatePolicies[inputName].immediateCall(nameType, value, opts);else this.inputRatePolicies[inputName].normalCall(nameType, value, opts);
|
||||
};
|
||||
this.setRatePolicy = function (name, mode, millis) {
|
||||
this.setRatePolicy = function (nameType, mode, millis) {
|
||||
var _splitInputNameType4 = splitInputNameType(nameType);
|
||||
|
||||
var inputName = _splitInputNameType4.name;
|
||||
|
||||
|
||||
if (mode === 'direct') {
|
||||
this.inputRatePolicies[name] = new Invoker(this, this.$doSetInput);
|
||||
this.inputRatePolicies[inputName] = new Invoker(this, this.$doSetInput);
|
||||
} else if (mode === 'debounce') {
|
||||
this.inputRatePolicies[name] = new Debouncer(this, this.$doSetInput, millis);
|
||||
this.inputRatePolicies[inputName] = new Debouncer(this, this.$doSetInput, millis);
|
||||
} else if (mode === 'throttle') {
|
||||
this.inputRatePolicies[name] = new Throttler(this, this.$doSetInput, millis);
|
||||
this.inputRatePolicies[inputName] = new Throttler(this, this.$doSetInput, millis);
|
||||
}
|
||||
};
|
||||
this.$ensureInit = function (name) {
|
||||
if (!(name in this.inputRatePolicies)) this.setRatePolicy(name, 'direct');
|
||||
};
|
||||
this.$doSetInput = function (name, value, opts) {
|
||||
this.target.setInput(name, value, opts);
|
||||
this.$doSetInput = function (nameType, value, opts) {
|
||||
this.target.setInput(nameType, value, opts);
|
||||
};
|
||||
}).call(InputRateDecorator.prototype);
|
||||
|
||||
@@ -684,8 +718,8 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
this.pendingInput = {};
|
||||
};
|
||||
(function () {
|
||||
this.setInput = function (name, value, opts) {
|
||||
if (/^\./.test(name)) this.target.setInput(name, value, opts);else this.pendingInput[name] = { value: value, opts: opts };
|
||||
this.setInput = function (nameType, value, opts) {
|
||||
if (/^\./.test(nameType)) this.target.setInput(nameType, value, opts);else this.pendingInput[name] = { value: value, opts: opts };
|
||||
};
|
||||
this.submit = function () {
|
||||
for (var name in this.pendingInput) {
|
||||
@@ -701,12 +735,12 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
this.target = target;
|
||||
};
|
||||
(function () {
|
||||
this.setInput = function (name, value, opts) {
|
||||
if (!name) throw "Can't set input with empty name.";
|
||||
this.setInput = function (nameType, value, opts) {
|
||||
if (!nameType) throw "Can't set input with empty name.";
|
||||
|
||||
opts = addDefaultInputOpts(opts);
|
||||
|
||||
this.target.setInput(name, value, opts);
|
||||
this.target.setInput(nameType, value, opts);
|
||||
};
|
||||
}).call(InputValidateDecorator.prototype);
|
||||
|
||||
@@ -733,8 +767,8 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
return opts;
|
||||
}
|
||||
|
||||
function splitInputNameType(name) {
|
||||
var name2 = name.split(':');
|
||||
function splitInputNameType(nameType) {
|
||||
var name2 = nameType.split(':');
|
||||
return {
|
||||
name: name2[0],
|
||||
inputType: name2.length > 1 ? name2[1] : ''
|
||||
@@ -1771,7 +1805,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
var $container = $('.shiny-progress-container');
|
||||
if ($container.length === 0) {
|
||||
$container = $('<div class="shiny-progress-container"></div>');
|
||||
$('body').append($container);
|
||||
$(document.body).append($container);
|
||||
}
|
||||
|
||||
// Add div for just this progress ID
|
||||
@@ -2025,7 +2059,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
|
||||
if ($panel.length > 0) return $panel;
|
||||
|
||||
$('body').append('<div id="shiny-notification-panel">');
|
||||
$(document.body).append('<div id="shiny-notification-panel">');
|
||||
|
||||
return $panel;
|
||||
}
|
||||
@@ -2105,7 +2139,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
var $modal = $('#shiny-modal-wrapper');
|
||||
if ($modal.length === 0) {
|
||||
$modal = $('<div id="shiny-modal-wrapper"></div>');
|
||||
$('body').append($modal);
|
||||
$(document.body).append($modal);
|
||||
|
||||
// If the wrapper's content is a Bootstrap modal, then when the inner
|
||||
// modal is hidden, remove the entire thing, including wrapper.
|
||||
@@ -4326,7 +4360,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
receiveMessage: function receiveMessage(el, data) {
|
||||
if (data.hasOwnProperty('value')) this.setValue(el, data.value);
|
||||
|
||||
if (data.hasOwnProperty('label')) $(el).parent().find('label[for="' + $escape(el.id) + '"]').text(data.label);
|
||||
updateLabel(data.label, this._getLabelNode(el));
|
||||
|
||||
if (data.hasOwnProperty('placeholder')) el.placeholder = data.placeholder;
|
||||
|
||||
@@ -4334,7 +4368,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
},
|
||||
getState: function getState(el) {
|
||||
return {
|
||||
label: $(el).parent().find('label[for="' + $escape(el.id) + '"]').text(),
|
||||
label: this._getLabelNode(el).text(),
|
||||
value: el.value,
|
||||
placeholder: el.placeholder
|
||||
};
|
||||
@@ -4344,6 +4378,9 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
policy: 'debounce',
|
||||
delay: 250
|
||||
};
|
||||
},
|
||||
_getLabelNode: function _getLabelNode(el) {
|
||||
return $(el).parent().find('label[for="' + $escape(el.id) + '"]');
|
||||
}
|
||||
});
|
||||
inputBindings.register(textInputBinding, 'shiny.textInput');
|
||||
@@ -4399,16 +4436,19 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
if (data.hasOwnProperty('max')) el.max = data.max;
|
||||
if (data.hasOwnProperty('step')) el.step = data.step;
|
||||
|
||||
if (data.hasOwnProperty('label')) $(el).parent().find('label[for="' + $escape(el.id) + '"]').text(data.label);
|
||||
updateLabel(data.label, this._getLabelNode(el));
|
||||
|
||||
$(el).trigger('change');
|
||||
},
|
||||
getState: function getState(el) {
|
||||
return { label: $(el).parent().find('label[for="' + $escape(el.id) + '"]').text(),
|
||||
return { label: this._getLabelNode(el).text(),
|
||||
value: this.getValue(el),
|
||||
min: Number(el.min),
|
||||
max: Number(el.max),
|
||||
step: Number(el.step) };
|
||||
},
|
||||
_getLabelNode: function _getLabelNode(el) {
|
||||
return $(el).parent().find('label[for="' + $escape(el.id) + '"]');
|
||||
}
|
||||
});
|
||||
inputBindings.register(numberInputBinding, 'shiny.numberInput');
|
||||
@@ -4444,6 +4484,8 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
receiveMessage: function receiveMessage(el, data) {
|
||||
if (data.hasOwnProperty('value')) el.checked = data.value;
|
||||
|
||||
// checkboxInput()'s label works different from other
|
||||
// input labels...the label container should always exist
|
||||
if (data.hasOwnProperty('label')) $(el).parent().find('span').text(data.label);
|
||||
|
||||
$(el).trigger('change');
|
||||
@@ -4572,7 +4614,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
}
|
||||
}
|
||||
|
||||
if (data.hasOwnProperty('label')) $el.parent().find('label[for="' + $escape(el.id) + '"]').text(data.label);
|
||||
updateLabel(data.label, this._getLabelNode(el));
|
||||
|
||||
var domElements = ['data-type', 'time-format', 'timezone'];
|
||||
for (var i = 0; i < domElements.length; i++) {
|
||||
@@ -4614,7 +4656,9 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
|
||||
$el.ionRangeSlider(opts);
|
||||
},
|
||||
|
||||
_getLabelNode: function _getLabelNode(el) {
|
||||
return $(el).parent().find('label[for="' + $escape(el.id) + '"]');
|
||||
},
|
||||
// Number of values; 1 for single slider, 2 for range slider
|
||||
_numValues: function _numValues(el) {
|
||||
if ($(el).data('ionRangeSlider').options.type === 'double') return 2;else return 1;
|
||||
@@ -4776,7 +4820,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
if (startview === 2) startview = 'decade';else if (startview === 1) startview = 'year';else if (startview === 0) startview = 'month';
|
||||
|
||||
return {
|
||||
label: $el.find('label[for="' + $escape(el.id) + '"]').text(),
|
||||
label: this._getLabelNode(el).text(),
|
||||
value: this.getValue(el),
|
||||
valueString: $input.val(),
|
||||
min: min,
|
||||
@@ -4790,7 +4834,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
receiveMessage: function receiveMessage(el, data) {
|
||||
var $input = $(el).find('input');
|
||||
|
||||
if (data.hasOwnProperty('label')) $(el).find('label[for="' + $escape(el.id) + '"]').text(data.label);
|
||||
updateLabel(data.label, this._getLabelNode(el));
|
||||
|
||||
if (data.hasOwnProperty('min')) this._setMin($input[0], data.min);
|
||||
|
||||
@@ -4845,6 +4889,9 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
this._setMax($input[0], $input.data('max-date'));
|
||||
}
|
||||
},
|
||||
_getLabelNode: function _getLabelNode(el) {
|
||||
return $(el).find('label[for="' + $escape(el.id) + '"]');
|
||||
},
|
||||
// Given a format object from a date picker, return a string
|
||||
_formatToString: function _formatToString(format) {
|
||||
// Format object has structure like:
|
||||
@@ -4991,7 +5038,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
if (startview === 2) startview = 'decade';else if (startview === 1) startview = 'year';else if (startview === 0) startview = 'month';
|
||||
|
||||
return {
|
||||
label: $el.find('label[for="' + $escape(el.id) + '"]').text(),
|
||||
label: this._getLabelNode(el).text(),
|
||||
value: this.getValue(el),
|
||||
valueString: [$startinput.val(), $endinput.val()],
|
||||
min: min,
|
||||
@@ -5008,7 +5055,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
var $startinput = $inputs.eq(0);
|
||||
var $endinput = $inputs.eq(1);
|
||||
|
||||
if (data.hasOwnProperty('label')) $el.find('label[for="' + $escape(el.id) + '"]').text(data.label);
|
||||
updateLabel(data.label, this._getLabelNode(el));
|
||||
|
||||
if (data.hasOwnProperty('min')) {
|
||||
this._setMin($startinput[0], data.min);
|
||||
@@ -5064,6 +5111,9 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
},
|
||||
unsubscribe: function unsubscribe(el) {
|
||||
$(el).off('.dateRangeInputBinding');
|
||||
},
|
||||
_getLabelNode: function _getLabelNode(el) {
|
||||
return $(el).find('label[for="' + $escape(el.id) + '"]');
|
||||
}
|
||||
});
|
||||
inputBindings.register(dateRangeInputBinding, 'shiny.dateRangeInput');
|
||||
@@ -5113,7 +5163,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
}
|
||||
|
||||
return {
|
||||
label: $(el).parent().find('label[for="' + $escape(el.id) + '"]').text(),
|
||||
label: this._getLabelNode(el),
|
||||
value: this.getValue(el),
|
||||
options: options
|
||||
};
|
||||
@@ -5195,13 +5245,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
this.setValue(el, data.value);
|
||||
}
|
||||
|
||||
if (data.hasOwnProperty('label')) {
|
||||
var escaped_id = $escape(el.id);
|
||||
if (this._is_selectize(el)) {
|
||||
escaped_id += "-selectized";
|
||||
}
|
||||
$(el).parent().parent().find('label[for="' + escaped_id + '"]').text(data.label);
|
||||
}
|
||||
updateLabel(data.label, this._getLabelNode(el));
|
||||
|
||||
$(el).trigger('change');
|
||||
},
|
||||
@@ -5224,6 +5268,13 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
initialize: function initialize(el) {
|
||||
this._selectize(el);
|
||||
},
|
||||
_getLabelNode: function _getLabelNode(el) {
|
||||
var escaped_id = $escape(el.id);
|
||||
if (this._is_selectize(el)) {
|
||||
escaped_id += "-selectized";
|
||||
}
|
||||
return $(el).parent().parent().find('label[for="' + escaped_id + '"]');
|
||||
},
|
||||
// Return true if it's a selectize input, false if it's a regular select input.
|
||||
_is_selectize: function _is_selectize(el) {
|
||||
var config = $(el).parent().find('script[data-for="' + $escape(el.id) + '"]');
|
||||
@@ -5301,7 +5352,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
}
|
||||
|
||||
return {
|
||||
label: $(el).parent().find('label[for="' + $escape(el.id) + '"]').text(),
|
||||
label: this._getLabelNode(el).text(),
|
||||
value: this.getValue(el),
|
||||
options: options
|
||||
};
|
||||
@@ -5320,7 +5371,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
|
||||
if (data.hasOwnProperty('value')) this.setValue(el, data.value);
|
||||
|
||||
if (data.hasOwnProperty('label')) $(el).parent().find('label[for="' + $escape(el.id) + '"]').text(data.label);
|
||||
updateLabel(data.label, this._getLabelNode(el));
|
||||
|
||||
$(el).trigger('change');
|
||||
},
|
||||
@@ -5332,6 +5383,10 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
unsubscribe: function unsubscribe(el) {
|
||||
$(el).off('.radioInputBinding');
|
||||
},
|
||||
// Get the DOM element that contains the top-level label
|
||||
_getLabelNode: function _getLabelNode(el) {
|
||||
return $(el).parent().find('label[for="' + $escape(el.id) + '"]');
|
||||
},
|
||||
// Given an input DOM object, get the associated label. Handles labels
|
||||
// that wrap the input as well as labels associated with 'for' attribute.
|
||||
_getLabel: function _getLabel(obj) {
|
||||
@@ -5397,7 +5452,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
label: this._getLabel($objs[i]) };
|
||||
}
|
||||
|
||||
return { label: $(el).find('label[for="' + $escape(el.id) + '"]').text(),
|
||||
return { label: this._getLabelNode(el).text(),
|
||||
value: this.getValue(el),
|
||||
options: options
|
||||
};
|
||||
@@ -5416,7 +5471,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
|
||||
if (data.hasOwnProperty('value')) this.setValue(el, data.value);
|
||||
|
||||
if (data.hasOwnProperty('label')) $el.find('label[for="' + $escape(el.id) + '"]').text(data.label);
|
||||
updateLabel(data.label, this._getLabelNode(el));
|
||||
|
||||
$(el).trigger('change');
|
||||
},
|
||||
@@ -5428,6 +5483,10 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
unsubscribe: function unsubscribe(el) {
|
||||
$(el).off('.checkboxGroupInputBinding');
|
||||
},
|
||||
// Get the DOM element that contains the top-level label
|
||||
_getLabelNode: function _getLabelNode(el) {
|
||||
return $(el).find('label[for="' + $escape(el.id) + '"]');
|
||||
},
|
||||
// Given an input DOM object, get the associated label. Handles labels
|
||||
// that wrap the input as well as labels associated with 'for' attribute.
|
||||
_getLabel: function _getLabel(obj) {
|
||||
@@ -5593,7 +5652,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
this.iframe.id = iframeId;
|
||||
this.iframe.name = iframeId;
|
||||
this.iframe.setAttribute('style', 'position: fixed; top: 0; left: 0; width: 0; height: 0; border: none');
|
||||
$('body').append(this.iframe);
|
||||
$(document.body).append(this.iframe);
|
||||
var iframeDestroy = function iframeDestroy() {
|
||||
// Forces Shiny to flushReact, flush outputs, etc. Without this we get
|
||||
// invalidated reactives, but observers don't actually execute.
|
||||
@@ -6457,14 +6516,14 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
// Need to register callbacks for each Bootstrap 3 class.
|
||||
var bs3classes = ['modal', 'dropdown', 'tab', 'tooltip', 'popover', 'collapse'];
|
||||
$.each(bs3classes, function (idx, classname) {
|
||||
$('body').on('shown.bs.' + classname + '.sendImageSize', '*', filterEventsByNamespace('bs', sendImageSize));
|
||||
$('body').on('shown.bs.' + classname + '.sendOutputHiddenState ' + 'hidden.bs.' + classname + '.sendOutputHiddenState', '*', filterEventsByNamespace('bs', sendOutputHiddenState));
|
||||
$(document.body).on('shown.bs.' + classname + '.sendImageSize', '*', filterEventsByNamespace('bs', sendImageSize));
|
||||
$(document.body).on('shown.bs.' + classname + '.sendOutputHiddenState ' + 'hidden.bs.' + classname + '.sendOutputHiddenState', '*', filterEventsByNamespace('bs', sendOutputHiddenState));
|
||||
});
|
||||
|
||||
// This is needed for Bootstrap 2 compatibility and for non-Bootstrap
|
||||
// related shown/hidden events (like conditionalPanel)
|
||||
$('body').on('shown.sendImageSize', '*', sendImageSize);
|
||||
$('body').on('shown.sendOutputHiddenState hidden.sendOutputHiddenState', '*', sendOutputHiddenState);
|
||||
$(document.body).on('shown.sendImageSize', '*', sendImageSize);
|
||||
$(document.body).on('shown.sendOutputHiddenState hidden.sendOutputHiddenState', '*', sendOutputHiddenState);
|
||||
|
||||
// Send initial pixel ratio, and update it if it changes
|
||||
initialValues['.clientdata_pixelratio'] = pixelRatio();
|
||||
|
||||
File diff suppressed because one or more lines are too long
8
inst/www/shared/shiny.min.js
vendored
8
inst/www/shared/shiny.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -27,6 +27,8 @@ $.extend(checkboxInputBinding, {
|
||||
if (data.hasOwnProperty('value'))
|
||||
el.checked = data.value;
|
||||
|
||||
// checkboxInput()'s label works different from other
|
||||
// input labels...the label container should always exist
|
||||
if (data.hasOwnProperty('label'))
|
||||
$(el).parent().find('span').text(data.label);
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ $.extend(checkboxGroupInputBinding, {
|
||||
label: this._getLabel($objs[i]) };
|
||||
}
|
||||
|
||||
return { label: $(el).find('label[for="' + $escape(el.id) + '"]').text(),
|
||||
return { label: this._getLabelNode(el).text(),
|
||||
value: this.getValue(el),
|
||||
options: options
|
||||
};
|
||||
@@ -59,8 +59,7 @@ $.extend(checkboxGroupInputBinding, {
|
||||
if (data.hasOwnProperty('value'))
|
||||
this.setValue(el, data.value);
|
||||
|
||||
if (data.hasOwnProperty('label'))
|
||||
$el.find('label[for="' + $escape(el.id) + '"]').text(data.label);
|
||||
updateLabel(data.label, this._getLabelNode(el));
|
||||
|
||||
$(el).trigger('change');
|
||||
},
|
||||
@@ -72,6 +71,10 @@ $.extend(checkboxGroupInputBinding, {
|
||||
unsubscribe: function(el) {
|
||||
$(el).off('.checkboxGroupInputBinding');
|
||||
},
|
||||
// Get the DOM element that contains the top-level label
|
||||
_getLabelNode: function(el) {
|
||||
return $(el).find('label[for="' + $escape(el.id) + '"]');
|
||||
},
|
||||
// Given an input DOM object, get the associated label. Handles labels
|
||||
// that wrap the input as well as labels associated with 'for' attribute.
|
||||
_getLabel: function(obj) {
|
||||
|
||||
@@ -46,7 +46,7 @@ $.extend(dateInputBinding, {
|
||||
else if (startview === 0) startview = 'month';
|
||||
|
||||
return {
|
||||
label: $el.find('label[for="' + $escape(el.id) + '"]').text(),
|
||||
label: this._getLabelNode(el).text(),
|
||||
value: this.getValue(el),
|
||||
valueString: $input.val(),
|
||||
min: min,
|
||||
@@ -60,8 +60,7 @@ $.extend(dateInputBinding, {
|
||||
receiveMessage: function(el, data) {
|
||||
var $input = $(el).find('input');
|
||||
|
||||
if (data.hasOwnProperty('label'))
|
||||
$(el).find('label[for="' + $escape(el.id) + '"]').text(data.label);
|
||||
updateLabel(data.label, this._getLabelNode(el));
|
||||
|
||||
if (data.hasOwnProperty('min'))
|
||||
this._setMin($input[0], data.min);
|
||||
@@ -119,6 +118,9 @@ $.extend(dateInputBinding, {
|
||||
this._setMax($input[0], $input.data('max-date'));
|
||||
}
|
||||
},
|
||||
_getLabelNode: function(el) {
|
||||
return $(el).find('label[for="' + $escape(el.id) + '"]');
|
||||
},
|
||||
// Given a format object from a date picker, return a string
|
||||
_formatToString: function(format) {
|
||||
// Format object has structure like:
|
||||
|
||||
@@ -63,7 +63,7 @@ $.extend(dateRangeInputBinding, dateInputBinding, {
|
||||
else if (startview === 0) startview = 'month';
|
||||
|
||||
return {
|
||||
label: $el.find('label[for="' + $escape(el.id) + '"]').text(),
|
||||
label: this._getLabelNode(el).text(),
|
||||
value: this.getValue(el),
|
||||
valueString: [ $startinput.val(), $endinput.val() ],
|
||||
min: min,
|
||||
@@ -80,8 +80,7 @@ $.extend(dateRangeInputBinding, dateInputBinding, {
|
||||
var $startinput = $inputs.eq(0);
|
||||
var $endinput = $inputs.eq(1);
|
||||
|
||||
if (data.hasOwnProperty('label'))
|
||||
$el.find('label[for="' + $escape(el.id) + '"]').text(data.label);
|
||||
updateLabel(data.label, this._getLabelNode(el));
|
||||
|
||||
if (data.hasOwnProperty('min')) {
|
||||
this._setMin($startinput[0], data.min);
|
||||
@@ -140,6 +139,9 @@ $.extend(dateRangeInputBinding, dateInputBinding, {
|
||||
},
|
||||
unsubscribe: function(el) {
|
||||
$(el).off('.dateRangeInputBinding');
|
||||
}
|
||||
},
|
||||
_getLabelNode: function(el) {
|
||||
return $(el).find('label[for="' + $escape(el.id) + '"]');
|
||||
},
|
||||
});
|
||||
inputBindings.register(dateRangeInputBinding, 'shiny.dateRangeInput');
|
||||
|
||||
@@ -24,17 +24,19 @@ $.extend(numberInputBinding, textInputBinding, {
|
||||
if (data.hasOwnProperty('max')) el.max = data.max;
|
||||
if (data.hasOwnProperty('step')) el.step = data.step;
|
||||
|
||||
if (data.hasOwnProperty('label'))
|
||||
$(el).parent().find('label[for="' + $escape(el.id) + '"]').text(data.label);
|
||||
updateLabel(data.label, this._getLabelNode(el));
|
||||
|
||||
$(el).trigger('change');
|
||||
},
|
||||
getState: function(el) {
|
||||
return { label: $(el).parent().find('label[for="' + $escape(el.id) + '"]').text(),
|
||||
return { label: this._getLabelNode(el).text(),
|
||||
value: this.getValue(el),
|
||||
min: Number(el.min),
|
||||
max: Number(el.max),
|
||||
step: Number(el.step) };
|
||||
},
|
||||
_getLabelNode: function(el) {
|
||||
return $(el).parent().find('label[for="' + $escape(el.id) + '"]');
|
||||
}
|
||||
});
|
||||
inputBindings.register(numberInputBinding, 'shiny.numberInput');
|
||||
|
||||
@@ -21,7 +21,7 @@ $.extend(radioInputBinding, {
|
||||
}
|
||||
|
||||
return {
|
||||
label: $(el).parent().find('label[for="' + $escape(el.id) + '"]').text(),
|
||||
label: this._getLabelNode(el).text(),
|
||||
value: this.getValue(el),
|
||||
options: options
|
||||
};
|
||||
@@ -41,8 +41,7 @@ $.extend(radioInputBinding, {
|
||||
if (data.hasOwnProperty('value'))
|
||||
this.setValue(el, data.value);
|
||||
|
||||
if (data.hasOwnProperty('label'))
|
||||
$(el).parent().find('label[for="' + $escape(el.id) + '"]').text(data.label);
|
||||
updateLabel(data.label, this._getLabelNode(el));
|
||||
|
||||
$(el).trigger('change');
|
||||
},
|
||||
@@ -54,6 +53,10 @@ $.extend(radioInputBinding, {
|
||||
unsubscribe: function(el) {
|
||||
$(el).off('.radioInputBinding');
|
||||
},
|
||||
// Get the DOM element that contains the top-level label
|
||||
_getLabelNode: function(el) {
|
||||
return $(el).parent().find('label[for="' + $escape(el.id) + '"]');
|
||||
},
|
||||
// Given an input DOM object, get the associated label. Handles labels
|
||||
// that wrap the input as well as labels associated with 'for' attribute.
|
||||
_getLabel: function(obj) {
|
||||
|
||||
@@ -40,7 +40,7 @@ $.extend(selectInputBinding, {
|
||||
}
|
||||
|
||||
return {
|
||||
label: $(el).parent().find('label[for="' + $escape(el.id) + '"]').text(),
|
||||
label: this._getLabelNode(el),
|
||||
value: this.getValue(el),
|
||||
options: options
|
||||
};
|
||||
@@ -123,15 +123,7 @@ $.extend(selectInputBinding, {
|
||||
this.setValue(el, data.value);
|
||||
}
|
||||
|
||||
if (data.hasOwnProperty('label')) {
|
||||
let escaped_id = $escape(el.id);
|
||||
if (this._is_selectize(el)) {
|
||||
escaped_id += "-selectized";
|
||||
}
|
||||
$(el).parent().parent()
|
||||
.find('label[for="' + escaped_id + '"]')
|
||||
.text(data.label);
|
||||
}
|
||||
updateLabel(data.label, this._getLabelNode(el));
|
||||
|
||||
$(el).trigger('change');
|
||||
},
|
||||
@@ -152,6 +144,13 @@ $.extend(selectInputBinding, {
|
||||
initialize: function(el) {
|
||||
this._selectize(el);
|
||||
},
|
||||
_getLabelNode: function(el) {
|
||||
let escaped_id = $escape(el.id);
|
||||
if (this._is_selectize(el)) {
|
||||
escaped_id += "-selectized";
|
||||
}
|
||||
return $(el).parent().parent().find('label[for="' + escaped_id + '"]');
|
||||
},
|
||||
// Return true if it's a selectize input, false if it's a regular select input.
|
||||
_is_selectize: function(el) {
|
||||
var config = $(el).parent().find('script[data-for="' + $escape(el.id) + '"]');
|
||||
|
||||
@@ -130,8 +130,7 @@ $.extend(sliderInputBinding, textInputBinding, {
|
||||
}
|
||||
}
|
||||
|
||||
if (data.hasOwnProperty('label'))
|
||||
$el.parent().find('label[for="' + $escape(el.id) + '"]').text(data.label);
|
||||
updateLabel(data.label, this._getLabelNode(el));
|
||||
|
||||
var domElements = ['data-type', 'time-format', 'timezone'];
|
||||
for (var i = 0; i < domElements.length; i++) {
|
||||
@@ -174,7 +173,9 @@ $.extend(sliderInputBinding, textInputBinding, {
|
||||
|
||||
$el.ionRangeSlider(opts);
|
||||
},
|
||||
|
||||
_getLabelNode: function(el) {
|
||||
return $(el).parent().find('label[for="' + $escape(el.id) + '"]');
|
||||
},
|
||||
// Number of values; 1 for single slider, 2 for range slider
|
||||
_numValues: function(el) {
|
||||
if ($(el).data('ionRangeSlider').options.type === 'double')
|
||||
|
||||
@@ -27,8 +27,7 @@ $.extend(textInputBinding, {
|
||||
if (data.hasOwnProperty('value'))
|
||||
this.setValue(el, data.value);
|
||||
|
||||
if (data.hasOwnProperty('label'))
|
||||
$(el).parent().find('label[for="' + $escape(el.id) + '"]').text(data.label);
|
||||
updateLabel(data.label, this._getLabelNode(el));
|
||||
|
||||
if (data.hasOwnProperty('placeholder'))
|
||||
el.placeholder = data.placeholder;
|
||||
@@ -37,7 +36,7 @@ $.extend(textInputBinding, {
|
||||
},
|
||||
getState: function(el) {
|
||||
return {
|
||||
label: $(el).parent().find('label[for="' + $escape(el.id) + '"]').text(),
|
||||
label: this._getLabelNode(el).text(),
|
||||
value: el.value,
|
||||
placeholder: el.placeholder
|
||||
};
|
||||
@@ -47,6 +46,9 @@ $.extend(textInputBinding, {
|
||||
policy: 'debounce',
|
||||
delay: 250
|
||||
};
|
||||
},
|
||||
_getLabelNode: function(el) {
|
||||
return $(el).parent().find('label[for="' + $escape(el.id) + '"]');
|
||||
}
|
||||
});
|
||||
inputBindings.register(textInputBinding, 'shiny.textInput');
|
||||
|
||||
@@ -189,8 +189,8 @@ var InputBatchSender = function(shinyapp) {
|
||||
this.lastChanceCallback = [];
|
||||
};
|
||||
(function() {
|
||||
this.setInput = function(name, value, opts) {
|
||||
this.pendingData[name] = value;
|
||||
this.setInput = function(nameType, value, opts) {
|
||||
this.pendingData[nameType] = value;
|
||||
|
||||
if (!this.reentrant) {
|
||||
if (opts.priority === "event") {
|
||||
@@ -227,8 +227,8 @@ var InputNoResendDecorator = function(target, initialValues) {
|
||||
this.lastSentValues = this.reset(initialValues);
|
||||
};
|
||||
(function() {
|
||||
this.setInput = function(name, value, opts) {
|
||||
const { name: inputName, inputType: inputType } = splitInputNameType(name);
|
||||
this.setInput = function(nameType, value, opts) {
|
||||
const { name: inputName, inputType: inputType } = splitInputNameType(nameType);
|
||||
const jsonValue = JSON.stringify(value);
|
||||
|
||||
if (opts.priority !== "event" &&
|
||||
@@ -267,10 +267,10 @@ var InputEventDecorator = function(target) {
|
||||
this.target = target;
|
||||
};
|
||||
(function() {
|
||||
this.setInput = function(name, value, opts) {
|
||||
this.setInput = function(nameType, value, opts) {
|
||||
var evt = jQuery.Event("shiny:inputchanged");
|
||||
|
||||
const input = splitInputNameType(name);
|
||||
const input = splitInputNameType(nameType);
|
||||
evt.name = input.name;
|
||||
evt.inputType = input.inputType;
|
||||
evt.value = value;
|
||||
@@ -297,31 +297,41 @@ var InputRateDecorator = function(target) {
|
||||
this.inputRatePolicies = {};
|
||||
};
|
||||
(function() {
|
||||
this.setInput = function(name, value, opts) {
|
||||
this.$ensureInit(name);
|
||||
// Note that the first argument of setInput() and setRatePolicy()
|
||||
// are passed both the input name (i.e., inputId) and type.
|
||||
// https://github.com/rstudio/shiny/blob/67d3a/srcjs/init_shiny.js#L111-L126
|
||||
// However, $ensureInit() and $doSetInput() are meant to be passed just
|
||||
// the input name (i.e., inputId), which is why we distinguish between
|
||||
// nameType and name.
|
||||
this.setInput = function(nameType, value, opts) {
|
||||
const {name: inputName} = splitInputNameType(nameType);
|
||||
|
||||
this.$ensureInit(inputName);
|
||||
|
||||
if (opts.priority !== "deferred")
|
||||
this.inputRatePolicies[name].immediateCall(name, value, opts);
|
||||
this.inputRatePolicies[inputName].immediateCall(nameType, value, opts);
|
||||
else
|
||||
this.inputRatePolicies[name].normalCall(name, value, opts);
|
||||
this.inputRatePolicies[inputName].normalCall(nameType, value, opts);
|
||||
};
|
||||
this.setRatePolicy = function(name, mode, millis) {
|
||||
this.setRatePolicy = function(nameType, mode, millis) {
|
||||
const {name: inputName} = splitInputNameType(nameType);
|
||||
|
||||
if (mode === 'direct') {
|
||||
this.inputRatePolicies[name] = new Invoker(this, this.$doSetInput);
|
||||
this.inputRatePolicies[inputName] = new Invoker(this, this.$doSetInput);
|
||||
}
|
||||
else if (mode === 'debounce') {
|
||||
this.inputRatePolicies[name] = new Debouncer(this, this.$doSetInput, millis);
|
||||
this.inputRatePolicies[inputName] = new Debouncer(this, this.$doSetInput, millis);
|
||||
}
|
||||
else if (mode === 'throttle') {
|
||||
this.inputRatePolicies[name] = new Throttler(this, this.$doSetInput, millis);
|
||||
this.inputRatePolicies[inputName] = new Throttler(this, this.$doSetInput, millis);
|
||||
}
|
||||
};
|
||||
this.$ensureInit = function(name) {
|
||||
if (!(name in this.inputRatePolicies))
|
||||
this.setRatePolicy(name, 'direct');
|
||||
};
|
||||
this.$doSetInput = function(name, value, opts) {
|
||||
this.target.setInput(name, value, opts);
|
||||
this.$doSetInput = function(nameType, value, opts) {
|
||||
this.target.setInput(nameType, value, opts);
|
||||
};
|
||||
}).call(InputRateDecorator.prototype);
|
||||
|
||||
@@ -331,9 +341,9 @@ var InputDeferDecorator = function(target) {
|
||||
this.pendingInput = {};
|
||||
};
|
||||
(function() {
|
||||
this.setInput = function(name, value, opts) {
|
||||
if (/^\./.test(name))
|
||||
this.target.setInput(name, value, opts);
|
||||
this.setInput = function(nameType, value, opts) {
|
||||
if (/^\./.test(nameType))
|
||||
this.target.setInput(nameType, value, opts);
|
||||
else
|
||||
this.pendingInput[name] = { value, opts };
|
||||
};
|
||||
@@ -352,13 +362,13 @@ const InputValidateDecorator = function(target) {
|
||||
this.target = target;
|
||||
};
|
||||
(function() {
|
||||
this.setInput = function(name, value, opts) {
|
||||
if (!name)
|
||||
this.setInput = function(nameType, value, opts) {
|
||||
if (!nameType)
|
||||
throw "Can't set input with empty name.";
|
||||
|
||||
opts = addDefaultInputOpts(opts);
|
||||
|
||||
this.target.setInput(name, value, opts);
|
||||
this.target.setInput(nameType, value, opts);
|
||||
};
|
||||
}).call(InputValidateDecorator.prototype);
|
||||
|
||||
@@ -387,8 +397,8 @@ function addDefaultInputOpts(opts) {
|
||||
}
|
||||
|
||||
|
||||
function splitInputNameType(name) {
|
||||
const name2 = name.split(':');
|
||||
function splitInputNameType(nameType) {
|
||||
const name2 = nameType.split(':');
|
||||
return {
|
||||
name: name2[0],
|
||||
inputType: name2.length > 1 ? name2[1] : ''
|
||||
|
||||
@@ -326,3 +326,23 @@ exports.compareVersion = function(a, op, b) {
|
||||
else if (op === "<") return (diff < 0);
|
||||
else throw `Unknown operator: ${op}`;
|
||||
};
|
||||
|
||||
|
||||
function updateLabel(labelTxt, labelNode) {
|
||||
// Only update if label was specified in the update method
|
||||
if (typeof labelTxt === "undefined") return;
|
||||
if (labelNode.length !== 1) {
|
||||
throw new Error("labelNode must be of length 1");
|
||||
}
|
||||
|
||||
// Should the label be empty?
|
||||
var emptyLabel = $.isArray(labelTxt) && labelTxt.length === 0;
|
||||
|
||||
if (emptyLabel) {
|
||||
labelNode.addClass("shiny-label-null");
|
||||
} else {
|
||||
labelNode.text(labelTxt);
|
||||
labelNode.removeClass("shiny-label-null");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -521,8 +521,8 @@ test_that("names() and reactiveValuesToList()", {
|
||||
# Assigning names fails
|
||||
expect_error(isolate(names(v) <- c('x', 'y')))
|
||||
|
||||
expect_equal(isolate(reactiveValuesToList(values)), list(A=1))
|
||||
expect_equal(isolate(reactiveValuesToList(values, all.names=TRUE)), list(A=1, .B=2))
|
||||
expect_mapequal(isolate(reactiveValuesToList(values)), list(A=1))
|
||||
expect_mapequal(isolate(reactiveValuesToList(values, all.names=TRUE)), list(A=1, .B=2))
|
||||
|
||||
|
||||
flushReact()
|
||||
@@ -1137,10 +1137,10 @@ test_that("reactive domain works across async handlers", {
|
||||
~{hasReactiveDomain <<- identical(getDefaultReactiveDomain(), obj)}
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
while (is.null(hasReactiveDomain) && !later::loop_empty()) {
|
||||
later::run_now()
|
||||
}
|
||||
|
||||
|
||||
testthat::expect_true(hasReactiveDomain)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user