mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-11 16:08:19 -05:00
Compare commits
64 Commits
feature/ap
...
bookmarkab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1912784bf3 | ||
|
|
fd795e8937 | ||
|
|
34be3c06b9 | ||
|
|
cfeec933ef | ||
|
|
fa7a33d0a2 | ||
|
|
2d5438eb81 | ||
|
|
051f720fe0 | ||
|
|
d180f27e46 | ||
|
|
9b49b7a3dd | ||
|
|
bad566f6c7 | ||
|
|
08d7f36b36 | ||
|
|
0cdd96a8e4 | ||
|
|
a688c22929 | ||
|
|
f110787709 | ||
|
|
e57773cfa6 | ||
|
|
71e0f535b7 | ||
|
|
6cb3921333 | ||
|
|
a474e9f0ea | ||
|
|
7bae46325b | ||
|
|
b42d6dce55 | ||
|
|
bd39c40fd8 | ||
|
|
e47bf922b1 | ||
|
|
282893faff | ||
|
|
87309a64d2 | ||
|
|
f38fe7d488 | ||
|
|
23451b7c0f | ||
|
|
0e52b34ab9 | ||
|
|
94804d972c | ||
|
|
d7c94052a2 | ||
|
|
9bc136773c | ||
|
|
1ea1a16fb7 | ||
|
|
cc09429e22 | ||
|
|
ed0c5d4f55 | ||
|
|
e3ce1ba14d | ||
|
|
0c4048068b | ||
|
|
be9d884ae2 | ||
|
|
7065652e9a | ||
|
|
60f7b9077d | ||
|
|
aa787f42e4 | ||
|
|
33e605509b | ||
|
|
e31ac5a73d | ||
|
|
6282edc537 | ||
|
|
75b41eb7d8 | ||
|
|
54f6f8793d | ||
|
|
3d5ee44388 | ||
|
|
c355da585c | ||
|
|
ae7b5afbb3 | ||
|
|
2782369e20 | ||
|
|
46559be05a | ||
|
|
70a022cb4b | ||
|
|
c207e130f8 | ||
|
|
21a436189a | ||
|
|
b028e5a4da | ||
|
|
8b9cf38082 | ||
|
|
00c5fa82f9 | ||
|
|
3dad19d4f1 | ||
|
|
b9a0f5dffb | ||
|
|
ca80273aef | ||
|
|
441298a1cb | ||
|
|
aaeab9fcfd | ||
|
|
4259002073 | ||
|
|
1ba2a584e3 | ||
|
|
510e60e151 | ||
|
|
6a3818b4a0 |
@@ -16,5 +16,3 @@
|
||||
^CONTRIBUTING.md$
|
||||
^cran-comments.md$
|
||||
^.*\.o$
|
||||
^appveyor\.yml$
|
||||
^revdep$
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
language: r
|
||||
r:
|
||||
- oldrel
|
||||
- release
|
||||
- devel
|
||||
sudo: false
|
||||
cache: packages
|
||||
|
||||
|
||||
@@ -1,38 +1,10 @@
|
||||
|
||||
We welcome contributions to the **shiny** package. To submit a contribution:
|
||||
|
||||
1. [Fork](https://github.com/rstudio/shiny/fork) the repository and make your changes.
|
||||
|
||||
2. If the change is non-trivial, ensure that you have signed the [individual](http://www.rstudio.com/wp-content/uploads/2014/06/RStudioIndividualContributorAgreement.pdf) or [corporate](http://www.rstudio.com/wp-content/uploads/2014/06/RStudioCorporateContributorAgreement.pdf) contributor agreement as appropriate. You can send the signed copy to jj@rstudio.com. For trivial changes (like typo fixes), a contributor agreement is not needed.
|
||||
2. Ensure that you have signed the [individual](http://www.rstudio.com/wp-content/uploads/2014/06/RStudioIndividualContributorAgreement.pdf) or [corporate](http://www.rstudio.com/wp-content/uploads/2014/06/RStudioCorporateContributorAgreement.pdf) contributor agreement as appropriate. You can send the signed copy to jj@rstudio.com.
|
||||
|
||||
3. Submit a [pull request](https://help.github.com/articles/using-pull-requests).
|
||||
|
||||
We generally do not merge pull requests that update included web libraries (such as Bootstrap or jQuery) because it is difficult for us to verify that the update is done correctly; we prefer to update these libraries ourselves.
|
||||
|
||||
|
||||
## How to make changes
|
||||
|
||||
Before you submit a pull request, please do the following:
|
||||
|
||||
* Add an entry to NEWS.md concisely describing what you changed.
|
||||
|
||||
* If appropriate, add unit tests in the tests/ directory.
|
||||
|
||||
* If you made any changes to the JavaScript files in the srcjs/ directory, make sure you build the output JavaScript files. See tools/README.md file for information on using the build system.
|
||||
|
||||
* Run Build->Check Package in the RStudio IDE, or `devtools::check()`, to make sure your change did not add any messages, warnings, or errors.
|
||||
|
||||
Doing these things will make it easier for the Shiny development team to evaluate your pull request. Even so, we may still decide to modify your code or even not merge it at all. Factors that may prevent us from merging the pull request include:
|
||||
|
||||
* breaking backward compatibility
|
||||
* adding a feature that we do not consider relevant for Shiny
|
||||
* is hard to understand
|
||||
* is hard to maintain in the future
|
||||
* is computationally expensive
|
||||
* is not intuitive for people to use
|
||||
|
||||
We will try to be responsive and provide feedback in case we decide not to merge your pull request.
|
||||
|
||||
|
||||
## Filing issues
|
||||
|
||||
If you find a bug in Shiny, you can also [file an issue](https://github.com/rstudio/shiny/issues/new). Please provide as much relevant information as you can, and include a minimal reproducible example if possible.
|
||||
We'll try to be as responsive as possible in reviewing and accepting pull requests. We appreciate your contributions!
|
||||
|
||||
17
DESCRIPTION
17
DESCRIPTION
@@ -1,7 +1,8 @@
|
||||
Package: shiny
|
||||
Type: Package
|
||||
Title: Web Application Framework for R
|
||||
Version: 1.0.0
|
||||
Version: 0.13.2.9004
|
||||
Date: 2016-02-17
|
||||
Authors@R: c(
|
||||
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@rstudio.com"),
|
||||
person("Joe", "Cheng", role = "aut", email = "joe@rstudio.com"),
|
||||
@@ -14,7 +15,7 @@ Authors@R: c(
|
||||
person(family = "jQuery contributors", role = c("ctb", "cph"),
|
||||
comment = "jQuery library; authors listed in inst/www/shared/jquery-AUTHORS.txt"),
|
||||
person(family = "jQuery UI contributors", role = c("ctb", "cph"),
|
||||
comment = "jQuery UI library; authors listed in inst/www/shared/jqueryui/AUTHORS.txt"),
|
||||
comment = "jQuery UI library; authors listed in inst/www/shared/jqueryui/1.10.4/AUTHORS.txt"),
|
||||
person("Mark", "Otto", role = "ctb",
|
||||
comment = "Bootstrap library"),
|
||||
person("Jacob", "Thornton", role = "ctb",
|
||||
@@ -79,15 +80,12 @@ Suggests:
|
||||
knitr (>= 1.6),
|
||||
markdown,
|
||||
rmarkdown,
|
||||
ggplot2,
|
||||
magrittr
|
||||
ggplot2
|
||||
URL: http://shiny.rstudio.com
|
||||
BugReports: https://github.com/rstudio/shiny/issues
|
||||
VignetteBuilder: knitr
|
||||
Collate:
|
||||
'app.R'
|
||||
'bookmark-state-local.R'
|
||||
'stack.R'
|
||||
'bookmark-state.R'
|
||||
'bootstrap-layout.R'
|
||||
'conditions.R'
|
||||
'map.R'
|
||||
@@ -97,6 +95,7 @@ Collate:
|
||||
'cache.R'
|
||||
'diagnose.R'
|
||||
'fileupload.R'
|
||||
'stack.R'
|
||||
'graph.R'
|
||||
'hooks.R'
|
||||
'html-deps.R'
|
||||
@@ -117,7 +116,6 @@ Collate:
|
||||
'input-slider.R'
|
||||
'input-submit.R'
|
||||
'input-text.R'
|
||||
'input-textarea.R'
|
||||
'input-utils.R'
|
||||
'insert-ui.R'
|
||||
'jqueryui.R'
|
||||
@@ -134,6 +132,8 @@ Collate:
|
||||
'render-plot.R'
|
||||
'render-table.R'
|
||||
'run-url.R'
|
||||
'save-state-local.R'
|
||||
'save-state.R'
|
||||
'serializers.R'
|
||||
'server-input-handlers.R'
|
||||
'server.R'
|
||||
@@ -143,7 +143,6 @@ Collate:
|
||||
'shinywrappers.R'
|
||||
'showcase.R'
|
||||
'tar.R'
|
||||
'test-export.R'
|
||||
'timer.R'
|
||||
'update-input.R'
|
||||
RoxygenNote: 5.0.1
|
||||
|
||||
37
NAMESPACE
37
NAMESPACE
@@ -2,24 +2,17 @@
|
||||
|
||||
S3method("$",reactivevalues)
|
||||
S3method("$",session_proxy)
|
||||
S3method("$",shinyapi)
|
||||
S3method("$",shinyoutput)
|
||||
S3method("$<-",reactivevalues)
|
||||
S3method("$<-",session_proxy)
|
||||
S3method("$<-",shinyapi)
|
||||
S3method("$<-",shinyoutput)
|
||||
S3method("[",reactivevalues)
|
||||
S3method("[",shinyapi)
|
||||
S3method("[",shinyoutput)
|
||||
S3method("[<-",reactivevalues)
|
||||
S3method("[<-",shinyapi)
|
||||
S3method("[<-",shinyoutput)
|
||||
S3method("[[",reactivevalues)
|
||||
S3method("[[",session_proxy)
|
||||
S3method("[[",shinyapi)
|
||||
S3method("[[",shinyoutput)
|
||||
S3method("[[<-",reactivevalues)
|
||||
S3method("[[<-",shinyapi)
|
||||
S3method("[[<-",shinyoutput)
|
||||
S3method("names<-",reactivevalues)
|
||||
S3method(as.list,reactivevalues)
|
||||
@@ -46,7 +39,6 @@ export(addResourcePath)
|
||||
export(animationOptions)
|
||||
export(as.shiny.appobj)
|
||||
export(basicPage)
|
||||
export(bookmarkButton)
|
||||
export(bootstrapLib)
|
||||
export(bootstrapPage)
|
||||
export(br)
|
||||
@@ -54,6 +46,7 @@ export(browserViewer)
|
||||
export(brushOpts)
|
||||
export(brushedPoints)
|
||||
export(callModule)
|
||||
export(cancelOutput)
|
||||
export(captureStackTraces)
|
||||
export(checkboxGroupInput)
|
||||
export(checkboxInput)
|
||||
@@ -62,21 +55,19 @@ export(code)
|
||||
export(column)
|
||||
export(conditionStackTrace)
|
||||
export(conditionalPanel)
|
||||
export(configureBookmarking)
|
||||
export(createWebDependency)
|
||||
export(dataTableOutput)
|
||||
export(dateInput)
|
||||
export(dateRangeInput)
|
||||
export(dblclickOpts)
|
||||
export(debounce)
|
||||
export(dialogViewer)
|
||||
export(div)
|
||||
export(downloadButton)
|
||||
export(downloadHandler)
|
||||
export(downloadLink)
|
||||
export(em)
|
||||
export(enableBookmarking)
|
||||
export(eventReactive)
|
||||
export(exportTestValues)
|
||||
export(exprToFunction)
|
||||
export(extractStackTrace)
|
||||
export(fileInput)
|
||||
@@ -90,7 +81,6 @@ export(flowLayout)
|
||||
export(fluidPage)
|
||||
export(fluidRow)
|
||||
export(formatStackTrace)
|
||||
export(freezeReactiveValue)
|
||||
export(getDefaultReactiveDomain)
|
||||
export(getShinyOption)
|
||||
export(h1)
|
||||
@@ -118,11 +108,11 @@ export(inputPanel)
|
||||
export(insertUI)
|
||||
export(installExprFunction)
|
||||
export(invalidateLater)
|
||||
export(invalidateReactiveValue)
|
||||
export(is.reactive)
|
||||
export(is.reactivevalues)
|
||||
export(is.shiny.appobj)
|
||||
export(is.singleton)
|
||||
export(isTruthy)
|
||||
export(isolate)
|
||||
export(knit_print.html)
|
||||
export(knit_print.reactive)
|
||||
@@ -145,14 +135,7 @@ export(ns.sep)
|
||||
export(numericInput)
|
||||
export(observe)
|
||||
export(observeEvent)
|
||||
export(onBookmark)
|
||||
export(onBookmarked)
|
||||
export(onFlush)
|
||||
export(onFlushed)
|
||||
export(onReactiveDomainEnded)
|
||||
export(onRestore)
|
||||
export(onRestored)
|
||||
export(onSessionEnded)
|
||||
export(outputOptions)
|
||||
export(p)
|
||||
export(pageWithSidebar)
|
||||
@@ -198,15 +181,10 @@ export(runGist)
|
||||
export(runGitHub)
|
||||
export(runUrl)
|
||||
export(safeError)
|
||||
export(saveStateButton)
|
||||
export(selectInput)
|
||||
export(selectizeInput)
|
||||
export(serveCSV)
|
||||
export(serveJSON)
|
||||
export(servePlot)
|
||||
export(serveRaw)
|
||||
export(serveText)
|
||||
export(serverInfo)
|
||||
export(setBookmarkExclude)
|
||||
export(setProgress)
|
||||
export(shinyApp)
|
||||
export(shinyAppDir)
|
||||
@@ -214,7 +192,6 @@ export(shinyAppFile)
|
||||
export(shinyOptions)
|
||||
export(shinyServer)
|
||||
export(shinyUI)
|
||||
export(showBookmarkUrlModal)
|
||||
export(showModal)
|
||||
export(showNotification)
|
||||
export(showReactLog)
|
||||
@@ -238,10 +215,8 @@ export(tagAppendChildren)
|
||||
export(tagList)
|
||||
export(tagSetChildren)
|
||||
export(tags)
|
||||
export(textAreaInput)
|
||||
export(textInput)
|
||||
export(textOutput)
|
||||
export(throttle)
|
||||
export(titlePanel)
|
||||
export(uiOutput)
|
||||
export(updateActionButton)
|
||||
@@ -249,16 +224,15 @@ export(updateCheckboxGroupInput)
|
||||
export(updateCheckboxInput)
|
||||
export(updateDateInput)
|
||||
export(updateDateRangeInput)
|
||||
export(updateLocationBar)
|
||||
export(updateNavbarPage)
|
||||
export(updateNavlistPanel)
|
||||
export(updateNumericInput)
|
||||
export(updateQueryString)
|
||||
export(updateRadioButtons)
|
||||
export(updateSelectInput)
|
||||
export(updateSelectizeInput)
|
||||
export(updateSliderInput)
|
||||
export(updateTabsetPanel)
|
||||
export(updateTextAreaInput)
|
||||
export(updateTextInput)
|
||||
export(urlModal)
|
||||
export(validate)
|
||||
@@ -278,4 +252,3 @@ import(httpuv)
|
||||
import(methods)
|
||||
import(mime)
|
||||
import(xtable)
|
||||
importFrom(utils,write.csv)
|
||||
|
||||
57
R/app.R
57
R/app.R
@@ -20,29 +20,19 @@
|
||||
#' @param onStart A function that will be called before the app is actually run.
|
||||
#' This is only needed for \code{shinyAppObj}, since in the \code{shinyAppDir}
|
||||
#' case, a \code{global.R} file can be used for this purpose.
|
||||
#' @param options Named options that should be passed to the \code{runApp} call
|
||||
#' (these can be any of the following: "port", "launch.browser", "host", "quiet",
|
||||
#' "display.mode" and "test.mode"). You can also specify \code{width} and
|
||||
#' \code{height} parameters which provide a hint to the embedding environment
|
||||
#' about the ideal height/width for the app.
|
||||
#' @param options Named options that should be passed to the `runApp` call. You
|
||||
#' can also specify \code{width} and \code{height} parameters which provide a
|
||||
#' hint to the embedding environment about the ideal height/width for the app.
|
||||
#' @param uiPattern A regular expression that will be applied to each \code{GET}
|
||||
#' request to determine whether the \code{ui} should be used to handle the
|
||||
#' request. Note that the entire request path must match the regular
|
||||
#' expression in order for the match to be considered successful.
|
||||
#' @param enableBookmarking Can be one of \code{"url"}, \code{"server"}, or
|
||||
#' \code{"disable"}. This is equivalent to calling the
|
||||
#' \code{\link{enableBookmarking}()} function just before calling
|
||||
#' \code{shinyApp()}. With the default value (\code{NULL}), the app will
|
||||
#' respect the setting from any previous calls to \code{enableBookmarking()}.
|
||||
#' See \code{\link{enableBookmarking}} for more information.
|
||||
#' @return An object that represents the app. Printing the object or passing it
|
||||
#' to \code{\link{runApp}} will run the app.
|
||||
#'
|
||||
#' @examples
|
||||
#' ## Only run this example in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' options(device.ask.default = FALSE)
|
||||
#'
|
||||
#' shinyApp(
|
||||
#' ui = fluidPage(
|
||||
#' numericInput("n", "n", 1),
|
||||
@@ -69,9 +59,10 @@
|
||||
#'
|
||||
#' runApp(app)
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
shinyApp <- function(ui=NULL, server=NULL, onStart=NULL, options=list(),
|
||||
uiPattern="/", enableBookmarking = NULL) {
|
||||
uiPattern="/") {
|
||||
if (is.null(server)) {
|
||||
stop("`server` missing from shinyApp")
|
||||
}
|
||||
@@ -85,24 +76,12 @@ shinyApp <- function(ui=NULL, server=NULL, onStart=NULL, options=list(),
|
||||
server
|
||||
}
|
||||
|
||||
if (!is.null(enableBookmarking)) {
|
||||
bookmarkStore <- match.arg(enableBookmarking, c("url", "server", "disable"))
|
||||
enableBookmarking(bookmarkStore)
|
||||
}
|
||||
|
||||
# Store the appDir and bookmarking-related options, so that we can read them
|
||||
# from within the app.
|
||||
shinyOptions(appDir = getwd())
|
||||
appOptions <- consumeAppOptions()
|
||||
|
||||
structure(
|
||||
list(
|
||||
httpHandler = httpHandler,
|
||||
serverFuncSource = serverFuncSource,
|
||||
onStart = onStart,
|
||||
options = options,
|
||||
appOptions = appOptions
|
||||
),
|
||||
options = options),
|
||||
class = "shiny.appobj"
|
||||
)
|
||||
}
|
||||
@@ -120,6 +99,10 @@ shinyAppDir <- function(appDir, options=list()) {
|
||||
# affected by future changes to the path)
|
||||
appDir <- normalizePath(appDir, mustWork = TRUE)
|
||||
|
||||
# Store appDir in options so that we can find out where we are from within the
|
||||
# app.
|
||||
shinyOptions(appDir = appDir)
|
||||
|
||||
if (file.exists.ci(appDir, "server.R")) {
|
||||
shinyAppDir_serverR(appDir, options = options)
|
||||
} else if (file.exists.ci(appDir, "app.R")) {
|
||||
@@ -136,6 +119,9 @@ shinyAppFile <- function(appFile, options=list()) {
|
||||
appFile <- normalizePath(appFile, mustWork = TRUE)
|
||||
appDir <- dirname(appFile)
|
||||
|
||||
# Store appDir in options so that we can find out where we are
|
||||
shinyOptions(appDir = appDir)
|
||||
|
||||
shinyAppDir_appR(basename(appFile), appDir, options = options)
|
||||
}
|
||||
|
||||
@@ -201,8 +187,6 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
|
||||
}
|
||||
}
|
||||
|
||||
shinyOptions(appDir = appDir)
|
||||
|
||||
oldwd <- NULL
|
||||
monitorHandle <- NULL
|
||||
onStart <- function() {
|
||||
@@ -224,8 +208,7 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
|
||||
serverFuncSource = serverFuncSource,
|
||||
onStart = onStart,
|
||||
onEnd = onEnd,
|
||||
options = options
|
||||
),
|
||||
options = options),
|
||||
class = "shiny.appobj"
|
||||
)
|
||||
}
|
||||
@@ -235,13 +218,13 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
|
||||
# ignored when checking extensions. If any changes are detected, all connected
|
||||
# Shiny sessions are reloaded.
|
||||
#
|
||||
# Use options(shiny.autoreload = TRUE) to enable this behavior. Since monitoring
|
||||
# Use option(shiny.autoreload = TRUE) to enable this behavior. Since monitoring
|
||||
# for changes is expensive (we are polling for mtimes here, nothing fancy) this
|
||||
# feature is intended only for development.
|
||||
#
|
||||
# You can customize the file patterns Shiny will monitor by setting the
|
||||
# shiny.autoreload.pattern option. For example, to monitor only ui.R:
|
||||
# options(shiny.autoreload.pattern = glob2rx("ui.R"))
|
||||
# option(shiny.autoreload.pattern = glob2rx("ui.R"))
|
||||
#
|
||||
# The return value is a function that halts monitoring when called.
|
||||
initAutoReloadMonitor <- function(dir) {
|
||||
@@ -278,8 +261,7 @@ initAutoReloadMonitor <- function(dir) {
|
||||
|
||||
# This reads in an app dir for a single-file application (e.g. app.R), and
|
||||
# returns a shiny.appobj.
|
||||
shinyAppDir_appR <- function(fileName, appDir, options=list())
|
||||
{
|
||||
shinyAppDir_appR <- function(fileName, appDir, options=list()) {
|
||||
fullpath <- file.path.ci(appDir, fileName)
|
||||
|
||||
# This sources app.R and caches the content. When appObj() is called but
|
||||
@@ -292,8 +274,6 @@ shinyAppDir_appR <- function(fileName, appDir, options=list())
|
||||
if (!is.shiny.appobj(result))
|
||||
stop("app.R did not return a shiny.appobj object.")
|
||||
|
||||
unconsumeAppOptions(result$appOptions)
|
||||
|
||||
return(result)
|
||||
}
|
||||
)
|
||||
@@ -377,8 +357,7 @@ is.shiny.appobj <- function(x) {
|
||||
print.shiny.appobj <- function(x, ...) {
|
||||
opts <- x$options %OR% list()
|
||||
opts <- opts[names(opts) %in%
|
||||
c("port", "launch.browser", "host", "quiet",
|
||||
"display.mode", "test.mode")]
|
||||
c("port", "launch.browser", "host", "quiet", "display.mode")]
|
||||
|
||||
args <- c(list(x), opts)
|
||||
|
||||
|
||||
1097
R/bookmark-state.R
1097
R/bookmark-state.R
File diff suppressed because it is too large
Load Diff
@@ -277,7 +277,6 @@ titlePanel <- function(title, windowTitle=title) {
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' options(device.ask.default = FALSE)
|
||||
#'
|
||||
#' # Define UI
|
||||
#' ui <- fluidPage(
|
||||
@@ -419,6 +418,7 @@ flowLayout <- function(..., cellArgs = list()) {
|
||||
#' suitable for wrapping inputs.
|
||||
#'
|
||||
#' @param ... Input controls or other HTML elements.
|
||||
#'
|
||||
#' @export
|
||||
inputPanel <- function(...) {
|
||||
div(class = "shiny-input-panel",
|
||||
@@ -443,7 +443,6 @@ inputPanel <- function(...) {
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' options(device.ask.default = FALSE)
|
||||
#'
|
||||
#' # Server code used for all examples
|
||||
#' server <- function(input, output) {
|
||||
|
||||
@@ -25,6 +25,7 @@ NULL
|
||||
#' \code{\link{fluidPage}} function instead.
|
||||
#'
|
||||
#' @seealso \code{\link{fluidPage}}, \code{\link{fixedPage}}
|
||||
#'
|
||||
#' @export
|
||||
bootstrapPage <- function(..., title = NULL, responsive = NULL, theme = NULL) {
|
||||
|
||||
@@ -60,7 +61,7 @@ bootstrapPage <- function(..., title = NULL, responsive = NULL, theme = NULL) {
|
||||
#' @inheritParams bootstrapPage
|
||||
#' @export
|
||||
bootstrapLib <- function(theme = NULL) {
|
||||
htmlDependency("bootstrap", "3.3.7",
|
||||
htmlDependency("bootstrap", "3.3.6",
|
||||
c(
|
||||
href = "shared/bootstrap",
|
||||
file = system.file("www/shared/bootstrap", package = "shiny")
|
||||
@@ -152,6 +153,7 @@ basicPage <- function(...) {
|
||||
#' div(style = "background-color: blue; width: 100%; height: 100%;")
|
||||
#' )
|
||||
#' )
|
||||
#'
|
||||
#' @export
|
||||
fillPage <- function(..., padding = 0, title = NULL, bootstrap = TRUE,
|
||||
theme = NULL) {
|
||||
@@ -213,6 +215,7 @@ collapseSizes <- function(padding) {
|
||||
#' plotOutput("distPlot")
|
||||
#' )
|
||||
#' )
|
||||
#'
|
||||
#' @export
|
||||
pageWithSidebar <- function(headerPanel,
|
||||
sidebarPanel,
|
||||
@@ -335,25 +338,14 @@ navbarPage <- function(title,
|
||||
if (inverse)
|
||||
navbarClass <- paste(navbarClass, "navbar-inverse")
|
||||
|
||||
if (!is.null(id))
|
||||
selected <- restoreInput(id = id, default = selected)
|
||||
|
||||
# build the tabset
|
||||
tabs <- list(...)
|
||||
tabset <- buildTabset(tabs, "nav navbar-nav", NULL, id, selected)
|
||||
|
||||
# function to return plain or fluid class name
|
||||
className <- function(name) {
|
||||
if (fluid)
|
||||
paste(name, "-fluid", sep="")
|
||||
else
|
||||
name
|
||||
}
|
||||
|
||||
# built the container div dynamically to support optional collapsibility
|
||||
if (collapsible) {
|
||||
navId <- paste("navbar-collapse-", p_randomInt(1000, 10000), sep="")
|
||||
containerDiv <- div(class=className("container"),
|
||||
containerDiv <- div(class="container",
|
||||
div(class="navbar-header",
|
||||
tags$button(type="button", class="navbar-toggle collapsed",
|
||||
`data-toggle`="collapse", `data-target`=paste0("#", navId),
|
||||
@@ -367,7 +359,7 @@ navbarPage <- function(title,
|
||||
div(class="navbar-collapse collapse", id=navId, tabset$navList)
|
||||
)
|
||||
} else {
|
||||
containerDiv <- div(class=className("container"),
|
||||
containerDiv <- div(class="container",
|
||||
div(class="navbar-header",
|
||||
span(class="navbar-brand", pageTitle)
|
||||
),
|
||||
@@ -375,6 +367,14 @@ navbarPage <- function(title,
|
||||
)
|
||||
}
|
||||
|
||||
# function to return plain or fluid class name
|
||||
className <- function(name) {
|
||||
if (fluid)
|
||||
paste(name, "-fluid", sep="")
|
||||
else
|
||||
name
|
||||
}
|
||||
|
||||
# build the main tab content div
|
||||
contentDiv <- div(class=className("container"))
|
||||
if (!is.null(header))
|
||||
@@ -430,6 +430,7 @@ headerPanel <- function(title, windowTitle=title) {
|
||||
#'
|
||||
#' @param ... UI elements to include inside the panel.
|
||||
#' @return The newly created panel.
|
||||
#'
|
||||
#' @export
|
||||
wellPanel <- function(...) {
|
||||
div(class="well", ...)
|
||||
@@ -533,6 +534,7 @@ mainPanel <- function(..., width = 8) {
|
||||
#' )
|
||||
#' )
|
||||
#' )
|
||||
#'
|
||||
#' @export
|
||||
conditionalPanel <- function(condition, ...) {
|
||||
div('data-display-if'=condition, ...)
|
||||
@@ -633,13 +635,9 @@ tabsetPanel <- function(...,
|
||||
version = "0.10.2.2")
|
||||
}
|
||||
|
||||
if (!is.null(id))
|
||||
selected <- restoreInput(id = id, default = selected)
|
||||
|
||||
# build the tabset
|
||||
tabs <- list(...)
|
||||
type <- match.arg(type)
|
||||
|
||||
tabset <- buildTabset(tabs, paste0("nav nav-", type), NULL, id, selected)
|
||||
|
||||
# create the content
|
||||
@@ -702,9 +700,6 @@ navlistPanel <- function(...,
|
||||
tags$li(class="navbar-brand", text)
|
||||
}
|
||||
|
||||
if (!is.null(id))
|
||||
selected <- restoreInput(id = id, default = selected)
|
||||
|
||||
# build the tabset
|
||||
tabs <- list(...)
|
||||
tabset <- buildTabset(tabs,
|
||||
@@ -935,34 +930,21 @@ textOutput <- function(outputId, container = if (inline) span else div, inline =
|
||||
#' Render a reactive output variable as verbatim text within an
|
||||
#' application page. The text will be included within an HTML \code{pre} tag.
|
||||
#' @param outputId output variable to read the value from
|
||||
#' @param placeholder if the output is empty or \code{NULL}, should an empty
|
||||
#' rectangle be displayed to serve as a placeholder? (does not affect
|
||||
#' behavior when the the output in nonempty)
|
||||
#' @return A verbatim text output element that can be included in a panel
|
||||
#' @details Text is HTML-escaped prior to rendering. This element is often used
|
||||
#' with the \link{renderPrint} function to preserve fixed-width formatting
|
||||
#' of printed objects.
|
||||
#' with the \link{renderPrint} function to preserve fixed-width formatting
|
||||
#' of printed objects.
|
||||
#' @examples
|
||||
#' ## Only run this example in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' shinyApp(
|
||||
#' ui = basicPage(
|
||||
#' textInput("txt", "Enter the text to display below:"),
|
||||
#' verbatimTextOutput("default"),
|
||||
#' verbatimTextOutput("placeholder", placeholder = TRUE)
|
||||
#' ),
|
||||
#' server = function(input, output) {
|
||||
#' output$default <- renderText({ input$txt })
|
||||
#' output$placeholder <- renderText({ input$txt })
|
||||
#' }
|
||||
#' )
|
||||
#' }
|
||||
#' mainPanel(
|
||||
#' h4("Summary"),
|
||||
#' verbatimTextOutput("summary"),
|
||||
#'
|
||||
#' h4("Observations"),
|
||||
#' tableOutput("view")
|
||||
#' )
|
||||
#' @export
|
||||
verbatimTextOutput <- function(outputId, placeholder = FALSE) {
|
||||
pre(id = outputId,
|
||||
class = paste(c("shiny-text-output", if (!placeholder) "noplaceholder"),
|
||||
collapse = " ")
|
||||
)
|
||||
verbatimTextOutput <- function(outputId) {
|
||||
textOutput(outputId, container = pre)
|
||||
}
|
||||
|
||||
|
||||
@@ -1136,7 +1118,7 @@ imageOutput <- function(outputId, width = "100%", height="400px",
|
||||
#' same \code{id} to disappear.
|
||||
#' @inheritParams textOutput
|
||||
#' @note The arguments \code{clickId} and \code{hoverId} only work for R base
|
||||
#' graphics (see the \pkg{\link[graphics:graphics-package]{graphics}} package). They do not work for
|
||||
#' graphics (see the \pkg{\link{graphics}} package). They do not work for
|
||||
#' \pkg{\link[grid:grid-package]{grid}}-based graphics, such as \pkg{ggplot2},
|
||||
#' \pkg{lattice}, and so on.
|
||||
#'
|
||||
@@ -1434,7 +1416,6 @@ uiOutput <- htmlOutput
|
||||
#' is assigned to.
|
||||
#' @param label The label that should appear on the button.
|
||||
#' @param class Additional CSS classes to apply to the tag, if any.
|
||||
#' @param ... Other arguments to pass to the container tag function.
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
@@ -1457,25 +1438,23 @@ uiOutput <- htmlOutput
|
||||
#' @export
|
||||
downloadButton <- function(outputId,
|
||||
label="Download",
|
||||
class=NULL, ...) {
|
||||
class=NULL) {
|
||||
aTag <- tags$a(id=outputId,
|
||||
class=paste('btn btn-default shiny-download-link', class),
|
||||
href='',
|
||||
target='_blank',
|
||||
download=NA,
|
||||
icon("download"),
|
||||
label, ...)
|
||||
label)
|
||||
}
|
||||
|
||||
#' @rdname downloadButton
|
||||
#' @export
|
||||
downloadLink <- function(outputId, label="Download", class=NULL, ...) {
|
||||
downloadLink <- function(outputId, label="Download", class=NULL) {
|
||||
tags$a(id=outputId,
|
||||
class=paste(c('shiny-download-link', class), collapse=" "),
|
||||
href='',
|
||||
target='_blank',
|
||||
download=NA,
|
||||
label, ...)
|
||||
label)
|
||||
}
|
||||
|
||||
|
||||
@@ -1516,6 +1495,7 @@ downloadLink <- function(outputId, label="Download", class=NULL, ...) {
|
||||
#' tabPanel("Summary", icon = icon("list-alt")),
|
||||
#' tabPanel("Table", icon = icon("table"))
|
||||
#' )
|
||||
#'
|
||||
#' @export
|
||||
icon <- function(name, class = NULL, lib = "font-awesome") {
|
||||
prefixes <- list(
|
||||
@@ -1543,7 +1523,7 @@ icon <- function(name, class = NULL, lib = "font-awesome") {
|
||||
# font-awesome needs an additional dependency (glyphicon is in bootstrap)
|
||||
if (lib == "font-awesome") {
|
||||
htmlDependencies(iconTag) <- htmlDependency(
|
||||
"font-awesome", "4.7.0", c(href="shared/font-awesome"),
|
||||
"font-awesome", "4.5.0", c(href="shared/font-awesome"),
|
||||
stylesheet = "css/font-awesome.min.css"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ getCallNames <- function(calls) {
|
||||
}
|
||||
|
||||
getLocs <- function(calls) {
|
||||
vapply(calls, function(call) {
|
||||
sapply(calls, function(call) {
|
||||
srcref <- attr(call, "srcref", exact = TRUE)
|
||||
if (!is.null(srcref)) {
|
||||
srcfile <- attr(srcref, "srcfile", exact = TRUE)
|
||||
@@ -86,7 +86,7 @@ getLocs <- function(calls) {
|
||||
}
|
||||
}
|
||||
return("")
|
||||
}, character(1))
|
||||
})
|
||||
}
|
||||
|
||||
#' @details \code{captureStackTraces} runs the given \code{expr} and if any
|
||||
|
||||
@@ -94,7 +94,7 @@ FileUploadContext <- R6Class(
|
||||
},
|
||||
createUploadOperation = function(fileInfos) {
|
||||
while (TRUE) {
|
||||
id <- createUniqueId(12)
|
||||
id <- paste(as.raw(p_runif(12, min=0, max=0xFF)), collapse='')
|
||||
private$ids <- c(private$ids, id)
|
||||
dir <- file.path(private$basedir, id)
|
||||
if (!dir.create(dir))
|
||||
|
||||
@@ -43,6 +43,7 @@ writeReactLog <- function(file=stdout(), sessionToken = NULL) {
|
||||
#'
|
||||
#' @param time A boolean that specifies whether or not to display the
|
||||
#' time that each reactive.
|
||||
#'
|
||||
#' @export
|
||||
showReactLog <- function(time = TRUE) {
|
||||
utils::browseURL(renderReactLog(time = as.logical(time)))
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#' URL.
|
||||
#'
|
||||
#' @param dependency A single HTML dependency object, created using
|
||||
#' \code{\link[htmltools]{htmlDependency}}. If the \code{src} value is named, then
|
||||
#' \code{\link{htmlDependency}}. If the \code{src} value is named, then
|
||||
#' \code{href} and/or \code{file} names must be present.
|
||||
#'
|
||||
#' @return A single HTML dependency object that has an \code{href}-named element
|
||||
|
||||
@@ -21,10 +21,11 @@
|
||||
#' @param width Width in pixels.
|
||||
#' @param height Height in pixels.
|
||||
#' @param res Resolution in pixels per inch. This value is passed to
|
||||
#' \code{\link[grDevices]{png}}. Note that this affects the resolution of PNG rendering in
|
||||
#' \code{\link{png}}. Note that this affects the resolution of PNG rendering in
|
||||
#' R; it won't change the actual ppi of the browser.
|
||||
#' @param ... Arguments to be passed through to \code{\link[grDevices]{png}}.
|
||||
#' These can be used to set the width, height, background color, etc.
|
||||
#'
|
||||
#' @export
|
||||
plotPNG <- function(func, filename=tempfile(fileext='.png'),
|
||||
width=400, height=400, res=72, ...) {
|
||||
|
||||
@@ -37,16 +37,13 @@
|
||||
#' }
|
||||
#'
|
||||
#' @seealso \code{\link{observeEvent}} and \code{\link{eventReactive}}
|
||||
#'
|
||||
#' @export
|
||||
actionButton <- function(inputId, label, icon = NULL, width = NULL, ...) {
|
||||
|
||||
value <- restoreInput(id = inputId, default = NULL)
|
||||
|
||||
tags$button(id=inputId,
|
||||
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
|
||||
type="button",
|
||||
class="btn btn-default action-button",
|
||||
`data-val` = value,
|
||||
list(validateIcon(icon), label),
|
||||
...
|
||||
)
|
||||
@@ -55,12 +52,9 @@ actionButton <- function(inputId, label, icon = NULL, width = NULL, ...) {
|
||||
#' @rdname actionButton
|
||||
#' @export
|
||||
actionLink <- function(inputId, label, icon = NULL, ...) {
|
||||
value <- restoreInput(id = inputId, default = NULL)
|
||||
|
||||
tags$a(id=inputId,
|
||||
href="#",
|
||||
class="action-button",
|
||||
`data-val` = value,
|
||||
list(validateIcon(icon), label),
|
||||
...
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#' \item \code{yy} Year without century (12)
|
||||
#' \item \code{yyyy} Year with century (2012)
|
||||
#' \item \code{mm} Month number, with leading zero (01-12)
|
||||
#' \item \code{m} Month number, without leading zero (1-12)
|
||||
#' \item \code{m} Month number, without leading zero (01-12)
|
||||
#' \item \code{M} Abbreviated month name
|
||||
#' \item \code{MM} Full month name
|
||||
#' \item \code{dd} Day of month with leading zero
|
||||
@@ -21,26 +21,23 @@
|
||||
#'
|
||||
#' @inheritParams textInput
|
||||
#' @param value The starting date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format. If NULL (the default), will use the current date
|
||||
#' in the client's time zone.
|
||||
#' \code{yyyy-mm-dd} format. If NULL (the default), will use the current
|
||||
#' date in the client's time zone.
|
||||
#' @param min The minimum allowed date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format.
|
||||
#' @param max The maximum allowed date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format.
|
||||
#' @param format The format of the date to display in the browser. Defaults to
|
||||
#' \code{"yyyy-mm-dd"}.
|
||||
#' @param startview The date range shown when the input object is first clicked.
|
||||
#' Can be "month" (the default), "year", or "decade".
|
||||
#' @param startview The date range shown when the input object is first
|
||||
#' clicked. Can be "month" (the default), "year", or "decade".
|
||||
#' @param weekstart Which day is the start of the week. Should be an integer
|
||||
#' from 0 (Sunday) to 6 (Saturday).
|
||||
#' @param language The language used for month and day names. Default is "en".
|
||||
#' Other valid values include "ar", "az", "bg", "bs", "ca", "cs", "cy", "da",
|
||||
#' "de", "el", "en-AU", "en-GB", "eo", "es", "et", "eu", "fa", "fi", "fo",
|
||||
#' "fr-CH", "fr", "gl", "he", "hr", "hu", "hy", "id", "is", "it-CH", "it",
|
||||
#' "ja", "ka", "kh", "kk", "ko", "kr", "lt", "lv", "me", "mk", "mn", "ms",
|
||||
#' "nb", "nl-BE", "nl", "no", "pl", "pt-BR", "pt", "ro", "rs-latin", "rs",
|
||||
#' "ru", "sk", "sl", "sq", "sr-latin", "sr", "sv", "sw", "th", "tr", "uk",
|
||||
#' "vi", "zh-CN", and "zh-TW".
|
||||
#' Other valid values include "bg", "ca", "cs", "da", "de", "el", "es", "fi",
|
||||
#' "fr", "he", "hr", "hu", "id", "is", "it", "ja", "kr", "lt", "lv", "ms",
|
||||
#' "nb", "nl", "pl", "pt", "pt-BR", "ro", "rs", "rs-latin", "ru", "sk", "sl",
|
||||
#' "sv", "sw", "th", "tr", "uk", "zh-CN", and "zh-TW".
|
||||
#'
|
||||
#' @family input elements
|
||||
#' @seealso \code{\link{dateRangeInput}}, \code{\link{updateDateInput}}
|
||||
@@ -63,7 +60,7 @@
|
||||
#'
|
||||
#' # Use different language and different first day of week
|
||||
#' dateInput("date5", "Date:",
|
||||
#' language = "ru",
|
||||
#' language = "de",
|
||||
#' weekstart = 1),
|
||||
#'
|
||||
#' # Start with decade view instead of default month view
|
||||
@@ -86,34 +83,29 @@ dateInput <- function(inputId, label, value = NULL, min = NULL, max = NULL,
|
||||
|
||||
value <- restoreInput(id = inputId, default = value)
|
||||
|
||||
tags$div(id = inputId,
|
||||
class = "shiny-date-input form-group shiny-input-container",
|
||||
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
|
||||
attachDependencies(
|
||||
tags$div(id = inputId,
|
||||
class = "shiny-date-input form-group shiny-input-container",
|
||||
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
|
||||
|
||||
controlLabel(inputId, label),
|
||||
tags$input(type = "text",
|
||||
class = "form-control",
|
||||
`data-date-language` = language,
|
||||
`data-date-week-start` = weekstart,
|
||||
`data-date-format` = format,
|
||||
`data-date-start-view` = startview,
|
||||
`data-min-date` = min,
|
||||
`data-max-date` = max,
|
||||
`data-initial-date` = value
|
||||
controlLabel(inputId, label),
|
||||
tags$input(type = "text",
|
||||
# datepicker class necessary for dropdown to display correctly
|
||||
class = "form-control datepicker",
|
||||
`data-date-language` = language,
|
||||
`data-date-weekstart` = weekstart,
|
||||
`data-date-format` = format,
|
||||
`data-date-start-view` = startview,
|
||||
`data-min-date` = min,
|
||||
`data-max-date` = max,
|
||||
`data-initial-date` = value
|
||||
)
|
||||
),
|
||||
datePickerDependency
|
||||
)
|
||||
}
|
||||
|
||||
datePickerDependency <- htmlDependency(
|
||||
"bootstrap-datepicker", "1.6.4", c(href = "shared/datepicker"),
|
||||
"bootstrap-datepicker", "1.0.2", c(href = "shared/datepicker"),
|
||||
script = "js/bootstrap-datepicker.min.js",
|
||||
stylesheet = "css/bootstrap-datepicker3.min.css",
|
||||
# Need to enable noConflict mode. See #1346.
|
||||
head = "<script>
|
||||
(function() {
|
||||
var datepicker = $.fn.datepicker.noConflict();
|
||||
$.fn.bsDatepicker = datepicker;
|
||||
})();
|
||||
</script>"
|
||||
)
|
||||
stylesheet = "css/datepicker.css")
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#' \item \code{yy} Year without century (12)
|
||||
#' \item \code{yyyy} Year with century (2012)
|
||||
#' \item \code{mm} Month number, with leading zero (01-12)
|
||||
#' \item \code{m} Month number, without leading zero (1-12)
|
||||
#' \item \code{m} Month number, without leading zero (01-12)
|
||||
#' \item \code{M} Abbreviated month name
|
||||
#' \item \code{MM} Full month name
|
||||
#' \item \code{dd} Day of month with leading zero
|
||||
|
||||
@@ -15,12 +15,7 @@
|
||||
#'
|
||||
#' @inheritParams textInput
|
||||
#' @param choices List of values to select from. If elements of the list are
|
||||
#' named, then that name rather than the value is displayed to the user.
|
||||
#' This can also be a named list whose elements are (either named or
|
||||
#' unnamed) lists or vectors. If this is the case, the outermost names
|
||||
#' will be used as the "optgroup" label for the elements in the respective
|
||||
#' sublist. This allows you to group and label similar choices. See the
|
||||
#' example section for a small demo of this feature.
|
||||
#' named then that name rather than the value is displayed to the user.
|
||||
#' @param selected The initially selected value (or multiple values if
|
||||
#' \code{multiple = TRUE}). If not specified then defaults to the first value
|
||||
#' for single-select lists and no values for multiple select lists.
|
||||
@@ -39,38 +34,21 @@
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#'
|
||||
#' # basic example
|
||||
#' shinyApp(
|
||||
#' ui = fluidPage(
|
||||
#' selectInput("variable", "Variable:",
|
||||
#' c("Cylinders" = "cyl",
|
||||
#' "Transmission" = "am",
|
||||
#' "Gears" = "gear")),
|
||||
#' tableOutput("data")
|
||||
#' ),
|
||||
#' server = function(input, output) {
|
||||
#' output$data <- renderTable({
|
||||
#' mtcars[, c("mpg", input$variable), drop = FALSE]
|
||||
#' }, rownames = TRUE)
|
||||
#' }
|
||||
#' ui <- fluidPage(
|
||||
#' selectInput("variable", "Variable:",
|
||||
#' c("Cylinders" = "cyl",
|
||||
#' "Transmission" = "am",
|
||||
#' "Gears" = "gear")),
|
||||
#' tableOutput("data")
|
||||
#' )
|
||||
#'
|
||||
#' # demoing optgroup support in the `choices` arg
|
||||
#' shinyApp(
|
||||
#' ui = fluidPage(
|
||||
#' selectInput("state", "Choose a state:",
|
||||
#' list(`East Coast` = c("NY", "NJ", "CT"),
|
||||
#' `West Coast` = c("WA", "OR", "CA"),
|
||||
#' `Midwest` = c("MN", "WI", "IA"))
|
||||
#' ),
|
||||
#' textOutput("result")
|
||||
#' ),
|
||||
#' server = function(input, output) {
|
||||
#' output$result <- renderText({
|
||||
#' paste("You chose", input$state)
|
||||
#' })
|
||||
#' }
|
||||
#' )
|
||||
#' server <- function(input, output) {
|
||||
#' output$data <- renderTable({
|
||||
#' mtcars[, c("mpg", input$variable), drop = FALSE]
|
||||
#' }, rownames = TRUE)
|
||||
#' }
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#' @export
|
||||
selectInput <- function(inputId, label, choices, selected = NULL,
|
||||
@@ -155,7 +133,7 @@ needOptgroup <- function(choices) {
|
||||
#' @rdname selectInput
|
||||
#' @param ... Arguments passed to \code{selectInput()}.
|
||||
#' @param options A list of options. See the documentation of \pkg{selectize.js}
|
||||
#' for possible options (character option values inside \code{\link[base]{I}()} will
|
||||
#' for possible options (character option values inside \code{\link{I}()} will
|
||||
#' be treated as literal JavaScript code; see \code{\link{renderDataTable}()}
|
||||
#' for details).
|
||||
#' @param width The width of the input, e.g. \code{'400px'}, or \code{'100\%'};
|
||||
@@ -194,7 +172,7 @@ selectizeIt <- function(inputId, select, options, nonempty = FALSE) {
|
||||
|
||||
if ('drag_drop' %in% options$plugins) {
|
||||
selectizeDep <- list(selectizeDep, htmlDependency(
|
||||
'jqueryui', '1.12.1', c(href = 'shared/jqueryui'),
|
||||
'jqueryui', '1.11.4', c(href = 'shared/jqueryui'),
|
||||
script = 'jquery-ui.min.js'
|
||||
))
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
#' format string, to be passed to the Javascript strftime library. See
|
||||
#' \url{https://github.com/samsonjs/strftime} for more details. The allowed
|
||||
#' format specifications are very similar, but not identical, to those for R's
|
||||
#' \code{\link[base]{strftime}} function. For Dates, the default is \code{"\%F"}
|
||||
#' \code{\link{strftime}} function. For Dates, the default is \code{"\%F"}
|
||||
#' (like \code{"2015-07-01"}), and for POSIXt, the default is \code{"\%F \%T"}
|
||||
#' (like \code{"2015-07-01 15:32:10"}).
|
||||
#' @param timezone Only used if the values are POSIXt objects. A string
|
||||
@@ -51,7 +51,6 @@
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' options(device.ask.default = FALSE)
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' sliderInput("obs", "Number of observations:",
|
||||
@@ -164,6 +163,7 @@ sliderInput <- function(inputId, label, min, max, value, step = NULL,
|
||||
`data-grid` = ticks,
|
||||
`data-grid-num` = n_ticks,
|
||||
`data-grid-snap` = FALSE,
|
||||
`data-prettify-separator` = sep,
|
||||
`data-prefix` = pre,
|
||||
`data-postfix` = post,
|
||||
`data-keyboard` = TRUE,
|
||||
@@ -175,12 +175,6 @@ sliderInput <- function(inputId, label, min, max, value, step = NULL,
|
||||
`data-timezone` = timezone
|
||||
))
|
||||
|
||||
if (sep == "") {
|
||||
sliderProps$`data-prettify-enabled` <- "0"
|
||||
} else {
|
||||
sliderProps$`data-prettify-separator` <- sep
|
||||
}
|
||||
|
||||
# Replace any TRUE and FALSE with "true" and "false"
|
||||
sliderProps <- lapply(sliderProps, function(x) {
|
||||
if (identical(x, TRUE)) "true"
|
||||
@@ -250,6 +244,7 @@ hasDecimals <- function(value) {
|
||||
#' or list of tags (using \code{\link{tag}} and friends), or raw HTML (using
|
||||
#' \code{\link{HTML}}).
|
||||
#' @param pauseButton Similar to \code{playButton}, but for the pause button.
|
||||
#'
|
||||
#' @export
|
||||
animationOptions <- function(interval=1000,
|
||||
loop=FALSE,
|
||||
|
||||
@@ -1,27 +1,8 @@
|
||||
#' Create a submit button
|
||||
#'
|
||||
#' Create a submit button for an app. Apps that include a submit
|
||||
#' Create a submit button for an input form. Forms that include a submit
|
||||
#' button do not automatically update their outputs when inputs change,
|
||||
#' rather they wait until the user explicitly clicks the submit button.
|
||||
#' The use of \code{submitButton} is generally discouraged in favor of
|
||||
#' the more versatile \code{\link{actionButton}} (see details below).
|
||||
#'
|
||||
#' Submit buttons are unusual Shiny inputs, and we recommend using
|
||||
#' \code{\link{actionButton}} instead of \code{submitButton} when you
|
||||
#' want to delay a reaction.
|
||||
#' See \href{http://shiny.rstudio.com/articles/action-buttons.html}{this
|
||||
#' article} for more information (including a demo of how to "translate"
|
||||
#' code using a \code{submitButton} to code using an \code{actionButton}).
|
||||
#'
|
||||
#' In essence, the presence of a submit button stops all inputs from
|
||||
#' sending their values automatically to the server. This means, for
|
||||
#' instance, that if there are \emph{two} submit buttons in the same app,
|
||||
#' clicking either one will cause all inputs in the app to send their
|
||||
#' values to the server. This is probably not what you'd want, which is
|
||||
#' why submit button are unwieldy for all but the simplest apps. There
|
||||
#' are other problems with submit buttons: for example, dynamically
|
||||
#' created submit buttons (for example, with \code{\link{renderUI}}
|
||||
#' or \code{\link{insertUI}}) will not work.
|
||||
#'
|
||||
#' @param text Button caption
|
||||
#' @param icon Optional \code{\link{icon}} to appear on the button
|
||||
@@ -32,26 +13,8 @@
|
||||
#' @family input elements
|
||||
#'
|
||||
#' @examples
|
||||
#' if (interactive()) {
|
||||
#'
|
||||
#' shinyApp(
|
||||
#' ui = basicPage(
|
||||
#' numericInput("num", label = "Make changes", value = 1),
|
||||
#' submitButton("Update View", icon("refresh")),
|
||||
#' helpText("When you click the button above, you should see",
|
||||
#' "the output below update to reflect the value you",
|
||||
#' "entered at the top:"),
|
||||
#' verbatimTextOutput("value")
|
||||
#' ),
|
||||
#' server = function(input, output) {
|
||||
#'
|
||||
#' # submit buttons do not have a value of their own,
|
||||
#' # they control when the app accesses values of other widgets.
|
||||
#' # input$num is the value of the number widget.
|
||||
#' output$value <- renderPrint({ input$num })
|
||||
#' }
|
||||
#' )
|
||||
#' }
|
||||
#' submitButton("Update View")
|
||||
#' submitButton("Update View", icon("refresh"))
|
||||
#' @export
|
||||
submitButton <- function(text = "Apply Changes", icon = NULL, width = NULL) {
|
||||
div(
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
#' Create a textarea input control
|
||||
#'
|
||||
#' Create a textarea input control for entry of unstructured text values.
|
||||
#'
|
||||
#' @inheritParams textInput
|
||||
#' @param height The height of the input, e.g. \code{'400px'}, or
|
||||
#' \code{'100\%'}; see \code{\link{validateCssUnit}}.
|
||||
#' @param cols Value of the visible character columns of the input, e.g.
|
||||
#' \code{80}. If used with \code{width}, \code{width} will take precedence in
|
||||
#' the browser's rendering.
|
||||
#' @param rows The value of the visible character rows of the input, e.g.
|
||||
#' \code{6}. If used with \code{height}, \code{height} will take precedence in
|
||||
#' the browser's rendering.
|
||||
#' @param resize Which directions the textarea box can be resized. Can be one of
|
||||
#' \code{"both"}, \code{"none"}, \code{"vertical"}, and \code{"horizontal"}.
|
||||
#' The default, \code{NULL}, will use the client browser's default setting for
|
||||
#' resizing textareas.
|
||||
#' @return A textarea input control that can be added to a UI definition.
|
||||
#'
|
||||
#' @family input elements
|
||||
#' @seealso \code{\link{updateTextAreaInput}}
|
||||
#'
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' textAreaInput("caption", "Caption", "Data Summary", width = "1000px"),
|
||||
#' verbatimTextOutput("value")
|
||||
#' )
|
||||
#' server <- function(input, output) {
|
||||
#' output$value <- renderText({ input$caption })
|
||||
#' }
|
||||
#' shinyApp(ui, server)
|
||||
#'
|
||||
#' }
|
||||
#' @export
|
||||
textAreaInput <- function(inputId, label, value = "", width = NULL, height = NULL,
|
||||
cols = NULL, rows = NULL, placeholder = NULL, resize = NULL) {
|
||||
|
||||
value <- restoreInput(id = inputId, default = value)
|
||||
|
||||
if (!is.null(resize)) {
|
||||
resize <- match.arg(resize, c("both", "none", "vertical", "horizontal"))
|
||||
}
|
||||
|
||||
style <- paste(
|
||||
if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
|
||||
if (!is.null(height)) paste0("height: ", validateCssUnit(height), ";"),
|
||||
if (!is.null(resize)) paste0("resize: ", resize, ";")
|
||||
)
|
||||
|
||||
# Workaround for tag attribute=character(0) bug:
|
||||
# https://github.com/rstudio/htmltools/issues/65
|
||||
if (length(style) == 0) style <- NULL
|
||||
|
||||
div(class = "form-group shiny-input-container",
|
||||
label %AND% tags$label(label, `for` = inputId),
|
||||
tags$textarea(
|
||||
id = inputId,
|
||||
class = "form-control",
|
||||
placeholder = placeholder,
|
||||
style = style,
|
||||
rows = rows,
|
||||
cols = cols,
|
||||
value
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -4,13 +4,10 @@ controlLabel <- function(controlName, label) {
|
||||
|
||||
|
||||
# Before shiny 0.9, `selected` refers to names/labels of `choices`; now it
|
||||
# refers to values. Below is a function for backward compatibility. It also
|
||||
# coerces the value to `character`.
|
||||
# refers to values. Below is a function for backward compatibility.
|
||||
validateSelected <- function(selected, choices, inputId) {
|
||||
# this line accomplishes two tings:
|
||||
# - coerces selected to character
|
||||
# - drops name, otherwise toJSON() keeps it too
|
||||
selected <- as.character(selected)
|
||||
# drop names, otherwise toJSON() keeps them too
|
||||
selected <- unname(selected)
|
||||
# if you are using optgroups, you're using shiny > 0.10.0, and you should
|
||||
# already know that `selected` must be a value instead of a label
|
||||
if (needOptgroup(choices)) return(selected)
|
||||
@@ -66,7 +63,7 @@ generateOptions <- function(inputId, choices, selected, inline, type = 'checkbox
|
||||
|
||||
|
||||
# Takes a vector or list, and adds names (same as the value) to any entries
|
||||
# without names. Coerces all leaf nodes to `character`.
|
||||
# without names.
|
||||
choicesWithNames <- function(choices) {
|
||||
# Take a vector or list, and convert to list. Also, if any children are
|
||||
# vectors with length > 1, convert those to list. If the list is unnamed,
|
||||
@@ -82,7 +79,7 @@ choicesWithNames <- function(choices) {
|
||||
if (is.list(val))
|
||||
listify(val)
|
||||
else if (length(val) == 1 && is.null(names(val)))
|
||||
as.character(val)
|
||||
val
|
||||
else
|
||||
makeNamed(as.list(val))
|
||||
})
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
#' will determine the element(s) relative to which you want to insert your
|
||||
#' UI object.
|
||||
#'
|
||||
#' @param multiple In case your selector matches more than one element,
|
||||
#' \code{multiple} determines whether Shiny should insert the UI object
|
||||
#' relative to all matched elements or just relative to the first
|
||||
#' matched element (default).
|
||||
#'
|
||||
#' @param where Where your UI object should go relative to the selector:
|
||||
#' \describe{
|
||||
#' \item{\code{beforeBegin}}{Before the selector element itself}
|
||||
@@ -37,11 +42,6 @@
|
||||
#' reference or remove it later on). If you want to insert raw html, use
|
||||
#' \code{ui = HTML()}.
|
||||
#'
|
||||
#' @param multiple In case your selector matches more than one element,
|
||||
#' \code{multiple} determines whether Shiny should insert the UI object
|
||||
#' relative to all matched elements or just relative to the first
|
||||
#' matched element (default).
|
||||
#'
|
||||
#' @param immediate Whether the UI object should be immediately inserted into
|
||||
#' the app when you call \code{insertUI}, or whether Shiny should wait until
|
||||
#' all outputs have been updated and all observers have been run (default).
|
||||
@@ -73,11 +73,12 @@
|
||||
#' # Complete app with UI and server components
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
insertUI <- function(selector,
|
||||
multiple = FALSE,
|
||||
where = c("beforeBegin", "afterBegin", "beforeEnd", "afterEnd"),
|
||||
ui,
|
||||
multiple = FALSE,
|
||||
immediate = FALSE,
|
||||
session = getDefaultReactiveDomain()) {
|
||||
|
||||
@@ -154,6 +155,7 @@ insertUI <- function(selector,
|
||||
#' # Complete app with UI and server components
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
removeUI <- function(selector,
|
||||
multiple = FALSE,
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#' over text). The default is \code{"auto"}, which is equivalent to
|
||||
#' \code{ifelse(draggable, "move", "inherit")}.
|
||||
#' @return An HTML element or list of elements.
|
||||
#'
|
||||
#' @export
|
||||
absolutePanel <- function(...,
|
||||
top = NULL, left = NULL, right = NULL, bottom = NULL,
|
||||
@@ -79,6 +80,8 @@ absolutePanel <- function(...,
|
||||
if (isTRUE(draggable)) {
|
||||
divTag <- tagAppendAttributes(divTag, class='draggable')
|
||||
return(tagList(
|
||||
# IMPORTANT NOTE: If you update jqueryui, make sure you DON'T include the datepicker,
|
||||
# as it collides with our bootstrap datepicker!
|
||||
singleton(tags$head(tags$script(src='shared/jqueryui/jquery-ui.min.js'))),
|
||||
divTag,
|
||||
tags$script('$(".draggable").draggable();')
|
||||
|
||||
@@ -41,229 +41,3 @@ sessionHandler <- function(req) {
|
||||
shinysession$handleRequest(subreq)
|
||||
})
|
||||
}
|
||||
|
||||
apiHandler <- function(serverFuncSource) {
|
||||
function(req) {
|
||||
path <- req$PATH_INFO
|
||||
if (is.null(path))
|
||||
return(NULL)
|
||||
|
||||
matches <- regmatches(path, regexec('^/api/(.*)$', path))
|
||||
if (length(matches[[1]]) == 0)
|
||||
return(NULL)
|
||||
|
||||
apiName <- matches[[1]][2]
|
||||
|
||||
sharedSecret <- getOption('shiny.sharedSecret')
|
||||
if (!is.null(sharedSecret)
|
||||
&& !identical(sharedSecret, req$HTTP_SHINY_SHARED_SECRET)) {
|
||||
stop("Incorrect shared secret")
|
||||
}
|
||||
|
||||
if (!is.null(getOption("shiny.observer.error", NULL))) {
|
||||
warning(
|
||||
call. = FALSE,
|
||||
"options(shiny.observer.error) is no longer supported; please unset it!"
|
||||
)
|
||||
stopApp()
|
||||
}
|
||||
|
||||
# need to give a fake websocket to the session
|
||||
ws <- list(
|
||||
request = req,
|
||||
sendMessage = function(...) {
|
||||
#print(list(...))
|
||||
}
|
||||
)
|
||||
|
||||
# Accept JSON query string and/or JSON body as input values
|
||||
inputVals <- c(
|
||||
parseQueryStringJSON(req$QUERY_STRING),
|
||||
parseJSONBody(req)
|
||||
)
|
||||
|
||||
shinysession <- ShinySession$new(ws)
|
||||
on.exit({
|
||||
try({
|
||||
# Clean up the session. Very important, so that observers
|
||||
# and such don't hang around, and to let memory get gc'd.
|
||||
shinysession$wsClosed()
|
||||
appsByToken$remove(shinysession$token)
|
||||
})
|
||||
}, add = TRUE)
|
||||
appsByToken$set(shinysession$token, shinysession)
|
||||
shinysession$setShowcase(.globals$showcaseDefault)
|
||||
|
||||
serverFunc <- withReactiveDomain(NULL, serverFuncSource())
|
||||
|
||||
tryCatch({
|
||||
withReactiveDomain(shinysession, {
|
||||
shinysession$manageInputs(inputVals)
|
||||
do.call(serverFunc, argsForServerFunc(serverFunc, shinysession))
|
||||
result <- NULL
|
||||
shinysession$enableApi(apiName, function(value) {
|
||||
result <<- try(withLogErrors(value), silent = TRUE)
|
||||
})
|
||||
flushReact()
|
||||
resultToResponse(result)
|
||||
})
|
||||
}, error = function(e) {
|
||||
return(httpResponse(
|
||||
status=500,
|
||||
content=htmlEscape(conditionMessage(e))
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
apiWsHandler <- function(serverFuncSource) {
|
||||
function(ws) {
|
||||
path <- ws$request$PATH_INFO
|
||||
if (is.null(path))
|
||||
return(NULL)
|
||||
|
||||
matches <- regmatches(path, regexec('^/api/(.*)$', path))
|
||||
if (length(matches[[1]]) == 0)
|
||||
return(NULL)
|
||||
|
||||
apiName <- matches[[1]][2]
|
||||
|
||||
sharedSecret <- getOption('shiny.sharedSecret')
|
||||
if (!is.null(sharedSecret)
|
||||
&& !identical(sharedSecret, ws$request$HTTP_SHINY_SHARED_SECRET)) {
|
||||
ws$close()
|
||||
return(TRUE)
|
||||
}
|
||||
|
||||
if (!is.null(getOption("shiny.observer.error", NULL))) {
|
||||
warning(
|
||||
call. = FALSE,
|
||||
"options(shiny.observer.error) is no longer supported; please unset it!"
|
||||
)
|
||||
stopApp()
|
||||
}
|
||||
|
||||
inputVals <- parseQueryStringJSON(ws$request$QUERY_STRING)
|
||||
|
||||
# Give a fake websocket to suppress messages from session
|
||||
shinysession <- ShinySession$new(list(
|
||||
request = ws$request,
|
||||
sendMessage = function(...) {
|
||||
#print(list(...))
|
||||
}
|
||||
))
|
||||
appsByToken$set(shinysession$token, shinysession)
|
||||
shinysession$setShowcase(.globals$showcaseDefault)
|
||||
|
||||
serverFunc <- withReactiveDomain(NULL, serverFuncSource())
|
||||
|
||||
tryCatch({
|
||||
withReactiveDomain(shinysession, {
|
||||
shinysession$manageInputs(inputVals)
|
||||
do.call(serverFunc, argsForServerFunc(serverFunc, shinysession))
|
||||
shinysession$enableApi(apiName, function(value) {
|
||||
resp <- resultToResponse(value)
|
||||
if (resp$status != 200L) {
|
||||
warning("Error: ", responseToContent(resp))
|
||||
ws$close()
|
||||
} else {
|
||||
content <- responseToContent(resp)
|
||||
if (grepl("^image/", resp$content_type)) {
|
||||
content <- paste0("data:", resp$content_type, ";base64,",
|
||||
httpuv::rawToBase64(content))
|
||||
}
|
||||
try(ws$send(content), silent=TRUE)
|
||||
}
|
||||
})
|
||||
flushReact()
|
||||
})
|
||||
}, error = function(e) {
|
||||
ws$close()
|
||||
})
|
||||
|
||||
ws$onClose(function() {
|
||||
# Clean up the session. Very important, so that observers
|
||||
# and such don't hang around, and to let memory get gc'd.
|
||||
shinysession$wsClosed()
|
||||
appsByToken$remove(shinysession$token)
|
||||
})
|
||||
|
||||
# TODO: What to do on ws$onMessage?
|
||||
}
|
||||
}
|
||||
|
||||
parseJSONBody <- function(req) {
|
||||
if (identical(req[["REQUEST_METHOD"]], "POST")) {
|
||||
if (isTRUE(grepl(perl=TRUE, "^(text|application)/json(;\\s*charset\\s*=\\s*utf-8)?$", req[["HTTP_CONTENT_TYPE"]]))) {
|
||||
tmp <- file("", "w+b")
|
||||
on.exit(close(tmp))
|
||||
|
||||
input_file <- req[["rook.input"]]
|
||||
while (TRUE) {
|
||||
chunk <- input_file$read(8192L)
|
||||
if (length(chunk) == 0)
|
||||
break
|
||||
writeBin(chunk, tmp)
|
||||
}
|
||||
|
||||
return(jsonlite::fromJSON(tmp))
|
||||
}
|
||||
|
||||
if (is.null(req[["HTTP_CONTENT_TYPE"]])) {
|
||||
if (!is.null(req[["rook.input"]]) && length(req[["rook.input"]]$read(1L)) > 0) {
|
||||
stop("Invalid POST request (body provided without content type)")
|
||||
}
|
||||
return()
|
||||
}
|
||||
|
||||
stop("Invalid POST request (content type not supported)")
|
||||
}
|
||||
}
|
||||
|
||||
resultToResponse <- function(result) {
|
||||
if (inherits(result, "httpResponse")) {
|
||||
return(result)
|
||||
} else if (inherits(result, "try-error")) {
|
||||
return(httpResponse(
|
||||
status=500,
|
||||
content_type="text/plain",
|
||||
content=conditionMessage(attr(result, "condition"))
|
||||
))
|
||||
} else if (!is.null(attr(result, "content.type"))) {
|
||||
return(httpResponse(
|
||||
status=200L,
|
||||
content_type=attr(result, "content.type"),
|
||||
content=result
|
||||
))
|
||||
} else {
|
||||
return(httpResponse(
|
||||
status=200L,
|
||||
content_type="application/json",
|
||||
content=toJSON(result, pretty=TRUE)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
responseToContent <- function(result) {
|
||||
ct <- result$content_type
|
||||
textMode <- grepl("^text/", ct) || ct == "application/json" ||
|
||||
grepl("^application/xml($|\\+)", ct)
|
||||
|
||||
# TODO: Make sure text is UTF-8
|
||||
|
||||
if ("file" %in% names(result$content)) {
|
||||
filename <- result$content$file
|
||||
if ("owned" %in% names(result$content) && result$content$owned) {
|
||||
on.exit(unlink(filename), add = TRUE)
|
||||
}
|
||||
if (textMode)
|
||||
return(paste(readLines(filename), collapse = "\n"))
|
||||
else
|
||||
return(readBin(filename, raw(), file.info(filename)$size))
|
||||
} else {
|
||||
if (textMode)
|
||||
return(paste(result$content, collapse = "\n"))
|
||||
else
|
||||
return(result$content)
|
||||
}
|
||||
}
|
||||
|
||||
20
R/modal.R
20
R/modal.R
@@ -36,15 +36,11 @@ removeModal <- function(session = getDefaultReactiveDomain()) {
|
||||
#' @param ... UI elements for the body of the modal dialog box.
|
||||
#' @param title An optional title for the dialog.
|
||||
#' @param footer UI for footer. Use \code{NULL} for no footer.
|
||||
#' @param size One of \code{"s"} for small, \code{"m"} (the default) for medium,
|
||||
#' or \code{"l"} for large.
|
||||
#' @param easyClose If \code{TRUE}, the modal dialog can be dismissed by
|
||||
#' clicking outside the dialog box, or be pressing the Escape key. If
|
||||
#' \code{FALSE} (the default), the modal dialog can't be dismissed in those
|
||||
#' ways; instead it must be dismissed by clicking on the dismiss button, or
|
||||
#' from a call to \code{\link{removeModal}} on the server.
|
||||
#' @param fade If \code{FALSE}, the modal dialog will have no fade-in animation
|
||||
#' (it will simply appear rather than fade in to view).
|
||||
#'
|
||||
#' @examples
|
||||
#' if (interactive()) {
|
||||
@@ -84,7 +80,7 @@ removeModal <- function(session = getDefaultReactiveDomain()) {
|
||||
#' )
|
||||
#'
|
||||
#'
|
||||
#' # Display a modal that requires valid input before continuing.
|
||||
# Display a modal that requires valid input before continuing.
|
||||
#' shinyApp(
|
||||
#' ui = basicPage(
|
||||
#' actionButton("show", "Show modal dialog"),
|
||||
@@ -124,8 +120,7 @@ removeModal <- function(session = getDefaultReactiveDomain()) {
|
||||
#' # message.
|
||||
#' observeEvent(input$ok, {
|
||||
#' # Check that data object exists and is data frame.
|
||||
#' if (!is.null(input$dataset) && nzchar(input$dataset) &&
|
||||
#' exists(input$dataset) && is.data.frame(get(input$dataset))) {
|
||||
#' if (exists(input$dataset) && is.data.frame(get(input$dataset))) {
|
||||
#' vals$data <- get(input$dataset)
|
||||
#' removeModal()
|
||||
#' } else {
|
||||
@@ -145,18 +140,13 @@ removeModal <- function(session = getDefaultReactiveDomain()) {
|
||||
#' }
|
||||
#' @export
|
||||
modalDialog <- function(..., title = NULL, footer = modalButton("Dismiss"),
|
||||
size = c("m", "s", "l"), easyClose = FALSE, fade = TRUE) {
|
||||
easyClose = FALSE) {
|
||||
|
||||
size <- match.arg(size)
|
||||
|
||||
cls <- if (fade) "modal fade" else "modal"
|
||||
div(id = "shiny-modal", class = cls, tabindex = "-1",
|
||||
div(id = "shiny-modal", class = "modal fade", tabindex = "-1",
|
||||
`data-backdrop` = if (!easyClose) "static",
|
||||
`data-keyboard` = if (!easyClose) "false",
|
||||
|
||||
div(
|
||||
class = "modal-dialog",
|
||||
class = switch(size, s = "modal-sm", m = NULL, l = "modal-lg"),
|
||||
div(class = "modal-dialog",
|
||||
div(class = "modal-content",
|
||||
if (!is.null(title)) div(class = "modal-header",
|
||||
tags$h4(class = "modal-title", title)
|
||||
|
||||
19
R/modules.R
19
R/modules.R
@@ -1,4 +1,4 @@
|
||||
# Creates an object whose $ and [[ pass through to the parent
|
||||
# Creates an object whose $ and $<- pass through to the parent
|
||||
# session, unless the name is matched in ..., in which case
|
||||
# that value is returned instead. (See Decorator pattern.)
|
||||
createSessionProxy <- function(parentSession, ...) {
|
||||
@@ -14,24 +14,18 @@ createSessionProxy <- function(parentSession, ...) {
|
||||
|
||||
#' @export
|
||||
`$.session_proxy` <- function(x, name) {
|
||||
if (name %in% names(.subset2(x, "overrides")))
|
||||
.subset2(x, "overrides")[[name]]
|
||||
if (name %in% names(x[["overrides"]]))
|
||||
x[["overrides"]][[name]]
|
||||
else
|
||||
.subset2(x, "parent")[[name]]
|
||||
x[["parent"]][[name]]
|
||||
}
|
||||
|
||||
#' @export
|
||||
`[[.session_proxy` <- `$.session_proxy`
|
||||
|
||||
|
||||
#' @export
|
||||
`$<-.session_proxy` <- function(x, name, value) {
|
||||
stop("Attempted to assign value on session proxy.")
|
||||
x[["parent"]][[name]] <- value
|
||||
x
|
||||
}
|
||||
|
||||
`[[<-.session_proxy` <- `$<-.session_proxy`
|
||||
|
||||
|
||||
#' Invoke a Shiny module
|
||||
#'
|
||||
#' Shiny's module feature lets you break complicated UI and server logic into
|
||||
@@ -48,6 +42,7 @@ createSessionProxy <- function(parentSession, ...) {
|
||||
#'
|
||||
#' @return The return value, if any, from executing the module server function
|
||||
#' @seealso \url{http://shiny.rstudio.com/articles/modules.html}
|
||||
#'
|
||||
#' @export
|
||||
callModule <- function(module, id, ..., session = getDefaultReactiveDomain()) {
|
||||
childScope <- session$makeScope(id)
|
||||
|
||||
55
R/progress.R
55
R/progress.R
@@ -12,14 +12,6 @@
|
||||
#' method is called. Calling \code{close} will cause the progress panel
|
||||
#' to be removed.
|
||||
#'
|
||||
#' As of version 0.14, the progress indicators use Shiny's new notification API.
|
||||
#' If you want to use the old styling (for example, you may have used customized
|
||||
#' CSS), you can use \code{style="old"} each time you call
|
||||
#' \code{Progress$new()}. If you don't want to set the style each time
|
||||
#' \code{Progress$new} is called, you can instead call
|
||||
#' \code{\link{shinyOptions}(progress.style="old")} just once, inside the server
|
||||
#' function.
|
||||
#'
|
||||
#' \strong{Methods}
|
||||
#' \describe{
|
||||
#' \item{\code{initialize(session, min = 0, max = 1)}}{
|
||||
@@ -56,10 +48,6 @@
|
||||
#' @param value A numeric value at which to set
|
||||
#' the progress bar, relative to \code{min} and \code{max}.
|
||||
#' \code{NULL} hides the progress bar, if it is currently visible.
|
||||
#' @param style Progress display style. If \code{"notification"} (the default),
|
||||
#' the progress indicator will show using Shiny's notification API. If
|
||||
#' \code{"old"}, use the same HTML and CSS used in Shiny 0.13.2 and below
|
||||
#' (this is for backward-compatibility).
|
||||
#' @param amount Single-element numeric vector; the value at which to set
|
||||
#' the progress bar, relative to \code{min} and \code{max}.
|
||||
#' \code{NULL} hides the progress bar, if it is currently visible.
|
||||
@@ -101,10 +89,7 @@ Progress <- R6Class(
|
||||
portable = TRUE,
|
||||
public = list(
|
||||
|
||||
initialize = function(session = getDefaultReactiveDomain(),
|
||||
min = 0, max = 1,
|
||||
style = getShinyOption("progress.style", default = "notification"))
|
||||
{
|
||||
initialize = function(session = getDefaultReactiveDomain(), min = 0, max = 1) {
|
||||
if (is.null(session$progressStack))
|
||||
stop("'session' is not a ShinySession object.")
|
||||
|
||||
@@ -112,11 +97,10 @@ Progress <- R6Class(
|
||||
private$id <- createUniqueId(8)
|
||||
private$min <- min
|
||||
private$max <- max
|
||||
private$style <- match.arg(style, choices = c("notification", "old"))
|
||||
private$value <- NULL
|
||||
private$closed <- FALSE
|
||||
|
||||
session$sendProgress('open', list(id = private$id, style = private$style))
|
||||
session$sendProgress('open', list(id = private$id))
|
||||
},
|
||||
|
||||
set = function(value = NULL, message = NULL, detail = NULL) {
|
||||
@@ -138,8 +122,7 @@ Progress <- R6Class(
|
||||
id = private$id,
|
||||
message = message,
|
||||
detail = detail,
|
||||
value = value,
|
||||
style = private$style
|
||||
value = value
|
||||
))
|
||||
|
||||
private$session$sendProgress('update', data)
|
||||
@@ -165,9 +148,7 @@ Progress <- R6Class(
|
||||
return()
|
||||
}
|
||||
|
||||
private$session$sendProgress('close',
|
||||
list(id = private$id, style = private$style)
|
||||
)
|
||||
private$session$sendProgress('close', list(id = private$id))
|
||||
private$closed <- TRUE
|
||||
}
|
||||
),
|
||||
@@ -177,7 +158,6 @@ Progress <- R6Class(
|
||||
id = character(0),
|
||||
min = numeric(0),
|
||||
max = numeric(0),
|
||||
style = character(0),
|
||||
value = NULL,
|
||||
closed = logical(0)
|
||||
)
|
||||
@@ -206,14 +186,6 @@ Progress <- R6Class(
|
||||
#' is not common) or otherwise cannot be encapsulated by a single scope. In that
|
||||
#' case, you can use the \code{Progress} reference class.
|
||||
#'
|
||||
#' As of version 0.14, the progress indicators use Shiny's new notification API.
|
||||
#' If you want to use the old styling (for example, you may have used customized
|
||||
#' CSS), you can use \code{style="old"} each time you call
|
||||
#' \code{withProgress()}. If you don't want to set the style each time
|
||||
#' \code{withProgress} is called, you can instead call
|
||||
#' \code{\link{shinyOptions}(progress.style="old")} just once, inside the server
|
||||
#' function.
|
||||
#'
|
||||
#' @param session The Shiny session object, as provided by \code{shinyServer} to
|
||||
#' the server function. The default is to automatically find the session by
|
||||
#' using the current reactive domain.
|
||||
@@ -234,10 +206,6 @@ Progress <- R6Class(
|
||||
#' displayed to the user, or \code{NULL} to hide the current detail message
|
||||
#' (if any). The detail message will be shown with a de-emphasized appearance
|
||||
#' relative to \code{message}.
|
||||
#' @param style Progress display style. If \code{"notification"} (the default),
|
||||
#' the progress indicator will show using Shiny's notification API. If
|
||||
#' \code{"old"}, use the same HTML and CSS used in Shiny 0.13.2 and below
|
||||
#' (this is for backward-compatibility).
|
||||
#' @param value Single-element numeric vector; the value at which to set the
|
||||
#' progress bar, relative to \code{min} and \code{max}. \code{NULL} hides the
|
||||
#' progress bar, if it is currently visible.
|
||||
@@ -245,7 +213,6 @@ Progress <- R6Class(
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' options(device.ask.default = FALSE)
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' plotOutput("plot")
|
||||
@@ -270,12 +237,10 @@ Progress <- R6Class(
|
||||
#' @rdname withProgress
|
||||
#' @export
|
||||
withProgress <- function(expr, min = 0, max = 1,
|
||||
value = min + (max - min) * 0.1,
|
||||
message = NULL, detail = NULL,
|
||||
style = getShinyOption("progress.style", default = "notification"),
|
||||
session = getDefaultReactiveDomain(),
|
||||
env = parent.frame(), quoted = FALSE)
|
||||
{
|
||||
value = min + (max - min) * 0.1,
|
||||
message = NULL, detail = NULL,
|
||||
session = getDefaultReactiveDomain(),
|
||||
env = parent.frame(), quoted = FALSE) {
|
||||
|
||||
if (!quoted)
|
||||
expr <- substitute(expr)
|
||||
@@ -283,9 +248,7 @@ withProgress <- function(expr, min = 0, max = 1,
|
||||
if (is.null(session$progressStack))
|
||||
stop("'session' is not a ShinySession object.")
|
||||
|
||||
style <- match.arg(style, c("notification", "old"))
|
||||
|
||||
p <- Progress$new(session, min = min, max = max, style = style)
|
||||
p <- Progress$new(session, min = min, max = max)
|
||||
|
||||
session$progressStack$push(p)
|
||||
on.exit({
|
||||
|
||||
@@ -115,16 +115,13 @@ ReactiveEnvironment <- R6Class(
|
||||
addPendingFlush = function(ctx, priority) {
|
||||
.pendingFlush$enqueue(ctx, priority)
|
||||
},
|
||||
hasPendingFlush = function() {
|
||||
return(!.pendingFlush$isEmpty())
|
||||
},
|
||||
flush = function() {
|
||||
# If already in a flush, don't start another one
|
||||
if (.inFlush) return()
|
||||
.inFlush <<- TRUE
|
||||
on.exit(.inFlush <<- FALSE)
|
||||
|
||||
while (hasPendingFlush()) {
|
||||
while (!.pendingFlush$isEmpty()) {
|
||||
ctx <- .pendingFlush$dequeue()
|
||||
ctx$executeFlushCallbacks()
|
||||
}
|
||||
|
||||
@@ -42,11 +42,11 @@ NULL
|
||||
#
|
||||
## ------------------------------------------------------------------------
|
||||
createMockDomain <- function() {
|
||||
callbacks <- Callbacks$new()
|
||||
callbacks <- list()
|
||||
ended <- FALSE
|
||||
domain <- new.env(parent = emptyenv())
|
||||
domain$onEnded <- function(callback) {
|
||||
return(callbacks$register(callback))
|
||||
callbacks <<- c(callbacks, callback)
|
||||
}
|
||||
domain$isEnded <- function() {
|
||||
ended
|
||||
@@ -55,7 +55,7 @@ createMockDomain <- function() {
|
||||
domain$end <- function() {
|
||||
if (!ended) {
|
||||
ended <<- TRUE
|
||||
callbacks$invoke()
|
||||
lapply(callbacks, do.call, list())
|
||||
}
|
||||
invisible()
|
||||
}
|
||||
|
||||
599
R/reactives.R
599
R/reactives.R
@@ -81,8 +81,8 @@ ReactiveValues <- R6Class(
|
||||
})
|
||||
}
|
||||
|
||||
if (isFrozen(key))
|
||||
reactiveStop()
|
||||
if (isInvalid(key))
|
||||
stopWithCondition(c("validation", "shiny.silent.error"), "")
|
||||
|
||||
if (!exists(key, envir=.values, inherits=FALSE))
|
||||
NULL
|
||||
@@ -161,18 +161,18 @@ ReactiveValues <- R6Class(
|
||||
.metadata[[key]][[metaKey]] <<- value
|
||||
},
|
||||
|
||||
# Mark a value as frozen If accessed while frozen, a shiny.silent.error will
|
||||
# be thrown.
|
||||
freeze = function(key) {
|
||||
setMeta(key, "frozen", TRUE)
|
||||
# Mark a value as invalid. If accessed while invalid, a shiny.silent.error
|
||||
# will be thrown.
|
||||
invalidate = function(key) {
|
||||
setMeta(key, "invalid", TRUE)
|
||||
},
|
||||
|
||||
thaw = function(key) {
|
||||
setMeta(key, "frozen", NULL)
|
||||
unInvalidate = function(key) {
|
||||
setMeta(key, "invalid", NULL)
|
||||
},
|
||||
|
||||
isFrozen = function(key) {
|
||||
isTRUE(getMeta(key, "frozen"))
|
||||
isInvalid = function(key) {
|
||||
isTRUE(getMeta(key, "invalid"))
|
||||
},
|
||||
|
||||
toList = function(all.names=FALSE) {
|
||||
@@ -232,6 +232,7 @@ ReactiveValues <- R6Class(
|
||||
#' these objects must be named.
|
||||
#'
|
||||
#' @seealso \code{\link{isolate}} and \code{\link{is.reactivevalues}}.
|
||||
#'
|
||||
#' @export
|
||||
reactiveValues <- function(...) {
|
||||
args <- list(...)
|
||||
@@ -344,7 +345,7 @@ as.list.reactivevalues <- function(x, all.names=FALSE, ...) {
|
||||
|
||||
#' Convert a reactivevalues object to a list
|
||||
#'
|
||||
#' This function does something similar to what you might \code{\link[base]{as.list}}
|
||||
#' This function does something similar to what you might \code{\link{as.list}}
|
||||
#' to do. The difference is that the calling context will take dependencies on
|
||||
#' every object in the reactivevalues object. To avoid taking dependencies on
|
||||
#' all the objects, you can wrap the call with \code{\link{isolate}()}.
|
||||
@@ -362,25 +363,10 @@ as.list.reactivevalues <- function(x, all.names=FALSE, ...) {
|
||||
#' # isolate() can also be used when calling from outside a reactive context (e.g.
|
||||
#' # at the console)
|
||||
#' isolate(reactiveValuesToList(values))
|
||||
#'
|
||||
#' @export
|
||||
reactiveValuesToList <- function(x, all.names=FALSE) {
|
||||
# Default case
|
||||
res <- .subset2(x, 'impl')$toList(all.names)
|
||||
|
||||
prefix <- .subset2(x, 'ns')("")
|
||||
# Special handling for namespaces
|
||||
if (nzchar(prefix)) {
|
||||
fullNames <- names(res)
|
||||
|
||||
# Filter out items that match namespace
|
||||
fullNames <- fullNames[substring(fullNames, 1, nchar(prefix)) == prefix]
|
||||
res <- res[fullNames]
|
||||
|
||||
# Remove namespace prefix
|
||||
names(res) <- substring(fullNames, nchar(prefix) + 1)
|
||||
}
|
||||
|
||||
res
|
||||
.subset2(x, 'impl')$toList(all.names)
|
||||
}
|
||||
|
||||
# This function is needed because str() on a reactivevalues object will call
|
||||
@@ -395,62 +381,27 @@ str.reactivevalues <- function(object, indent.str = " ", ...) {
|
||||
}
|
||||
|
||||
|
||||
#' Freeze a reactive value
|
||||
#' Invalidate a reactive value
|
||||
#'
|
||||
#' This freezes a reactive value. If the value is accessed while frozen, a
|
||||
#' This invalidates a reactive value. If the value is accessed while invalid, a
|
||||
#' "silent" exception is raised and the operation is stopped. This is the same
|
||||
#' thing that happens if \code{req(FALSE)} is called. The value is thawed
|
||||
#' (un-frozen; accessing it will no longer raise an exception) when the current
|
||||
#' reactive domain is flushed. In a Shiny application, this occurs after all of
|
||||
#' the observers are executed.
|
||||
#' thing that happens if \code{req(FALSE)} is called. The value is
|
||||
#' un-invalidated (accessing it will no longer raise an exception) when the
|
||||
#' current reactive domain is flushed; in a Shiny application, this occurs after
|
||||
#' all of the observers are executed.
|
||||
#'
|
||||
#' @param x A \code{\link{reactiveValues}} object (like \code{input}).
|
||||
#' @param name The name of a value in the \code{\link{reactiveValues}} object.
|
||||
#'
|
||||
#' @seealso \code{\link{req}}
|
||||
#' @examples
|
||||
#' ## Only run this examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' selectInput("data", "Data Set", c("mtcars", "pressure")),
|
||||
#' checkboxGroupInput("cols", "Columns (select 2)", character(0)),
|
||||
#' plotOutput("plot")
|
||||
#' )
|
||||
#'
|
||||
#' server <- function(input, output, session) {
|
||||
#' observe({
|
||||
#' data <- get(input$data)
|
||||
#' # Sets a flag on input$cols to essentially do req(FALSE) if input$cols
|
||||
#' # is accessed. Without this, an error will momentarily show whenever a
|
||||
#' # new data set is selected.
|
||||
#' freezeReactiveValue(input, "cols")
|
||||
#' updateCheckboxGroupInput(session, "cols", choices = names(data))
|
||||
#' })
|
||||
#'
|
||||
#' output$plot <- renderPlot({
|
||||
#' # When a new data set is selected, input$cols will have been invalidated
|
||||
#' # above, and this will essentially do the same as req(FALSE), causing
|
||||
#' # this observer to stop and raise a silent exception.
|
||||
#' cols <- input$cols
|
||||
#' data <- get(input$data)
|
||||
#'
|
||||
#' if (length(cols) == 2) {
|
||||
#' plot(data[[ cols[1] ]], data[[ cols[2] ]])
|
||||
#' }
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#' @export
|
||||
freezeReactiveValue <- function(x, name) {
|
||||
invalidateReactiveValue <- function(x, name) {
|
||||
domain <- getDefaultReactiveDomain()
|
||||
if (is.null(getDefaultReactiveDomain)) {
|
||||
stop("freezeReactiveValue() must be called when a default reactive domain is active.")
|
||||
stop("invalidateReactiveValue() must be called when a default reactive domain is active.")
|
||||
}
|
||||
|
||||
domain$freezeValue(x, name)
|
||||
domain$invalidateValue(x, name)
|
||||
invisible()
|
||||
}
|
||||
|
||||
@@ -526,7 +477,6 @@ Observable <- R6Class(
|
||||
.mostRecentCtxId <<- ctx$id
|
||||
ctx$onInvalidate(function() {
|
||||
.invalidated <<- TRUE
|
||||
.value <<- NULL # Value can be GC'd, it won't be read once invalidated
|
||||
.dependents$invalidate()
|
||||
})
|
||||
.execCount <<- .execCount + 1L
|
||||
@@ -621,6 +571,7 @@ Observable <- R6Class(
|
||||
#' isolate(reactiveB())
|
||||
#' isolate(reactiveC())
|
||||
#' isolate(reactiveD())
|
||||
#'
|
||||
#' @export
|
||||
reactive <- function(x, env = parent.frame(), quoted = FALSE, label = NULL,
|
||||
domain = getDefaultReactiveDomain(),
|
||||
@@ -712,18 +663,12 @@ Observer <- R6Class(
|
||||
.domain = 'ANY',
|
||||
.priority = numeric(0),
|
||||
.autoDestroy = logical(0),
|
||||
# A function that, when invoked, unsubscribes the autoDestroy
|
||||
# listener (or NULL if autodestroy is disabled for this observer).
|
||||
# We must unsubscribe when this observer is destroyed, or else
|
||||
# the observer cannot be garbage collected until the session ends.
|
||||
.autoDestroyHandle = 'ANY',
|
||||
.invalidateCallbacks = list(),
|
||||
.execCount = integer(0),
|
||||
.onResume = 'function',
|
||||
.suspended = logical(0),
|
||||
.destroyed = logical(0),
|
||||
.prevId = character(0),
|
||||
.ctx = NULL,
|
||||
|
||||
initialize = function(observerFunc, label, suspended = FALSE, priority = 0,
|
||||
domain = getDefaultReactiveDomain(),
|
||||
@@ -738,14 +683,15 @@ registerDebugHook("observerFunc", environment(), label)
|
||||
..stacktraceon..(observerFunc())
|
||||
else
|
||||
observerFunc(),
|
||||
# It's OK for shiny.silent.error errors to cause an observer to stop running
|
||||
shiny.silent.error = function(e) NULL
|
||||
# validation = function(e) NULL,
|
||||
# shiny.output.cancel = function(e) NULL
|
||||
validation = function(e) {
|
||||
# It's OK for a validation error to cause an observer to stop
|
||||
# running
|
||||
}
|
||||
)
|
||||
}
|
||||
.label <<- label
|
||||
.domain <<- domain
|
||||
.autoDestroy <<- autoDestroy
|
||||
.priority <<- normalizePriority(priority)
|
||||
.execCount <<- 0L
|
||||
.suspended <<- suspended
|
||||
@@ -753,9 +699,7 @@ registerDebugHook("observerFunc", environment(), label)
|
||||
.destroyed <<- FALSE
|
||||
.prevId <<- ''
|
||||
|
||||
.autoDestroy <<- FALSE
|
||||
.autoDestroyHandle <<- NULL
|
||||
setAutoDestroy(autoDestroy)
|
||||
onReactiveDomainEnded(.domain, self$.onDomainEnded)
|
||||
|
||||
# Defer the first running of this until flushReact is called
|
||||
.createContext()$invalidate()
|
||||
@@ -764,23 +708,7 @@ registerDebugHook("observerFunc", environment(), label)
|
||||
ctx <- Context$new(.domain, .label, type='observer', prevId=.prevId)
|
||||
.prevId <<- ctx$id
|
||||
|
||||
if (!is.null(.ctx)) {
|
||||
# If this happens, something went wrong.
|
||||
warning("Created a new context without invalidating previous context.")
|
||||
}
|
||||
# Store the context explicitly in the Observer object. This is necessary
|
||||
# to make sure that when the observer is destroyed, it also gets
|
||||
# invalidated. Otherwise the upstream reactive (on which the observer
|
||||
# depends) will hold a (indirect) reference to this context until the
|
||||
# reactive is invalidated, which may not happen immediately or at all.
|
||||
# This can lead to a memory leak (#1253).
|
||||
.ctx <<- ctx
|
||||
|
||||
ctx$onInvalidate(function() {
|
||||
# Context is invalidated, so we don't need to store a reference to it
|
||||
# anymore.
|
||||
.ctx <<- NULL
|
||||
|
||||
lapply(.invalidateCallbacks, function(invalidateCallback) {
|
||||
invalidateCallback()
|
||||
NULL
|
||||
@@ -833,28 +761,11 @@ registerDebugHook("observerFunc", environment(), label)
|
||||
"Sets whether this observer should be automatically destroyed when its
|
||||
domain (if any) ends. If autoDestroy is TRUE and the domain already
|
||||
ended, then destroy() is called immediately."
|
||||
|
||||
if (.autoDestroy == autoDestroy) {
|
||||
return(.autoDestroy)
|
||||
}
|
||||
|
||||
oldValue <- .autoDestroy
|
||||
.autoDestroy <<- autoDestroy
|
||||
|
||||
if (autoDestroy) {
|
||||
if (!.destroyed && !is.null(.domain)) { # Make sure to not try to destroy twice.
|
||||
if (.domain$isEnded()) {
|
||||
destroy()
|
||||
} else {
|
||||
.autoDestroyHandle <<- onReactiveDomainEnded(.domain, .onDomainEnded)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!is.null(.autoDestroyHandle))
|
||||
.autoDestroyHandle()
|
||||
.autoDestroyHandle <<- NULL
|
||||
if (!is.null(.domain) && .domain$isEnded()) {
|
||||
destroy()
|
||||
}
|
||||
|
||||
invisible(oldValue)
|
||||
},
|
||||
suspend = function() {
|
||||
@@ -880,21 +791,8 @@ registerDebugHook("observerFunc", environment(), label)
|
||||
"Prevents this observer from ever executing again (even if a flush has
|
||||
already been scheduled)."
|
||||
|
||||
# Make sure to not try to destory twice.
|
||||
if (.destroyed)
|
||||
return()
|
||||
|
||||
suspend()
|
||||
.destroyed <<- TRUE
|
||||
|
||||
if (!is.null(.autoDestroyHandle)) {
|
||||
.autoDestroyHandle()
|
||||
}
|
||||
.autoDestroyHandle <<- NULL
|
||||
|
||||
if (!is.null(.ctx)) {
|
||||
.ctx$invalidate()
|
||||
}
|
||||
},
|
||||
.onDomainEnded = function() {
|
||||
if (isTRUE(.autoDestroy)) {
|
||||
@@ -923,8 +821,8 @@ registerDebugHook("observerFunc", environment(), label)
|
||||
#' soon as their dependencies change, they schedule themselves to re-execute.
|
||||
#'
|
||||
#' Starting with Shiny 0.10.0, observers are automatically destroyed by default
|
||||
#' when the \link[=domains]{domain} that owns them ends (e.g. when a Shiny
|
||||
#' session ends).
|
||||
#' when the \link[=domains]{domain} that owns them ends (e.g. when a Shiny session
|
||||
#' ends).
|
||||
#'
|
||||
#' @param x An expression (quoted or unquoted). Any return value will be
|
||||
#' ignored.
|
||||
@@ -935,13 +833,12 @@ registerDebugHook("observerFunc", environment(), label)
|
||||
#' This is useful when you want to use an expression that is stored in a
|
||||
#' variable; to do so, it must be quoted with \code{quote()}.
|
||||
#' @param label A label for the observer, useful for debugging.
|
||||
#' @param suspended If \code{TRUE}, start the observer in a suspended state. If
|
||||
#' \code{FALSE} (the default), start in a non-suspended state.
|
||||
#' @param suspended If \code{TRUE}, start the observer in a suspended state.
|
||||
#' If \code{FALSE} (the default), start in a non-suspended state.
|
||||
#' @param priority An integer or numeric that controls the priority with which
|
||||
#' this observer should be executed. A higher value means higher priority: an
|
||||
#' observer with a higher priority value will execute before all observers
|
||||
#' with lower priority values. Positive, negative, and zero values are
|
||||
#' allowed.
|
||||
#' this observer should be executed. An observer with a given priority level
|
||||
#' will always execute sooner than all observers with a lower priority level.
|
||||
#' Positive, negative, and zero values are allowed.
|
||||
#' @param domain See \link{domains}.
|
||||
#' @param autoDestroy If \code{TRUE} (the default), the observer will be
|
||||
#' automatically destroyed when its domain (if any) ends.
|
||||
@@ -1000,6 +897,7 @@ registerDebugHook("observerFunc", environment(), label)
|
||||
#' # In a normal Shiny app, the web client will trigger flush events. If you
|
||||
#' # are at the console, you can force a flush with flushReact()
|
||||
#' shiny:::flushReact()
|
||||
#'
|
||||
#' @export
|
||||
observe <- function(x, env=parent.frame(), quoted=FALSE, label=NULL,
|
||||
suspended=FALSE, priority=0,
|
||||
@@ -1110,7 +1008,7 @@ setAutoflush <- local({
|
||||
#' @return A no-parameter function that can be called from a reactive context,
|
||||
#' in order to cause that context to be invalidated the next time the timer
|
||||
#' interval elapses. Calling the returned function also happens to yield the
|
||||
#' current time (as in \code{\link[base]{Sys.time}}).
|
||||
#' current time (as in \code{\link{Sys.time}}).
|
||||
#' @seealso \code{\link{invalidateLater}}
|
||||
#'
|
||||
#' @examples
|
||||
@@ -1149,6 +1047,7 @@ setAutoflush <- local({
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
reactiveTimer <- function(intervalMs=1000, session = getDefaultReactiveDomain()) {
|
||||
dependents <- Map$new()
|
||||
@@ -1307,6 +1206,7 @@ coerceToFunc <- function(x) {
|
||||
#' data()
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
reactivePoll <- function(intervalMillis, session, checkFunc, valueFunc) {
|
||||
intervalMillis <- coerceToFunc(intervalMillis)
|
||||
@@ -1384,6 +1284,7 @@ reactivePoll <- function(intervalMillis, session, checkFunc, valueFunc) {
|
||||
#' })
|
||||
#' }
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
reactiveFileReader <- function(intervalMillis, session, filePath, readFunc, ...) {
|
||||
filePath <- coerceToFunc(filePath)
|
||||
@@ -1418,7 +1319,7 @@ reactiveFileReader <- function(intervalMillis, session, filePath, readFunc, ...)
|
||||
#' The expression given to \code{isolate()} is evaluated in the calling
|
||||
#' environment. This means that if you assign a variable inside the
|
||||
#' \code{isolate()}, its value will be visible outside of the \code{isolate()}.
|
||||
#' If you want to avoid this, you can use \code{\link[base]{local}()} inside the
|
||||
#' If you want to avoid this, you can use \code{\link{local}()} inside the
|
||||
#' \code{isolate()}.
|
||||
#'
|
||||
#' This function can also be useful for calling reactive expression at the
|
||||
@@ -1471,6 +1372,7 @@ reactiveFileReader <- function(intervalMillis, session, filePath, readFunc, ...)
|
||||
#'
|
||||
#' # isolate also works if the reactive expression accesses values from the
|
||||
#' # input object, like input$x
|
||||
#'
|
||||
#' @export
|
||||
isolate <- function(expr) {
|
||||
ctx <- Context$new(getDefaultReactiveDomain(), '[isolate]', type='isolate')
|
||||
@@ -1492,6 +1394,7 @@ isolate <- function(expr) {
|
||||
#' @return The value of \code{expr}.
|
||||
#'
|
||||
#' @seealso \code{\link{isolate}}
|
||||
#'
|
||||
#' @export
|
||||
maskReactiveContext <- function(expr) {
|
||||
.getReactiveEnvironment()$runWith(NULL, function() {
|
||||
@@ -1531,8 +1434,6 @@ maskReactiveContext <- function(expr) {
|
||||
#' invalidations that come from its reactive dependencies; it only invalidates
|
||||
#' in response to the given event.
|
||||
#'
|
||||
#' @section \code{ignoreNULL} and \code{ignoreInit}:
|
||||
#'
|
||||
#' Both \code{observeEvent} and \code{eventReactive} take an \code{ignoreNULL}
|
||||
#' parameter that affects behavior when the \code{eventExpr} evaluates to
|
||||
#' \code{NULL} (or in the special case of an \code{\link{actionButton}},
|
||||
@@ -1545,44 +1446,6 @@ maskReactiveContext <- function(expr) {
|
||||
#' the action/calculation and just let the user re-initiate it (like a
|
||||
#' "Recalculate" button).
|
||||
#'
|
||||
#' Unlike what happens for \code{ignoreNULL}, only \code{observeEvent} takes in an
|
||||
#' \code{ignoreInit} argument. By default, \code{observeEvent} will run right when
|
||||
#' it is created (except if, at that moment, \code{eventExpr} evaluates to \code{NULL}
|
||||
#' and \code{ignoreNULL} is \code{TRUE}). But when responding to a click of an action
|
||||
#' button, it may often be useful to set \code{ignoreInit} to \code{TRUE}. For
|
||||
#' example, if you're setting up an \code{observeEvent} for a dynamically created
|
||||
#' button, then \code{ignoreInit = TRUE} will guarantee that the action (in
|
||||
#' \code{handlerExpr}) will only be triggered when the button is actually clicked,
|
||||
#' instead of also being triggered when it is created/initialized.
|
||||
#'
|
||||
#' Even though \code{ignoreNULL} and \code{ignoreInit} can be used for similar
|
||||
#' purposes they are independent from one another. Here's the result of combining
|
||||
#' these:
|
||||
#'
|
||||
#' \describe{
|
||||
#' \item{\code{ignoreNULL = TRUE} and \code{ignoreInit = FALSE}}{
|
||||
#' This is the default. This combination means that \code{handlerExpr} will
|
||||
#' run every time that \code{eventExpr} is not \code{NULL}. If, at the time
|
||||
#' of the \code{observeEvent}'s creation, \code{handleExpr} happens to
|
||||
#' \emph{not} be \code{NULL}, then the code runs.
|
||||
#' }
|
||||
#' \item{\code{ignoreNULL = FALSE} and \code{ignoreInit = FALSE}}{
|
||||
#' This combination means that \code{handlerExpr} will run every time no
|
||||
#' matter what.
|
||||
#' }
|
||||
#' \item{\code{ignoreNULL = FALSE} and \code{ignoreInit = TRUE}}{
|
||||
#' This combination means that \code{handlerExpr} will \emph{not} run when
|
||||
#' the \code{observeEvent} is created (because \code{ignoreInit = TRUE}),
|
||||
#' but it will run every other time.
|
||||
#' }
|
||||
#' \item{\code{ignoreNULL = TRUE} and \code{ignoreInit = TRUE}}{
|
||||
#' This combination means that \code{handlerExpr} will \emph{not} run when
|
||||
#' the \code{observeEvent} is created (because \code{ignoreInit = TRUE}).
|
||||
#' After that, \code{handlerExpr} will run every time that \code{eventExpr}
|
||||
#' is not \code{NULL}.
|
||||
#' }
|
||||
#' }
|
||||
#'
|
||||
#' @param eventExpr A (quoted or unquoted) expression that represents the event;
|
||||
#' this can be a simple reactive value like \code{input$click}, a call to a
|
||||
#' reactive expression like \code{dataset()}, or even a complex expression
|
||||
@@ -1624,15 +1487,6 @@ maskReactiveContext <- function(expr) {
|
||||
#' @param ignoreNULL Whether the action should be triggered (or value
|
||||
#' calculated, in the case of \code{eventReactive}) when the input is
|
||||
#' \code{NULL}. See Details.
|
||||
#' @param ignoreInit If \code{TRUE}, then, when this \code{observeEvent} is
|
||||
#' first created/initialized, ignore the \code{handlerExpr} (the second
|
||||
#' argument), whether it is otherwise supposed to run or not. The default is
|
||||
#' \code{FALSE}. See Details.
|
||||
#' @param once Whether this \code{observeEvent} should be immediately destroyed
|
||||
#' after the first time that the code in \code{handlerExpr} is run. This
|
||||
#' pattern is useful when you want to subscribe to a event that should only
|
||||
#' happen once.
|
||||
#'
|
||||
#' @return \code{observeEvent} returns an observer reference class object (see
|
||||
#' \code{\link{observe}}). \code{eventReactive} returns a reactive expression
|
||||
#' object (see \code{\link{reactive}}).
|
||||
@@ -1642,71 +1496,38 @@ maskReactiveContext <- function(expr) {
|
||||
#' @examples
|
||||
#' ## Only run this example in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#'
|
||||
#' ## App 1: Sample usage
|
||||
#' shinyApp(
|
||||
#' ui = fluidPage(
|
||||
#' column(4,
|
||||
#' numericInput("x", "Value", 5),
|
||||
#' br(),
|
||||
#' actionButton("button", "Show")
|
||||
#' ),
|
||||
#' column(8, tableOutput("table"))
|
||||
#' ui <- fluidPage(
|
||||
#' column(4,
|
||||
#' numericInput("x", "Value", 5),
|
||||
#' br(),
|
||||
#' actionButton("button", "Show")
|
||||
#' ),
|
||||
#' server = function(input, output) {
|
||||
#' # Take an action every time button is pressed;
|
||||
#' # here, we just print a message to the console
|
||||
#' observeEvent(input$button, {
|
||||
#' cat("Showing", input$x, "rows\n")
|
||||
#' })
|
||||
#' # Take a reactive dependency on input$button, but
|
||||
#' # not on any of the stuff inside the function
|
||||
#' df <- eventReactive(input$button, {
|
||||
#' head(cars, input$x)
|
||||
#' })
|
||||
#' output$table <- renderTable({
|
||||
#' df()
|
||||
#' })
|
||||
#' }
|
||||
#' )
|
||||
#'
|
||||
#' ## App 2: Using `once`
|
||||
#' shinyApp(
|
||||
#' ui = basicPage( actionButton("go", "Go")),
|
||||
#' server = function(input, output, session) {
|
||||
#' observeEvent(input$go, {
|
||||
#' print(paste("This will only be printed once; all",
|
||||
#' "subsequent button clicks won't do anything"))
|
||||
#' }, once = TRUE)
|
||||
#' }
|
||||
#' )
|
||||
#'
|
||||
#' ## App 3: Using `ignoreInit` and `once`
|
||||
#' shinyApp(
|
||||
#' ui = basicPage(actionButton("go", "Go")),
|
||||
#' server = function(input, output, session) {
|
||||
#' observeEvent(input$go, {
|
||||
#' insertUI("#go", "afterEnd",
|
||||
#' actionButton("dynamic", "click to remove"))
|
||||
#'
|
||||
#' # set up an observer that depends on the dynamic
|
||||
#' # input, so that it doesn't run when the input is
|
||||
#' # created, and only runs once after that (since
|
||||
#' # the side effect is remove the input from the DOM)
|
||||
#' observeEvent(input$dynamic, {
|
||||
#' removeUI("#dynamic")
|
||||
#' }, ignoreInit = TRUE, once = TRUE)
|
||||
#' })
|
||||
#' }
|
||||
#' column(8, tableOutput("table"))
|
||||
#' )
|
||||
#' server <- function(input, output) {
|
||||
#' # Take an action every time button is pressed;
|
||||
#' # here, we just print a message to the console
|
||||
#' observeEvent(input$button, {
|
||||
#' cat("Showing", input$x, "rows\n")
|
||||
#' })
|
||||
#' # Take a reactive dependency on input$button, but
|
||||
#' # not on any of the stuff inside the function
|
||||
#' df <- eventReactive(input$button, {
|
||||
#' head(cars, input$x)
|
||||
#' })
|
||||
#' output$table <- renderTable({
|
||||
#' df()
|
||||
#' })
|
||||
#' }
|
||||
#' shinyApp(ui=ui, server=server)
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
observeEvent <- function(eventExpr, handlerExpr,
|
||||
event.env = parent.frame(), event.quoted = FALSE,
|
||||
handler.env = parent.frame(), handler.quoted = FALSE,
|
||||
label = NULL, suspended = FALSE, priority = 0,
|
||||
domain = getDefaultReactiveDomain(), autoDestroy = TRUE,
|
||||
ignoreNULL = TRUE, ignoreInit = FALSE, once = FALSE) {
|
||||
label=NULL, suspended=FALSE, priority=0, domain=getDefaultReactiveDomain(),
|
||||
autoDestroy = TRUE, ignoreNULL = TRUE) {
|
||||
|
||||
eventFunc <- exprToFunction(eventExpr, event.env, event.quoted)
|
||||
if (is.null(label))
|
||||
@@ -1716,29 +1537,16 @@ observeEvent <- function(eventExpr, handlerExpr,
|
||||
handlerFunc <- exprToFunction(handlerExpr, handler.env, handler.quoted)
|
||||
handlerFunc <- wrapFunctionLabel(handlerFunc, "observeEventHandler", ..stacktraceon = TRUE)
|
||||
|
||||
initialized <- FALSE
|
||||
|
||||
o <- observe({
|
||||
invisible(observe({
|
||||
e <- eventFunc()
|
||||
|
||||
if (ignoreInit && !initialized) {
|
||||
initialized <<- TRUE
|
||||
return()
|
||||
}
|
||||
|
||||
if (ignoreNULL && isNullEvent(e)) {
|
||||
return()
|
||||
}
|
||||
|
||||
if (once) {
|
||||
on.exit(o$destroy())
|
||||
}
|
||||
|
||||
isolate(handlerFunc())
|
||||
}, label = label, suspended = suspended, priority = priority, domain = domain,
|
||||
autoDestroy = TRUE, ..stacktraceon = FALSE)
|
||||
|
||||
invisible(o)
|
||||
autoDestroy = TRUE, ..stacktraceon = FALSE))
|
||||
}
|
||||
|
||||
#' @rdname observeEvent
|
||||
@@ -1746,8 +1554,8 @@ observeEvent <- function(eventExpr, handlerExpr,
|
||||
eventReactive <- function(eventExpr, valueExpr,
|
||||
event.env = parent.frame(), event.quoted = FALSE,
|
||||
value.env = parent.frame(), value.quoted = FALSE,
|
||||
label = NULL, domain = getDefaultReactiveDomain(),
|
||||
ignoreNULL = TRUE, ignoreInit = FALSE) {
|
||||
label=NULL, domain=getDefaultReactiveDomain(),
|
||||
ignoreNULL = TRUE) {
|
||||
|
||||
eventFunc <- exprToFunction(eventExpr, event.env, event.quoted)
|
||||
if (is.null(label))
|
||||
@@ -1757,17 +1565,13 @@ eventReactive <- function(eventExpr, valueExpr,
|
||||
handlerFunc <- exprToFunction(valueExpr, value.env, value.quoted)
|
||||
handlerFunc <- wrapFunctionLabel(handlerFunc, "eventReactiveHandler", ..stacktraceon = TRUE)
|
||||
|
||||
initialized <- FALSE
|
||||
|
||||
invisible(reactive({
|
||||
e <- eventFunc()
|
||||
|
||||
if (ignoreInit && !initialized) {
|
||||
initialized <<- TRUE
|
||||
req(FALSE)
|
||||
}
|
||||
|
||||
req(!ignoreNULL || !isNullEvent(e))
|
||||
validate(need(
|
||||
!ignoreNULL || !isNullEvent(e),
|
||||
message = FALSE
|
||||
))
|
||||
|
||||
isolate(handlerFunc())
|
||||
}, label = label, domain = domain, ..stacktraceon = FALSE))
|
||||
@@ -1776,246 +1580,3 @@ eventReactive <- function(eventExpr, valueExpr,
|
||||
isNullEvent <- function(value) {
|
||||
is.null(value) || (inherits(value, 'shinyActionButtonValue') && value == 0)
|
||||
}
|
||||
|
||||
#' Slow down a reactive expression with debounce/throttle
|
||||
#'
|
||||
#' Transforms a reactive expression by preventing its invalidation signals from
|
||||
#' being sent unnecessarily often. This lets you ignore a very "chatty" reactive
|
||||
#' expression until it becomes idle, which is useful when the intermediate
|
||||
#' values don't matter as much as the final value, and the downstream
|
||||
#' calculations that depend on the reactive expression take a long time.
|
||||
#' \code{debounce} and \code{throttle} use different algorithms for slowing down
|
||||
#' invalidation signals; see Details.
|
||||
#'
|
||||
#' @section Limitations:
|
||||
#'
|
||||
#' Because R is single threaded, we can't come close to guaranteeing that the
|
||||
#' timing of debounce/throttle (or any other timing-related functions in
|
||||
#' Shiny) will be consistent or accurate; at the time we want to emit an
|
||||
#' invalidation signal, R may be performing a different task and we have no
|
||||
#' way to interrupt it (nor would we necessarily want to if we could).
|
||||
#' Therefore, it's best to think of the time windows you pass to these
|
||||
#' functions as minimums.
|
||||
#'
|
||||
#' You may also see undesirable behavior if the amount of time spent doing
|
||||
#' downstream processing for each change approaches or exceeds the time
|
||||
#' window: in this case, debounce/throttle may not have any effect, as the
|
||||
#' time each subsequent event is considered is already after the time window
|
||||
#' has expired.
|
||||
#'
|
||||
#' @details
|
||||
#'
|
||||
#' This is not a true debounce/throttle in that it will not prevent \code{r}
|
||||
#' from being called many times (in fact it may be called more times than
|
||||
#' usual), but rather, the reactive invalidation signal that is produced by
|
||||
#' \code{r} is debounced/throttled instead. Therefore, these functions should be
|
||||
#' used when \code{r} is cheap but the things it will trigger (downstream
|
||||
#' outputs and reactives) are expensive.
|
||||
#'
|
||||
#' Debouncing means that every invalidation from \code{r} will be held for the
|
||||
#' specified time window. If \code{r} invalidates again within that time window,
|
||||
#' then the timer starts over again. This means that as long as invalidations
|
||||
#' continually arrive from \code{r} within the time window, the debounced
|
||||
#' reactive will not invalidate at all. Only after the invalidations stop (or
|
||||
#' slow down sufficiently) will the downstream invalidation be sent.
|
||||
#'
|
||||
#' \code{ooo-oo-oo---- => -----------o-}
|
||||
#'
|
||||
#' (In this graphical depiction, each character represents a unit of time, and
|
||||
#' the time window is 3 characters.)
|
||||
#'
|
||||
#' Throttling, on the other hand, delays invalidation if the \emph{throttled}
|
||||
#' reactive recently (within the time window) invalidated. New \code{r}
|
||||
#' invalidations do not reset the time window. This means that if invalidations
|
||||
#' continually come from \code{r} within the time window, the throttled reactive
|
||||
#' will invalidate regularly, at a rate equal to or slower than than the time
|
||||
#' window.
|
||||
#'
|
||||
#' \code{ooo-oo-oo---- => o--o--o--o---}
|
||||
#'
|
||||
#' @param r A reactive expression (that invalidates too often).
|
||||
#' @param millis The debounce/throttle time window. You may optionally pass a
|
||||
#' no-arg function or reactive expression instead, e.g. to let the end-user
|
||||
#' control the time window.
|
||||
#' @param priority Debounce/throttle is implemented under the hood using
|
||||
#' \link[=observe]{observers}. Use this parameter to set the priority of
|
||||
#' these observers. Generally, this should be higher than the priorities of
|
||||
#' downstream observers and outputs (which default to zero).
|
||||
#' @param domain See \link{domains}.
|
||||
#'
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' options(device.ask.default = FALSE)
|
||||
#'
|
||||
#' library(shiny)
|
||||
#' library(magrittr)
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' plotOutput("plot", click = clickOpts("hover")),
|
||||
#' helpText("Quickly click on the plot above, while watching the result table below:"),
|
||||
#' tableOutput("result")
|
||||
#' )
|
||||
#'
|
||||
#' server <- function(input, output, session) {
|
||||
#' hover <- reactive({
|
||||
#' if (is.null(input$hover))
|
||||
#' list(x = NA, y = NA)
|
||||
#' else
|
||||
#' input$hover
|
||||
#' })
|
||||
#' hover_d <- hover %>% debounce(1000)
|
||||
#' hover_t <- hover %>% throttle(1000)
|
||||
#'
|
||||
#' output$plot <- renderPlot({
|
||||
#' plot(cars)
|
||||
#' })
|
||||
#'
|
||||
#' output$result <- renderTable({
|
||||
#' data.frame(
|
||||
#' mode = c("raw", "throttle", "debounce"),
|
||||
#' x = c(hover()$x, hover_t()$x, hover_d()$x),
|
||||
#' y = c(hover()$y, hover_t()$y, hover_d()$y)
|
||||
#' )
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
debounce <- function(r, millis, priority = 100, domain = getDefaultReactiveDomain()) {
|
||||
|
||||
# TODO: make a nice label for the observer(s)
|
||||
|
||||
force(r)
|
||||
force(millis)
|
||||
|
||||
if (!is.function(millis)) {
|
||||
origMillis <- millis
|
||||
millis <- function() origMillis
|
||||
}
|
||||
|
||||
v <- reactiveValues(
|
||||
trigger = NULL,
|
||||
when = NULL # the deadline for the timer to fire; NULL if not scheduled
|
||||
)
|
||||
|
||||
# Responsible for tracking when f() changes.
|
||||
firstRun <- TRUE
|
||||
observe({
|
||||
r()
|
||||
|
||||
if (firstRun) {
|
||||
# During the first run we don't want to set v$when, as this will kick off
|
||||
# the timer. We only want to do that when we see r() change.
|
||||
firstRun <<- FALSE
|
||||
return()
|
||||
}
|
||||
|
||||
# The value (or possibly millis) changed. Start or reset the timer.
|
||||
v$when <- Sys.time() + millis()/1000
|
||||
}, label = "debounce tracker", domain = domain, priority = priority)
|
||||
|
||||
# This observer is the timer. It rests until v$when elapses, then touches
|
||||
# v$trigger.
|
||||
observe({
|
||||
if (is.null(v$when))
|
||||
return()
|
||||
|
||||
now <- Sys.time()
|
||||
if (now >= v$when) {
|
||||
# Mod by 999999999 to get predictable overflow behavior
|
||||
v$trigger <- isolate(v$trigger %OR% 0) %% 999999999 + 1
|
||||
v$when <- NULL
|
||||
} else {
|
||||
invalidateLater((v$when - now) * 1000)
|
||||
}
|
||||
}, label = "debounce timer", domain = domain, priority = priority)
|
||||
|
||||
# This is the actual reactive that is returned to the user. It returns the
|
||||
# value of r(), but only invalidates/updates when v$trigger is touched.
|
||||
er <- eventReactive(v$trigger, {
|
||||
r()
|
||||
}, label = "debounce result", ignoreNULL = FALSE, domain = domain)
|
||||
|
||||
# Force the value of er to be immediately cached upon creation. It's very hard
|
||||
# to explain why this observer is needed, but if you want to understand, try
|
||||
# commenting it out and studying the unit test failure that results.
|
||||
primer <- observe({
|
||||
primer$destroy()
|
||||
er()
|
||||
}, label = "debounce primer", domain = domain, priority = priority)
|
||||
|
||||
er
|
||||
}
|
||||
|
||||
#' @rdname debounce
|
||||
#' @export
|
||||
throttle <- function(r, millis, priority = 100, domain = getDefaultReactiveDomain()) {
|
||||
|
||||
# TODO: make a nice label for the observer(s)
|
||||
|
||||
force(r)
|
||||
force(millis)
|
||||
|
||||
if (!is.function(millis)) {
|
||||
origMillis <- millis
|
||||
millis <- function() origMillis
|
||||
}
|
||||
|
||||
v <- reactiveValues(
|
||||
trigger = 0,
|
||||
lastTriggeredAt = NULL, # Last time we fired; NULL if never
|
||||
pending = FALSE # If TRUE, trigger again when timer elapses
|
||||
)
|
||||
|
||||
blackoutMillisLeft <- function() {
|
||||
if (is.null(v$lastTriggeredAt)) {
|
||||
0
|
||||
} else {
|
||||
max(0, (v$lastTriggeredAt + millis()/1000) - Sys.time()) * 1000
|
||||
}
|
||||
}
|
||||
|
||||
trigger <- function() {
|
||||
v$lastTriggeredAt <- Sys.time()
|
||||
# Mod by 999999999 to get predictable overflow behavior
|
||||
v$trigger <- isolate(v$trigger) %% 999999999 + 1
|
||||
v$pending <- FALSE
|
||||
}
|
||||
|
||||
# Responsible for tracking when f() changes.
|
||||
observeEvent(r(), {
|
||||
if (v$pending) {
|
||||
# In a blackout period and someone already scheduled; do nothing
|
||||
} else if (blackoutMillisLeft() > 0) {
|
||||
# In a blackout period but this is the first change in that period; set
|
||||
# v$pending so that a trigger will be scheduled at the end of the period
|
||||
v$pending <- TRUE
|
||||
} else {
|
||||
# Not in a blackout period. Trigger, which will start a new blackout
|
||||
# period.
|
||||
trigger()
|
||||
}
|
||||
}, label = "throttle tracker", ignoreNULL = FALSE, priority = priority, domain = domain)
|
||||
|
||||
observe({
|
||||
if (!v$pending) {
|
||||
return()
|
||||
}
|
||||
|
||||
timeout <- blackoutMillisLeft()
|
||||
if (timeout > 0) {
|
||||
invalidateLater(timeout)
|
||||
} else {
|
||||
trigger()
|
||||
}
|
||||
}, priority = priority, domain = domain)
|
||||
|
||||
# This is the actual reactive that is returned to the user. It returns the
|
||||
# value of r(), but only invalidates/updates when v$trigger is touched.
|
||||
eventReactive(v$trigger, {
|
||||
r()
|
||||
}, label = "throttle result", ignoreNULL = FALSE, domain = domain)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#' inline plot, you must provide numeric values (in pixels) to both
|
||||
#' \code{width} and \code{height}.
|
||||
#' @param res Resolution of resulting plot, in pixels per inch. This value is
|
||||
#' passed to \code{\link[grDevices]{png}}. Note that this affects the resolution of PNG
|
||||
#' passed to \code{\link{png}}. Note that this affects the resolution of PNG
|
||||
#' rendering in R; it won't change the actual ppi of the browser.
|
||||
#' @param ... Arguments to be passed through to \code{\link[grDevices]{png}}.
|
||||
#' These can be used to set the width, height, background color, etc.
|
||||
@@ -165,9 +165,9 @@ renderPlot <- function(expr, width='auto', height='auto', res=72, ...,
|
||||
|
||||
plotObj <- reactive(label = "plotObj", {
|
||||
if (execOnResize) {
|
||||
dims <- getDims()
|
||||
} else {
|
||||
isolate({ dims <- getDims() })
|
||||
} else {
|
||||
dims <- getDims()
|
||||
}
|
||||
|
||||
if (is.null(dims$width) || is.null(dims$height) ||
|
||||
@@ -185,9 +185,6 @@ renderPlot <- function(expr, width='auto', height='auto', res=72, ...,
|
||||
success <-FALSE
|
||||
tryCatch(
|
||||
{
|
||||
# This is necessary to enable displaylist recording
|
||||
grDevices::dev.control(displaylist = "enable")
|
||||
|
||||
# Actually perform the plotting
|
||||
result <- withVisible(func())
|
||||
success <- TRUE
|
||||
@@ -418,39 +415,23 @@ getPrevPlotCoordmap <- function(width, height) {
|
||||
|
||||
# Given a ggplot_build_gtable object, return a coordmap for it.
|
||||
getGgplotCoordmap <- function(p, pixelratio, res) {
|
||||
# Structure of ggplot objects changed after 2.1.0
|
||||
new_ggplot <- (utils::packageVersion("ggplot2") > "2.1.0")
|
||||
|
||||
if (!inherits(p, "ggplot_build_gtable"))
|
||||
return(NULL)
|
||||
|
||||
# Given a built ggplot object, return x and y domains (data space coords) for
|
||||
# each panel.
|
||||
find_panel_info <- function(b) {
|
||||
if (new_ggplot) {
|
||||
layout <- b$layout$panel_layout
|
||||
} else {
|
||||
layout <- b$panel$layout
|
||||
}
|
||||
layout <- b$panel$layout
|
||||
# Convert factor to numbers
|
||||
layout$PANEL <- as.integer(as.character(layout$PANEL))
|
||||
|
||||
# Names of facets
|
||||
facet <- b$plot$facet
|
||||
facet_vars <- NULL
|
||||
if (new_ggplot) {
|
||||
facet <- b$layout$facet
|
||||
if (inherits(facet, "FacetGrid")) {
|
||||
facet_vars <- vapply(c(facet$params$cols, facet$params$rows), as.character, character(1))
|
||||
} else if (inherits(facet, "FacetWrap")) {
|
||||
facet_vars <- vapply(facet$params$facets, as.character, character(1))
|
||||
}
|
||||
} else {
|
||||
facet <- b$plot$facet
|
||||
if (inherits(facet, "grid")) {
|
||||
facet_vars <- vapply(c(facet$cols, facet$rows), as.character, character(1))
|
||||
} else if (inherits(facet, "wrap")) {
|
||||
facet_vars <- vapply(facet$facets, as.character, character(1))
|
||||
}
|
||||
if (inherits(facet, "grid")) {
|
||||
facet_vars <- vapply(c(facet$cols, facet$rows), as.character, character(1))
|
||||
} else if (inherits(facet, "wrap")) {
|
||||
facet_vars <- vapply(facet$facets, as.character, character(1))
|
||||
}
|
||||
|
||||
# Iterate over each row in the layout data frame
|
||||
@@ -492,11 +473,7 @@ getGgplotCoordmap <- function(p, pixelratio, res) {
|
||||
# Given a single range object (representing the data domain) from a built
|
||||
# ggplot object, return the domain.
|
||||
find_panel_domain <- function(b, panel_num, scalex_num = 1, scaley_num = 1) {
|
||||
if (new_ggplot) {
|
||||
range <- b$layout$panel_ranges[[panel_num]]
|
||||
} else {
|
||||
range <- b$panel$ranges[[panel_num]]
|
||||
}
|
||||
range <- b$panel$ranges[[panel_num]]
|
||||
domain <- list(
|
||||
left = range$x.range[1],
|
||||
right = range$x.range[2],
|
||||
@@ -505,13 +482,9 @@ getGgplotCoordmap <- function(p, pixelratio, res) {
|
||||
)
|
||||
|
||||
# Check for reversed scales
|
||||
if (new_ggplot) {
|
||||
xscale <- b$layout$panel_scales$x[[scalex_num]]
|
||||
yscale <- b$layout$panel_scales$y[[scaley_num]]
|
||||
} else {
|
||||
xscale <- b$panel$x_scales[[scalex_num]]
|
||||
yscale <- b$panel$y_scales[[scaley_num]]
|
||||
}
|
||||
xscale <- b$panel$x_scales[[scalex_num]]
|
||||
yscale <- b$panel$y_scales[[scaley_num]]
|
||||
|
||||
if (!is.null(xscale$trans) && xscale$trans$name == "reverse") {
|
||||
domain$left <- -domain$left
|
||||
domain$right <- -domain$right
|
||||
@@ -546,18 +519,10 @@ getGgplotCoordmap <- function(p, pixelratio, res) {
|
||||
y_names <- character(0)
|
||||
|
||||
# Continuous scales have a trans; discrete ones don't
|
||||
if (new_ggplot) {
|
||||
if (!is.null(b$layout$panel_scales$x[[scalex_num]]$trans))
|
||||
x_names <- b$layout$panel_scales$x[[scalex_num]]$trans$name
|
||||
if (!is.null(b$layout$panel_scales$y[[scaley_num]]$trans))
|
||||
y_names <- b$layout$panel_scales$y[[scaley_num]]$trans$name
|
||||
|
||||
} else {
|
||||
if (!is.null(b$panel$x_scales[[scalex_num]]$trans))
|
||||
x_names <- b$panel$x_scales[[scalex_num]]$trans$name
|
||||
if (!is.null(b$panel$y_scales[[scaley_num]]$trans))
|
||||
y_names <- b$panel$y_scales[[scaley_num]]$trans$name
|
||||
}
|
||||
if (!is.null(b$panel$x_scales[[scalex_num]]$trans))
|
||||
x_names <- b$panel$x_scales[[scalex_num]]$trans$name
|
||||
if (!is.null(b$panel$y_scales[[scaley_num]]$trans))
|
||||
y_names <- b$panel$y_scales[[scaley_num]]$trans$name
|
||||
|
||||
coords <- b$plot$coordinates
|
||||
if (!is.null(coords$trans)) {
|
||||
@@ -611,11 +576,6 @@ getGgplotCoordmap <- function(p, pixelratio, res) {
|
||||
)
|
||||
}
|
||||
|
||||
# Look for CoordFlip
|
||||
if (inherits(b$plot$coordinates, "CoordFlip")) {
|
||||
mappings[c("x", "y")] <- mappings[c("y", "x")]
|
||||
}
|
||||
|
||||
mappings_cache <<- mappings
|
||||
mappings
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#' @param outputArgs A list of arguments to be passed through to the
|
||||
#' implicit call to \code{\link{tableOutput}} when \code{renderTable} is
|
||||
#' used in an interactive R Markdown document.
|
||||
#'
|
||||
#' @export
|
||||
renderTable <- function(expr, striped = FALSE, hover = FALSE,
|
||||
bordered = FALSE, spacing = c("s", "xs", "m", "l"),
|
||||
@@ -79,8 +80,6 @@ renderTable <- function(expr, striped = FALSE, hover = FALSE,
|
||||
digitsWrapper <- createWrapper(digits)
|
||||
naWrapper <- createWrapper(na)
|
||||
|
||||
dots <- list(...) ## used later (but defined here because of scoping)
|
||||
|
||||
renderFunc <- function(shinysession, name, ...) {
|
||||
striped <- stripedWrapper()
|
||||
hover <- hoverWrapper()
|
||||
@@ -114,6 +113,7 @@ renderTable <- function(expr, striped = FALSE, hover = FALSE,
|
||||
return(NULL)
|
||||
|
||||
# Separate the ... args to pass to xtable() vs print.xtable()
|
||||
dots <- list(...)
|
||||
xtable_argnames <- setdiff(names(formals(xtable)), c("x", "..."))
|
||||
xtable_args <- dots[intersect(names(dots), xtable_argnames)]
|
||||
non_xtable_args <- dots[setdiff(names(dots), xtable_argnames)]
|
||||
@@ -156,30 +156,15 @@ renderTable <- function(expr, striped = FALSE, hover = FALSE,
|
||||
|
||||
# Set up print args
|
||||
print_args <- list(
|
||||
x = xtable_res,
|
||||
xtable_res,
|
||||
type = 'html',
|
||||
include.rownames = {
|
||||
if ("include.rownames" %in% names(dots)) dots$include.rownames
|
||||
else rownames
|
||||
},
|
||||
include.colnames = {
|
||||
if ("include.colnames" %in% names(dots)) dots$include.colnames
|
||||
else colnames
|
||||
},
|
||||
NA.string = {
|
||||
if ("NA.string" %in% names(dots)) dots$NA.string
|
||||
else na
|
||||
},
|
||||
html.table.attributes =
|
||||
paste0({
|
||||
if ("html.table.attributes" %in% names(dots)) dots$html.table.attributes
|
||||
else ""
|
||||
}, " ",
|
||||
"class = '", htmlEscape(classNames, TRUE), "' ",
|
||||
"style = 'width:", validateCssUnit(width), ";'"))
|
||||
include.rownames = rownames,
|
||||
include.colnames = colnames,
|
||||
NA.string = na,
|
||||
html.table.attributes = paste0("class = '", htmlEscape(classNames, TRUE), "' ",
|
||||
"style = 'width:", validateCssUnit(width), ";'"))
|
||||
|
||||
print_args <- c(print_args, non_xtable_args)
|
||||
print_args <- print_args[unique(names(print_args))]
|
||||
|
||||
# Capture the raw html table returned by print.xtable(), and store it in
|
||||
# a variable for further processing
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# Function wrappers for saving and restoring state to/from disk when running
|
||||
# Shiny locally.
|
||||
# Function wrappers for persisting or restoring state when running Shiny locally
|
||||
#
|
||||
# These functions provide a directory to the callback function.
|
||||
#
|
||||
@@ -8,11 +7,12 @@
|
||||
# a directory. It must take one argument, \code{stateDir}, which is a
|
||||
# directory to which it writes/reads.
|
||||
|
||||
saveInterfaceLocal <- function(id, callback) {
|
||||
# Try to save in app directory
|
||||
persistInterfaceLocal <- function(id, callback) {
|
||||
# Try to save in app directory, or, if that's not available, in the current
|
||||
# directory.
|
||||
appDir <- getShinyOption("appDir", default = getwd())
|
||||
|
||||
stateDir <- file.path(appDir, "shiny_bookmarks", id)
|
||||
stateDir <- file.path(appDir, "shiny_persist", id)
|
||||
if (!dirExists(stateDir))
|
||||
dir.create(stateDir, recursive = TRUE)
|
||||
|
||||
@@ -20,9 +20,10 @@ saveInterfaceLocal <- function(id, callback) {
|
||||
}
|
||||
|
||||
loadInterfaceLocal <- function(id, callback) {
|
||||
# Try to load from app directory
|
||||
# Try to save in app directory, or, if that's not available, in the current
|
||||
# directory.
|
||||
appDir <- getShinyOption("appDir", default = getwd())
|
||||
|
||||
stateDir <- file.path(appDir, "shiny_bookmarks", id)
|
||||
stateDir <- file.path(appDir, "shiny_persist", id)
|
||||
callback(stateDir)
|
||||
}
|
||||
525
R/save-state.R
Normal file
525
R/save-state.R
Normal file
@@ -0,0 +1,525 @@
|
||||
ShinySaveState <- R6Class("ShinySaveState",
|
||||
public = list(
|
||||
input = NULL,
|
||||
exclude = NULL,
|
||||
onSave = NULL, # A callback to invoke during the saving process.
|
||||
|
||||
# These are set not in initialize(), but by external functions that modify
|
||||
# the ShinySaveState object.
|
||||
dir = NULL,
|
||||
values = NULL,
|
||||
|
||||
initialize = function(input = NULL, exclude = NULL, onSave = NULL)
|
||||
{
|
||||
self$input <- input
|
||||
self$exclude <- exclude
|
||||
self$onSave <- onSave
|
||||
},
|
||||
|
||||
# Persist this state object to disk. Returns a query string which can be
|
||||
# used to restore the session.
|
||||
persist = function() {
|
||||
id <- createUniqueId(8)
|
||||
|
||||
persistInterface <- getShinyOption("persist.interface",
|
||||
default = persistInterfaceLocal)
|
||||
|
||||
persistInterface(id, function(stateDir) {
|
||||
# Directory is provided by the persistInterface function.
|
||||
self$dir <- stateDir
|
||||
|
||||
# Allow user-supplied onSave function to do things like add self$values, or
|
||||
# save data to state dir.
|
||||
if (!is.null(self$onSave))
|
||||
isolate(self$onSave(self))
|
||||
|
||||
# Serialize values, possibly saving some extra data to stateDir
|
||||
inputValues <- serializeReactiveValues(self$input, self$exclude, self$dir)
|
||||
saveRDS(inputValues, file.path(stateDir, "input.rds"))
|
||||
|
||||
# If there values passed in, save them also
|
||||
if (!is.null(self$values))
|
||||
saveRDS(self$values, file.path(stateDir, "values.rds"))
|
||||
})
|
||||
|
||||
paste0("__state_id__=", encodeURIComponent(id))
|
||||
},
|
||||
|
||||
# Encode the state to a URL. This does not save to disk.
|
||||
encode = function() {
|
||||
inputVals <- serializeReactiveValues(self$input, self$exclude, stateDir = NULL)
|
||||
|
||||
# Allow user-supplied onSave function to do things like add self$values.
|
||||
if (!is.null(self$onSave))
|
||||
self$onSave(self)
|
||||
|
||||
inputVals <- vapply(inputVals,
|
||||
function(x) toJSON(x, strict_atomic = FALSE),
|
||||
character(1),
|
||||
USE.NAMES = TRUE
|
||||
)
|
||||
|
||||
res <- paste0(
|
||||
encodeURIComponent(names(inputVals)),
|
||||
"=",
|
||||
encodeURIComponent(inputVals),
|
||||
collapse = "&"
|
||||
)
|
||||
|
||||
# If 'values' is present, add them as well.
|
||||
if (length(self$values) != 0) {
|
||||
values <- vapply(self$values,
|
||||
function(x) toJSON(x, strict_atomic = FALSE),
|
||||
character(1),
|
||||
USE.NAMES = TRUE
|
||||
)
|
||||
|
||||
res <- paste0(res, "&_values_&",
|
||||
paste0(
|
||||
encodeURIComponent(names(values)),
|
||||
"=",
|
||||
encodeURIComponent(values),
|
||||
collapse = "&"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
RestoreContext <- R6Class("RestoreContext",
|
||||
public = list(
|
||||
# This is a RestoreInputSet for input values. This is a key-value store with
|
||||
# some special handling.
|
||||
input = NULL,
|
||||
|
||||
# Directory for extra files, if restoring from persisted state
|
||||
dir = NULL,
|
||||
|
||||
# For values other than input values. These values don't need the special
|
||||
# phandling that's needed for input values, because they're only accessed
|
||||
# from the onRestore function.
|
||||
values = NULL,
|
||||
|
||||
initialize = function(queryString = NULL) {
|
||||
if (!is.null(queryString)) {
|
||||
tryCatch(
|
||||
{
|
||||
qsValues <- parseQueryString(queryString, nested = TRUE)
|
||||
|
||||
if (!is.null(qsValues[["__subapp__"]]) && qsValues[["__subapp__"]] == 1) {
|
||||
# Ignore subapps in shiny docs
|
||||
self$reset()
|
||||
|
||||
} else if (!is.null(qsValues[["__state_id__"]]) && nzchar(qsValues[["__state_id__"]])) {
|
||||
# If we have a "__state_id__" key, restore from persisted state and ignore
|
||||
# other key/value pairs. If not, restore from key/value pairs in the
|
||||
# query string.
|
||||
private$loadStateQueryString(queryString)
|
||||
|
||||
} else {
|
||||
# The query string contains the saved keys and values
|
||||
private$decodeStateQueryString(queryString)
|
||||
}
|
||||
},
|
||||
error = function(e) {
|
||||
# If there's an error in restoring problem, just reset these values
|
||||
self$reset()
|
||||
warning(e$message)
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
reset = function() {
|
||||
self$input <- RestoreInputSet$new(list())
|
||||
self$values <- list()
|
||||
self$dir <- NULL
|
||||
},
|
||||
|
||||
# This should be called before a restore context is popped off the stack.
|
||||
flushPending = function() {
|
||||
self$input$flushPending()
|
||||
},
|
||||
|
||||
|
||||
# Returns a list representation of the RestoreContext object. This is passed
|
||||
# to the app author's onRestore function. An important difference between
|
||||
# the RestoreContext object and the list is that the former's `input` field
|
||||
# is a RestoreInputSet object, while the latter's `input` field is just a
|
||||
# list.
|
||||
asList = function() {
|
||||
list(
|
||||
input = self$input$asList(),
|
||||
dir = self$dir,
|
||||
values = self$values
|
||||
)
|
||||
}
|
||||
),
|
||||
|
||||
private = list(
|
||||
# Given a query string with a __state_id__, load persisted state with that ID.
|
||||
loadStateQueryString = function(queryString) {
|
||||
values <- parseQueryString(queryString, nested = TRUE)
|
||||
id <- values[["__state_id__"]]
|
||||
|
||||
# This function is passed to the loadInterface function; given a
|
||||
# directory, it will load state from that directory
|
||||
loadFun <- function(stateDir) {
|
||||
self$dir <- stateDir
|
||||
|
||||
inputValues <- readRDS(file.path(stateDir, "input.rds"))
|
||||
self$input <- RestoreInputSet$new(inputValues)
|
||||
|
||||
valuesFile <- file.path(stateDir, "values.rds")
|
||||
if (file.exists(valuesFile)) {
|
||||
self$values <- readRDS(valuesFile)
|
||||
} else {
|
||||
self$values <- list()
|
||||
}
|
||||
}
|
||||
|
||||
loadInterface <- getShinyOption("load.interface", default = loadInterfaceLocal)
|
||||
loadInterface(id, loadFun)
|
||||
|
||||
invisible()
|
||||
},
|
||||
|
||||
# Given a query string with values encoded in it, restore persisted state
|
||||
# from those values.
|
||||
decodeStateQueryString = function(queryString) {
|
||||
# Remove leading '?'
|
||||
if (substr(queryString, 1, 1) == '?')
|
||||
queryString <- substr(queryString, 2, nchar(queryString))
|
||||
|
||||
if (grepl("(^|&)_values_(&|$)", queryString)) {
|
||||
splitStr <- strsplit(queryString, "(^|&)_values_(&|$)")[[1]]
|
||||
inputValueStr <- splitStr[1]
|
||||
valueStr <- splitStr[2]
|
||||
if (is.na(valueStr))
|
||||
valueStr <- ""
|
||||
|
||||
} else {
|
||||
inputValueStr <- queryString
|
||||
valueStr <- ""
|
||||
}
|
||||
|
||||
inputValues <- parseQueryString(inputValueStr, nested = TRUE)
|
||||
values <- parseQueryString(valueStr, nested = TRUE)
|
||||
|
||||
valuesFromJSON <- function(vals) {
|
||||
mapply(names(vals), vals, SIMPLIFY = FALSE,
|
||||
FUN = function(name, value) {
|
||||
tryCatch(
|
||||
jsonlite::fromJSON(value),
|
||||
error = function(e) {
|
||||
stop("Failed to parse URL parameter \"", name, "\"")
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
inputValues <- valuesFromJSON(inputValues)
|
||||
self$input <- RestoreInputSet$new(inputValues)
|
||||
|
||||
self$values <- valuesFromJSON(values)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# Restore input set. This is basically a key-value store, except for one
|
||||
# important difference: When the user `get()`s a value, the value is marked as
|
||||
# pending; when `flushPending()` is called, those pending values are marked as
|
||||
# used. When a value is marked as used, `get()` will not return it, unless
|
||||
# called with `force=TRUE`. This is to make sure that a particular value can be
|
||||
# restored only within a single call to `withRestoreContext()`. Without this, if
|
||||
# a value is restored in a dynamic UI, it could completely prevent any other
|
||||
# (non- restored) kvalue from being used.
|
||||
RestoreInputSet <- R6Class("RestoreInputSet",
|
||||
private = list(
|
||||
values = NULL,
|
||||
pending = character(0),
|
||||
used = character(0) # Names of values which have been used
|
||||
),
|
||||
|
||||
public = list(
|
||||
initialize = function(values) {
|
||||
private$values <- new.env(parent = emptyenv())
|
||||
list2env(values, private$values)
|
||||
},
|
||||
|
||||
exists = function(name) {
|
||||
exists(name, envir = private$values)
|
||||
},
|
||||
|
||||
# Return TRUE if the value exists and has not been marked as used.
|
||||
available = function(name) {
|
||||
self$exists(name) && !self$isUsed(name)
|
||||
},
|
||||
|
||||
isPending = function(name) {
|
||||
name %in% private$pending
|
||||
},
|
||||
|
||||
isUsed = function(name) {
|
||||
name %in% private$used
|
||||
},
|
||||
|
||||
# Get a value. If `force` is TRUE, get the value without checking whether
|
||||
# has been used, and without marking it as pending.
|
||||
get = function(name, force = FALSE) {
|
||||
if (force)
|
||||
return(private$values[[name]])
|
||||
|
||||
if (!self$available(name))
|
||||
return(NULL)
|
||||
|
||||
# Mark this name as pending. Use unique so that it's not added twice.
|
||||
private$pending <- unique(c(private$pending, name))
|
||||
private$values[[name]]
|
||||
},
|
||||
|
||||
# Take pending names and mark them as used, then clear pending list.
|
||||
flushPending = function() {
|
||||
private$used <- unique(c(private$used, private$pending))
|
||||
private$pending <- character(0)
|
||||
},
|
||||
|
||||
asList = function() {
|
||||
as.list.environment(private$values)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
restoreCtxStack <- Stack$new()
|
||||
|
||||
withRestoreContext <- function(ctx, expr) {
|
||||
restoreCtxStack$push(ctx)
|
||||
|
||||
on.exit({
|
||||
# Mark pending names as used
|
||||
restoreCtxStack$peek()$flushPending()
|
||||
restoreCtxStack$pop()
|
||||
}, add = TRUE)
|
||||
|
||||
force(expr)
|
||||
}
|
||||
|
||||
# Is there a current restore context?
|
||||
hasCurrentRestoreContext <- function() {
|
||||
restoreCtxStack$size() > 0
|
||||
}
|
||||
|
||||
# Call to access the current restore context
|
||||
getCurrentRestoreContext <- function() {
|
||||
ctx <- restoreCtxStack$peek()
|
||||
if (is.null(ctx)) {
|
||||
stop("No restore context found")
|
||||
}
|
||||
ctx
|
||||
}
|
||||
|
||||
#' Restore an input value
|
||||
#'
|
||||
#' This restores an input value from the current restore context..
|
||||
#'
|
||||
#' @param id Name of the input value to restore.
|
||||
#' @param default A default value to use, if there's no value to restore.
|
||||
#'
|
||||
#' @export
|
||||
restoreInput <- function(id, default) {
|
||||
# Need to evaluate `default` in case it contains reactives like input$x. If we
|
||||
# don't, then the calling code won't take a reactive dependency on input$x
|
||||
# when restoring a value.
|
||||
force(default)
|
||||
|
||||
if (identical(getShinyOption("restorable"), FALSE) || !hasCurrentRestoreContext())
|
||||
return(default)
|
||||
|
||||
oldInputs <- getCurrentRestoreContext()$input
|
||||
if (oldInputs$available(id)) {
|
||||
oldInputs$get(id)
|
||||
} else {
|
||||
default
|
||||
}
|
||||
}
|
||||
|
||||
#' Update URL in browser's location bar
|
||||
#'
|
||||
#' @param queryString The new query string to show in the location bar.
|
||||
#' @param session A Shiny session object.
|
||||
#' @export
|
||||
updateLocationBar <- function(queryString, session = getDefaultReactiveDomain()) {
|
||||
session$updateLocationBar(queryString)
|
||||
}
|
||||
|
||||
#' Create a button for bookmarking/sharing
|
||||
#'
|
||||
#' A \code{bookmarkButton} is a \code{\link{actionButton}} with a default label
|
||||
#' that consists of a link icon and the text "Share...". It is meant to be used
|
||||
#' for bookmarking state.
|
||||
#'
|
||||
#' @seealso configureBookmarking
|
||||
#' @inheritParams actionButton
|
||||
#' @export
|
||||
saveStateButton <- function(inputId, label = "Save and share...",
|
||||
icon = shiny::icon("link", lib = "glyphicon"),
|
||||
title = "Save this application's current state and get a URL for sharing.",
|
||||
...)
|
||||
{
|
||||
actionButton(inputId, label, icon, title = title, ...)
|
||||
}
|
||||
|
||||
|
||||
#' Generate a modal dialog that displays a URL
|
||||
#'
|
||||
#' The modal dialog generated by \code{urlModal} will display the URL in a
|
||||
#' textarea input, and the URL text will be selected so that it can be easily
|
||||
#' copied. The result from \code{urlModal} should be passed to the
|
||||
#' \code{\link{showModal}} function to display it in the browser.
|
||||
#'
|
||||
#' @param url A URL to display in the dialog box.
|
||||
#' @param title A title for the dialog box.
|
||||
#' @param subtitle Text to display underneath URL.
|
||||
#' @export
|
||||
urlModal <- function(url, title = "Saved application link", subtitle = NULL) {
|
||||
|
||||
subtitleTag <- NULL
|
||||
if (!is.null(subtitle)) {
|
||||
subtitleTag <- tagList(
|
||||
br(),
|
||||
span(class = "text-muted", subtitle)
|
||||
)
|
||||
}
|
||||
|
||||
modalDialog(
|
||||
title = title,
|
||||
easyClose = TRUE,
|
||||
footer = NULL,
|
||||
tags$textarea(class = "form-control", rows = "1", style = "resize: none;",
|
||||
readonly = "readonly",
|
||||
url
|
||||
),
|
||||
subtitleTag,
|
||||
# Need separate show and shown listeners. The show listener sizes the
|
||||
# textarea just as the modal starts to fade in. The 200ms delay is needed
|
||||
# because if we try to resize earlier, it can't calculate the text height
|
||||
# (scrollHeight will be reported as zero). The shown listener selects the
|
||||
# text; it's needed because because selection has to be done after the fade-
|
||||
# in is completed.
|
||||
tags$script(
|
||||
"$('#shiny-modal').
|
||||
one('show.bs.modal', function() {
|
||||
setTimeout(function() {
|
||||
var $textarea = $('#shiny-modal textarea');
|
||||
$textarea.innerHeight($textarea[0].scrollHeight);
|
||||
}, 200);
|
||||
});
|
||||
$('#shiny-modal')
|
||||
.one('shown.bs.modal', function() {
|
||||
$('#shiny-modal textarea').select().focus();
|
||||
});"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
#' Configure bookmarking for the current session
|
||||
#'
|
||||
#' There are two types of bookmarking: saving state, and encoding state.
|
||||
#'
|
||||
#' @param eventExpr An expression to listen for, similar to
|
||||
#' \code{\link{observeEvent}}.
|
||||
#' @param type Either \code{"encode"}, which encodes all of the relevant values
|
||||
#' in a URL, \code{"persist"}, which saves to disk, or \code{"disable"}, which
|
||||
#' disables any previously-enabled bookmarking.
|
||||
#' @param exclude Input values to exclude from bookmarking.
|
||||
#' @param onBookmark A function to call before saving state. This function
|
||||
#' should return a list, which will be saved as \code{values}.
|
||||
#' @param onRestore A function to call when a session is restored. It will be
|
||||
#' passed one argument, a restoreContext object.
|
||||
#' @param onBookmarked A callback function to invoke after the bookmarking has
|
||||
#' been done.
|
||||
#' @param session A Shiny session object.
|
||||
#' @export
|
||||
configureBookmarking <- function(eventExpr,
|
||||
type = c("encode", "persist", "disable"), exclude = NULL,
|
||||
onBookmark = NULL, onRestore = NULL, onBookmarked = NULL,
|
||||
session = getDefaultReactiveDomain())
|
||||
{
|
||||
|
||||
eventExpr <- substitute(eventExpr)
|
||||
type <- match.arg(type)
|
||||
|
||||
# If there's an existing onBookmarked observer, destroy it before creating a
|
||||
# new one.
|
||||
if (!is.null(session$bookmarkObserver)) {
|
||||
session$bookmarkObserver$destroy()
|
||||
session$bookmarkObserver <- NULL
|
||||
}
|
||||
|
||||
if (type == "disable") {
|
||||
return(invisible())
|
||||
}
|
||||
|
||||
# If no onBookmarked function is provided, use one of these defaults.
|
||||
if (is.null(onBookmarked)) {
|
||||
if (type == "persist") {
|
||||
onBookmarked <- function(url) {
|
||||
showModal(urlModal(
|
||||
url,
|
||||
subtitle = "The current state of this application has been persisted."
|
||||
))
|
||||
}
|
||||
} else if (type == "encode") {
|
||||
onBookmarked <- function(url) {
|
||||
showModal(urlModal(
|
||||
url,
|
||||
subtitle = "This link encodes the current state of this application."
|
||||
))
|
||||
}
|
||||
}
|
||||
} else if (!is.function(onBookmarked)) {
|
||||
stop("onBookmarked must be a function.")
|
||||
}
|
||||
|
||||
session$bookmarkObserver <- observeEvent(
|
||||
eventExpr,
|
||||
event.env = parent.frame(),
|
||||
event.quoted = TRUE,
|
||||
{
|
||||
saveState <- ShinySaveState$new(session$input, exclude, onBookmark)
|
||||
|
||||
if (type == "persist") {
|
||||
url <- saveState$persist()
|
||||
} else {
|
||||
url <- saveState$encode()
|
||||
}
|
||||
|
||||
clientData <- session$clientData
|
||||
url <- paste0(
|
||||
clientData$url_protocol, "//",
|
||||
clientData$url_hostname,
|
||||
if (nzchar(clientData$url_port)) paste0(":", clientData$url_port),
|
||||
clientData$url_pathname,
|
||||
"?", url
|
||||
)
|
||||
|
||||
onBookmarked(url)
|
||||
}
|
||||
)
|
||||
|
||||
# Run the onRestore function immediately
|
||||
if (!is.null(onRestore)) {
|
||||
restoreState <- getCurrentRestoreContext()$asList()
|
||||
onRestore(restoreState)
|
||||
}
|
||||
|
||||
invisible()
|
||||
}
|
||||
@@ -71,63 +71,6 @@ removeInputHandler <- function(type){
|
||||
inputHandlers$remove(type)
|
||||
}
|
||||
|
||||
|
||||
# Apply input handler to a single input value
|
||||
applyInputHandler <- function(name, val, shinysession) {
|
||||
splitName <- strsplit(name, ':')[[1]]
|
||||
if (length(splitName) > 1) {
|
||||
if (!inputHandlers$containsKey(splitName[[2]])) {
|
||||
# No input handler registered for this type
|
||||
stop("No handler registered for type ", name)
|
||||
}
|
||||
|
||||
inputName <- splitName[[1]]
|
||||
|
||||
# Get the function for processing this type of input
|
||||
inputHandler <- inputHandlers$get(splitName[[2]])
|
||||
|
||||
return(inputHandler(val, shinysession, inputName))
|
||||
|
||||
} else if (is.list(val) && is.null(names(val))) {
|
||||
return(unlist(val, recursive = TRUE))
|
||||
} else {
|
||||
return(val)
|
||||
}
|
||||
}
|
||||
|
||||
#' Apply input handlers to raw input values
|
||||
#'
|
||||
#' The purpose of this function is to make it possible for external packages to
|
||||
#' test Shiny inputs. It takes a named list of raw input values, applies input
|
||||
#' handlers to those values, and then returns a named list of the processed
|
||||
#' values.
|
||||
#'
|
||||
#' The raw input values should be in a named list. Some values may have names
|
||||
#' like \code{"x:shiny.date"}. This function would apply the \code{"shiny.date"}
|
||||
#' input handler to the value, and then rename the result to \code{"x"}, in the
|
||||
#' output.
|
||||
#'
|
||||
#' @param inputs A named list of input values.
|
||||
#' @param shinysession A Shiny session object.
|
||||
#'
|
||||
#' @seealso registerInputHandler
|
||||
#' @keywords internal
|
||||
applyInputHandlers <- function(inputs, shinysession = getDefaultReactiveDomain()) {
|
||||
inputs <- mapply(applyInputHandler, names(inputs), inputs,
|
||||
MoreArgs = list(shinysession = shinysession),
|
||||
SIMPLIFY = FALSE)
|
||||
|
||||
# Convert names like "button1:shiny.action" to "button1"
|
||||
names(inputs) <- vapply(
|
||||
names(inputs),
|
||||
function(name) { strsplit(name, ":")[[1]][1] },
|
||||
FUN.VALUE = character(1)
|
||||
)
|
||||
|
||||
inputs
|
||||
}
|
||||
|
||||
|
||||
# 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, ...) {
|
||||
@@ -155,20 +98,7 @@ registerInputHandler("shiny.password", function(val, shinysession, name) {
|
||||
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
|
||||
as.Date(unlist(datelist))
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.datetime", function(val, ...){
|
||||
@@ -181,6 +111,9 @@ registerInputHandler("shiny.datetime", function(val, ...){
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.action", function(val, shinysession, name) {
|
||||
# Mark as not serializable
|
||||
.subset2(shinysession$input, "impl")$setMeta(name, "shiny.serializer", serializerUnserializable)
|
||||
|
||||
# mark up the action button value with a special class so we can recognize it later
|
||||
class(val) <- c(class(val), "shinyActionButtonValue")
|
||||
val
|
||||
@@ -196,25 +129,14 @@ registerInputHandler("shiny.file", function(val, shinysession, name) {
|
||||
# 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.")
|
||||
# Make sure that the paths don't go up the directory tree, for security
|
||||
# reasons.
|
||||
if (any(grepl("..", val$datapath, fixed = TRUE))) {
|
||||
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`.
|
||||
.subset2(shinysession$input, "impl")$setMeta(name, "shiny.serializer", serializerFileInput)
|
||||
val$datapath <- file.path(getCurrentRestoreContext()$dir, val$datapath)
|
||||
|
||||
val
|
||||
})
|
||||
|
||||
180
R/server.R
180
R/server.R
@@ -34,7 +34,7 @@ registerClient <- function(client) {
|
||||
#' JavaScript/CSS files available to their components.
|
||||
#'
|
||||
#' @param prefix The URL prefix (without slashes). Valid characters are a-z,
|
||||
#' A-Z, 0-9, hyphen, period, and underscore.
|
||||
#' A-Z, 0-9, hyphen, period, and underscore; and must begin with a-z or A-Z.
|
||||
#' For example, a value of 'foo' means that any request paths that begin with
|
||||
#' '/foo' will be mapped to the given directory.
|
||||
#' @param directoryPath The directory that contains the static resources to be
|
||||
@@ -49,10 +49,11 @@ registerClient <- function(client) {
|
||||
#'
|
||||
#' @examples
|
||||
#' addResourcePath('datasets', system.file('data', package='datasets'))
|
||||
#'
|
||||
#' @export
|
||||
addResourcePath <- function(prefix, directoryPath) {
|
||||
prefix <- prefix[1]
|
||||
if (!grepl('^[a-z0-9\\-_][a-z0-9\\-_.]*$', prefix, ignore.case=TRUE, perl=TRUE)) {
|
||||
if (!grepl('^[a-z][a-z0-9\\-_.]*$', prefix, ignore.case=TRUE, perl=TRUE)) {
|
||||
stop("addResourcePath called with invalid prefix; please see documentation")
|
||||
}
|
||||
|
||||
@@ -140,6 +141,7 @@ resourcePathHandler <- function(req) {
|
||||
#' })
|
||||
#' }
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
shinyServer <- function(func) {
|
||||
.globals$server <- list(func)
|
||||
@@ -189,7 +191,6 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
|
||||
appHandlers <- list(
|
||||
http = joinHandlers(c(
|
||||
sessionHandler,
|
||||
apiHandler(serverFuncSource),
|
||||
httpHandlers,
|
||||
sys.www.root,
|
||||
resourcePathHandler,
|
||||
@@ -201,11 +202,6 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
|
||||
return(TRUE)
|
||||
}
|
||||
|
||||
if (grepl("^/api/", ws$request$PATH_INFO)) {
|
||||
apiWsHandler(serverFuncSource)(ws)
|
||||
return(TRUE)
|
||||
}
|
||||
|
||||
if (!is.null(getOption("shiny.observer.error", NULL))) {
|
||||
warning(
|
||||
call. = FALSE,
|
||||
@@ -224,8 +220,7 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
|
||||
if (is.character(msg))
|
||||
msg <- charToRaw(msg)
|
||||
|
||||
traceOption <- getOption('shiny.trace', FALSE)
|
||||
if (isTRUE(traceOption) || traceOption == "recv") {
|
||||
if (isTRUE(getOption('shiny.trace'))) {
|
||||
if (binary)
|
||||
message("RECV ", '$$binary data$$')
|
||||
else
|
||||
@@ -242,19 +237,44 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
|
||||
# used by an input handler (like the one for "shiny.file"). This
|
||||
# should only happen once, when the app starts.
|
||||
if (is.null(shinysession$restoreContext)) {
|
||||
bookmarkStore <- getShinyOption("bookmarkStore", default = "disable")
|
||||
if (bookmarkStore == "disable") {
|
||||
# If bookmarking is disabled, use empty context
|
||||
shinysession$restoreContext <- RestoreContext$new()
|
||||
} else {
|
||||
# If there's bookmarked state, save it on the session object
|
||||
shinysession$restoreContext <- RestoreContext$new(msg$data$.clientdata_url_search)
|
||||
}
|
||||
# If there's bookmarked state, save it on the session object
|
||||
shinysession$restoreContext <- RestoreContext$new(msg$data$.clientdata_url_search)
|
||||
}
|
||||
|
||||
withRestoreContext(shinysession$restoreContext, {
|
||||
|
||||
msg$data <- applyInputHandlers(msg$data)
|
||||
unpackInput <- function(name, val) {
|
||||
splitName <- strsplit(name, ':')[[1]]
|
||||
if (length(splitName) > 1) {
|
||||
if (!inputHandlers$containsKey(splitName[[2]])) {
|
||||
# No input handler registered for this type
|
||||
stop("No handler registered for for type ", name)
|
||||
}
|
||||
|
||||
inputName <- splitName[[1]]
|
||||
|
||||
# Get the function for processing this type of input
|
||||
inputHandler <- inputHandlers$get(splitName[[2]])
|
||||
|
||||
return(inputHandler(val, shinysession, inputName))
|
||||
|
||||
} else if (is.list(val) && is.null(names(val))) {
|
||||
return(unlist(val, recursive = TRUE))
|
||||
} else {
|
||||
return(val)
|
||||
}
|
||||
}
|
||||
|
||||
msg$data <- mapply(unpackInput, names(msg$data), msg$data,
|
||||
SIMPLIFY = FALSE)
|
||||
|
||||
# Convert names like "button1:shiny.action" to "button1"
|
||||
names(msg$data) <- vapply(
|
||||
names(msg$data),
|
||||
function(name) { strsplit(name, ":")[[1]][1] },
|
||||
FUN.VALUE = character(1)
|
||||
)
|
||||
|
||||
|
||||
switch(
|
||||
msg$method,
|
||||
@@ -334,8 +354,10 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
|
||||
} else {
|
||||
flushReact()
|
||||
}
|
||||
|
||||
flushAllSessions()
|
||||
lapply(appsByToken$values(), function(shinysession) {
|
||||
shinysession$flushOutput()
|
||||
NULL
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -436,12 +458,6 @@ startApp <- function(appObj, port, host, quiet) {
|
||||
message('\n', 'Listening on domain socket ', port)
|
||||
}
|
||||
mask <- attr(port, 'mask')
|
||||
if (is.null(mask)) {
|
||||
stop("`port` is not a valid domain socket (missing `mask` attribute). ",
|
||||
"Note that if you're using the default `host` + `port` ",
|
||||
"configuration (and not domain sockets), then `port` must ",
|
||||
"be numeric, not a string.")
|
||||
}
|
||||
return(startPipeServer(port, mask, handlerManager$createHttpuvApp()))
|
||||
}
|
||||
}
|
||||
@@ -455,7 +471,10 @@ serviceApp <- function() {
|
||||
}
|
||||
|
||||
flushReact()
|
||||
flushAllSessions()
|
||||
|
||||
for (shinysession in appsByToken$values()) {
|
||||
shinysession$flushOutput()
|
||||
}
|
||||
}
|
||||
|
||||
# If this R session is interactive, then call service() with a short timeout
|
||||
@@ -468,9 +487,6 @@ serviceApp <- function() {
|
||||
|
||||
.shinyServerMinVersion <- '0.3.4'
|
||||
|
||||
# Global flag that's TRUE whenever we're inside of the scope of a call to runApp
|
||||
.globals$running <- FALSE
|
||||
|
||||
#' Run Shiny Application
|
||||
#'
|
||||
#' Runs a Shiny application. This function normally does not return; interrupt R
|
||||
@@ -512,9 +528,6 @@ serviceApp <- function() {
|
||||
#' application. If set to \code{"normal"}, displays the application normally.
|
||||
#' Defaults to \code{"auto"}, which displays the application in the mode given
|
||||
#' in its \code{DESCRIPTION} file, if any.
|
||||
#' @param test.mode Should the application be launched in test mode? This is
|
||||
#' only used for recording or running automated tests. Defaults to the
|
||||
#' \code{shiny.testmode} option, or FALSE if the option is not set.
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
@@ -527,8 +540,6 @@ serviceApp <- function() {
|
||||
#'
|
||||
#' ## Only run this example in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' options(device.ask.default = FALSE)
|
||||
#'
|
||||
#' # Apps can be run without a server.r and ui.r file
|
||||
#' runApp(list(
|
||||
#' ui = bootstrapPage(
|
||||
@@ -560,76 +571,27 @@ runApp <- function(appDir=getwd(),
|
||||
interactive()),
|
||||
host=getOption('shiny.host', '127.0.0.1'),
|
||||
workerId="", quiet=FALSE,
|
||||
display.mode=c("auto", "normal", "showcase"),
|
||||
test.mode=getOption('shiny.testmode', FALSE)) {
|
||||
display.mode=c("auto", "normal", "showcase")) {
|
||||
on.exit({
|
||||
handlerManager$clear()
|
||||
}, add = TRUE)
|
||||
|
||||
if (.globals$running) {
|
||||
stop("Can't call `runApp()` from within `runApp()`. If your ,",
|
||||
"application code contains `runApp()`, please remove it.")
|
||||
}
|
||||
.globals$running <- TRUE
|
||||
on.exit({
|
||||
.globals$running <- FALSE
|
||||
}, add = TRUE)
|
||||
|
||||
# Enable per-app Shiny options
|
||||
oldOptionSet <- .globals$options
|
||||
on.exit({
|
||||
.globals$options <- oldOptionSet
|
||||
},add = TRUE)
|
||||
|
||||
if (is.null(host) || is.na(host))
|
||||
host <- '0.0.0.0'
|
||||
|
||||
# Make warnings print immediately
|
||||
# Set pool.scheduler to support pool package
|
||||
ops <- options(warn = 1, pool.scheduler = scheduleTask)
|
||||
ops <- options(warn = 1)
|
||||
on.exit(options(ops), add = TRUE)
|
||||
|
||||
appParts <- as.shiny.appobj(appDir)
|
||||
|
||||
# The lines below set some of the app's running options, which
|
||||
# can be:
|
||||
# - left unspeficied (in which case the arguments' default
|
||||
# values from `runApp` kick in);
|
||||
# - passed through `shinyApp`
|
||||
# - passed through `runApp` (this function)
|
||||
# - passed through both `shinyApp` and `runApp` (the latter
|
||||
# takes precedence)
|
||||
#
|
||||
# Matrix of possibilities:
|
||||
# | IN shinyApp | IN runApp | result | check |
|
||||
# |-------------|-----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------|
|
||||
# | no | no | use defaults | exhaust all possibilities: if it's missing (runApp does not specify); THEN if it's not in shinyApp appParts$options; THEN use defaults |
|
||||
# | yes | no | use shinyApp | if it's missing (runApp does not specify); THEN if it's in shinyApp appParts$options; THEN use shinyApp |
|
||||
# | no | yes | use runApp | if it's not missing (runApp specifies), use those |
|
||||
# | yes | yes | use runApp | if it's not missing (runApp specifies), use those |
|
||||
#
|
||||
# I tried to make this as compact and intuitive as possible,
|
||||
# given that there are four distinct possibilities to check
|
||||
appOps <- appParts$options
|
||||
findVal <- function(arg, default) {
|
||||
if (arg %in% names(appOps)) appOps[[arg]] else default
|
||||
}
|
||||
|
||||
if (missing(port))
|
||||
port <- findVal("port", port)
|
||||
if (missing(launch.browser))
|
||||
launch.browser <- findVal("launch.browser", launch.browser)
|
||||
if (missing(host))
|
||||
host <- findVal("host", host)
|
||||
if (missing(quiet))
|
||||
quiet <- findVal("quiet", quiet)
|
||||
if (missing(display.mode))
|
||||
display.mode <- findVal("display.mode", display.mode)
|
||||
if (missing(test.mode))
|
||||
test.mode <- findVal("test.mode", test.mode)
|
||||
|
||||
if (is.null(host) || is.na(host)) host <- '0.0.0.0'
|
||||
|
||||
workerId(workerId)
|
||||
|
||||
if (inShinyServer()) {
|
||||
if (nzchar(Sys.getenv('SHINY_PORT'))) {
|
||||
# If SHINY_PORT is set, we're running under Shiny Server. Check the version
|
||||
# to make sure it is compatible. Older versions of Shiny Server don't set
|
||||
# SHINY_SERVER_VERSION, those will return "" which is considered less than
|
||||
@@ -646,11 +608,6 @@ runApp <- function(appDir=getwd(),
|
||||
# the display.mode parameter. The latter takes precedence.
|
||||
setShowcaseDefault(0)
|
||||
|
||||
.globals$testMode <- test.mode
|
||||
if (test.mode) {
|
||||
message("Running application in test mode.")
|
||||
}
|
||||
|
||||
# If appDir specifies a path, and display mode is specified in the
|
||||
# DESCRIPTION file at that path, apply it here.
|
||||
if (is.character(appDir)) {
|
||||
@@ -718,14 +675,7 @@ runApp <- function(appDir=getwd(),
|
||||
}
|
||||
else {
|
||||
# Try up to 20 random ports
|
||||
while (TRUE) {
|
||||
port <- p_randomInt(3000, 8000)
|
||||
# Reject ports in this range that are considered unsafe by Chrome
|
||||
# http://superuser.com/questions/188058/which-ports-are-considered-unsafe-on-chrome
|
||||
if (!port %in% c(3659, 4045, 6000, 6665:6669)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
port <- p_randomInt(3000, 8000)
|
||||
}
|
||||
|
||||
# Test port to see if we can use it
|
||||
@@ -738,11 +688,7 @@ runApp <- function(appDir=getwd(),
|
||||
}
|
||||
}
|
||||
|
||||
# Extract appOptions (which is a list) and store them as shinyOptions, for
|
||||
# this app. (This is the only place we have to store settings that are
|
||||
# accessible both the UI and server portion of the app.)
|
||||
unconsumeAppOptions(appParts$appOptions)
|
||||
|
||||
appParts <- as.shiny.appobj(appDir)
|
||||
# Set up the onEnd before we call onStart, so that it gets called even if an
|
||||
# error happens in onStart.
|
||||
if (!is.null(appParts$onEnd))
|
||||
@@ -781,16 +727,12 @@ runApp <- function(appDir=getwd(),
|
||||
# Top-level ..stacktraceoff..; matches with ..stacktraceon in observe(),
|
||||
# reactive(), Callbacks$invoke(), and others
|
||||
..stacktraceoff..(
|
||||
captureStackTraces({
|
||||
# If any observers were created before runApp was called, this will make
|
||||
# sure they run once the app starts. (Issue #1013)
|
||||
scheduleFlush()
|
||||
|
||||
captureStackTraces(
|
||||
while (!.globals$stopped) {
|
||||
serviceApp()
|
||||
Sys.sleep(0.001)
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
if (isTRUE(.globals$reterror)) {
|
||||
@@ -809,6 +751,7 @@ runApp <- function(appDir=getwd(),
|
||||
#'
|
||||
#' @param returnValue The value that should be returned from
|
||||
#' \code{\link{runApp}}.
|
||||
#'
|
||||
#' @export
|
||||
stopApp <- function(returnValue = invisible()) {
|
||||
# reterror will indicate whether retval is an error (i.e. it should be passed
|
||||
@@ -927,6 +870,7 @@ runExample <- function(example=NA,
|
||||
#' # ...or as a single app object
|
||||
#' runGadget(shinyApp(ui, server))
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
runGadget <- function(app, server = NULL, port = getOption("shiny.port"),
|
||||
viewer = paneViewer(), stopOnCancel = TRUE) {
|
||||
@@ -1022,9 +966,3 @@ browserViewer <- function(browser = getOption("browser")) {
|
||||
utils::browseURL(url, browser = browser)
|
||||
}
|
||||
}
|
||||
|
||||
# Returns TRUE if we're running in Shiny Server or other hosting environment,
|
||||
# otherwise returns FALSE.
|
||||
inShinyServer <- function() {
|
||||
nzchar(Sys.getenv('SHINY_PORT'))
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ shinyOptions <- function(...) {
|
||||
newOpts <- list(...)
|
||||
|
||||
if (length(newOpts) > 0) {
|
||||
.globals$options <- dropNulls(mergeVectors(.globals$options, newOpts))
|
||||
.globals$options <- mergeVectors(.globals$options, newOpts)
|
||||
invisible(.globals$options)
|
||||
} else {
|
||||
.globals$options
|
||||
@@ -55,29 +55,3 @@ withLocalOptions <- function(expr) {
|
||||
|
||||
expr
|
||||
}
|
||||
|
||||
|
||||
# Get specific shiny options and put them in a list, reset those shiny options,
|
||||
# and then return the options list. This should be during the creation of a
|
||||
# shiny app object, which happens before another option frame is added to the
|
||||
# options stack (the new option frame is added when the app is run). This
|
||||
# function "consumes" the options when the shinyApp object is created, so the
|
||||
# options won't affect another app that is created later.
|
||||
consumeAppOptions <- function() {
|
||||
options <- list(
|
||||
appDir = getwd(),
|
||||
bookmarkStore = getShinyOption("bookmarkStore")
|
||||
)
|
||||
|
||||
shinyOptions(appDir = NULL, bookmarkStore = NULL)
|
||||
|
||||
options
|
||||
}
|
||||
|
||||
# Do the inverse of consumeAppOptions. This should be called once the app is
|
||||
# started.
|
||||
unconsumeAppOptions <- function(options) {
|
||||
if (!is.null(options)) {
|
||||
do.call(shinyOptions, options)
|
||||
}
|
||||
}
|
||||
|
||||
48
R/shinyui.R
48
R/shinyui.R
@@ -24,7 +24,7 @@ withMathJax <- function(...) {
|
||||
)
|
||||
}
|
||||
|
||||
renderPage <- function(ui, connection, showcase=0, testMode=FALSE) {
|
||||
renderPage <- function(ui, connection, showcase=0) {
|
||||
# If the ui is a NOT complete document (created by htmlTemplate()), then do some
|
||||
# preprocessing and make sure it's a complete document.
|
||||
if (!inherits(ui, "html_document")) {
|
||||
@@ -44,20 +44,12 @@ renderPage <- function(ui, connection, showcase=0, testMode=FALSE) {
|
||||
|
||||
shiny_deps <- list(
|
||||
htmlDependency("json2", "2014.02.04", c(href="shared"), script = "json2-min.js"),
|
||||
htmlDependency("jquery", "1.12.4", c(href="shared"), script = "jquery.min.js"),
|
||||
htmlDependency("jquery", "1.11.3", c(href="shared"), script = "jquery.min.js"),
|
||||
htmlDependency("babel-polyfill", "6.7.2", c(href="shared"), script = "babel-polyfill.min.js"),
|
||||
htmlDependency("shiny", utils::packageVersion("shiny"), c(href="shared"),
|
||||
script = if (getOption("shiny.minified", TRUE)) "shiny.min.js" else "shiny.js",
|
||||
stylesheet = "shiny.css")
|
||||
)
|
||||
|
||||
if (testMode) {
|
||||
# Add code injection listener if in test mode
|
||||
shiny_deps[[length(shiny_deps) + 1]] <-
|
||||
htmlDependency("shiny-testmode", utils::packageVersion("shiny"),
|
||||
c(href="shared"), script = "shiny-testmode.js")
|
||||
}
|
||||
|
||||
html <- renderDocument(ui, shiny_deps, processDep = createWebDependency)
|
||||
writeUTF8(html, con = connection)
|
||||
}
|
||||
@@ -72,6 +64,7 @@ renderPage <- function(ui, connection, showcase=0, testMode=FALSE) {
|
||||
#'
|
||||
#' @param ui A user interace definition
|
||||
#' @return The user interface definition, without modifications or side effects.
|
||||
#'
|
||||
#' @export
|
||||
shinyUI <- function(ui) {
|
||||
.globals$ui <- list(ui)
|
||||
@@ -98,40 +91,23 @@ uiHttpHandler <- function(ui, uiPattern = "^/$") {
|
||||
if (!is.null(mode))
|
||||
showcaseMode <- mode
|
||||
}
|
||||
|
||||
testMode <- .globals$testMode %OR% FALSE
|
||||
|
||||
# Create a restore context using query string
|
||||
bookmarkStore <- getShinyOption("bookmarkStore", default = "disable")
|
||||
if (bookmarkStore == "disable") {
|
||||
# If bookmarking is disabled, use empty context
|
||||
restoreContext <- RestoreContext$new()
|
||||
} else {
|
||||
restoreContext <- RestoreContext$new(req$QUERY_STRING)
|
||||
}
|
||||
|
||||
withRestoreContext(restoreContext, {
|
||||
uiValue <- NULL
|
||||
|
||||
if (is.function(ui)) {
|
||||
uiValue <- if (is.function(ui)) {
|
||||
withRestoreContext(RestoreContext$new(req$QUERY_STRING), {
|
||||
if (length(formals(ui)) > 0) {
|
||||
# No corresponding ..stacktraceoff.., this is pure user code
|
||||
uiValue <- ..stacktraceon..(ui(req))
|
||||
..stacktraceon..(ui(req))
|
||||
} else {
|
||||
# No corresponding ..stacktraceoff.., this is pure user code
|
||||
uiValue <- ..stacktraceon..(ui())
|
||||
..stacktraceon..(ui())
|
||||
}
|
||||
} else {
|
||||
if (getCurrentRestoreContext()$active) {
|
||||
warning("Trying to restore saved app state, but UI code must be a function for this to work! See ?enableBookmarking")
|
||||
}
|
||||
uiValue <- ui
|
||||
}
|
||||
})
|
||||
})
|
||||
} else {
|
||||
ui
|
||||
}
|
||||
if (is.null(uiValue))
|
||||
return(NULL)
|
||||
|
||||
renderPage(uiValue, textConn, showcaseMode, testMode)
|
||||
renderPage(uiValue, textConn, showcaseMode)
|
||||
html <- paste(readLines(textConn, encoding = 'UTF-8'), collapse='\n')
|
||||
return(httpResponse(200, content=enc2utf8(html)))
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ globalVariables('func')
|
||||
#' dynamically generated UIs, such as those created by Shiny code snippets
|
||||
#' embedded in R Markdown documents).
|
||||
#' @return The \code{renderFunc} function, with annotations.
|
||||
#'
|
||||
#' @export
|
||||
markRenderFunction <- function(uiFunc, renderFunc, outputArgs = list()) {
|
||||
# a mutable object that keeps track of whether `useRenderFunction` has been
|
||||
@@ -127,7 +128,6 @@ as.tags.shiny.render.function <- function(x, ..., inline = FALSE) {
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' options(device.ask.default = FALSE)
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' sliderInput("n", "Number of observations", 2, 1000, 500),
|
||||
@@ -221,7 +221,7 @@ renderImage <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
#'
|
||||
#' Makes a reactive version of the given function that captures any printed
|
||||
#' output, and also captures its printable result (unless
|
||||
#' \code{\link[base]{invisible}}), into a string. The resulting function is suitable
|
||||
#' \code{\link{invisible}}), into a string. The resulting function is suitable
|
||||
#' for assigning to an \code{output} slot.
|
||||
#'
|
||||
#' The corresponding HTML output tag can be anything (though \code{pre} is
|
||||
@@ -233,14 +233,14 @@ renderImage <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
#'
|
||||
#' Note that unlike most other Shiny output functions, if the given function
|
||||
#' returns \code{NULL} then \code{NULL} will actually be visible in the output.
|
||||
#' To display nothing, make your function return \code{\link[base]{invisible}()}.
|
||||
#' To display nothing, make your function return \code{\link{invisible}()}.
|
||||
#'
|
||||
#' @param expr An expression that may print output and/or return a printable R
|
||||
#' object.
|
||||
#' @param env The environment in which to evaluate \code{expr}.
|
||||
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
|
||||
#' is useful if you want to save an expression in a variable.
|
||||
#' @param width The value for \code{\link[base]{options}('width')}.
|
||||
#' @param width The value for \code{\link{options}('width')}.
|
||||
#' @param outputArgs A list of arguments to be passed through to the implicit
|
||||
#' call to \code{\link{verbatimTextOutput}} when \code{renderPrint} is used
|
||||
#' in an interactive R Markdown document.
|
||||
@@ -248,6 +248,7 @@ renderImage <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
#' function, instead of the printed output.
|
||||
#'
|
||||
#' @example res/text-example.R
|
||||
#'
|
||||
#' @export
|
||||
renderPrint <- function(expr, env = parent.frame(), quoted = FALSE,
|
||||
width = getOption('width'), outputArgs=list()) {
|
||||
@@ -288,6 +289,7 @@ renderPrint <- function(expr, env = parent.frame(), quoted = FALSE,
|
||||
#' function, rather than the returned text value.
|
||||
#'
|
||||
#' @example res/text-example.R
|
||||
#'
|
||||
#' @export
|
||||
renderText <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
outputArgs=list()) {
|
||||
@@ -319,6 +321,7 @@ renderText <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
#' interactive R Markdown document.
|
||||
#'
|
||||
#' @seealso conditionalPanel
|
||||
#'
|
||||
#' @export
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
@@ -354,98 +357,6 @@ renderUI <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
markRenderFunction(uiOutput, renderFunc, outputArgs = outputArgs)
|
||||
}
|
||||
|
||||
#' @export
|
||||
serveJSON <- function(expr, env=parent.frame(), quoted=FALSE) {
|
||||
installExprFunction(expr, "func", env, quoted)
|
||||
function() {
|
||||
structure(
|
||||
toJSON(func(), pretty = TRUE),
|
||||
content.type = "application/json"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#' @export
|
||||
servePlot <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
defaultWidth = 600, defaultHeight = 400) {
|
||||
|
||||
if (!is.function(defaultWidth))
|
||||
defaultWidth <- valueToFunc(defaultWidth)
|
||||
if (!is.function(defaultHeight))
|
||||
defaultHeight <- valueToFunc(defaultHeight)
|
||||
|
||||
installExprFunction(expr, "func", env, quoted)
|
||||
function() {
|
||||
input <- getDefaultReactiveDomain()$input
|
||||
w <- if (!is.null(input$`plot-width`)) as.numeric(input$`plot-width`) else defaultWidth()
|
||||
h <- if (!is.null(input$`plot-height`)) as.numeric(input$`plot-height`) else defaultHeight()
|
||||
|
||||
pngfile <- plotPNG(function() {
|
||||
result <- withVisible(func())
|
||||
if (result$visible) {
|
||||
# Use capture.output to squelch printing to the actual console; we
|
||||
# are only interested in plot output
|
||||
utils::capture.output({
|
||||
# The value needs to be printed just in case it's an object that
|
||||
# requires printing to generate plot output, similar to ggplot2. But
|
||||
# for base graphics, it would already have been rendered when func was
|
||||
# called above, and the print should have no effect.
|
||||
print(result$value)
|
||||
})
|
||||
}
|
||||
}, width = w, height = h)
|
||||
|
||||
structure(
|
||||
list(file = pngfile, owned = TRUE),
|
||||
content.type = "image/png"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#' @importFrom utils write.csv
|
||||
#' @export
|
||||
serveCSV <- function(expr, env=parent.frame(), quoted=FALSE, row.names=FALSE) {
|
||||
installExprFunction(expr, "func", env, quoted)
|
||||
function() {
|
||||
tmp <- tempfile(".csv")
|
||||
write.csv(func(), tmp, row.names=row.names)
|
||||
structure(
|
||||
list(file = tmp, owned = TRUE),
|
||||
content.type = "text/csv"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#' @export
|
||||
serveText <- function(expr, env=parent.frame(), quoted=FALSE) {
|
||||
installExprFunction(expr, "func", env, quoted)
|
||||
function() {
|
||||
structure(
|
||||
paste(func(), collapse = "\n"),
|
||||
content.type = "text/plain"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#' @export
|
||||
serveRaw <- function(expr, env=parent.frame(), quoted=FALSE, contentType) {
|
||||
|
||||
if (!is.function(contentType))
|
||||
contentType <- valueToFunc(contentType)
|
||||
|
||||
installExprFunction(expr, "func", env, quoted)
|
||||
function() {
|
||||
bytes <- func()
|
||||
if (!is.raw(bytes)) {
|
||||
stop("serveRaw expects raw vector data")
|
||||
}
|
||||
structure(
|
||||
bytes,
|
||||
content.type = contentType()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#' File Downloads
|
||||
#'
|
||||
#' Allows content from the Shiny application to be made available to the user as
|
||||
@@ -513,7 +424,7 @@ downloadHandler <- function(filename, content, contentType=NA, outputArgs=list()
|
||||
#' the server infrastructure.
|
||||
#'
|
||||
#' For the \code{options} argument, the character elements that have the class
|
||||
#' \code{"AsIs"} (usually returned from \code{\link[base]{I}()}) will be evaluated in
|
||||
#' \code{"AsIs"} (usually returned from \code{\link{I}()}) will be evaluated in
|
||||
#' JavaScript. This is useful when the type of the option value is not supported
|
||||
#' in JSON, e.g., a JavaScript function, which can be obtained by evaluating a
|
||||
#' character string. Note this only applies to the root-level elements of the
|
||||
|
||||
@@ -31,7 +31,7 @@ licenseLink <- function(licenseName) {
|
||||
showcaseHead <- function() {
|
||||
|
||||
deps <- list(
|
||||
htmlDependency("jqueryui", "1.12.1", c(href="shared/jqueryui"),
|
||||
htmlDependency("jqueryui", "1.11.4", c(href="shared/jqueryui"),
|
||||
script = "jquery-ui.min.js"),
|
||||
htmlDependency("showdown", "0.3.1", c(href="shared/showdown/compressed"),
|
||||
script = "showdown.js"),
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
#' Register expressions for export in test mode
|
||||
#'
|
||||
#' This function registers expressions that will be evaluated when a test export
|
||||
#' event occurs. These events are triggered by accessing a snapshot URL.
|
||||
#'
|
||||
#' This function only has an effect if the app is launched in test mode. This is
|
||||
#' done by calling \code{runApp()} with \code{test.mode=TRUE}, or by setting the
|
||||
#' global option \code{shiny.testmode} to \code{TRUE}.
|
||||
#'
|
||||
#' @param quoted_ Are the expression quoted? Default is \code{FALSE}.
|
||||
#' @param env_ The environment in which the expression should be evaluated.
|
||||
#' @param session_ A Shiny session object.
|
||||
#' @param ... Named arguments that are quoted or unquoted expressions that will
|
||||
#' be captured and evaluated when snapshot URL is visited.
|
||||
#' @examples
|
||||
#' ## Only run this example in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#'
|
||||
#' options(shiny.testmode = TRUE)
|
||||
#'
|
||||
#' # This application shows the test snapshot URL; clicking on it will
|
||||
#' # fetch the input, output, and exported values in JSON format.
|
||||
#' shinyApp(
|
||||
#' ui = basicPage(
|
||||
#' h4("Snapshot URL: "),
|
||||
#' uiOutput("url"),
|
||||
#' h4("Current values:"),
|
||||
#' verbatimTextOutput("values"),
|
||||
#' actionButton("inc", "Increment x")
|
||||
#' ),
|
||||
#'
|
||||
#' server = function(input, output, session) {
|
||||
#' vals <- reactiveValues(x = 1)
|
||||
#' y <- reactive({ vals$x + 1 })
|
||||
#'
|
||||
#' observeEvent(input$inc, {
|
||||
#' vals$x <<- vals$x + 1
|
||||
#' })
|
||||
#'
|
||||
#' exportTestValues(
|
||||
#' x = vals$x,
|
||||
#' y = y()
|
||||
#' )
|
||||
#'
|
||||
#' output$url <- renderUI({
|
||||
#' url <- session$getTestSnapshotUrl(format="json")
|
||||
#' a(href = url, url)
|
||||
#' })
|
||||
#'
|
||||
#' output$values <- renderText({
|
||||
#' paste0("vals$x: ", vals$x, "\ny: ", y())
|
||||
#' })
|
||||
#' }
|
||||
#' )
|
||||
#' }
|
||||
#' @export
|
||||
exportTestValues <- function(..., quoted_ = FALSE, env_ = parent.frame(),
|
||||
session_ = getDefaultReactiveDomain())
|
||||
{
|
||||
session_$exportTestValues(..., quoted_ = quoted_, env_ = env_)
|
||||
}
|
||||
20
R/timer.R
20
R/timer.R
@@ -22,11 +22,6 @@ TimerCallbacks <- R6Class(
|
||||
.times <<- data.frame()
|
||||
},
|
||||
schedule = function(millis, func) {
|
||||
# If args could fail to evaluate, let's make them do that before
|
||||
# we change any state
|
||||
force(millis)
|
||||
force(func)
|
||||
|
||||
id <- .nextId
|
||||
.nextId <<- .nextId + 1L
|
||||
|
||||
@@ -61,7 +56,7 @@ TimerCallbacks <- R6Class(
|
||||
},
|
||||
executeElapsed = function() {
|
||||
elapsed <- takeElapsed()
|
||||
if (nrow(elapsed) == 0)
|
||||
if (length(elapsed) == 0)
|
||||
return(FALSE)
|
||||
|
||||
for (id in elapsed$id) {
|
||||
@@ -76,16 +71,3 @@ TimerCallbacks <- R6Class(
|
||||
)
|
||||
|
||||
timerCallbacks <- TimerCallbacks$new()
|
||||
|
||||
scheduleTask <- function(millis, callback) {
|
||||
cancelled <- FALSE
|
||||
timerCallbacks$schedule(millis, function() {
|
||||
if (!cancelled)
|
||||
callback()
|
||||
})
|
||||
|
||||
function() {
|
||||
cancelled <<- TRUE
|
||||
callback <<- NULL # to allow for callback to be gc'ed
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,44 +39,6 @@ updateTextInput <- function(session, inputId, label = NULL, value = NULL) {
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
|
||||
#' Change the value of a textarea input on the client
|
||||
#'
|
||||
#' @template update-input
|
||||
#' @param value The value to set for the input object.
|
||||
#'
|
||||
#' @seealso \code{\link{textAreaInput}}
|
||||
#'
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' sliderInput("controller", "Controller", 0, 20, 10),
|
||||
#' textAreaInput("inText", "Input textarea"),
|
||||
#' textAreaInput("inText2", "Input textarea 2")
|
||||
#' )
|
||||
#'
|
||||
#' server <- function(input, output, session) {
|
||||
#' observe({
|
||||
#' # We'll use the input$controller variable multiple times, so save it as x
|
||||
#' # for convenience.
|
||||
#' x <- input$controller
|
||||
#'
|
||||
#' # This will change the value of input$inText, based on x
|
||||
#' updateTextAreaInput(session, "inText", value = paste("New text", x))
|
||||
#'
|
||||
#' # Can also set the label, this time for input$inText2
|
||||
#' updateTextAreaInput(session, "inText2",
|
||||
#' label = paste("New label", x),
|
||||
#' value = paste("New text", x))
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#' @export
|
||||
updateTextAreaInput <- updateTextInput
|
||||
|
||||
|
||||
#' Change the value of a checkbox input on the client
|
||||
#'
|
||||
@@ -166,7 +128,7 @@ updateActionButton <- function(session, inputId, label = NULL, icon = NULL) {
|
||||
#'
|
||||
#' @template update-input
|
||||
#' @param value The desired date value. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format. Supply \code{NA} to clear the date.
|
||||
#' \code{yyyy-mm-dd} format.
|
||||
#' @param min The minimum allowed date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format.
|
||||
#' @param max The maximum allowed date. Either a Date object, or a string in
|
||||
@@ -179,18 +141,21 @@ updateActionButton <- function(session, inputId, label = NULL, icon = NULL) {
|
||||
#' if (interactive()) {
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' sliderInput("n", "Day of month", 1, 30, 10),
|
||||
#' sliderInput("controller", "Controller", 1, 30, 10),
|
||||
#' dateInput("inDate", "Input date")
|
||||
#' )
|
||||
#'
|
||||
#' server <- function(input, output, session) {
|
||||
#' observe({
|
||||
#' date <- as.Date(paste0("2013-04-", input$n))
|
||||
#' # We'll use the input$controller variable multiple times, so save it as x
|
||||
#' # for convenience.
|
||||
#' x <- input$controller
|
||||
#'
|
||||
#' updateDateInput(session, "inDate",
|
||||
#' label = paste("Date label", input$n),
|
||||
#' value = date,
|
||||
#' min = date - 3,
|
||||
#' max = date + 3
|
||||
#' label = paste("Date label", x),
|
||||
#' value = paste("2013-04-", x, sep=""),
|
||||
#' min = paste("2013-04-", x-1, sep=""),
|
||||
#' max = paste("2013-04-", x+1, sep="")
|
||||
#' )
|
||||
#' })
|
||||
#' }
|
||||
@@ -201,18 +166,11 @@ 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)
|
||||
# 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")
|
||||
|
||||
message <- dropNulls(list(label=label, value=value, min=min, max=max))
|
||||
session$sendInputMessage(inputId, message)
|
||||
@@ -223,9 +181,9 @@ updateDateInput <- function(session, inputId, label = NULL, value = NULL,
|
||||
#'
|
||||
#' @template update-input
|
||||
#' @param start The start date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format. Supplying \code{NA} clears the start date.
|
||||
#' \code{yyyy-mm-dd} format.
|
||||
#' @param end The end date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format. Supplying \code{NA} clears the end date.
|
||||
#' \code{yyyy-mm-dd} format.
|
||||
#' @param min The minimum allowed date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format.
|
||||
#' @param max The maximum allowed date. Either a Date object, or a string in
|
||||
@@ -238,20 +196,20 @@ updateDateInput <- function(session, inputId, label = NULL, value = NULL,
|
||||
#' if (interactive()) {
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' sliderInput("n", "Day of month", 1, 30, 10),
|
||||
#' sliderInput("controller", "Controller", 1, 30, 10),
|
||||
#' dateRangeInput("inDateRange", "Input date range")
|
||||
#' )
|
||||
#'
|
||||
#' server <- function(input, output, session) {
|
||||
#' observe({
|
||||
#' date <- as.Date(paste0("2013-04-", input$n))
|
||||
#' # We'll use the input$controller variable multiple times, so save it as x
|
||||
#' # for convenience.
|
||||
#' x <- input$controller
|
||||
#'
|
||||
#' updateDateRangeInput(session, "inDateRange",
|
||||
#' label = paste("Date range label", input$n),
|
||||
#' start = date - 1,
|
||||
#' end = date + 1,
|
||||
#' min = date - 5,
|
||||
#' max = date + 5
|
||||
#' label = paste("Date range label", x),
|
||||
#' start = paste("2013-01-", x, sep=""),
|
||||
#' end = paste("2013-12-", x, sep="")
|
||||
#' )
|
||||
#' })
|
||||
#' }
|
||||
@@ -271,7 +229,7 @@ updateDateRangeInput <- function(session, inputId, label = NULL,
|
||||
|
||||
message <- dropNulls(list(
|
||||
label = label,
|
||||
value = dropNulls(list(start = start, end = end)),
|
||||
value = c(start, end),
|
||||
min = min,
|
||||
max = max
|
||||
))
|
||||
@@ -457,11 +415,11 @@ updateInputOptions <- function(session, inputId, label = NULL, choices = NULL,
|
||||
if (!is.null(choices))
|
||||
choices <- choicesWithNames(choices)
|
||||
if (!is.null(selected))
|
||||
selected <- validateSelected(selected, choices, session$ns(inputId))
|
||||
selected <- validateSelected(selected, choices, inputId)
|
||||
|
||||
options <- if (!is.null(choices)) {
|
||||
format(tagList(
|
||||
generateOptions(session$ns(inputId), choices, selected, inline, type = type)
|
||||
generateOptions(inputId, choices, selected, inline, type = type)
|
||||
))
|
||||
}
|
||||
|
||||
@@ -622,7 +580,7 @@ updateSelectizeInput <- function(session, inputId, label = NULL, choices = NULL,
|
||||
res <- checkAsIs(options)
|
||||
cfg <- tags$script(
|
||||
type = 'application/json',
|
||||
`data-for` = session$ns(inputId),
|
||||
`data-for` = inputId,
|
||||
`data-eval` = if (length(res$eval)) HTML(toJSON(res$eval)),
|
||||
HTML(toJSON(res$options))
|
||||
)
|
||||
|
||||
235
R/utils.R
235
R/utils.R
@@ -23,6 +23,7 @@ NULL
|
||||
#' rnormA(3) # [1] 1.8285879 -0.7468041 -0.4639111
|
||||
#' rnormA(5) # [1] 1.8285879 -0.7468041 -0.4639111 -1.6510126 -1.4686924
|
||||
#' rnormB(5) # [1] -0.7946034 0.2568374 -0.6567597 1.2451387 -0.8375699
|
||||
#'
|
||||
#' @export
|
||||
repeatable <- function(rngfunc, seed = stats::runif(1, 0, .Machine$integer.max)) {
|
||||
force(seed)
|
||||
@@ -182,16 +183,6 @@ anyUnnamed <- function(x) {
|
||||
any(!nzchar(nms))
|
||||
}
|
||||
|
||||
|
||||
# Given a vector/list, returns a named vector (the labels will be blank).
|
||||
asNamedVector <- function(x) {
|
||||
if (!is.null(names(x)))
|
||||
return(x)
|
||||
|
||||
names(x) <- rep.int("", length(x))
|
||||
x
|
||||
}
|
||||
|
||||
# Given two named vectors, join them together, and keep only the last element
|
||||
# with a given name in the resulting vector. If b has any elements with the same
|
||||
# name as elements in a, the element in a is dropped. Also, if there are any
|
||||
@@ -206,30 +197,6 @@ mergeVectors <- function(a, b) {
|
||||
x[!drop_idx]
|
||||
}
|
||||
|
||||
# Sort a vector by the names of items. If there are multiple items with the
|
||||
# same name, preserve the original order of those items. For empty
|
||||
# vectors/lists/NULL, return the original value.
|
||||
sortByName <- function(x) {
|
||||
if (anyUnnamed(x))
|
||||
stop("All items must be named")
|
||||
|
||||
# Special case for empty vectors/lists, and NULL
|
||||
if (length(x) == 0)
|
||||
return(x)
|
||||
|
||||
x[order(names(x))]
|
||||
}
|
||||
|
||||
# Wrapper around list2env with a NULL check. In R <3.2.0, if an empty unnamed
|
||||
# list is passed to list2env(), it errors. But an empty named list is OK. For
|
||||
# R >=3.2.0, this wrapper is not necessary.
|
||||
list2env2 <- function(x, ...) {
|
||||
# Ensure that zero-length lists have a name attribute
|
||||
if (length(x) == 0)
|
||||
attr(x, "names") <- character(0)
|
||||
|
||||
list2env(x, ...)
|
||||
}
|
||||
|
||||
# Combine dir and (file)name into a file path. If a file already exists with a
|
||||
# name differing only by case, then use it instead.
|
||||
@@ -444,6 +411,7 @@ makeFunction <- function(args = pairlist(), body, env = parent.frame()) {
|
||||
#'
|
||||
#' isolate(tripleA())
|
||||
#' # "text, text, text"
|
||||
#'
|
||||
#' @export
|
||||
exprToFunction <- function(expr, env=parent.frame(), quoted=FALSE) {
|
||||
if (!quoted) {
|
||||
@@ -477,6 +445,7 @@ exprToFunction <- function(expr, env=parent.frame(), quoted=FALSE) {
|
||||
#' the name of the calling function.
|
||||
#' @param wrappedWithLabel,..stacktraceon Advanced use only. For stack manipulation purposes; see
|
||||
#' \code{\link{stacktrace}}.
|
||||
#'
|
||||
#' @export
|
||||
installExprFunction <- function(expr, name, eval.env = parent.frame(2),
|
||||
quoted = FALSE,
|
||||
@@ -576,20 +545,6 @@ parseQueryString <- function(str, nested = FALSE) {
|
||||
res
|
||||
}
|
||||
|
||||
parseQueryStringJSON <- function(str, nested = FALSE) {
|
||||
vals <- parseQueryString(str, nested)
|
||||
mapply(names(vals), vals, SIMPLIFY = FALSE,
|
||||
FUN = function(name, value) {
|
||||
tryCatch(
|
||||
jsonlite::fromJSON(value),
|
||||
error = function(e) {
|
||||
stop("Failed to parse URL parameter \"", name, "\"")
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
# Assign value to the bottom element of the list x using recursive indices idx
|
||||
assignNestedList <- function(x = list(), idx, value) {
|
||||
for (i in seq_along(idx)) {
|
||||
@@ -1028,62 +983,6 @@ safeError <- function(error) {
|
||||
error
|
||||
}
|
||||
|
||||
#***********************************************************************#
|
||||
#**** Keep this function internal for now, may chnage in the future ****#
|
||||
#***********************************************************************#
|
||||
# #' Propagate an error through Shiny, but catch it before it throws
|
||||
# #'
|
||||
# #' Throws a type of exception that is caught by observers. When such an
|
||||
# #' exception is triggered, all reactive links are broken. So, essentially,
|
||||
# #' \code{reactiveStop()} behaves just like \code{stop()}, except that
|
||||
# #' instead of ending the session, it is silently swalowed by Shiny.
|
||||
# #'
|
||||
# #' This function should be used when you want to disrupt the reactive
|
||||
# #' links in a reactive chain, but do not want to end the session. For
|
||||
# #' example, this enables you to disallow certain inputs, but get back
|
||||
# #' to business as usual when valid inputs are re-entered.
|
||||
# #' \code{reactiveStop} is also called internally by Shiny to create
|
||||
# #' special errors, such as the ones generated by \code{\link{validate}()},
|
||||
# #' \code{\link{req}()} and \code{\link{cancelOutput}()}.
|
||||
# #'
|
||||
# #' @param message An optional error message.
|
||||
# #' @param class An optional class to add to the error.
|
||||
# #' @export
|
||||
# #' @examples
|
||||
# #' ## Note: the breaking of the reactive chain that happens in the app
|
||||
# #' ## below (when input$txt = 'bad' and input$allowBad = 'FALSE') is
|
||||
# #' ## easily visualized with `showReactLog()`
|
||||
# #'
|
||||
# #' ## Only run examples in interactive R sessions
|
||||
# #' if (interactive()) {
|
||||
# #'
|
||||
# #' ui <- fluidPage(
|
||||
# #' textInput('txt', 'Enter some text...'),
|
||||
# #' selectInput('allowBad', 'Allow the string \'bad\'?',
|
||||
# #' c('TRUE', 'FALSE'), selected = 'FALSE')
|
||||
# #' )
|
||||
# #'
|
||||
# #' server <- function(input, output) {
|
||||
# #' val <- reactive({
|
||||
# #' if (!(as.logical(input$allowBad))) {
|
||||
# #' if (identical(input$txt, "bad")) {
|
||||
# #' reactiveStop()
|
||||
# #' }
|
||||
# #' }
|
||||
## ' })
|
||||
# #'
|
||||
# #' observe({
|
||||
# #' val()
|
||||
# #' })
|
||||
# #' }
|
||||
# #'
|
||||
# #' shinyApp(ui, server)
|
||||
# #' }
|
||||
# #' @export
|
||||
reactiveStop <- function(message = "", class = NULL) {
|
||||
stopWithCondition(c("shiny.silent.error", class), message)
|
||||
}
|
||||
|
||||
#' Validate input values and other conditions
|
||||
#'
|
||||
#' For an output rendering function (e.g. \code{\link{renderPlot}()}), you may
|
||||
@@ -1140,17 +1039,15 @@ reactiveStop <- function(message = "", class = NULL) {
|
||||
#' \code{shiny-output-error-} prepended to this value.
|
||||
#' @export
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' options(device.ask.default = FALSE)
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' # in ui.R
|
||||
#' fluidPage(
|
||||
#' checkboxGroupInput('in1', 'Check some letters', choices = head(LETTERS)),
|
||||
#' selectizeInput('in2', 'Select a state', choices = state.name),
|
||||
#' plotOutput('plot')
|
||||
#' )
|
||||
#'
|
||||
#' server <- function(input, output) {
|
||||
#' # in server.R
|
||||
#' function(input, output) {
|
||||
#' output$plot <- renderPlot({
|
||||
#' validate(
|
||||
#' need(input$in1, 'Check at least one letter!'),
|
||||
@@ -1159,10 +1056,6 @@ reactiveStop <- function(message = "", class = NULL) {
|
||||
#' plot(1:10, main = paste(c(input$in1, input$in2), collapse = ', '))
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
#'
|
||||
#' }
|
||||
validate <- function(..., errorClass = character(0)) {
|
||||
results <- sapply(list(...), function(x) {
|
||||
# Detect NULL or NA
|
||||
@@ -1183,7 +1076,8 @@ validate <- function(..., errorClass = character(0)) {
|
||||
# There may be empty strings remaining; these are message-less failures that
|
||||
# started as FALSE
|
||||
results <- results[nzchar(results)]
|
||||
reactiveStop(paste(results, collapse="\n"), c(errorClass, "validation"))
|
||||
stopWithCondition(c("validation", "shiny.silent.error", errorClass),
|
||||
paste(results, collapse="\n"))
|
||||
}
|
||||
|
||||
#' @param expr An expression to test. The condition will pass if the expression
|
||||
@@ -1245,7 +1139,7 @@ need <- function(expr, message = paste(label, "must be provided"), label) {
|
||||
#' \strong{Truthy and falsy values}
|
||||
#'
|
||||
#' The terms "truthy" and "falsy" generally indicate whether a value, when
|
||||
#' coerced to a \code{\link[base]{logical}}, is \code{TRUE} or \code{FALSE}. We use
|
||||
#' coerced to a \code{\link{logical}}, is \code{TRUE} or \code{FALSE}. We use
|
||||
#' the term a little loosely here; our usage tries to match the intuitive
|
||||
#' notions of "Is this value missing or available?", or "Has the user provided
|
||||
#' an answer?", or in the case of action buttons, "Has the button been
|
||||
@@ -1277,67 +1171,20 @@ need <- function(expr, message = paste(label, "must be provided"), label) {
|
||||
#'
|
||||
#' \code{req(input$a != 0)}
|
||||
#'
|
||||
#' \strong{Using \code{req(FALSE)}}
|
||||
#'
|
||||
#' You can use \code{req(FALSE)} (i.e. no condition) if you've already performed
|
||||
#' all the checks you needed to by that point and just want to stop the reactive
|
||||
#' chain now. There is no advantange to this, except perhaps ease of readibility
|
||||
#' if you have a complicated condition to check for (or perhaps if you'd like to
|
||||
#' divide your condition into nested \code{if} statements).
|
||||
#'
|
||||
#' \strong{Using \code{cancelOutput = TRUE}}
|
||||
#'
|
||||
#' When \code{req(..., cancelOutput = TRUE)} is used, the "silent" exception is
|
||||
#' also raised, but it is treated slightly differently if one or more outputs are
|
||||
#' currently being evaluated. In those cases, the reactive chain does not proceed
|
||||
#' or update, but the output(s) are left is whatever state they happen to be in
|
||||
#' (whatever was their last valid state).
|
||||
#'
|
||||
#' Note that this is always going to be the case if
|
||||
#' this is used inside an output context (e.g. \code{output$txt <- ...}). It may
|
||||
#' or may not be the case if it is used inside a non-output context (e.g.
|
||||
#' \code{\link{reactive}}, \code{\link{observe}} or \code{\link{observeEvent}})
|
||||
#' -- depending on whether or not there is an \code{output$...} that is triggered
|
||||
#' as a result of those calls. See the examples below for concrete scenarios.
|
||||
#'
|
||||
#' @param ... Values to check for truthiness.
|
||||
#' @param cancelOutput If \code{TRUE} and an output is being evaluated, stop
|
||||
#' processing as usual but instead of clearing the output, leave it in
|
||||
#' whatever state it happens to be in.
|
||||
#' @param x An expression whose truthiness value we want to determine
|
||||
#' @return The first value that was passed in.
|
||||
#'
|
||||
#' @export
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' ui <- fluidPage(
|
||||
#' textInput('data', 'Enter a dataset from the "datasets" package', 'cars'),
|
||||
#' p('(E.g. "cars", "mtcars", "pressure", "faithful")'), hr(),
|
||||
#' tableOutput('tbl')
|
||||
#' )
|
||||
#'
|
||||
#' server <- function(input, output) {
|
||||
#' output$tbl <- renderTable({
|
||||
#'
|
||||
#' ## to require that the user types something, use: `req(input$data)`
|
||||
#' ## but better: require that input$data is valid and leave the last
|
||||
#' ## valid table up
|
||||
#' req(exists(input$data, "package:datasets", inherits = FALSE),
|
||||
#' cancelOutput = TRUE)
|
||||
#'
|
||||
#' head(get(input$data, "package:datasets", inherits = FALSE))
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
req <- function(..., cancelOutput = FALSE) {
|
||||
dotloop(function(item) {
|
||||
if (!isTruthy(item)) {
|
||||
if (isTRUE(cancelOutput)) {
|
||||
cancelOutput()
|
||||
} else {
|
||||
reactiveStop(class = "validation")
|
||||
stopWithCondition(c("validation", "shiny.silent.error"), "")
|
||||
}
|
||||
}
|
||||
}, ...)
|
||||
@@ -1348,44 +1195,20 @@ req <- function(..., cancelOutput = FALSE) {
|
||||
invisible()
|
||||
}
|
||||
|
||||
#***********************************************************************#
|
||||
#**** Keep this function internal for now, may chnage in the future ****#
|
||||
#***********************************************************************#
|
||||
# #' Cancel processing of the current output
|
||||
# #'
|
||||
# #' Signals an error that Shiny treats specially if an output is currently being
|
||||
# #' evaluated. Execution will stop, but rather than clearing the output (as
|
||||
# #' \code{\link{req}} does) or showing an error message (as \code{\link{stop}}
|
||||
# #' does), the output simply remains unchanged.
|
||||
# #'
|
||||
# #' If \code{cancelOutput} is called in any non-output context (like in an
|
||||
# #' \code{\link{observe}} or \code{\link{observeEvent}}), the effect is the same
|
||||
# #' as \code{\link{req}(FALSE)}.
|
||||
# #' @export
|
||||
# #' @examples
|
||||
# #' ## Only run examples in interactive R sessions
|
||||
# #' if (interactive()) {
|
||||
# #'
|
||||
# #' # uncomment the desired line to experiment with cancelOutput() vs. req()
|
||||
# #'
|
||||
# #' ui <- fluidPage(
|
||||
# #' textInput('txt', 'Enter text'),
|
||||
# #' textOutput('check')
|
||||
# #' )
|
||||
# #'
|
||||
# #' server <- function(input, output) {
|
||||
# #' output$check <- renderText({
|
||||
# #' # req(input$txt)
|
||||
# #' if (input$txt == 'hi') return('hi')
|
||||
# #' else if (input$txt == 'bye') return('bye')
|
||||
# #' # else cancelOutput()
|
||||
# #' })
|
||||
# #' }
|
||||
# #'
|
||||
# #' shinyApp(ui, server)
|
||||
# #' }
|
||||
#' Cancel processing of the current output
|
||||
#'
|
||||
#' Signals an error that Shiny treats specially if an output is currently being
|
||||
#' evaluated. Execution will stop, but rather than clearing the output (as
|
||||
#' \code{\link{req}} does) or showing an error message (as \code{\link{stop}}
|
||||
#' does), the output simply remains unchanged.
|
||||
#'
|
||||
#' If \code{cancelOutput} is called in any non-output context (like in an
|
||||
#' \code{\link{observe}} or \code{\link{observeEvent}}), the effect is the same
|
||||
#' as \code{\link{req}(FALSE)}.
|
||||
#'
|
||||
#' @export
|
||||
cancelOutput <- function() {
|
||||
reactiveStop(class = "shiny.output.cancel")
|
||||
stopWithCondition(c("shiny.output.cancel", "shiny.silent.error"), "")
|
||||
}
|
||||
|
||||
# Execute a function against each element of ..., but only evaluate each element
|
||||
@@ -1400,8 +1223,6 @@ dotloop <- function(fun_, ...) {
|
||||
invisible()
|
||||
}
|
||||
|
||||
#' @export
|
||||
#' @rdname req
|
||||
isTruthy <- function(x) {
|
||||
if (inherits(x, 'try-error'))
|
||||
return(FALSE)
|
||||
@@ -1599,9 +1420,3 @@ Mutable <- R6Class("Mutable",
|
||||
get = function() { private$value }
|
||||
)
|
||||
)
|
||||
|
||||
# Turn a value into a no-arg function that returns that value
|
||||
valueToFunc <- function(val) {
|
||||
force(val)
|
||||
function() val
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# Shiny
|
||||
|
||||
*Travis:* [](https://travis-ci.org/rstudio/shiny)
|
||||
|
||||
*AppVeyor:* [](https://ci.appveyor.com/project/rstudio/shiny)
|
||||
[](https://travis-ci.org/rstudio/shiny)
|
||||
|
||||
Shiny is a new package from RStudio that makes it incredibly easy to build interactive web applications with R.
|
||||
|
||||
@@ -59,10 +57,6 @@ devtools::install_version("shiny", version = "0.10.2.2")
|
||||
|
||||
The Javascript code in Shiny is minified using tools that run on Node.js. See the tools/ directory for more information.
|
||||
|
||||
## Guidelines for contributing
|
||||
|
||||
We welcome contributions to the **shiny** package. Please see our [CONTRIBUTING.md](CONTRIBUTING.md) file for detailed guidelines of how to contribute.
|
||||
|
||||
## License
|
||||
|
||||
The shiny package is licensed under the GPLv3. See these files in the inst directory for additional details:
|
||||
|
||||
45
appveyor.yml
45
appveyor.yml
@@ -1,45 +0,0 @@
|
||||
# DO NOT CHANGE the "init" and "install" sections below
|
||||
|
||||
# Download script file from GitHub
|
||||
init:
|
||||
ps: |
|
||||
$ErrorActionPreference = "Stop"
|
||||
Invoke-WebRequest http://raw.github.com/krlmlr/r-appveyor/master/scripts/appveyor-tool.ps1 -OutFile "..\appveyor-tool.ps1"
|
||||
Import-Module '..\appveyor-tool.ps1'
|
||||
|
||||
install:
|
||||
ps: Bootstrap
|
||||
|
||||
cache:
|
||||
- C:\RLibrary
|
||||
|
||||
# Adapt as necessary starting from here
|
||||
|
||||
build_script:
|
||||
- travis-tool.sh install_deps
|
||||
|
||||
test_script:
|
||||
- travis-tool.sh run_tests
|
||||
|
||||
on_failure:
|
||||
- 7z a failure.zip *.Rcheck\*
|
||||
- appveyor PushArtifact failure.zip
|
||||
|
||||
artifacts:
|
||||
- path: '*.Rcheck\**\*.log'
|
||||
name: Logs
|
||||
|
||||
- path: '*.Rcheck\**\*.out'
|
||||
name: Logs
|
||||
|
||||
- path: '*.Rcheck\**\*.fail'
|
||||
name: Logs
|
||||
|
||||
- path: '*.Rcheck\**\*.Rout'
|
||||
name: Logs
|
||||
|
||||
- path: '\*_*.tar.gz'
|
||||
name: Bits
|
||||
|
||||
- path: '\*_*.zip'
|
||||
name: Bits
|
||||
@@ -0,0 +1,6 @@
|
||||
name: 01_hello
|
||||
account: admin
|
||||
server: localhost
|
||||
bundleId: 1
|
||||
url: http://localhost:3939/admin/01_hello/
|
||||
when: 1436550957.65385
|
||||
@@ -1 +1 @@
|
||||
This example demonstrates some additional widgets included in Shiny, such as `helpText` and `actionButton`. The latter is used to delay rendering output until the user explicitly requests it (a construct which also introduces two important server functions, `eventReactive` and `isolate`).
|
||||
This example demonstrates some additional widgets included in Shiny, such as `helpText` and `submitButton`. The latter is used to delay rendering output until the user explicitly requests it.
|
||||
|
||||
@@ -1,32 +1,26 @@
|
||||
library(shiny)
|
||||
library(datasets)
|
||||
|
||||
# Define server logic required to summarize and view the
|
||||
# Define server logic required to summarize and view the
|
||||
# selected dataset
|
||||
function(input, output) {
|
||||
|
||||
# Return the requested dataset. Note that we use `eventReactive()`
|
||||
# here, which takes a dependency on input$update (the action
|
||||
# button), so that the output is only updated when the user
|
||||
# clicks the button.
|
||||
datasetInput <- eventReactive(input$update, {
|
||||
|
||||
# Return the requested dataset
|
||||
datasetInput <- reactive({
|
||||
switch(input$dataset,
|
||||
"rock" = rock,
|
||||
"pressure" = pressure,
|
||||
"cars" = cars)
|
||||
}, ignoreNULL = FALSE)
|
||||
|
||||
})
|
||||
|
||||
# Generate a summary of the dataset
|
||||
output$summary <- renderPrint({
|
||||
dataset <- datasetInput()
|
||||
summary(dataset)
|
||||
})
|
||||
|
||||
# Show the first "n" observations. The use of `isolate()` here
|
||||
# is necessary because we don't want the table to update
|
||||
# whenever input$obs changes (only when the user clicks the
|
||||
# action button).
|
||||
|
||||
# Show the first "n" observations
|
||||
output$view <- renderTable({
|
||||
head(datasetInput(), n = isolate(input$obs))
|
||||
head(datasetInput(), n = input$obs)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,32 +2,32 @@ library(shiny)
|
||||
|
||||
# Define UI for dataset viewer application
|
||||
fluidPage(
|
||||
|
||||
|
||||
# Application title.
|
||||
titlePanel("More Widgets"),
|
||||
|
||||
|
||||
# Sidebar with controls to select a dataset and specify the
|
||||
# number of observations to view. The helpText function is
|
||||
# also used to include clarifying text. Most notably, the
|
||||
# inclusion of an actionButton defers the rendering of output
|
||||
# inclusion of a submitButton defers the rendering of output
|
||||
# until the user explicitly clicks the button (rather than
|
||||
# doing it immediately when inputs change). This is useful if
|
||||
# the computations required to render output are inordinately
|
||||
# time-consuming.
|
||||
sidebarLayout(
|
||||
sidebarPanel(
|
||||
selectInput("dataset", "Choose a dataset:",
|
||||
selectInput("dataset", "Choose a dataset:",
|
||||
choices = c("rock", "pressure", "cars")),
|
||||
|
||||
|
||||
numericInput("obs", "Number of observations to view:", 10),
|
||||
|
||||
|
||||
helpText("Note: while the data view will show only the specified",
|
||||
"number of observations, the summary will still be based",
|
||||
"on the full dataset."),
|
||||
|
||||
actionButton("update", "Update View")
|
||||
|
||||
submitButton("Update View")
|
||||
),
|
||||
|
||||
|
||||
# Show a summary of the dataset and an HTML table with the
|
||||
# requested number of observations. Note the use of the h4
|
||||
# function to provide an additional header above each output
|
||||
@@ -35,7 +35,7 @@ fluidPage(
|
||||
mainPanel(
|
||||
h4("Summary"),
|
||||
verbatimTextOutput("summary"),
|
||||
|
||||
|
||||
h4("Observations"),
|
||||
tableOutput("view")
|
||||
)
|
||||
|
||||
@@ -44,9 +44,7 @@ sd_section("UI Inputs",
|
||||
"sliderInput",
|
||||
"submitButton",
|
||||
"textInput",
|
||||
"textAreaInput",
|
||||
"passwordInput",
|
||||
"modalButton",
|
||||
"updateActionButton",
|
||||
"updateCheckboxGroupInput",
|
||||
"updateCheckboxInput",
|
||||
@@ -57,9 +55,7 @@ sd_section("UI Inputs",
|
||||
"updateSelectInput",
|
||||
"updateSliderInput",
|
||||
"updateTabsetPanel",
|
||||
"updateTextInput",
|
||||
"updateTextAreaInput",
|
||||
"updateQueryString"
|
||||
"updateTextInput"
|
||||
)
|
||||
)
|
||||
sd_section("UI Outputs",
|
||||
@@ -73,11 +69,7 @@ sd_section("UI Outputs",
|
||||
"verbatimTextOutput",
|
||||
"downloadButton",
|
||||
"Progress",
|
||||
"withProgress",
|
||||
"modalDialog",
|
||||
"urlModal",
|
||||
"showModal",
|
||||
"showNotification"
|
||||
"withProgress"
|
||||
)
|
||||
)
|
||||
sd_section("Interface builder functions",
|
||||
@@ -92,9 +84,7 @@ sd_section("Interface builder functions",
|
||||
"withTags",
|
||||
"htmlTemplate",
|
||||
"bootstrapLib",
|
||||
"suppressDependencies",
|
||||
"insertUI",
|
||||
"removeUI"
|
||||
"suppressDependencies"
|
||||
)
|
||||
)
|
||||
sd_section("Rendering functions",
|
||||
@@ -115,25 +105,23 @@ sd_section("Rendering functions",
|
||||
"reactiveUI"
|
||||
)
|
||||
)
|
||||
sd_section("Reactive programming",
|
||||
sd_section("Reactive constructs",
|
||||
"A sub-library that provides reactive programming facilities for R.",
|
||||
c(
|
||||
"reactive",
|
||||
"observe",
|
||||
"observeEvent",
|
||||
"reactiveValues",
|
||||
"reactiveValuesToList",
|
||||
"invalidateLater",
|
||||
"is.reactivevalues",
|
||||
"isolate",
|
||||
"invalidateLater",
|
||||
"debounce",
|
||||
"showReactLog",
|
||||
"makeReactiveBinding",
|
||||
"observe",
|
||||
"observeEvent",
|
||||
"reactive",
|
||||
"reactiveFileReader",
|
||||
"reactivePoll",
|
||||
"reactiveTimer",
|
||||
"reactiveValues",
|
||||
"reactiveValuesToList",
|
||||
"domains",
|
||||
"freezeReactiveValue"
|
||||
"showReactLog"
|
||||
)
|
||||
)
|
||||
sd_section("Boilerplate",
|
||||
@@ -155,16 +143,6 @@ sd_section("Running",
|
||||
"viewer"
|
||||
)
|
||||
)
|
||||
sd_section("Bookmarking state",
|
||||
"Functions that are used for bookmarking and restoring state.",
|
||||
c(
|
||||
"bookmarkButton",
|
||||
"enableBookmarking",
|
||||
"setBookmarkExclude",
|
||||
"showBookmarkUrlModal",
|
||||
"onBookmark"
|
||||
)
|
||||
)
|
||||
sd_section("Extending Shiny",
|
||||
"Functions that are intended to be called by third-party packages that extend Shiny.",
|
||||
c(
|
||||
@@ -179,18 +157,13 @@ sd_section("Utility functions",
|
||||
"Miscellaneous utilities that may be useful to advanced users or when extending Shiny.",
|
||||
c(
|
||||
"req",
|
||||
"cancelOutput",
|
||||
"validate",
|
||||
"session",
|
||||
"shinyOptions",
|
||||
"safeError",
|
||||
"onFlush",
|
||||
"restoreInput",
|
||||
"applyInputHandlers",
|
||||
"exprToFunction",
|
||||
"installExprFunction",
|
||||
"parseQueryString",
|
||||
"plotPNG",
|
||||
"exportTestValues",
|
||||
"repeatable",
|
||||
"shinyDeprecated",
|
||||
"serverInfo",
|
||||
|
||||
@@ -66,7 +66,7 @@ svg {
|
||||
}
|
||||
.node path {
|
||||
fill: white;
|
||||
stroke: #999;
|
||||
stroke: #777;
|
||||
stroke-width: 7.5px;
|
||||
transition: fill 0.75s ease;
|
||||
}
|
||||
@@ -83,9 +83,6 @@ svg {
|
||||
.node.running path {
|
||||
fill: #61B97E;
|
||||
}
|
||||
.node.fixed path {
|
||||
stroke: #000;
|
||||
}
|
||||
#legend {
|
||||
font-size: 22px;
|
||||
position: fixed;
|
||||
@@ -1097,13 +1094,7 @@ function update() {
|
||||
.on('mouseout', function(d, i) {
|
||||
$('#description').text('');
|
||||
})
|
||||
.on('dblclick', function(d) {
|
||||
d3.event.stopPropagation();
|
||||
d3.select(this).classed('fixed', d.fixed = false);
|
||||
})
|
||||
.call(force.drag().on('dragstart', function(d) {
|
||||
d3.select(this).classed('fixed', d.fixed = true);
|
||||
}));
|
||||
.call(force.drag);
|
||||
newG.append('path')
|
||||
.attr('transform', 'scale(0.08)')
|
||||
.attr('stroke', 'black')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap v3.3.7 (http://getbootstrap.com)
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Bootstrap v3.3.6 (http://getbootstrap.com)
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
.btn-default,
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
7
inst/www/shared/bootstrap/css/bootstrap.css
vendored
7
inst/www/shared/bootstrap/css/bootstrap.css
vendored
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap v3.3.7 (http://getbootstrap.com)
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Bootstrap v3.3.6 (http://getbootstrap.com)
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
|
||||
@@ -1106,6 +1106,7 @@ a:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:focus {
|
||||
outline: thin dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
@@ -2536,6 +2537,7 @@ select[size] {
|
||||
input[type="file"]:focus,
|
||||
input[type="radio"]:focus,
|
||||
input[type="checkbox"]:focus {
|
||||
outline: thin dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
@@ -3027,6 +3029,7 @@ select[multiple].input-lg {
|
||||
.btn.focus,
|
||||
.btn:active.focus,
|
||||
.btn.active.focus {
|
||||
outline: thin dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
114
inst/www/shared/bootstrap/js/bootstrap.js
vendored
114
inst/www/shared/bootstrap/js/bootstrap.js
vendored
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap v3.3.7 (http://getbootstrap.com)
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Bootstrap v3.3.6 (http://getbootstrap.com)
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under the MIT license
|
||||
*/
|
||||
|
||||
@@ -11,16 +11,16 @@ if (typeof jQuery === 'undefined') {
|
||||
+function ($) {
|
||||
'use strict';
|
||||
var version = $.fn.jquery.split(' ')[0].split('.')
|
||||
if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 3)) {
|
||||
throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4')
|
||||
if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 2)) {
|
||||
throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3')
|
||||
}
|
||||
}(jQuery);
|
||||
|
||||
/* ========================================================================
|
||||
* Bootstrap: transition.js v3.3.7
|
||||
* Bootstrap: transition.js v3.3.6
|
||||
* http://getbootstrap.com/javascript/#transitions
|
||||
* ========================================================================
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -77,10 +77,10 @@ if (typeof jQuery === 'undefined') {
|
||||
}(jQuery);
|
||||
|
||||
/* ========================================================================
|
||||
* Bootstrap: alert.js v3.3.7
|
||||
* Bootstrap: alert.js v3.3.6
|
||||
* http://getbootstrap.com/javascript/#alerts
|
||||
* ========================================================================
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -96,7 +96,7 @@ if (typeof jQuery === 'undefined') {
|
||||
$(el).on('click', dismiss, this.close)
|
||||
}
|
||||
|
||||
Alert.VERSION = '3.3.7'
|
||||
Alert.VERSION = '3.3.6'
|
||||
|
||||
Alert.TRANSITION_DURATION = 150
|
||||
|
||||
@@ -109,7 +109,7 @@ if (typeof jQuery === 'undefined') {
|
||||
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
|
||||
}
|
||||
|
||||
var $parent = $(selector === '#' ? [] : selector)
|
||||
var $parent = $(selector)
|
||||
|
||||
if (e) e.preventDefault()
|
||||
|
||||
@@ -172,10 +172,10 @@ if (typeof jQuery === 'undefined') {
|
||||
}(jQuery);
|
||||
|
||||
/* ========================================================================
|
||||
* Bootstrap: button.js v3.3.7
|
||||
* Bootstrap: button.js v3.3.6
|
||||
* http://getbootstrap.com/javascript/#buttons
|
||||
* ========================================================================
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -192,7 +192,7 @@ if (typeof jQuery === 'undefined') {
|
||||
this.isLoading = false
|
||||
}
|
||||
|
||||
Button.VERSION = '3.3.7'
|
||||
Button.VERSION = '3.3.6'
|
||||
|
||||
Button.DEFAULTS = {
|
||||
loadingText: 'loading...'
|
||||
@@ -214,10 +214,10 @@ if (typeof jQuery === 'undefined') {
|
||||
|
||||
if (state == 'loadingText') {
|
||||
this.isLoading = true
|
||||
$el.addClass(d).attr(d, d).prop(d, true)
|
||||
$el.addClass(d).attr(d, d)
|
||||
} else if (this.isLoading) {
|
||||
this.isLoading = false
|
||||
$el.removeClass(d).removeAttr(d).prop(d, false)
|
||||
$el.removeClass(d).removeAttr(d)
|
||||
}
|
||||
}, this), 0)
|
||||
}
|
||||
@@ -281,15 +281,10 @@ if (typeof jQuery === 'undefined') {
|
||||
|
||||
$(document)
|
||||
.on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
|
||||
var $btn = $(e.target).closest('.btn')
|
||||
var $btn = $(e.target)
|
||||
if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
|
||||
Plugin.call($btn, 'toggle')
|
||||
if (!($(e.target).is('input[type="radio"], input[type="checkbox"]'))) {
|
||||
// Prevent double click on radios, and the double selections (so cancellation) on checkboxes
|
||||
e.preventDefault()
|
||||
// The target component still receive the focus
|
||||
if ($btn.is('input,button')) $btn.trigger('focus')
|
||||
else $btn.find('input:visible,button:visible').first().trigger('focus')
|
||||
}
|
||||
if (!($(e.target).is('input[type="radio"]') || $(e.target).is('input[type="checkbox"]'))) e.preventDefault()
|
||||
})
|
||||
.on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
|
||||
$(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
|
||||
@@ -298,10 +293,10 @@ if (typeof jQuery === 'undefined') {
|
||||
}(jQuery);
|
||||
|
||||
/* ========================================================================
|
||||
* Bootstrap: carousel.js v3.3.7
|
||||
* Bootstrap: carousel.js v3.3.6
|
||||
* http://getbootstrap.com/javascript/#carousel
|
||||
* ========================================================================
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -329,7 +324,7 @@ if (typeof jQuery === 'undefined') {
|
||||
.on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
|
||||
}
|
||||
|
||||
Carousel.VERSION = '3.3.7'
|
||||
Carousel.VERSION = '3.3.6'
|
||||
|
||||
Carousel.TRANSITION_DURATION = 600
|
||||
|
||||
@@ -536,14 +531,13 @@ if (typeof jQuery === 'undefined') {
|
||||
}(jQuery);
|
||||
|
||||
/* ========================================================================
|
||||
* Bootstrap: collapse.js v3.3.7
|
||||
* Bootstrap: collapse.js v3.3.6
|
||||
* http://getbootstrap.com/javascript/#collapse
|
||||
* ========================================================================
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
/* jshint latedef: false */
|
||||
|
||||
+function ($) {
|
||||
'use strict';
|
||||
@@ -567,7 +561,7 @@ if (typeof jQuery === 'undefined') {
|
||||
if (this.options.toggle) this.toggle()
|
||||
}
|
||||
|
||||
Collapse.VERSION = '3.3.7'
|
||||
Collapse.VERSION = '3.3.6'
|
||||
|
||||
Collapse.TRANSITION_DURATION = 350
|
||||
|
||||
@@ -749,10 +743,10 @@ if (typeof jQuery === 'undefined') {
|
||||
}(jQuery);
|
||||
|
||||
/* ========================================================================
|
||||
* Bootstrap: dropdown.js v3.3.7
|
||||
* Bootstrap: dropdown.js v3.3.6
|
||||
* http://getbootstrap.com/javascript/#dropdowns
|
||||
* ========================================================================
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -769,7 +763,7 @@ if (typeof jQuery === 'undefined') {
|
||||
$(element).on('click.bs.dropdown', this.toggle)
|
||||
}
|
||||
|
||||
Dropdown.VERSION = '3.3.7'
|
||||
Dropdown.VERSION = '3.3.6'
|
||||
|
||||
function getParent($this) {
|
||||
var selector = $this.attr('data-target')
|
||||
@@ -915,10 +909,10 @@ if (typeof jQuery === 'undefined') {
|
||||
}(jQuery);
|
||||
|
||||
/* ========================================================================
|
||||
* Bootstrap: modal.js v3.3.7
|
||||
* Bootstrap: modal.js v3.3.6
|
||||
* http://getbootstrap.com/javascript/#modals
|
||||
* ========================================================================
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -949,7 +943,7 @@ if (typeof jQuery === 'undefined') {
|
||||
}
|
||||
}
|
||||
|
||||
Modal.VERSION = '3.3.7'
|
||||
Modal.VERSION = '3.3.6'
|
||||
|
||||
Modal.TRANSITION_DURATION = 300
|
||||
Modal.BACKDROP_TRANSITION_DURATION = 150
|
||||
@@ -1056,9 +1050,7 @@ if (typeof jQuery === 'undefined') {
|
||||
$(document)
|
||||
.off('focusin.bs.modal') // guard against infinite focus loop
|
||||
.on('focusin.bs.modal', $.proxy(function (e) {
|
||||
if (document !== e.target &&
|
||||
this.$element[0] !== e.target &&
|
||||
!this.$element.has(e.target).length) {
|
||||
if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
|
||||
this.$element.trigger('focus')
|
||||
}
|
||||
}, this))
|
||||
@@ -1255,11 +1247,11 @@ if (typeof jQuery === 'undefined') {
|
||||
}(jQuery);
|
||||
|
||||
/* ========================================================================
|
||||
* Bootstrap: tooltip.js v3.3.7
|
||||
* Bootstrap: tooltip.js v3.3.6
|
||||
* http://getbootstrap.com/javascript/#tooltip
|
||||
* Inspired by the original jQuery.tipsy by Jason Frame
|
||||
* ========================================================================
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -1282,7 +1274,7 @@ if (typeof jQuery === 'undefined') {
|
||||
this.init('tooltip', element, options)
|
||||
}
|
||||
|
||||
Tooltip.VERSION = '3.3.7'
|
||||
Tooltip.VERSION = '3.3.6'
|
||||
|
||||
Tooltip.TRANSITION_DURATION = 150
|
||||
|
||||
@@ -1573,11 +1565,9 @@ if (typeof jQuery === 'undefined') {
|
||||
|
||||
function complete() {
|
||||
if (that.hoverState != 'in') $tip.detach()
|
||||
if (that.$element) { // TODO: Check whether guarding this code with this `if` is really necessary.
|
||||
that.$element
|
||||
.removeAttr('aria-describedby')
|
||||
.trigger('hidden.bs.' + that.type)
|
||||
}
|
||||
that.$element
|
||||
.removeAttr('aria-describedby')
|
||||
.trigger('hidden.bs.' + that.type)
|
||||
callback && callback()
|
||||
}
|
||||
|
||||
@@ -1620,10 +1610,7 @@ if (typeof jQuery === 'undefined') {
|
||||
// width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
|
||||
elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
|
||||
}
|
||||
var isSvg = window.SVGElement && el instanceof window.SVGElement
|
||||
// Avoid using $.offset() on SVGs since it gives incorrect results in jQuery 3.
|
||||
// See https://github.com/twbs/bootstrap/issues/20280
|
||||
var elOffset = isBody ? { top: 0, left: 0 } : (isSvg ? null : $element.offset())
|
||||
var elOffset = isBody ? { top: 0, left: 0 } : $element.offset()
|
||||
var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
|
||||
var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
|
||||
|
||||
@@ -1739,7 +1726,6 @@ if (typeof jQuery === 'undefined') {
|
||||
that.$tip = null
|
||||
that.$arrow = null
|
||||
that.$viewport = null
|
||||
that.$element = null
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1776,10 +1762,10 @@ if (typeof jQuery === 'undefined') {
|
||||
}(jQuery);
|
||||
|
||||
/* ========================================================================
|
||||
* Bootstrap: popover.js v3.3.7
|
||||
* Bootstrap: popover.js v3.3.6
|
||||
* http://getbootstrap.com/javascript/#popovers
|
||||
* ========================================================================
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -1796,7 +1782,7 @@ if (typeof jQuery === 'undefined') {
|
||||
|
||||
if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
|
||||
|
||||
Popover.VERSION = '3.3.7'
|
||||
Popover.VERSION = '3.3.6'
|
||||
|
||||
Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
|
||||
placement: 'right',
|
||||
@@ -1885,10 +1871,10 @@ if (typeof jQuery === 'undefined') {
|
||||
}(jQuery);
|
||||
|
||||
/* ========================================================================
|
||||
* Bootstrap: scrollspy.js v3.3.7
|
||||
* Bootstrap: scrollspy.js v3.3.6
|
||||
* http://getbootstrap.com/javascript/#scrollspy
|
||||
* ========================================================================
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -1914,7 +1900,7 @@ if (typeof jQuery === 'undefined') {
|
||||
this.process()
|
||||
}
|
||||
|
||||
ScrollSpy.VERSION = '3.3.7'
|
||||
ScrollSpy.VERSION = '3.3.6'
|
||||
|
||||
ScrollSpy.DEFAULTS = {
|
||||
offset: 10
|
||||
@@ -2058,10 +2044,10 @@ if (typeof jQuery === 'undefined') {
|
||||
}(jQuery);
|
||||
|
||||
/* ========================================================================
|
||||
* Bootstrap: tab.js v3.3.7
|
||||
* Bootstrap: tab.js v3.3.6
|
||||
* http://getbootstrap.com/javascript/#tabs
|
||||
* ========================================================================
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -2078,7 +2064,7 @@ if (typeof jQuery === 'undefined') {
|
||||
// jscs:enable requireDollarBeforejQueryAssignment
|
||||
}
|
||||
|
||||
Tab.VERSION = '3.3.7'
|
||||
Tab.VERSION = '3.3.6'
|
||||
|
||||
Tab.TRANSITION_DURATION = 150
|
||||
|
||||
@@ -2214,10 +2200,10 @@ if (typeof jQuery === 'undefined') {
|
||||
}(jQuery);
|
||||
|
||||
/* ========================================================================
|
||||
* Bootstrap: affix.js v3.3.7
|
||||
* Bootstrap: affix.js v3.3.6
|
||||
* http://getbootstrap.com/javascript/#affix
|
||||
* ========================================================================
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
@@ -2243,7 +2229,7 @@ if (typeof jQuery === 'undefined') {
|
||||
this.checkPosition()
|
||||
}
|
||||
|
||||
Affix.VERSION = '3.3.7'
|
||||
Affix.VERSION = '3.3.6'
|
||||
|
||||
Affix.RESET = 'affix affix-top affix-bottom'
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,678 +0,0 @@
|
||||
/*!
|
||||
* Datepicker for Bootstrap v1.6.4 (https://github.com/eternicode/bootstrap-datepicker)
|
||||
*
|
||||
* Copyright 2012 Stefan Petre
|
||||
* Improvements by Andrew Rowls
|
||||
* Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
||||
*/
|
||||
.datepicker {
|
||||
border-radius: 4px;
|
||||
direction: ltr;
|
||||
}
|
||||
.datepicker-inline {
|
||||
width: 220px;
|
||||
}
|
||||
.datepicker.datepicker-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
.datepicker.datepicker-rtl table tr td span {
|
||||
float: right;
|
||||
}
|
||||
.datepicker-dropdown {
|
||||
top: 0;
|
||||
left: 0;
|
||||
padding: 4px;
|
||||
}
|
||||
.datepicker-dropdown:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
border-left: 7px solid transparent;
|
||||
border-right: 7px solid transparent;
|
||||
border-bottom: 7px solid rgba(0, 0, 0, 0.15);
|
||||
border-top: 0;
|
||||
border-bottom-color: rgba(0, 0, 0, 0.2);
|
||||
position: absolute;
|
||||
}
|
||||
.datepicker-dropdown:after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid #fff;
|
||||
border-top: 0;
|
||||
position: absolute;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-left:before {
|
||||
left: 6px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-left:after {
|
||||
left: 7px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-right:before {
|
||||
right: 6px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-right:after {
|
||||
right: 7px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-bottom:before {
|
||||
top: -7px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-bottom:after {
|
||||
top: -6px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-top:before {
|
||||
bottom: -7px;
|
||||
border-bottom: 0;
|
||||
border-top: 7px solid rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-top:after {
|
||||
bottom: -6px;
|
||||
border-bottom: 0;
|
||||
border-top: 6px solid #fff;
|
||||
}
|
||||
.datepicker table {
|
||||
margin: 0;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.datepicker table tr td,
|
||||
.datepicker table tr th {
|
||||
text-align: center;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
}
|
||||
.table-striped .datepicker table tr td,
|
||||
.table-striped .datepicker table tr th {
|
||||
background-color: transparent;
|
||||
}
|
||||
.datepicker table tr td.old,
|
||||
.datepicker table tr td.new {
|
||||
color: #777777;
|
||||
}
|
||||
.datepicker table tr td.day:hover,
|
||||
.datepicker table tr td.focused {
|
||||
background: #eeeeee;
|
||||
cursor: pointer;
|
||||
}
|
||||
.datepicker table tr td.disabled,
|
||||
.datepicker table tr td.disabled:hover {
|
||||
background: none;
|
||||
color: #777777;
|
||||
cursor: default;
|
||||
}
|
||||
.datepicker table tr td.highlighted {
|
||||
color: #000;
|
||||
background-color: #d9edf7;
|
||||
border-color: #85c5e5;
|
||||
border-radius: 0;
|
||||
}
|
||||
.datepicker table tr td.highlighted:focus,
|
||||
.datepicker table tr td.highlighted.focus {
|
||||
color: #000;
|
||||
background-color: #afd9ee;
|
||||
border-color: #298fc2;
|
||||
}
|
||||
.datepicker table tr td.highlighted:hover {
|
||||
color: #000;
|
||||
background-color: #afd9ee;
|
||||
border-color: #52addb;
|
||||
}
|
||||
.datepicker table tr td.highlighted:active,
|
||||
.datepicker table tr td.highlighted.active {
|
||||
color: #000;
|
||||
background-color: #afd9ee;
|
||||
border-color: #52addb;
|
||||
}
|
||||
.datepicker table tr td.highlighted:active:hover,
|
||||
.datepicker table tr td.highlighted.active:hover,
|
||||
.datepicker table tr td.highlighted:active:focus,
|
||||
.datepicker table tr td.highlighted.active:focus,
|
||||
.datepicker table tr td.highlighted:active.focus,
|
||||
.datepicker table tr td.highlighted.active.focus {
|
||||
color: #000;
|
||||
background-color: #91cbe8;
|
||||
border-color: #298fc2;
|
||||
}
|
||||
.datepicker table tr td.highlighted.disabled:hover,
|
||||
.datepicker table tr td.highlighted[disabled]:hover,
|
||||
fieldset[disabled] .datepicker table tr td.highlighted:hover,
|
||||
.datepicker table tr td.highlighted.disabled:focus,
|
||||
.datepicker table tr td.highlighted[disabled]:focus,
|
||||
fieldset[disabled] .datepicker table tr td.highlighted:focus,
|
||||
.datepicker table tr td.highlighted.disabled.focus,
|
||||
.datepicker table tr td.highlighted[disabled].focus,
|
||||
fieldset[disabled] .datepicker table tr td.highlighted.focus {
|
||||
background-color: #d9edf7;
|
||||
border-color: #85c5e5;
|
||||
}
|
||||
.datepicker table tr td.highlighted.focused {
|
||||
background: #afd9ee;
|
||||
}
|
||||
.datepicker table tr td.highlighted.disabled,
|
||||
.datepicker table tr td.highlighted.disabled:active {
|
||||
background: #d9edf7;
|
||||
color: #777777;
|
||||
}
|
||||
.datepicker table tr td.today {
|
||||
color: #000;
|
||||
background-color: #ffdb99;
|
||||
border-color: #ffb733;
|
||||
}
|
||||
.datepicker table tr td.today:focus,
|
||||
.datepicker table tr td.today.focus {
|
||||
color: #000;
|
||||
background-color: #ffc966;
|
||||
border-color: #b37400;
|
||||
}
|
||||
.datepicker table tr td.today:hover {
|
||||
color: #000;
|
||||
background-color: #ffc966;
|
||||
border-color: #f59e00;
|
||||
}
|
||||
.datepicker table tr td.today:active,
|
||||
.datepicker table tr td.today.active {
|
||||
color: #000;
|
||||
background-color: #ffc966;
|
||||
border-color: #f59e00;
|
||||
}
|
||||
.datepicker table tr td.today:active:hover,
|
||||
.datepicker table tr td.today.active:hover,
|
||||
.datepicker table tr td.today:active:focus,
|
||||
.datepicker table tr td.today.active:focus,
|
||||
.datepicker table tr td.today:active.focus,
|
||||
.datepicker table tr td.today.active.focus {
|
||||
color: #000;
|
||||
background-color: #ffbc42;
|
||||
border-color: #b37400;
|
||||
}
|
||||
.datepicker table tr td.today.disabled:hover,
|
||||
.datepicker table tr td.today[disabled]:hover,
|
||||
fieldset[disabled] .datepicker table tr td.today:hover,
|
||||
.datepicker table tr td.today.disabled:focus,
|
||||
.datepicker table tr td.today[disabled]:focus,
|
||||
fieldset[disabled] .datepicker table tr td.today:focus,
|
||||
.datepicker table tr td.today.disabled.focus,
|
||||
.datepicker table tr td.today[disabled].focus,
|
||||
fieldset[disabled] .datepicker table tr td.today.focus {
|
||||
background-color: #ffdb99;
|
||||
border-color: #ffb733;
|
||||
}
|
||||
.datepicker table tr td.today.focused {
|
||||
background: #ffc966;
|
||||
}
|
||||
.datepicker table tr td.today.disabled,
|
||||
.datepicker table tr td.today.disabled:active {
|
||||
background: #ffdb99;
|
||||
color: #777777;
|
||||
}
|
||||
.datepicker table tr td.range {
|
||||
color: #000;
|
||||
background-color: #eeeeee;
|
||||
border-color: #bbbbbb;
|
||||
border-radius: 0;
|
||||
}
|
||||
.datepicker table tr td.range:focus,
|
||||
.datepicker table tr td.range.focus {
|
||||
color: #000;
|
||||
background-color: #d5d5d5;
|
||||
border-color: #7c7c7c;
|
||||
}
|
||||
.datepicker table tr td.range:hover {
|
||||
color: #000;
|
||||
background-color: #d5d5d5;
|
||||
border-color: #9d9d9d;
|
||||
}
|
||||
.datepicker table tr td.range:active,
|
||||
.datepicker table tr td.range.active {
|
||||
color: #000;
|
||||
background-color: #d5d5d5;
|
||||
border-color: #9d9d9d;
|
||||
}
|
||||
.datepicker table tr td.range:active:hover,
|
||||
.datepicker table tr td.range.active:hover,
|
||||
.datepicker table tr td.range:active:focus,
|
||||
.datepicker table tr td.range.active:focus,
|
||||
.datepicker table tr td.range:active.focus,
|
||||
.datepicker table tr td.range.active.focus {
|
||||
color: #000;
|
||||
background-color: #c3c3c3;
|
||||
border-color: #7c7c7c;
|
||||
}
|
||||
.datepicker table tr td.range.disabled:hover,
|
||||
.datepicker table tr td.range[disabled]:hover,
|
||||
fieldset[disabled] .datepicker table tr td.range:hover,
|
||||
.datepicker table tr td.range.disabled:focus,
|
||||
.datepicker table tr td.range[disabled]:focus,
|
||||
fieldset[disabled] .datepicker table tr td.range:focus,
|
||||
.datepicker table tr td.range.disabled.focus,
|
||||
.datepicker table tr td.range[disabled].focus,
|
||||
fieldset[disabled] .datepicker table tr td.range.focus {
|
||||
background-color: #eeeeee;
|
||||
border-color: #bbbbbb;
|
||||
}
|
||||
.datepicker table tr td.range.focused {
|
||||
background: #d5d5d5;
|
||||
}
|
||||
.datepicker table tr td.range.disabled,
|
||||
.datepicker table tr td.range.disabled:active {
|
||||
background: #eeeeee;
|
||||
color: #777777;
|
||||
}
|
||||
.datepicker table tr td.range.highlighted {
|
||||
color: #000;
|
||||
background-color: #e4eef3;
|
||||
border-color: #9dc1d3;
|
||||
}
|
||||
.datepicker table tr td.range.highlighted:focus,
|
||||
.datepicker table tr td.range.highlighted.focus {
|
||||
color: #000;
|
||||
background-color: #c1d7e3;
|
||||
border-color: #4b88a6;
|
||||
}
|
||||
.datepicker table tr td.range.highlighted:hover {
|
||||
color: #000;
|
||||
background-color: #c1d7e3;
|
||||
border-color: #73a6c0;
|
||||
}
|
||||
.datepicker table tr td.range.highlighted:active,
|
||||
.datepicker table tr td.range.highlighted.active {
|
||||
color: #000;
|
||||
background-color: #c1d7e3;
|
||||
border-color: #73a6c0;
|
||||
}
|
||||
.datepicker table tr td.range.highlighted:active:hover,
|
||||
.datepicker table tr td.range.highlighted.active:hover,
|
||||
.datepicker table tr td.range.highlighted:active:focus,
|
||||
.datepicker table tr td.range.highlighted.active:focus,
|
||||
.datepicker table tr td.range.highlighted:active.focus,
|
||||
.datepicker table tr td.range.highlighted.active.focus {
|
||||
color: #000;
|
||||
background-color: #a8c8d8;
|
||||
border-color: #4b88a6;
|
||||
}
|
||||
.datepicker table tr td.range.highlighted.disabled:hover,
|
||||
.datepicker table tr td.range.highlighted[disabled]:hover,
|
||||
fieldset[disabled] .datepicker table tr td.range.highlighted:hover,
|
||||
.datepicker table tr td.range.highlighted.disabled:focus,
|
||||
.datepicker table tr td.range.highlighted[disabled]:focus,
|
||||
fieldset[disabled] .datepicker table tr td.range.highlighted:focus,
|
||||
.datepicker table tr td.range.highlighted.disabled.focus,
|
||||
.datepicker table tr td.range.highlighted[disabled].focus,
|
||||
fieldset[disabled] .datepicker table tr td.range.highlighted.focus {
|
||||
background-color: #e4eef3;
|
||||
border-color: #9dc1d3;
|
||||
}
|
||||
.datepicker table tr td.range.highlighted.focused {
|
||||
background: #c1d7e3;
|
||||
}
|
||||
.datepicker table tr td.range.highlighted.disabled,
|
||||
.datepicker table tr td.range.highlighted.disabled:active {
|
||||
background: #e4eef3;
|
||||
color: #777777;
|
||||
}
|
||||
.datepicker table tr td.range.today {
|
||||
color: #000;
|
||||
background-color: #f7ca77;
|
||||
border-color: #f1a417;
|
||||
}
|
||||
.datepicker table tr td.range.today:focus,
|
||||
.datepicker table tr td.range.today.focus {
|
||||
color: #000;
|
||||
background-color: #f4b747;
|
||||
border-color: #815608;
|
||||
}
|
||||
.datepicker table tr td.range.today:hover {
|
||||
color: #000;
|
||||
background-color: #f4b747;
|
||||
border-color: #bf800c;
|
||||
}
|
||||
.datepicker table tr td.range.today:active,
|
||||
.datepicker table tr td.range.today.active {
|
||||
color: #000;
|
||||
background-color: #f4b747;
|
||||
border-color: #bf800c;
|
||||
}
|
||||
.datepicker table tr td.range.today:active:hover,
|
||||
.datepicker table tr td.range.today.active:hover,
|
||||
.datepicker table tr td.range.today:active:focus,
|
||||
.datepicker table tr td.range.today.active:focus,
|
||||
.datepicker table tr td.range.today:active.focus,
|
||||
.datepicker table tr td.range.today.active.focus {
|
||||
color: #000;
|
||||
background-color: #f2aa25;
|
||||
border-color: #815608;
|
||||
}
|
||||
.datepicker table tr td.range.today.disabled:hover,
|
||||
.datepicker table tr td.range.today[disabled]:hover,
|
||||
fieldset[disabled] .datepicker table tr td.range.today:hover,
|
||||
.datepicker table tr td.range.today.disabled:focus,
|
||||
.datepicker table tr td.range.today[disabled]:focus,
|
||||
fieldset[disabled] .datepicker table tr td.range.today:focus,
|
||||
.datepicker table tr td.range.today.disabled.focus,
|
||||
.datepicker table tr td.range.today[disabled].focus,
|
||||
fieldset[disabled] .datepicker table tr td.range.today.focus {
|
||||
background-color: #f7ca77;
|
||||
border-color: #f1a417;
|
||||
}
|
||||
.datepicker table tr td.range.today.disabled,
|
||||
.datepicker table tr td.range.today.disabled:active {
|
||||
background: #f7ca77;
|
||||
color: #777777;
|
||||
}
|
||||
.datepicker table tr td.selected,
|
||||
.datepicker table tr td.selected.highlighted {
|
||||
color: #fff;
|
||||
background-color: #777777;
|
||||
border-color: #555555;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td.selected:focus,
|
||||
.datepicker table tr td.selected.highlighted:focus,
|
||||
.datepicker table tr td.selected.focus,
|
||||
.datepicker table tr td.selected.highlighted.focus {
|
||||
color: #fff;
|
||||
background-color: #5e5e5e;
|
||||
border-color: #161616;
|
||||
}
|
||||
.datepicker table tr td.selected:hover,
|
||||
.datepicker table tr td.selected.highlighted:hover {
|
||||
color: #fff;
|
||||
background-color: #5e5e5e;
|
||||
border-color: #373737;
|
||||
}
|
||||
.datepicker table tr td.selected:active,
|
||||
.datepicker table tr td.selected.highlighted:active,
|
||||
.datepicker table tr td.selected.active,
|
||||
.datepicker table tr td.selected.highlighted.active {
|
||||
color: #fff;
|
||||
background-color: #5e5e5e;
|
||||
border-color: #373737;
|
||||
}
|
||||
.datepicker table tr td.selected:active:hover,
|
||||
.datepicker table tr td.selected.highlighted:active:hover,
|
||||
.datepicker table tr td.selected.active:hover,
|
||||
.datepicker table tr td.selected.highlighted.active:hover,
|
||||
.datepicker table tr td.selected:active:focus,
|
||||
.datepicker table tr td.selected.highlighted:active:focus,
|
||||
.datepicker table tr td.selected.active:focus,
|
||||
.datepicker table tr td.selected.highlighted.active:focus,
|
||||
.datepicker table tr td.selected:active.focus,
|
||||
.datepicker table tr td.selected.highlighted:active.focus,
|
||||
.datepicker table tr td.selected.active.focus,
|
||||
.datepicker table tr td.selected.highlighted.active.focus {
|
||||
color: #fff;
|
||||
background-color: #4c4c4c;
|
||||
border-color: #161616;
|
||||
}
|
||||
.datepicker table tr td.selected.disabled:hover,
|
||||
.datepicker table tr td.selected.highlighted.disabled:hover,
|
||||
.datepicker table tr td.selected[disabled]:hover,
|
||||
.datepicker table tr td.selected.highlighted[disabled]:hover,
|
||||
fieldset[disabled] .datepicker table tr td.selected:hover,
|
||||
fieldset[disabled] .datepicker table tr td.selected.highlighted:hover,
|
||||
.datepicker table tr td.selected.disabled:focus,
|
||||
.datepicker table tr td.selected.highlighted.disabled:focus,
|
||||
.datepicker table tr td.selected[disabled]:focus,
|
||||
.datepicker table tr td.selected.highlighted[disabled]:focus,
|
||||
fieldset[disabled] .datepicker table tr td.selected:focus,
|
||||
fieldset[disabled] .datepicker table tr td.selected.highlighted:focus,
|
||||
.datepicker table tr td.selected.disabled.focus,
|
||||
.datepicker table tr td.selected.highlighted.disabled.focus,
|
||||
.datepicker table tr td.selected[disabled].focus,
|
||||
.datepicker table tr td.selected.highlighted[disabled].focus,
|
||||
fieldset[disabled] .datepicker table tr td.selected.focus,
|
||||
fieldset[disabled] .datepicker table tr td.selected.highlighted.focus {
|
||||
background-color: #777777;
|
||||
border-color: #555555;
|
||||
}
|
||||
.datepicker table tr td.active,
|
||||
.datepicker table tr td.active.highlighted {
|
||||
color: #fff;
|
||||
background-color: #337ab7;
|
||||
border-color: #2e6da4;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td.active:focus,
|
||||
.datepicker table tr td.active.highlighted:focus,
|
||||
.datepicker table tr td.active.focus,
|
||||
.datepicker table tr td.active.highlighted.focus {
|
||||
color: #fff;
|
||||
background-color: #286090;
|
||||
border-color: #122b40;
|
||||
}
|
||||
.datepicker table tr td.active:hover,
|
||||
.datepicker table tr td.active.highlighted:hover {
|
||||
color: #fff;
|
||||
background-color: #286090;
|
||||
border-color: #204d74;
|
||||
}
|
||||
.datepicker table tr td.active:active,
|
||||
.datepicker table tr td.active.highlighted:active,
|
||||
.datepicker table tr td.active.active,
|
||||
.datepicker table tr td.active.highlighted.active {
|
||||
color: #fff;
|
||||
background-color: #286090;
|
||||
border-color: #204d74;
|
||||
}
|
||||
.datepicker table tr td.active:active:hover,
|
||||
.datepicker table tr td.active.highlighted:active:hover,
|
||||
.datepicker table tr td.active.active:hover,
|
||||
.datepicker table tr td.active.highlighted.active:hover,
|
||||
.datepicker table tr td.active:active:focus,
|
||||
.datepicker table tr td.active.highlighted:active:focus,
|
||||
.datepicker table tr td.active.active:focus,
|
||||
.datepicker table tr td.active.highlighted.active:focus,
|
||||
.datepicker table tr td.active:active.focus,
|
||||
.datepicker table tr td.active.highlighted:active.focus,
|
||||
.datepicker table tr td.active.active.focus,
|
||||
.datepicker table tr td.active.highlighted.active.focus {
|
||||
color: #fff;
|
||||
background-color: #204d74;
|
||||
border-color: #122b40;
|
||||
}
|
||||
.datepicker table tr td.active.disabled:hover,
|
||||
.datepicker table tr td.active.highlighted.disabled:hover,
|
||||
.datepicker table tr td.active[disabled]:hover,
|
||||
.datepicker table tr td.active.highlighted[disabled]:hover,
|
||||
fieldset[disabled] .datepicker table tr td.active:hover,
|
||||
fieldset[disabled] .datepicker table tr td.active.highlighted:hover,
|
||||
.datepicker table tr td.active.disabled:focus,
|
||||
.datepicker table tr td.active.highlighted.disabled:focus,
|
||||
.datepicker table tr td.active[disabled]:focus,
|
||||
.datepicker table tr td.active.highlighted[disabled]:focus,
|
||||
fieldset[disabled] .datepicker table tr td.active:focus,
|
||||
fieldset[disabled] .datepicker table tr td.active.highlighted:focus,
|
||||
.datepicker table tr td.active.disabled.focus,
|
||||
.datepicker table tr td.active.highlighted.disabled.focus,
|
||||
.datepicker table tr td.active[disabled].focus,
|
||||
.datepicker table tr td.active.highlighted[disabled].focus,
|
||||
fieldset[disabled] .datepicker table tr td.active.focus,
|
||||
fieldset[disabled] .datepicker table tr td.active.highlighted.focus {
|
||||
background-color: #337ab7;
|
||||
border-color: #2e6da4;
|
||||
}
|
||||
.datepicker table tr td span {
|
||||
display: block;
|
||||
width: 23%;
|
||||
height: 54px;
|
||||
line-height: 54px;
|
||||
float: left;
|
||||
margin: 1%;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.datepicker table tr td span:hover,
|
||||
.datepicker table tr td span.focused {
|
||||
background: #eeeeee;
|
||||
}
|
||||
.datepicker table tr td span.disabled,
|
||||
.datepicker table tr td span.disabled:hover {
|
||||
background: none;
|
||||
color: #777777;
|
||||
cursor: default;
|
||||
}
|
||||
.datepicker table tr td span.active,
|
||||
.datepicker table tr td span.active:hover,
|
||||
.datepicker table tr td span.active.disabled,
|
||||
.datepicker table tr td span.active.disabled:hover {
|
||||
color: #fff;
|
||||
background-color: #337ab7;
|
||||
border-color: #2e6da4;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td span.active:focus,
|
||||
.datepicker table tr td span.active:hover:focus,
|
||||
.datepicker table tr td span.active.disabled:focus,
|
||||
.datepicker table tr td span.active.disabled:hover:focus,
|
||||
.datepicker table tr td span.active.focus,
|
||||
.datepicker table tr td span.active:hover.focus,
|
||||
.datepicker table tr td span.active.disabled.focus,
|
||||
.datepicker table tr td span.active.disabled:hover.focus {
|
||||
color: #fff;
|
||||
background-color: #286090;
|
||||
border-color: #122b40;
|
||||
}
|
||||
.datepicker table tr td span.active:hover,
|
||||
.datepicker table tr td span.active:hover:hover,
|
||||
.datepicker table tr td span.active.disabled:hover,
|
||||
.datepicker table tr td span.active.disabled:hover:hover {
|
||||
color: #fff;
|
||||
background-color: #286090;
|
||||
border-color: #204d74;
|
||||
}
|
||||
.datepicker table tr td span.active:active,
|
||||
.datepicker table tr td span.active:hover:active,
|
||||
.datepicker table tr td span.active.disabled:active,
|
||||
.datepicker table tr td span.active.disabled:hover:active,
|
||||
.datepicker table tr td span.active.active,
|
||||
.datepicker table tr td span.active:hover.active,
|
||||
.datepicker table tr td span.active.disabled.active,
|
||||
.datepicker table tr td span.active.disabled:hover.active {
|
||||
color: #fff;
|
||||
background-color: #286090;
|
||||
border-color: #204d74;
|
||||
}
|
||||
.datepicker table tr td span.active:active:hover,
|
||||
.datepicker table tr td span.active:hover:active:hover,
|
||||
.datepicker table tr td span.active.disabled:active:hover,
|
||||
.datepicker table tr td span.active.disabled:hover:active:hover,
|
||||
.datepicker table tr td span.active.active:hover,
|
||||
.datepicker table tr td span.active:hover.active:hover,
|
||||
.datepicker table tr td span.active.disabled.active:hover,
|
||||
.datepicker table tr td span.active.disabled:hover.active:hover,
|
||||
.datepicker table tr td span.active:active:focus,
|
||||
.datepicker table tr td span.active:hover:active:focus,
|
||||
.datepicker table tr td span.active.disabled:active:focus,
|
||||
.datepicker table tr td span.active.disabled:hover:active:focus,
|
||||
.datepicker table tr td span.active.active:focus,
|
||||
.datepicker table tr td span.active:hover.active:focus,
|
||||
.datepicker table tr td span.active.disabled.active:focus,
|
||||
.datepicker table tr td span.active.disabled:hover.active:focus,
|
||||
.datepicker table tr td span.active:active.focus,
|
||||
.datepicker table tr td span.active:hover:active.focus,
|
||||
.datepicker table tr td span.active.disabled:active.focus,
|
||||
.datepicker table tr td span.active.disabled:hover:active.focus,
|
||||
.datepicker table tr td span.active.active.focus,
|
||||
.datepicker table tr td span.active:hover.active.focus,
|
||||
.datepicker table tr td span.active.disabled.active.focus,
|
||||
.datepicker table tr td span.active.disabled:hover.active.focus {
|
||||
color: #fff;
|
||||
background-color: #204d74;
|
||||
border-color: #122b40;
|
||||
}
|
||||
.datepicker table tr td span.active.disabled:hover,
|
||||
.datepicker table tr td span.active:hover.disabled:hover,
|
||||
.datepicker table tr td span.active.disabled.disabled:hover,
|
||||
.datepicker table tr td span.active.disabled:hover.disabled:hover,
|
||||
.datepicker table tr td span.active[disabled]:hover,
|
||||
.datepicker table tr td span.active:hover[disabled]:hover,
|
||||
.datepicker table tr td span.active.disabled[disabled]:hover,
|
||||
.datepicker table tr td span.active.disabled:hover[disabled]:hover,
|
||||
fieldset[disabled] .datepicker table tr td span.active:hover,
|
||||
fieldset[disabled] .datepicker table tr td span.active:hover:hover,
|
||||
fieldset[disabled] .datepicker table tr td span.active.disabled:hover,
|
||||
fieldset[disabled] .datepicker table tr td span.active.disabled:hover:hover,
|
||||
.datepicker table tr td span.active.disabled:focus,
|
||||
.datepicker table tr td span.active:hover.disabled:focus,
|
||||
.datepicker table tr td span.active.disabled.disabled:focus,
|
||||
.datepicker table tr td span.active.disabled:hover.disabled:focus,
|
||||
.datepicker table tr td span.active[disabled]:focus,
|
||||
.datepicker table tr td span.active:hover[disabled]:focus,
|
||||
.datepicker table tr td span.active.disabled[disabled]:focus,
|
||||
.datepicker table tr td span.active.disabled:hover[disabled]:focus,
|
||||
fieldset[disabled] .datepicker table tr td span.active:focus,
|
||||
fieldset[disabled] .datepicker table tr td span.active:hover:focus,
|
||||
fieldset[disabled] .datepicker table tr td span.active.disabled:focus,
|
||||
fieldset[disabled] .datepicker table tr td span.active.disabled:hover:focus,
|
||||
.datepicker table tr td span.active.disabled.focus,
|
||||
.datepicker table tr td span.active:hover.disabled.focus,
|
||||
.datepicker table tr td span.active.disabled.disabled.focus,
|
||||
.datepicker table tr td span.active.disabled:hover.disabled.focus,
|
||||
.datepicker table tr td span.active[disabled].focus,
|
||||
.datepicker table tr td span.active:hover[disabled].focus,
|
||||
.datepicker table tr td span.active.disabled[disabled].focus,
|
||||
.datepicker table tr td span.active.disabled:hover[disabled].focus,
|
||||
fieldset[disabled] .datepicker table tr td span.active.focus,
|
||||
fieldset[disabled] .datepicker table tr td span.active:hover.focus,
|
||||
fieldset[disabled] .datepicker table tr td span.active.disabled.focus,
|
||||
fieldset[disabled] .datepicker table tr td span.active.disabled:hover.focus {
|
||||
background-color: #337ab7;
|
||||
border-color: #2e6da4;
|
||||
}
|
||||
.datepicker table tr td span.old,
|
||||
.datepicker table tr td span.new {
|
||||
color: #777777;
|
||||
}
|
||||
.datepicker .datepicker-switch {
|
||||
width: 145px;
|
||||
}
|
||||
.datepicker .datepicker-switch,
|
||||
.datepicker .prev,
|
||||
.datepicker .next,
|
||||
.datepicker tfoot tr th {
|
||||
cursor: pointer;
|
||||
}
|
||||
.datepicker .datepicker-switch:hover,
|
||||
.datepicker .prev:hover,
|
||||
.datepicker .next:hover,
|
||||
.datepicker tfoot tr th:hover {
|
||||
background: #eeeeee;
|
||||
}
|
||||
.datepicker .cw {
|
||||
font-size: 10px;
|
||||
width: 12px;
|
||||
padding: 0 2px 0 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.input-group.date .input-group-addon {
|
||||
cursor: pointer;
|
||||
}
|
||||
.input-daterange {
|
||||
width: 100%;
|
||||
}
|
||||
.input-daterange input {
|
||||
text-align: center;
|
||||
}
|
||||
.input-daterange input:first-child {
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
.input-daterange input:last-child {
|
||||
border-radius: 0 3px 3px 0;
|
||||
}
|
||||
.input-daterange .input-group-addon {
|
||||
width: auto;
|
||||
min-width: 16px;
|
||||
padding: 4px 5px;
|
||||
line-height: 1.42857143;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
border-width: 1px 0;
|
||||
margin-left: -5px;
|
||||
margin-right: -5px;
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-datepicker3.css.map */
|
||||
File diff suppressed because one or more lines are too long
442
inst/www/shared/datepicker/css/datepicker.css
Normal file
442
inst/www/shared/datepicker/css/datepicker.css
Normal file
@@ -0,0 +1,442 @@
|
||||
/*!
|
||||
* Datepicker for Bootstrap
|
||||
*
|
||||
* Copyright 2012 Stefan Petre
|
||||
* Improvements by Andrew Rowls
|
||||
* Licensed under the Apache License v2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*/
|
||||
.datepicker {
|
||||
padding: 4px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
direction: ltr;
|
||||
/*.dow {
|
||||
border-top: 1px solid #ddd !important;
|
||||
}*/
|
||||
|
||||
}
|
||||
.datepicker-inline {
|
||||
width: 220px;
|
||||
}
|
||||
.datepicker.datepicker-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
.datepicker.datepicker-rtl table tr td span {
|
||||
float: right;
|
||||
}
|
||||
.datepicker-dropdown {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.datepicker-dropdown:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
border-left: 7px solid transparent;
|
||||
border-right: 7px solid transparent;
|
||||
border-bottom: 7px solid #ccc;
|
||||
border-bottom-color: rgba(0, 0, 0, 0.2);
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
left: 6px;
|
||||
}
|
||||
.datepicker-dropdown:after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid #ffffff;
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
left: 7px;
|
||||
}
|
||||
.datepicker > div {
|
||||
display: none;
|
||||
}
|
||||
.datepicker.days div.datepicker-days {
|
||||
display: block;
|
||||
}
|
||||
.datepicker.months div.datepicker-months {
|
||||
display: block;
|
||||
}
|
||||
.datepicker.years div.datepicker-years {
|
||||
display: block;
|
||||
}
|
||||
.datepicker table {
|
||||
margin: 0;
|
||||
}
|
||||
.datepicker td,
|
||||
.datepicker th {
|
||||
text-align: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
}
|
||||
.table-striped .datepicker table tr td,
|
||||
.table-striped .datepicker table tr th {
|
||||
background-color: transparent;
|
||||
}
|
||||
.datepicker table tr td.day:hover {
|
||||
background: #eeeeee;
|
||||
cursor: pointer;
|
||||
}
|
||||
.datepicker table tr td.old,
|
||||
.datepicker table tr td.new {
|
||||
color: #999999;
|
||||
}
|
||||
.datepicker table tr td.disabled,
|
||||
.datepicker table tr td.disabled:hover {
|
||||
background: none;
|
||||
color: #999999;
|
||||
cursor: default;
|
||||
}
|
||||
.datepicker table tr td.today,
|
||||
.datepicker table tr td.today:hover,
|
||||
.datepicker table tr td.today.disabled,
|
||||
.datepicker table tr td.today.disabled:hover {
|
||||
background-color: #fde19a;
|
||||
background-image: -moz-linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-image: -ms-linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a));
|
||||
background-image: -webkit-linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-image: -o-linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-image: linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0);
|
||||
border-color: #fdf59a #fdf59a #fbed50;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #000 !important;
|
||||
}
|
||||
.datepicker table tr td.today:hover,
|
||||
.datepicker table tr td.today:hover:hover,
|
||||
.datepicker table tr td.today.disabled:hover,
|
||||
.datepicker table tr td.today.disabled:hover:hover,
|
||||
.datepicker table tr td.today:active,
|
||||
.datepicker table tr td.today:hover:active,
|
||||
.datepicker table tr td.today.disabled:active,
|
||||
.datepicker table tr td.today.disabled:hover:active,
|
||||
.datepicker table tr td.today.active,
|
||||
.datepicker table tr td.today:hover.active,
|
||||
.datepicker table tr td.today.disabled.active,
|
||||
.datepicker table tr td.today.disabled:hover.active,
|
||||
.datepicker table tr td.today.disabled,
|
||||
.datepicker table tr td.today:hover.disabled,
|
||||
.datepicker table tr td.today.disabled.disabled,
|
||||
.datepicker table tr td.today.disabled:hover.disabled,
|
||||
.datepicker table tr td.today[disabled],
|
||||
.datepicker table tr td.today:hover[disabled],
|
||||
.datepicker table tr td.today.disabled[disabled],
|
||||
.datepicker table tr td.today.disabled:hover[disabled] {
|
||||
background-color: #fdf59a;
|
||||
}
|
||||
.datepicker table tr td.today:active,
|
||||
.datepicker table tr td.today:hover:active,
|
||||
.datepicker table tr td.today.disabled:active,
|
||||
.datepicker table tr td.today.disabled:hover:active,
|
||||
.datepicker table tr td.today.active,
|
||||
.datepicker table tr td.today:hover.active,
|
||||
.datepicker table tr td.today.disabled.active,
|
||||
.datepicker table tr td.today.disabled:hover.active {
|
||||
background-color: #fbf069 \9;
|
||||
}
|
||||
.datepicker table tr td.range,
|
||||
.datepicker table tr td.range:hover,
|
||||
.datepicker table tr td.range.disabled,
|
||||
.datepicker table tr td.range.disabled:hover {
|
||||
background: #eeeeee;
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.datepicker table tr td.range.today,
|
||||
.datepicker table tr td.range.today:hover,
|
||||
.datepicker table tr td.range.today.disabled,
|
||||
.datepicker table tr td.range.today.disabled:hover {
|
||||
background-color: #f3d17a;
|
||||
background-image: -moz-linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-image: -ms-linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f3c17a), to(#f3e97a));
|
||||
background-image: -webkit-linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-image: -o-linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-image: linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f3c17a', endColorstr='#f3e97a', GradientType=0);
|
||||
border-color: #f3e97a #f3e97a #edde34;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.datepicker table tr td.range.today:hover,
|
||||
.datepicker table tr td.range.today:hover:hover,
|
||||
.datepicker table tr td.range.today.disabled:hover,
|
||||
.datepicker table tr td.range.today.disabled:hover:hover,
|
||||
.datepicker table tr td.range.today:active,
|
||||
.datepicker table tr td.range.today:hover:active,
|
||||
.datepicker table tr td.range.today.disabled:active,
|
||||
.datepicker table tr td.range.today.disabled:hover:active,
|
||||
.datepicker table tr td.range.today.active,
|
||||
.datepicker table tr td.range.today:hover.active,
|
||||
.datepicker table tr td.range.today.disabled.active,
|
||||
.datepicker table tr td.range.today.disabled:hover.active,
|
||||
.datepicker table tr td.range.today.disabled,
|
||||
.datepicker table tr td.range.today:hover.disabled,
|
||||
.datepicker table tr td.range.today.disabled.disabled,
|
||||
.datepicker table tr td.range.today.disabled:hover.disabled,
|
||||
.datepicker table tr td.range.today[disabled],
|
||||
.datepicker table tr td.range.today:hover[disabled],
|
||||
.datepicker table tr td.range.today.disabled[disabled],
|
||||
.datepicker table tr td.range.today.disabled:hover[disabled] {
|
||||
background-color: #f3e97a;
|
||||
}
|
||||
.datepicker table tr td.range.today:active,
|
||||
.datepicker table tr td.range.today:hover:active,
|
||||
.datepicker table tr td.range.today.disabled:active,
|
||||
.datepicker table tr td.range.today.disabled:hover:active,
|
||||
.datepicker table tr td.range.today.active,
|
||||
.datepicker table tr td.range.today:hover.active,
|
||||
.datepicker table tr td.range.today.disabled.active,
|
||||
.datepicker table tr td.range.today.disabled:hover.active {
|
||||
background-color: #efe24b \9;
|
||||
}
|
||||
.datepicker table tr td.selected,
|
||||
.datepicker table tr td.selected:hover,
|
||||
.datepicker table tr td.selected.disabled,
|
||||
.datepicker table tr td.selected.disabled:hover {
|
||||
background-color: #9e9e9e;
|
||||
background-image: -moz-linear-gradient(top, #b3b3b3, #808080);
|
||||
background-image: -ms-linear-gradient(top, #b3b3b3, #808080);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#b3b3b3), to(#808080));
|
||||
background-image: -webkit-linear-gradient(top, #b3b3b3, #808080);
|
||||
background-image: -o-linear-gradient(top, #b3b3b3, #808080);
|
||||
background-image: linear-gradient(top, #b3b3b3, #808080);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b3b3b3', endColorstr='#808080', GradientType=0);
|
||||
border-color: #808080 #808080 #595959;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td.selected:hover,
|
||||
.datepicker table tr td.selected:hover:hover,
|
||||
.datepicker table tr td.selected.disabled:hover,
|
||||
.datepicker table tr td.selected.disabled:hover:hover,
|
||||
.datepicker table tr td.selected:active,
|
||||
.datepicker table tr td.selected:hover:active,
|
||||
.datepicker table tr td.selected.disabled:active,
|
||||
.datepicker table tr td.selected.disabled:hover:active,
|
||||
.datepicker table tr td.selected.active,
|
||||
.datepicker table tr td.selected:hover.active,
|
||||
.datepicker table tr td.selected.disabled.active,
|
||||
.datepicker table tr td.selected.disabled:hover.active,
|
||||
.datepicker table tr td.selected.disabled,
|
||||
.datepicker table tr td.selected:hover.disabled,
|
||||
.datepicker table tr td.selected.disabled.disabled,
|
||||
.datepicker table tr td.selected.disabled:hover.disabled,
|
||||
.datepicker table tr td.selected[disabled],
|
||||
.datepicker table tr td.selected:hover[disabled],
|
||||
.datepicker table tr td.selected.disabled[disabled],
|
||||
.datepicker table tr td.selected.disabled:hover[disabled] {
|
||||
background-color: #808080;
|
||||
}
|
||||
.datepicker table tr td.selected:active,
|
||||
.datepicker table tr td.selected:hover:active,
|
||||
.datepicker table tr td.selected.disabled:active,
|
||||
.datepicker table tr td.selected.disabled:hover:active,
|
||||
.datepicker table tr td.selected.active,
|
||||
.datepicker table tr td.selected:hover.active,
|
||||
.datepicker table tr td.selected.disabled.active,
|
||||
.datepicker table tr td.selected.disabled:hover.active {
|
||||
background-color: #666666 \9;
|
||||
}
|
||||
.datepicker table tr td.active,
|
||||
.datepicker table tr td.active:hover,
|
||||
.datepicker table tr td.active.disabled,
|
||||
.datepicker table tr td.active.disabled:hover {
|
||||
background-color: #006dcc;
|
||||
background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
|
||||
background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -o-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: linear-gradient(top, #0088cc, #0044cc);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
|
||||
border-color: #0044cc #0044cc #002a80;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td.active:hover,
|
||||
.datepicker table tr td.active:hover:hover,
|
||||
.datepicker table tr td.active.disabled:hover,
|
||||
.datepicker table tr td.active.disabled:hover:hover,
|
||||
.datepicker table tr td.active:active,
|
||||
.datepicker table tr td.active:hover:active,
|
||||
.datepicker table tr td.active.disabled:active,
|
||||
.datepicker table tr td.active.disabled:hover:active,
|
||||
.datepicker table tr td.active.active,
|
||||
.datepicker table tr td.active:hover.active,
|
||||
.datepicker table tr td.active.disabled.active,
|
||||
.datepicker table tr td.active.disabled:hover.active,
|
||||
.datepicker table tr td.active.disabled,
|
||||
.datepicker table tr td.active:hover.disabled,
|
||||
.datepicker table tr td.active.disabled.disabled,
|
||||
.datepicker table tr td.active.disabled:hover.disabled,
|
||||
.datepicker table tr td.active[disabled],
|
||||
.datepicker table tr td.active:hover[disabled],
|
||||
.datepicker table tr td.active.disabled[disabled],
|
||||
.datepicker table tr td.active.disabled:hover[disabled] {
|
||||
background-color: #0044cc;
|
||||
}
|
||||
.datepicker table tr td.active:active,
|
||||
.datepicker table tr td.active:hover:active,
|
||||
.datepicker table tr td.active.disabled:active,
|
||||
.datepicker table tr td.active.disabled:hover:active,
|
||||
.datepicker table tr td.active.active,
|
||||
.datepicker table tr td.active:hover.active,
|
||||
.datepicker table tr td.active.disabled.active,
|
||||
.datepicker table tr td.active.disabled:hover.active {
|
||||
background-color: #003399 \9;
|
||||
}
|
||||
.datepicker table tr td span {
|
||||
display: block;
|
||||
width: 23%;
|
||||
height: 54px;
|
||||
line-height: 54px;
|
||||
float: left;
|
||||
margin: 1%;
|
||||
cursor: pointer;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.datepicker table tr td span:hover {
|
||||
background: #eeeeee;
|
||||
}
|
||||
.datepicker table tr td span.disabled,
|
||||
.datepicker table tr td span.disabled:hover {
|
||||
background: none;
|
||||
color: #999999;
|
||||
cursor: default;
|
||||
}
|
||||
.datepicker table tr td span.active,
|
||||
.datepicker table tr td span.active:hover,
|
||||
.datepicker table tr td span.active.disabled,
|
||||
.datepicker table tr td span.active.disabled:hover {
|
||||
background-color: #006dcc;
|
||||
background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
|
||||
background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -o-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: linear-gradient(top, #0088cc, #0044cc);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
|
||||
border-color: #0044cc #0044cc #002a80;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td span.active:hover,
|
||||
.datepicker table tr td span.active:hover:hover,
|
||||
.datepicker table tr td span.active.disabled:hover,
|
||||
.datepicker table tr td span.active.disabled:hover:hover,
|
||||
.datepicker table tr td span.active:active,
|
||||
.datepicker table tr td span.active:hover:active,
|
||||
.datepicker table tr td span.active.disabled:active,
|
||||
.datepicker table tr td span.active.disabled:hover:active,
|
||||
.datepicker table tr td span.active.active,
|
||||
.datepicker table tr td span.active:hover.active,
|
||||
.datepicker table tr td span.active.disabled.active,
|
||||
.datepicker table tr td span.active.disabled:hover.active,
|
||||
.datepicker table tr td span.active.disabled,
|
||||
.datepicker table tr td span.active:hover.disabled,
|
||||
.datepicker table tr td span.active.disabled.disabled,
|
||||
.datepicker table tr td span.active.disabled:hover.disabled,
|
||||
.datepicker table tr td span.active[disabled],
|
||||
.datepicker table tr td span.active:hover[disabled],
|
||||
.datepicker table tr td span.active.disabled[disabled],
|
||||
.datepicker table tr td span.active.disabled:hover[disabled] {
|
||||
background-color: #0044cc;
|
||||
}
|
||||
.datepicker table tr td span.active:active,
|
||||
.datepicker table tr td span.active:hover:active,
|
||||
.datepicker table tr td span.active.disabled:active,
|
||||
.datepicker table tr td span.active.disabled:hover:active,
|
||||
.datepicker table tr td span.active.active,
|
||||
.datepicker table tr td span.active:hover.active,
|
||||
.datepicker table tr td span.active.disabled.active,
|
||||
.datepicker table tr td span.active.disabled:hover.active {
|
||||
background-color: #003399 \9;
|
||||
}
|
||||
.datepicker table tr td span.old {
|
||||
color: #999999;
|
||||
}
|
||||
.datepicker th.datepicker-switch {
|
||||
width: 145px;
|
||||
}
|
||||
.datepicker thead tr:first-child th,
|
||||
.datepicker tfoot tr:first-child th {
|
||||
cursor: pointer;
|
||||
}
|
||||
.datepicker thead tr:first-child th:hover,
|
||||
.datepicker tfoot tr:first-child th:hover {
|
||||
background: #eeeeee;
|
||||
}
|
||||
.datepicker .cw {
|
||||
font-size: 10px;
|
||||
width: 12px;
|
||||
padding: 0 2px 0 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.datepicker thead tr:first-child th.cw {
|
||||
cursor: default;
|
||||
background-color: transparent;
|
||||
}
|
||||
.input-append.date .add-on i,
|
||||
.input-prepend.date .add-on i {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.input-daterange input {
|
||||
text-align: center;
|
||||
}
|
||||
.input-daterange input:first-child {
|
||||
-webkit-border-radius: 3px 0 0 3px;
|
||||
-moz-border-radius: 3px 0 0 3px;
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
.input-daterange input:last-child {
|
||||
-webkit-border-radius: 0 3px 3px 0;
|
||||
-moz-border-radius: 0 3px 3px 0;
|
||||
border-radius: 0 3px 3px 0;
|
||||
}
|
||||
.input-daterange .add-on {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
min-width: 16px;
|
||||
height: 18px;
|
||||
padding: 4px 5px;
|
||||
font-weight: normal;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
text-shadow: 0 1px 0 #ffffff;
|
||||
vertical-align: middle;
|
||||
background-color: #eeeeee;
|
||||
border: 1px solid #ccc;
|
||||
margin-left: -5px;
|
||||
margin-right: -5px;
|
||||
}
|
||||
2133
inst/www/shared/datepicker/js/bootstrap-datepicker.js
vendored
2133
inst/www/shared/datepicker/js/bootstrap-datepicker.js
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates.ar={days:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت","الأحد"],daysShort:["أحد","اثنين","ثلاثاء","أربعاء","خميس","جمعة","سبت","أحد"],daysMin:["ح","ن","ث","ع","خ","ج","س","ح"],months:["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],monthsShort:["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],today:"هذا اليوم",rtl:!0}}(jQuery);
|
||||
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates.az={days:["Bazar","Bazar ertəsi","Çərşənbə axşamı","Çərşənbə","Cümə axşamı","Cümə","Şənbə"],daysShort:["B.","B.e","Ç.a","Ç.","C.a","C.","Ş."],daysMin:["B.","B.e","Ç.a","Ç.","C.a","C.","Ş."],months:["Yanvar","Fevral","Mart","Aprel","May","İyun","İyul","Avqust","Sentyabr","Oktyabr","Noyabr","Dekabr"],monthsShort:["Yan","Fev","Mar","Apr","May","İyun","İyul","Avq","Sen","Okt","Noy","Dek"],today:"Bu gün",weekStart:1}}(jQuery);
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.bg.js
vendored
Normal file
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.bg.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Bulgarian translation for bootstrap-datepicker
|
||||
* Apostol Apostolov <apostol.s.apostolov@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['bg'] = {
|
||||
days: ["Неделя", "Понеделник", "Вторник", "Сряда", "Четвъртък", "Петък", "Събота", "Неделя"],
|
||||
daysShort: ["Нед", "Пон", "Вто", "Сря", "Чет", "Пет", "Съб", "Нед"],
|
||||
daysMin: ["Н", "П", "В", "С", "Ч", "П", "С", "Н"],
|
||||
months: ["Януари", "Февруари", "Март", "Април", "Май", "Юни", "Юли", "Август", "Септември", "Октомври", "Ноември", "Декември"],
|
||||
monthsShort: ["Ян", "Фев", "Мар", "Апр", "Май", "Юни", "Юли", "Авг", "Сеп", "Окт", "Ное", "Дек"],
|
||||
today: "днес"
|
||||
};
|
||||
}(jQuery));
|
||||
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates.bg={days:["Неделя","Понеделник","Вторник","Сряда","Четвъртък","Петък","Събота"],daysShort:["Нед","Пон","Вто","Сря","Чет","Пет","Съб"],daysMin:["Н","П","В","С","Ч","П","С"],months:["Януари","Февруари","Март","Април","Май","Юни","Юли","Август","Септември","Октомври","Ноември","Декември"],monthsShort:["Ян","Фев","Мар","Апр","Май","Юни","Юли","Авг","Сеп","Окт","Ное","Дек"],today:"днес"}}(jQuery);
|
||||
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates.bs={days:["Nedjelja","Ponedjeljak","Utorak","Srijeda","Četvrtak","Petak","Subota"],daysShort:["Ned","Pon","Uto","Sri","Čet","Pet","Sub"],daysMin:["N","Po","U","Sr","Č","Pe","Su"],months:["Januar","Februar","Mart","April","Maj","Juni","Juli","August","Septembar","Oktobar","Novembar","Decembar"],monthsShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],today:"Danas",weekStart:1,format:"dd.mm.yyyy"}}(jQuery);
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.ca.js
vendored
Normal file
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.ca.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Catalan translation for bootstrap-datepicker
|
||||
* J. Garcia <jogaco.en@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['ca'] = {
|
||||
days: ["Diumenge", "Dilluns", "Dimarts", "Dimecres", "Dijous", "Divendres", "Dissabte", "Diumenge"],
|
||||
daysShort: ["Diu", "Dil", "Dmt", "Dmc", "Dij", "Div", "Dis", "Diu"],
|
||||
daysMin: ["dg", "dl", "dt", "dc", "dj", "dv", "ds", "dg"],
|
||||
months: ["Gener", "Febrer", "Març", "Abril", "Maig", "Juny", "Juliol", "Agost", "Setembre", "Octubre", "Novembre", "Desembre"],
|
||||
monthsShort: ["Gen", "Feb", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Oct", "Nov", "Des"],
|
||||
today: "Avui"
|
||||
};
|
||||
}(jQuery));
|
||||
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates.ca={days:["Diumenge","Dilluns","Dimarts","Dimecres","Dijous","Divendres","Dissabte"],daysShort:["Diu","Dil","Dmt","Dmc","Dij","Div","Dis"],daysMin:["dg","dl","dt","dc","dj","dv","ds"],months:["Gener","Febrer","Març","Abril","Maig","Juny","Juliol","Agost","Setembre","Octubre","Novembre","Desembre"],monthsShort:["Gen","Feb","Mar","Abr","Mai","Jun","Jul","Ago","Set","Oct","Nov","Des"],today:"Avui",monthsTitle:"Mesos",clear:"Esborrar",weekStart:1,format:"dd/mm/yyyy"}}(jQuery);
|
||||
15
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.cs.js
vendored
Normal file
15
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.cs.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Czech translation for bootstrap-datepicker
|
||||
* Matěj Koubík <matej@koubik.name>
|
||||
* Fixes by Michal Remiš <michal.remis@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['cs'] = {
|
||||
days: ["Neděle", "Pondělí", "Úterý", "Středa", "Čtvrtek", "Pátek", "Sobota", "Neděle"],
|
||||
daysShort: ["Ned", "Pon", "Úte", "Stř", "Čtv", "Pát", "Sob", "Ned"],
|
||||
daysMin: ["Ne", "Po", "Út", "St", "Čt", "Pá", "So", "Ne"],
|
||||
months: ["Leden", "Únor", "Březen", "Duben", "Květen", "Červen", "Červenec", "Srpen", "Září", "Říjen", "Listopad", "Prosinec"],
|
||||
monthsShort: ["Led", "Úno", "Bře", "Dub", "Kvě", "Čer", "Čnc", "Srp", "Zář", "Říj", "Lis", "Pro"],
|
||||
today: "Dnes"
|
||||
};
|
||||
}(jQuery));
|
||||
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates.cs={days:["Neděle","Pondělí","Úterý","Středa","Čtvrtek","Pátek","Sobota"],daysShort:["Ned","Pon","Úte","Stř","Čtv","Pát","Sob"],daysMin:["Ne","Po","Út","St","Čt","Pá","So"],months:["Leden","Únor","Březen","Duben","Květen","Červen","Červenec","Srpen","Září","Říjen","Listopad","Prosinec"],monthsShort:["Led","Úno","Bře","Dub","Kvě","Čer","Čnc","Srp","Zář","Říj","Lis","Pro"],today:"Dnes",clear:"Vymazat",weekStart:1,format:"dd.m.yyyy"}}(jQuery);
|
||||
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates.cy={days:["Sul","Llun","Mawrth","Mercher","Iau","Gwener","Sadwrn"],daysShort:["Sul","Llu","Maw","Mer","Iau","Gwe","Sad"],daysMin:["Su","Ll","Ma","Me","Ia","Gwe","Sa"],months:["Ionawr","Chewfror","Mawrth","Ebrill","Mai","Mehefin","Gorfennaf","Awst","Medi","Hydref","Tachwedd","Rhagfyr"],monthsShort:["Ion","Chw","Maw","Ebr","Mai","Meh","Gor","Aws","Med","Hyd","Tach","Rha"],today:"Heddiw"}}(jQuery);
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.da.js
vendored
Normal file
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.da.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Danish translation for bootstrap-datepicker
|
||||
* Christian Pedersen <http://github.com/chripede>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['da'] = {
|
||||
days: ["Søndag", "Mandag", "Tirsdag", "Onsdag", "Torsdag", "Fredag", "Lørdag", "Søndag"],
|
||||
daysShort: ["Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør", "Søn"],
|
||||
daysMin: ["Sø", "Ma", "Ti", "On", "To", "Fr", "Lø", "Sø"],
|
||||
months: ["Januar", "Februar", "Marts", "April", "Maj", "Juni", "Juli", "August", "September", "Oktober", "November", "December"],
|
||||
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"],
|
||||
today: "I Dag"
|
||||
};
|
||||
}(jQuery));
|
||||
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates.da={days:["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"],daysShort:["søn","man","tir","ons","tor","fre","lør"],daysMin:["sø","ma","ti","on","to","fr","lø"],months:["januar","februar","marts","april","maj","juni","juli","august","september","oktober","november","december"],monthsShort:["jan","feb","mar","apr","maj","jun","jul","aug","sep","okt","nov","dec"],today:"I Dag",clear:"Nulstil"}}(jQuery);
|
||||
16
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.de.js
vendored
Normal file
16
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.de.js
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* German translation for bootstrap-datepicker
|
||||
* Sam Zurcher <sam@orelias.ch>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['de'] = {
|
||||
days: ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"],
|
||||
daysShort: ["Son", "Mon", "Die", "Mit", "Don", "Fre", "Sam", "Son"],
|
||||
daysMin: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"],
|
||||
months: ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"],
|
||||
monthsShort: ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"],
|
||||
today: "Heute",
|
||||
weekStart: 1,
|
||||
format: "dd.mm.yyyy"
|
||||
};
|
||||
}(jQuery));
|
||||
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates.de={days:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],daysShort:["Son","Mon","Die","Mit","Don","Fre","Sam"],daysMin:["So","Mo","Di","Mi","Do","Fr","Sa"],months:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthsShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],today:"Heute",monthsTitle:"Monate",clear:"Löschen",weekStart:1,format:"dd.mm.yyyy"}}(jQuery);
|
||||
13
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.el.js
vendored
Normal file
13
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.el.js
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Greek translation for bootstrap-datepicker
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['el'] = {
|
||||
days: ["Κυριακή", "Δευτέρα", "Τρίτη", "Τετάρτη", "Πέμπτη", "Παρασκευή", "Σάββατο", "Κυριακή"],
|
||||
daysShort: ["Κυρ", "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ", "Κυρ"],
|
||||
daysMin: ["Κυ", "Δε", "Τρ", "Τε", "Πε", "Πα", "Σα", "Κυ"],
|
||||
months: ["Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος"],
|
||||
monthsShort: ["Ιαν", "Φεβ", "Μαρ", "Απρ", "Μάι", "Ιουν", "Ιουλ", "Αυγ", "Σεπ", "Οκτ", "Νοε", "Δεκ"],
|
||||
today: "Σήμερα"
|
||||
};
|
||||
}(jQuery));
|
||||
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates.el={days:["Κυριακή","Δευτέρα","Τρίτη","Τετάρτη","Πέμπτη","Παρασκευή","Σάββατο"],daysShort:["Κυρ","Δευ","Τρι","Τετ","Πεμ","Παρ","Σαβ"],daysMin:["Κυ","Δε","Τρ","Τε","Πε","Πα","Σα"],months:["Ιανουάριος","Φεβρουάριος","Μάρτιος","Απρίλιος","Μάιος","Ιούνιος","Ιούλιος","Αύγουστος","Σεπτέμβριος","Οκτώβριος","Νοέμβριος","Δεκέμβριος"],monthsShort:["Ιαν","Φεβ","Μαρ","Απρ","Μάι","Ιουν","Ιουλ","Αυγ","Σεπ","Οκτ","Νοε","Δεκ"],today:"Σήμερα",clear:"Καθαρισμός",weekStart:1,format:"d/m/yyyy"}}(jQuery);
|
||||
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates["en-AU"]={days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",monthsTitle:"Months",clear:"Clear",weekStart:1,format:"d/mm/yyyy"}}(jQuery);
|
||||
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates["en-GB"]={days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",monthsTitle:"Months",clear:"Clear",weekStart:1,format:"dd/mm/yyyy"}}(jQuery);
|
||||
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates.eo={days:["dimanĉo","lundo","mardo","merkredo","ĵaŭdo","vendredo","sabato"],daysShort:["dim.","lun.","mar.","mer.","ĵaŭ.","ven.","sam."],daysMin:["d","l","ma","me","ĵ","v","s"],months:["januaro","februaro","marto","aprilo","majo","junio","julio","aŭgusto","septembro","oktobro","novembro","decembro"],monthsShort:["jan.","feb.","mar.","apr.","majo","jun.","jul.","aŭg.","sep.","okt.","nov.","dec."],today:"Hodiaŭ",clear:"Nuligi",weekStart:1,format:"yyyy-mm-dd"}}(jQuery);
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.es.js
vendored
Normal file
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.es.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Spanish translation for bootstrap-datepicker
|
||||
* Bruno Bonamin <bruno.bonamin@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['es'] = {
|
||||
days: ["Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo"],
|
||||
daysShort: ["Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb", "Dom"],
|
||||
daysMin: ["Do", "Lu", "Ma", "Mi", "Ju", "Vi", "Sa", "Do"],
|
||||
months: ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"],
|
||||
monthsShort: ["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"],
|
||||
today: "Hoy"
|
||||
};
|
||||
}(jQuery));
|
||||
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates.es={days:["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"],daysShort:["Dom","Lun","Mar","Mié","Jue","Vie","Sáb"],daysMin:["Do","Lu","Ma","Mi","Ju","Vi","Sa"],months:["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"],monthsShort:["Ene","Feb","Mar","Abr","May","Jun","Jul","Ago","Sep","Oct","Nov","Dic"],today:"Hoy",monthsTitle:"Meses",clear:"Borrar",weekStart:1,format:"dd/mm/yyyy"}}(jQuery);
|
||||
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates.et={days:["Pühapäev","Esmaspäev","Teisipäev","Kolmapäev","Neljapäev","Reede","Laupäev"],daysShort:["Pühap","Esmasp","Teisip","Kolmap","Neljap","Reede","Laup"],daysMin:["P","E","T","K","N","R","L"],months:["Jaanuar","Veebruar","Märts","Aprill","Mai","Juuni","Juuli","August","September","Oktoober","November","Detsember"],monthsShort:["Jaan","Veebr","Märts","Apr","Mai","Juuni","Juuli","Aug","Sept","Okt","Nov","Dets"],today:"Täna",clear:"Tühjenda",weekStart:1,format:"dd.mm.yyyy"}}(jQuery);
|
||||
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates.eu={days:["Igandea","Astelehena","Asteartea","Asteazkena","Osteguna","Ostirala","Larunbata"],daysShort:["Ig","Al","Ar","Az","Og","Ol","Lr"],daysMin:["Ig","Al","Ar","Az","Og","Ol","Lr"],months:["Urtarrila","Otsaila","Martxoa","Apirila","Maiatza","Ekaina","Uztaila","Abuztua","Iraila","Urria","Azaroa","Abendua"],monthsShort:["Urt","Ots","Mar","Api","Mai","Eka","Uzt","Abu","Ira","Urr","Aza","Abe"],today:"Gaur"}}(jQuery);
|
||||
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates.fa={days:["یکشنبه","دوشنبه","سهشنبه","چهارشنبه","پنجشنبه","جمعه","شنبه","یکشنبه"],daysShort:["یک","دو","سه","چهار","پنج","جمعه","شنبه","یک"],daysMin:["ی","د","س","چ","پ","ج","ش","ی"],months:["ژانویه","فوریه","مارس","آوریل","مه","ژوئن","ژوئیه","اوت","سپتامبر","اکتبر","نوامبر","دسامبر"],monthsShort:["ژان","فور","مار","آور","مه","ژون","ژوی","اوت","سپت","اکت","نوا","دسا"],today:"امروز",clear:"پاک کن",weekStart:1,format:"yyyy/mm/dd"}}(jQuery);
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.fi.js
vendored
Normal file
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.fi.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Finnish translation for bootstrap-datepicker
|
||||
* Jaakko Salonen <https://github.com/jsalonen>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['fi'] = {
|
||||
days: ["sunnuntai", "maanantai", "tiistai", "keskiviikko", "torstai", "perjantai", "lauantai", "sunnuntai"],
|
||||
daysShort: ["sun", "maa", "tii", "kes", "tor", "per", "lau", "sun"],
|
||||
daysMin: ["su", "ma", "ti", "ke", "to", "pe", "la", "su"],
|
||||
months: ["tammikuu", "helmikuu", "maaliskuu", "huhtikuu", "toukokuu", "kesäkuu", "heinäkuu", "elokuu", "syyskuu", "lokakuu", "marraskuu", "joulukuu"],
|
||||
monthsShort: ["tam", "hel", "maa", "huh", "tou", "kes", "hei", "elo", "syy", "lok", "mar", "jou"],
|
||||
today: "tänään"
|
||||
};
|
||||
}(jQuery));
|
||||
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates.fi={days:["sunnuntai","maanantai","tiistai","keskiviikko","torstai","perjantai","lauantai"],daysShort:["sun","maa","tii","kes","tor","per","lau"],daysMin:["su","ma","ti","ke","to","pe","la"],months:["tammikuu","helmikuu","maaliskuu","huhtikuu","toukokuu","kesäkuu","heinäkuu","elokuu","syyskuu","lokakuu","marraskuu","joulukuu"],monthsShort:["tam","hel","maa","huh","tou","kes","hei","elo","syy","lok","mar","jou"],today:"tänään",clear:"Tyhjennä",weekStart:1,format:"d.m.yyyy"}}(jQuery);
|
||||
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates.fo={days:["Sunnudagur","Mánadagur","Týsdagur","Mikudagur","Hósdagur","Fríggjadagur","Leygardagur"],daysShort:["Sun","Mán","Týs","Mik","Hós","Frí","Ley"],daysMin:["Su","Má","Tý","Mi","Hó","Fr","Le"],months:["Januar","Februar","Marts","Apríl","Mei","Juni","Juli","August","Septembur","Oktobur","Novembur","Desembur"],monthsShort:["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Aug","Sep","Okt","Nov","Des"],today:"Í Dag",clear:"Reinsa"}}(jQuery);
|
||||
@@ -1 +0,0 @@
|
||||
!function(a){a.fn.datepicker.dates.fr={days:["Dimanche","Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi"],daysShort:["Dim","Lun","Mar","Mer","Jeu","Ven","Sam"],daysMin:["D","L","Ma","Me","J","V","S"],months:["Janvier","Février","Mars","Avril","Mai","Juin","Juillet","Août","Septembre","Octobre","Novembre","Décembre"],monthsShort:["Jan","Fév","Mar","Avr","Mai","Jui","Jul","Aou","Sep","Oct","Nov","Déc"],today:"Aujourd'hui",monthsTitle:"Mois",clear:"Effacer",weekStart:1,format:"dd.mm.yyyy"}}(jQuery);
|
||||
16
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.fr.js
vendored
Normal file
16
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.fr.js
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* French translation for bootstrap-datepicker
|
||||
* Nico Mollet <nico.mollet@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['fr'] = {
|
||||
days: ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche"],
|
||||
daysShort: ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam", "Dim"],
|
||||
daysMin: ["D", "L", "Ma", "Me", "J", "V", "S", "D"],
|
||||
months: ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"],
|
||||
monthsShort: ["Jan", "Fev", "Mar", "Avr", "Mai", "Jui", "Jul", "Aou", "Sep", "Oct", "Nov", "Dec"],
|
||||
today: "Aujourd'hui",
|
||||
weekStart: 1,
|
||||
format: "dd/mm/yyyy"
|
||||
};
|
||||
}(jQuery));
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user