Compare commits

..

1 Commits

Author SHA1 Message Date
JJ Allaire
e92ed4af85 add renderHTML function 2014-05-13 14:00:11 -04:00
140 changed files with 1958 additions and 581 deletions

View File

@@ -10,4 +10,3 @@
^man-roxygen$
^\.travis\.yml$
^staticdocs$
^tools$

View File

@@ -1,7 +1,7 @@
Package: shiny
Type: Package
Title: Web Application Framework for R
Version: 0.9.1.9008
Version: 0.9.1.9007
Date: 2014-03-19
Author: RStudio, Inc.
Maintainer: Winston Chang <winston@rstudio.com>
@@ -20,14 +20,13 @@ Imports:
caTools,
RJSONIO,
xtable,
digest,
htmltools (>= 0.2.4)
digest
Suggests:
datasets,
markdown,
Cairo (>= 1.5-5),
testthat,
knitr (>= 1.6),
markdown
knitr
URL: http://www.rstudio.com/shiny/
BugReports: https://github.com/rstudio/shiny/issues
Roxygen: list(wrap = FALSE)
@@ -37,13 +36,13 @@ Collate:
'map.R'
'globals.R'
'utils.R'
'htmltools.R'
'bootstrap.R'
'cache.R'
'fileupload.R'
'graph.R'
'hooks.R'
'html-deps.R'
'htmltools.R'
'imageutils.R'
'jqueryui.R'
'middleware-shiny.R'
@@ -59,6 +58,7 @@ Collate:
'shinywrappers.R'
'showcase.R'
'slider.R'
'tags.R'
'tar.R'
'timer.R'
'update-input.R'

View File

@@ -1,4 +1,4 @@
# Generated by roxygen2 (4.0.1): do not edit by hand
# Generated by roxygen2 (4.0.0): do not edit by hand
S3method("$",reactivevalues)
S3method("$",shinyoutput)
@@ -13,14 +13,21 @@ S3method("[[",shinyoutput)
S3method("[[<-",reactivevalues)
S3method("[[<-",shinyoutput)
S3method("names<-",reactivevalues)
S3method(as.character,shiny.tag)
S3method(as.character,shiny.tag.list)
S3method(as.list,reactivevalues)
S3method(as.shiny.appobj,character)
S3method(as.shiny.appobj,list)
S3method(as.shiny.appobj,shiny.appobj)
S3method(as.tags,shiny.render.function)
S3method(format,html)
S3method(format,shiny.tag)
S3method(format,shiny.tag.list)
S3method(names,reactivevalues)
S3method(print,html)
S3method(print,reactive)
S3method(print,shiny.appobj)
S3method(print,shiny.tag)
S3method(print,shiny.tag.list)
S3method(str,reactivevalues)
export(HTML)
export(a)
@@ -55,6 +62,7 @@ export(flowLayout)
export(fluidPage)
export(fluidRow)
export(getDefaultReactiveDomain)
export(getProvidedHtmlDependencies)
export(h1)
export(h2)
export(h3)
@@ -78,10 +86,11 @@ export(installExprFunction)
export(invalidateLater)
export(is.reactive)
export(is.reactivevalues)
export(is.singleton)
export(isolate)
export(knit_print.shiny.appobj)
export(knit_print.shiny.render.function)
export(knit_print.shiny.tag)
export(knit_print.shiny.tag.list)
export(mainPanel)
export(makeReactiveBinding)
export(markRenderFunction)
@@ -115,6 +124,7 @@ export(reactiveValuesToList)
export(registerInputHandler)
export(removeInputHandler)
export(renderDataTable)
export(renderHTML)
export(renderImage)
export(renderPlot)
export(renderPrint)
@@ -179,7 +189,6 @@ export(withTags)
import(RJSONIO)
import(caTools)
import(digest)
import(htmltools)
import(httpuv)
import(methods)
import(xtable)

9
NEWS
View File

@@ -27,15 +27,6 @@ shiny 0.9.1.9XXX
* `sliderInput` and `selectizeInput`/`selectInput` now use a standard horizontal
size instead of filling up all available horizontal space.
* Fixed a bug of renderDataTable() when the data object only has 1 row and 1
column. (Thanks, ZJ Dai, #429)
* `renderPrint` gained a new argument 'width' to control the width of the text
output, e.g. renderPrint({mtcars}, width = 40).
* Fixed #220: the zip file for a directory created by some programs may not have
the directory name as its first entry, in which case runUrl() can fail. (#220)
shiny 0.9.1
--------------------------------------------------------------------------------

72
R/app.R
View File

@@ -106,18 +106,14 @@ shinyAppDir <- function(appDir, options=list()) {
# ui.R as a webpage. The "cachedFuncWithFile" call makes sure that the closure
# we're creating here only gets executed when ui.R's contents change.
uiHandlerSource <- cachedFuncWithFile(appDir, "ui.R", case.sensitive = FALSE,
function(uiR) {
function() {
# Have to use file.path.ci every time in case the case of ui.R has
# changed. (Hmmm, overengineering a bit?)
uiR <- file.path.ci(appDir, "ui.R")
if (file.exists(uiR)) {
# If ui.R contains a call to shinyUI (which sets .globals$ui), use that.
# If not, then take the last expression that's returned from ui.R.
.globals$ui <- NULL
on.exit(.globals$ui <- NULL, add = FALSE)
ui <- source(uiR,
local = new.env(parent = globalenv()),
keep.source = TRUE)$value
if (!is.null(.globals$ui)) {
ui <- .globals$ui[[1]]
}
return(uiHttpHandler(ui))
} else {
return(function(req) NULL)
@@ -130,29 +126,14 @@ shinyAppDir <- function(appDir, options=list()) {
wwwDir <- file.path.ci(appDir, "www")
fallbackWWWDir <- system.file("www-dir", package = "shiny")
serverSource <- cachedFuncWithFile(appDir, "server.R", case.sensitive = FALSE,
function(serverR) {
# If server.R contains a call to shinyServer (which sets .globals$server),
# use that. If not, then take the last expression that's returned from
# server.R.
.globals$server <- NULL
on.exit(.globals$server <- NULL, add = TRUE)
result <- source(
serverR,
local = new.env(parent = globalenv()),
keep.source = TRUE
)$value
if (!is.null(.globals$server)) {
result <- .globals$server[[1]]
}
return(result)
}
)
serverSource <- cachedSource(appDir, "server.R", case.sensitive = FALSE)
# This function stands in for the server function, and reloads the
# real server function as necessary whenever server.R changes
serverFuncSource <- function() {
serverFunction <- serverSource()
serverFunction <- serverSource(
local = new.env(parent = globalenv()),
keep.source = TRUE)$value
if (is.null(serverFunction)) {
return(function(input, output) NULL)
} else if (is.function(serverFunction)) {
@@ -273,10 +254,37 @@ knit_print.shiny.appobj <- function(x, ...) {
# need to grab those and put them in meta, like in knit_print.shiny.tag. But
# for now it's not an issue, so just return the HTML and warning.
knitr::asis_output(htmlPreserve(format(output, indent=FALSE)),
knitr::asis_output(html_preserve(format(output, indent=FALSE)),
meta = shiny_warning, cacheable = FALSE)
}
#' @rdname knitr_methods
#' @export
knit_print.shiny.tag <- function(x, ...) {
output <- surroundSingletons(x)
deps <- getNewestDeps(findDependencies(x))
content <- takeHeads(output)
head_content <- doRenderTags(tagList(content$head))
meta <- if (length(head_content) > 1 || head_content != "") {
list(structure(head_content, class = "shiny_head"))
}
meta <- c(meta, deps)
knitr::asis_output(html_preserve(format(content$ui, indent=FALSE)), meta = meta)
}
knit_print.html <- function(x, ...) {
deps <- getNewestDeps(findDependencies(x))
knitr::asis_output(html_preserve(as.character(x)),
meta = if (length(deps)) list(deps))
}
#' @rdname knitr_methods
#' @export
knit_print.shiny.tag.list <- knit_print.shiny.tag
# Lets us use a nicer syntax in knitr chunks than literally
# calling output$value <- renderFoo(...) and fooOutput().
#' @rdname knitr_methods
@@ -286,3 +294,11 @@ knit_print.shiny.render.function <- function(x, ...) {
attr(output, "knit_cacheable") <- FALSE
output
}
html_preserve <- function(x) {
x <- paste(x, collapse = "\r\n")
if (nzchar(x))
sprintf("<!--html_preserve-->%s<!--/html_preserve-->", x)
else
x
}

View File

@@ -1,4 +1,5 @@
#' @include utils.R
#' @include htmltools.R
NULL
#' Create a Bootstrap page
@@ -37,19 +38,16 @@ bootstrapPage <- function(..., title = NULL, responsive = TRUE, theme = NULL) {
}
cssExt <- ext(".css")
jsExt = ext(".js")
bs <- c(
href = "shared/bootstrap",
file = system.file("www/shared/bootstrap", package = "shiny")
)
bs <- "shared/bootstrap"
list(
htmlDependency("bootstrap", "2.3.2", bs,
html_dependency("bootstrap", "2.3.2", path = bs,
script = sprintf("js/bootstrap%s", jsExt),
stylesheet = if (is.null(theme))
sprintf("css/bootstrap%s", cssExt)
),
if (responsive) {
htmlDependency("bootstrap-responsive", "2.3.2", bs,
html_dependency("bootstrap-responsive", "2.3.2", path = bs,
stylesheet = sprintf("css/bootstrap-responsive%s", cssExt),
meta = list(viewport = "width=device-width, initial-scale=1.0")
)
@@ -57,7 +55,7 @@ bootstrapPage <- function(..., title = NULL, responsive = TRUE, theme = NULL) {
)
}
attachDependencies(
attach_dependency(
tagList(
if (!is.null(title)) tags$head(tags$title(title)),
if (!is.null(theme)) {
@@ -605,8 +603,6 @@ checkboxGroupInput <- function(inputId, label, choices, selected = NULL) {
# Before shiny 0.9, `selected` refers to names/labels of `choices`; now it
# refers to values. Below is a function for backward compatibility.
validateSelected <- function(selected, choices, inputId) {
# drop names, otherwise toJSON() keeps them too
selected <- unname(selected)
if (is.list(choices)) {
# <optgroup> is not there yet
if (any(sapply(choices, length) > 1)) return(selected)
@@ -619,7 +615,7 @@ validateSelected <- function(selected, choices, inputId) {
i <- (selected %in% nms) & !(selected %in% choices)
if (any(i)) {
warnFun <- if (all(i)) {
# replace names with values
# replace names with values; drop names, otherwise toJSON() keeps them too
selected <- unname(choices[selected])
warning
} else stop # stop when it is ambiguous (some labels == values)
@@ -700,7 +696,7 @@ choicesWithNames <- function(choices) {
#' "Gears" = "gear"))
#' @export
selectInput <- function(inputId, label, choices, selected = NULL,
multiple = FALSE, selectize = TRUE, width = NULL) {
multiple = FALSE, selectize = TRUE) {
# resolve names
choices <- choicesWithNames(choices)
@@ -727,7 +723,7 @@ selectInput <- function(inputId, label, choices, selected = NULL,
# return label and select tag
res <- tagList(controlLabel(inputId, label), selectTag)
if (!selectize) return(res)
selectizeIt(inputId, res, NULL, width, nonempty = !multiple && !("" %in% choices))
selectizeIt(inputId, res, NULL, nonempty = !multiple && !("" %in% choices))
}
#' @rdname selectInput
@@ -736,8 +732,6 @@ selectInput <- function(inputId, label, choices, selected = NULL,
#' 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\%'};
#' see \code{\link{validateCssUnit}}.
#' @note The selectize input created from \code{selectizeInput()} allows
#' deletion of the selected option even in a single select input, which will
#' return an empty string as its value. This is the default behavior of
@@ -747,16 +741,15 @@ selectInput <- function(inputId, label, choices, selected = NULL,
#' \code{choices} argument. This is to keep compatibility with
#' \code{selectInput(..., selectize = FALSE)}.
#' @export
selectizeInput <- function(inputId, ..., options = NULL, width = NULL) {
selectizeIt(inputId, selectInput(inputId, ..., selectize = FALSE), options, width)
selectizeInput <- function(inputId, ..., options = NULL) {
selectizeIt(inputId, selectInput(inputId, ..., selectize = FALSE), options)
}
# given a select input and its id, selectize it
selectizeIt <- function(inputId, select, options, width = NULL, nonempty = FALSE) {
selectizeIt <- function(inputId, select, options, nonempty = FALSE) {
res <- checkAsIs(options)
selectizeDep <- htmlDependency(
"selectize", "0.8.5", c(href = "shared/selectize"),
selectizeDep <- html_dependency("selectize", "0.8.5", "shared/selectize",
stylesheet = "css/selectize.bootstrap2.css",
head = format(tagList(
HTML('<!--[if lt IE 9]>'),
@@ -765,14 +758,13 @@ selectizeIt <- function(inputId, select, options, width = NULL, nonempty = FALSE
tags$script(src = 'shared/selectize/js/selectize.min.js')
))
)
attachDependencies(
attach_dependency(
tagList(
select,
tags$script(
type = 'application/json',
`data-for` = inputId, `data-nonempty` = if (nonempty) '',
`data-eval` = if (length(res$eval)) HTML(toJSON(res$eval)),
`data-width` = validateCssUnit(width),
if (length(res$options)) HTML(toJSON(res$options)) else '{}'
)
),
@@ -936,7 +928,7 @@ actionLink <- function(inputId, label, icon = NULL, ...) {
#' @param animate \code{TRUE} to show simple animation controls with default
#' settings; \code{FALSE} not to; or a custom settings list, such as those
#' created using \code{animationOptions}.
#' @inheritParams selectizeInput
#'
#' @family input elements
#' @seealso \code{\link{updateSliderInput}}
#'
@@ -955,7 +947,7 @@ actionLink <- function(inputId, label, icon = NULL, ...) {
#' @export
sliderInput <- function(inputId, label, min, max, value, step = NULL,
round=FALSE, format='#,##0.#####', locale='us',
ticks=TRUE, animate=FALSE, width=NULL) {
ticks=TRUE, animate=FALSE) {
if (identical(animate, TRUE))
animate <- animationOptions()
@@ -969,8 +961,7 @@ sliderInput <- function(inputId, label, min, max, value, step = NULL,
# build slider
sliderTag <- slider(inputId, min=min, max=max, value=value, step=step,
round=round, locale=locale, format=format, ticks=ticks, animate=animate,
width=width)
round=round, locale=locale, format=format, ticks=ticks, animate=animate)
if (is.null(label)) {
sliderTag
@@ -982,9 +973,8 @@ sliderInput <- function(inputId, label, min, max, value, step = NULL,
}
}
datePickerDependency <- htmlDependency(
"bootstrap-datepicker", "1.0.2", c(href = "shared/datepicker"),
script = "js/bootstrap-datepicker.min.js",
datePickerDependency <- html_dependency("bootstrap-datepicker", "1.0.2",
"shared/datepicker", script = "js/bootstrap-datepicker.min.js",
stylesheet = "css/datepicker.css")
#' Create date input
@@ -1063,7 +1053,7 @@ dateInput <- function(inputId, label, value = NULL, min = NULL, max = NULL,
if (inherits(min, "Date")) min <- format(min, "%Y-%m-%d")
if (inherits(max, "Date")) max <- format(max, "%Y-%m-%d")
attachDependencies(
attach_dependency(
tags$div(id = inputId,
class = "shiny-date-input",
@@ -1162,7 +1152,7 @@ dateRangeInput <- function(inputId, label, start = NULL, end = NULL,
if (inherits(min, "Date")) min <- format(min, "%Y-%m-%d")
if (inherits(max, "Date")) max <- format(max, "%Y-%m-%d")
attachDependencies(
attach_dependency(
tags$div(id = inputId,
# input-daterange class is needed for dropdown behavior
class = "shiny-date-range-input input-daterange",
@@ -1608,12 +1598,12 @@ tableOutput <- function(outputId) {
}
dataTableDependency <- list(
htmlDependency(
"datatables", "1.9.4", c(href = "shared/datatables"),
html_dependency(
"datatables", "1.9.4", "shared/datatables",
script = "js/jquery.dataTables.min.js"
),
htmlDependency(
"datatables-bootstrap", "1.9.4", c(href = "shared/datatables"),
html_dependency(
"datatables-bootstrap", "1.9.4", "shared/datatables",
stylesheet = "css/DT_bootstrap.css",
script = "js/DT_bootstrap.js"
)
@@ -1622,7 +1612,7 @@ dataTableDependency <- list(
#' @rdname tableOutput
#' @export
dataTableOutput <- function(outputId) {
attachDependencies(
attach_dependency(
div(id = outputId, class="shiny-datatable-output"),
dataTableDependency
)
@@ -1766,3 +1756,49 @@ icon <- function(name, class = NULL, lib = "font-awesome") {
iconClass <- function(icon) {
if (!is.null(icon)) icon[[2]]$attribs$class
}
#' Validate proper CSS formatting of a unit
#'
#' Checks that the argument is valid for use as a CSS unit of length.
#'
#' \code{NULL} and \code{NA} are returned unchanged.
#'
#' Single element numeric vectors are returned as a character vector with the
#' number plus a suffix of \code{"px"}.
#'
#' Single element character vectors must be \code{"auto"} or \code{"inherit"},
#' or a number. If the number has a suffix, it must be valid: \code{px},
#' \code{\%}, \code{em}, \code{pt}, \code{in}, \code{cm}, \code{mm}, \code{ex},
#' or \code{pc}. If the number has no suffix, the suffix \code{"px"} is
#' appended.
#'
#' Any other value will cause an error to be thrown.
#'
#' @param x The unit to validate. Will be treated as a number of pixels if a
#' unit is not specified.
#' @return A properly formatted CSS unit of length, if possible. Otherwise, will
#' throw an error.
#' @examples
#' validateCssUnit("10%")
#' validateCssUnit(400) #treated as '400px'
#' @export
validateCssUnit <- function(x) {
if (is.null(x) || is.na(x))
return(x)
if (length(x) > 1 || (!is.character(x) && !is.numeric(x)))
stop('CSS units must be a numeric or character vector with a single element')
# if the input is a character vector consisting only of digits (e.g. "960"), coerce it to a
# numeric value
if (is.character(x) && nchar(x) > 0 && gsub("\\d*", "", x) == "")
x <- as.numeric(x)
if (is.character(x) &&
!grepl("^(auto|inherit|((\\.\\d+)|(\\d+(\\.\\d+)?))(%|in|cm|mm|em|ex|pt|pc|px))$", x)) {
stop('"', x, '" is not a valid CSS unit (e.g., "100%", "400px", "auto")')
} else if (is.numeric(x)) {
x <- paste(x, "px", sep = "")
}
x
}

View File

@@ -1,3 +1,5 @@
pathPattern <- "^(~|/|[a-zA-Z]:[/\\\\]|\\\\\\\\)"
createWebDependency <- function(dependency) {
if (is.null(dependency))
return(NULL)
@@ -5,11 +7,53 @@ createWebDependency <- function(dependency) {
if (!inherits(dependency, "html_dependency"))
stop("Unexpected non-html_dependency type")
if (is.null(dependency$src$href)) {
# Does it look like a path on disk? Register it as a resource and replace the
# disk-based path with a relative URL
if (grepl(pathPattern, dependency$path, perl = TRUE)) {
prefix <- paste(dependency$name, "-", dependency$version, sep = "")
addResourcePath(prefix, dependency$src$file)
dependency$src$href <- prefix
addResourcePath(prefix, dependency$path)
dependency$path <- prefix
}
return(dependency)
}
# Given a list of dependencies, choose the latest versions and return them as a
# named list in the correct order.
getNewestDeps <- function(dependencies) {
result <- list()
for (dep in dependencies) {
if (!is.null(dep)) {
other <- result[[dep$name]]
if (is.null(other) || compareVersion(dep$version, other$version) > 0) {
# Note that if the dep was already in the result list, then this
# assignment preserves its position in the list
result[[dep$name]] <- dep
}
}
}
return(result)
}
# Remove `remove` from `dependencies` if the name matches.
# dependencies is a named list of dependencies.
# remove is a named list of dependencies that take priority.
# If warnOnConflict, then warn when a dependency is being removed because of an
# older version already being loaded.
removeDeps <- function(dependencies, remove, warnOnConflict = TRUE) {
matches <- names(dependencies) %in% names(remove)
if (warnOnConflict) {
for (depname in names(dependencies)[matches]) {
loser <- dependencies[[depname]]
winner <- remove[[depname]]
if (compareVersion(loser$version, winner$version) > 0) {
warning(sprintf(paste("The dependency %s %s conflicts with",
"version %s"), loser$name, loser$version, winner$version
))
}
}
}
# Return only deps that weren't in remove
return(dependencies[!matches])
}

View File

@@ -1,101 +1,97 @@
#' @export
a <- htmltools::a
#' @export
br <- htmltools::br
#' @export
code <- htmltools::code
# Define an HTML dependency
#
# Define an HTML dependency (e.g. CSS or Javascript and related library). HTML
# dependency definitions are required for \code{\link{html_output}} that
# require CSS or JavaScript within the document head to render correctly.
#
# @param name Library name
# @param version Library version
# @param path Full path to library
# @param meta Named list of meta tags to insert into document head
# @param script Script(s) to include within the document head (should be
# specified relative to the \code{path} parameter).
# @param stylesheet Stylesheet(s) to include within the document (should be
# specified relative to the \code{path} parameter).
# @param head Arbitrary lines of HTML to insert into the document head
#
# @return An object that can be included in the list of dependencies passed to
# \code{\link{html_print}} or \code{\link{html_knit_print}}.
#
# @details See the documentation on
# \href{http://rmarkdown.rstudio.com/developer_html_widgets.html}{R
# Markdown HTML Widgets} for examples and additional details.
#
html_dependency <- function(name,
version,
path,
meta = NULL,
script = NULL,
stylesheet = NULL,
head = NULL) {
structure(class = "html_dependency", list(
name = name,
version = version,
path = path,
meta = meta,
script = script,
stylesheet = stylesheet,
head = head
))
}
#' @export
div <- htmltools::div
#' @export
em <- htmltools::em
# Given a list of HTML dependencies produce a character representation
# suitable for inclusion within the head of an HTML document
html_dependencies_as_character <- function(dependencies, lib_dir = NULL) {
#' @export
h1 <- htmltools::h1
html <- c()
#' @export
h2 <- htmltools::h2
for (dep in dependencies) {
#' @export
h3 <- htmltools::h3
# copy library files if necessary
if (!is.null(lib_dir)) {
#' @export
h4 <- htmltools::h4
if (!file.exists(lib_dir))
dir.create(lib_dir)
#' @export
h5 <- htmltools::h5
target_dir <- file.path(lib_dir, basename(dep$path))
if (!file.exists(target_dir))
file.copy(from = dep$path, to = lib_dir, recursive = TRUE)
#' @export
h6 <- htmltools::h6
dep$path <- file.path(basename(lib_dir), basename(target_dir))
}
#' @export
hr <- htmltools::hr
# add meta content
for (name in names(dep$meta)) {
html <- c(html, paste("<meta name=\"", name,
"\" content=\"", dep$meta[[name]], "\" />",
sep = ""))
}
#' @export
HTML <- htmltools::HTML
# add stylesheets
for (stylesheet in dep$stylesheet) {
stylesheet <- file.path(dep$path, stylesheet)
html <- c(html, paste("<link href=\"", stylesheet, "\" ",
"rel=\"stylesheet\" />",
sep = ""))
}
#' @export
img <- htmltools::img
# add scripts
for (script in dep$script) {
script <- file.path(dep$path, script)
html <- c(html,
paste("<script src=\"", script, "\"></script>", sep = ""))
}
#' @export
includeCSS <- htmltools::includeCSS
# add raw head content
html <- c(html, dep$head)
}
#' @export
includeHTML <- htmltools::includeHTML
html
}
#' @export
includeMarkdown <- htmltools::includeMarkdown
#' @export
includeScript <- htmltools::includeScript
#' @export
includeText <- htmltools::includeText
#' @export
is.singleton <- htmltools::is.singleton
#' @export
p <- htmltools::p
#' @export
pre <- htmltools::pre
#' @export
singleton <- htmltools::singleton
#' @export
span <- htmltools::span
#' @export
strong <- htmltools::strong
#' @export
tag <- htmltools::tag
#' @export
tagAppendAttributes <- htmltools::tagAppendAttributes
#' @export
tagAppendChild <- htmltools::tagAppendChild
#' @export
tagAppendChildren <- htmltools::tagAppendChildren
#' @export
tagList <- htmltools::tagList
#' @export
tags <- htmltools::tags
#' @export
tagSetChildren <- htmltools::tagSetChildren
#' @export
validateCssUnit <- htmltools::validateCssUnit
#' @export
withTags <- htmltools::withTags
attach_dependency <- function(x, dependency) {
structure(x, html_dependency = dependency)
}

View File

@@ -42,9 +42,6 @@ plotPNG <- function(func, filename=tempfile(fileext='.png'),
}
pngfun(filename=filename, width=width, height=height, res=res, ...)
# Call plot.new() so that even if no plotting operations are performed
# at least we have a blank background
plot.new()
dv <- dev.cur()
tryCatch(shinyCallingHandlers(func()), finally = dev.off(dv))

View File

@@ -373,7 +373,7 @@ Observable <- setRefClass(
on.exit(.running <<- wasRunning)
ctx$run(function() {
result <- withVisible(try(shinyCallingHandlers(.func()), silent=TRUE))
result <- withVisible(try(shinyCallingHandlers(.func()), silent=FALSE))
.visible <<- result$visible
.value <<- result$value
})

View File

@@ -137,8 +137,6 @@ runUrl <- function(url, filetype = NULL, subdir = NULL, port = NULL,
message("Downloading ", url)
filePath <- tempfile('shinyapp', fileext=fileext)
fileDir <- tempfile('shinyapp')
dir.create(fileDir, showWarnings = FALSE)
if (download(url, filePath, mode = "wb", quiet = TRUE) != 0)
stop("Failed to download URL ", url)
on.exit(unlink(filePath))
@@ -150,18 +148,17 @@ runUrl <- function(url, filetype = NULL, subdir = NULL, port = NULL,
# 2) If the internal untar implementation is used, it chokes on the 'g'
# type flag that github uses (to stash their commit hash info).
# By using our own forked/modified untar2 we sidestep both issues.
first <- untar2(filePath, list=TRUE)[1]
untar2(filePath, exdir = fileDir)
dirname <- untar2(filePath, list=TRUE)[1]
untar2(filePath, exdir = dirname(filePath))
} else if (fileext == ".zip") {
first <- as.character(unzip(filePath, list=TRUE)$Name)[1]
unzip(filePath, exdir = fileDir)
dirname <- as.character(unzip(filePath, list=TRUE)$Name[1])
unzip(filePath, exdir = dirname(filePath))
}
on.exit(unlink(fileDir, recursive = TRUE), add = TRUE)
appdir <- file.path(fileDir, first)
if (!file_test('-d', appdir)) appdir <- dirname(appdir)
appdir <- file.path(dirname(filePath), dirname)
on.exit(unlink(appdir, recursive = TRUE), add = TRUE)
if (!is.null(subdir)) appdir <- file.path(appdir, subdir)
runApp(appdir, port=port, launch.browser=launch.browser)
appsubdir <- ifelse(is.null(subdir), appdir, file.path(appdir, subdir))
runApp(appsubdir, port=port, launch.browser=launch.browser)
}

View File

@@ -240,7 +240,6 @@ resourcePathHandler <- function(req) {
#'
#' @export
shinyServer <- function(func) {
.globals$server <- list(func)
invisible(func)
}
@@ -463,7 +462,7 @@ identicalFunctionBodies <- function(a, b) {
handlerManager <- HandlerManager$new()
addSubApp <- function(appObj, autoRemove = TRUE) {
path <- createUniqueId(16, "/app")
path <- sprintf("/%s", createUniqueId(16))
appHandlers <- createAppHandlers(appObj$httpHandler, appObj$serverFuncSource)
# remove the leading / from the path so a relative path is returned

View File

@@ -15,19 +15,12 @@ NULL
#' @name shiny-package
#' @aliases shiny
#' @docType package
#' @import htmltools httpuv caTools RJSONIO xtable digest methods
#' @import httpuv caTools RJSONIO xtable digest methods
NULL
createUniqueId <- function(bytes, prefix = "", suffix = "") {
createUniqueId <- function(bytes) {
withPrivateSeed({
paste(
prefix,
paste(
format(as.hexmode(sample(256, bytes, replace = TRUE)-1), width=2),
collapse = ""),
suffix,
sep = ""
)
paste(as.hexmode(sample(256, bytes, replace = TRUE)-1), collapse = "")
})
}
@@ -87,10 +80,10 @@ workerId <- local({
#' }
#' }
#' \code{clientData} also contains information about each output.
#' \code{output_\var{outputId}_width} and \code{output_\var{outputId}_height}
#' \code{output_\emph{outputId}_width} and \code{output_\emph{outputId}_height}
#' give the dimensions (using \code{offsetWidth} and \code{offsetHeight}) of
#' the DOM element that is bound to \code{\var{outputId}}, and
#' \code{output_\var{outputId}_hidden} is a logical that indicates whether
#' the DOM element that is bound to \code{\emph{outputId}}, and
#' \code{output_\emph{outputId}_hidden} is a logical that indicates whether
#' the element is hidden. These values may be \code{NULL} if the output is
#' not bound.
#' }
@@ -321,25 +314,7 @@ ShinySession <- setRefClass(
obs <- observe({
value <- try(
{
tryCatch(
shinyCallingHandlers(func()),
shiny.silent.error = function(cond) {
# Don't let shiny.silent.error go through the normal stop
# path of try, because we don't want it to print. But we
# do want to try to return the same looking result so that
# the code below can send the error to the browser.
structure(
NULL,
class = "try-error",
condition = cond
)
}
)
},
silent=FALSE
)
value <- try(shinyCallingHandlers(func()), silent=FALSE)
.invalidatedOutputErrors$remove(name)
.invalidatedOutputValues$remove(name)

View File

@@ -1,5 +1,143 @@
#' @include globals.R
NULL
#' @rdname builder
#' @export
p <- function(...) tags$p(...)
#' @rdname builder
#' @export
h1 <- function(...) tags$h1(...)
#' @rdname builder
#' @export
h2 <- function(...) tags$h2(...)
#' @rdname builder
#' @export
h3 <- function(...) tags$h3(...)
#' @rdname builder
#' @export
h4 <- function(...) tags$h4(...)
#' @rdname builder
#' @export
h5 <- function(...) tags$h5(...)
#' @rdname builder
#' @export
h6 <- function(...) tags$h6(...)
#' @rdname builder
#' @export
a <- function(...) tags$a(...)
#' @rdname builder
#' @export
br <- function(...) tags$br(...)
#' @rdname builder
#' @export
div <- function(...) tags$div(...)
#' @rdname builder
#' @export
span <- function(...) tags$span(...)
#' @rdname builder
#' @export
pre <- function(...) tags$pre(...)
#' @rdname builder
#' @export
code <- function(...) tags$code(...)
#' @rdname builder
#' @export
img <- function(...) tags$img(...)
#' @rdname builder
#' @export
strong <- function(...) tags$strong(...)
#' @rdname builder
#' @export
em <- function(...) tags$em(...)
#' @rdname builder
#' @export
hr <- function(...) tags$hr(...)
#' Include Content From a File
#'
#' Include HTML, text, or rendered Markdown into a \link[=shinyUI]{Shiny UI}.
#'
#' These functions provide a convenient way to include an extensive amount of
#' HTML, textual, Markdown, CSS, or JavaScript content, rather than using a
#' large literal R string.
#'
#' @note \code{includeText} escapes its contents, but does no other processing.
#' This means that hard breaks and multiple spaces will be rendered as they
#' usually are in HTML: as a single space character. If you are looking for
#' preformatted text, wrap the call with \code{\link{pre}}, or consider using
#' \code{includeMarkdown} instead.
#'
#' @note The \code{includeMarkdown} function requires the \code{markdown}
#' package.
#'
#' @param path The path of the file to be included. It is highly recommended to
#' use a relative path (the base path being the Shiny application directory),
#' not an absolute path.
#'
#' @rdname include
#' @name include
#' @aliases includeHTML
#' @export
includeHTML <- function(path) {
dependsOnFile(path)
lines <- readLines(path, warn=FALSE, encoding='UTF-8')
return(HTML(paste(lines, collapse='\r\n')))
}
#' @rdname include
#' @export
includeText <- function(path) {
dependsOnFile(path)
lines <- readLines(path, warn=FALSE, encoding='UTF-8')
return(paste(lines, collapse='\r\n'))
}
#' @rdname include
#' @export
includeMarkdown <- function(path) {
library(markdown)
dependsOnFile(path)
html <- markdown::markdownToHTML(path, fragment.only=TRUE)
Encoding(html) <- 'UTF-8'
return(HTML(html))
}
#' @param ... Any additional attributes to be applied to the generated tag.
#' @rdname include
#' @export
includeCSS <- function(path, ...) {
dependsOnFile(path)
lines <- readLines(path, warn=FALSE, encoding='UTF-8')
args <- list(...)
if (is.null(args$type))
args$type <- 'text/css'
return(do.call(tags$style,
c(list(HTML(paste(lines, collapse='\r\n'))), args)))
}
#' @rdname include
#' @export
includeScript <- function(path, ...) {
dependsOnFile(path)
lines <- readLines(path, warn=FALSE, encoding='UTF-8')
return(tags$script(HTML(paste(lines, collapse='\r\n')), ...))
}
#' Load the MathJax library and typeset math expressions
#'
@@ -24,6 +162,22 @@ withMathJax <- function(...) {
)
}
#' Include Content Only Once
#'
#' Use \code{singleton} to wrap contents (tag, text, HTML, or lists) that should
#' be included in the generated document only once, yet may appear in the
#' document-generating code more than once. Only the first appearance of the
#' content (in document order) will be used. Useful for custom components that
#' have JavaScript files or stylesheets.
#'
#' @param x A \code{\link{tag}}, text, \code{\link{HTML}}, or list.
#'
#' @export
singleton <- function(x) {
class(x) <- c(class(x), 'shiny.singleton')
return(x)
}
renderPage <- function(ui, connection, showcase=0) {
if (showcase > 0)
@@ -33,18 +187,17 @@ renderPage <- function(ui, connection, showcase=0) {
deps <- c(
list(
htmlDependency("jquery", "1.11.0", c(href="shared"), script = "jquery.js"),
htmlDependency("shiny", packageVersion("shiny"), c(href="shared"),
html_dependency("jquery", "1.11.0", "shared", script = "jquery.js"),
html_dependency("shiny", packageVersion("shiny"), "shared",
script = "shiny.js", stylesheet = "shiny.css")
),
result$dependencies
)
deps <- resolveDependencies(deps)
deps <- lapply(deps, createWebDependency)
depStr <- paste(sapply(deps, function(dep) {
sprintf("%s[%s]", dep$name, dep$version)
}), collapse = ";")
depHtml <- renderDependencies(deps, "href")
depHtml <- html_dependencies_as_character(deps)
# write preamble
writeLines(c('<!DOCTYPE html>',
@@ -93,10 +246,7 @@ renderPage <- function(ui, connection, showcase=0) {
#' @return The user interface definition, without modifications or side effects.
#'
#' @export
shinyUI <- function(ui) {
.globals$ui <- list(ui)
ui
}
shinyUI <- function(ui) ui
uiHttpHandler <- function(ui, path = "/") {
@@ -131,3 +281,20 @@ uiHttpHandler <- function(ui, path = "/") {
return(httpResponse(200, content=html))
}
}
#' Return HTML dependencies provided by Shiny
#'
#' By default, Shiny supplies some framework scripts when it renders a page.
#' \code{getProvidedHtmlDependencies} returns a list of those provided objects.
#'
#' @return A list of objects of type \code{html_dependency}, one per dependency
#'
#' @export
getProvidedHtmlDependencies <- function() {
list(structure(
list(name = "jquery",
version = "1.11.0",
path = system.file("www/shared/jquery.js", package="shiny"),
script = "jquery.js"),
class = "html_dependency"))
}

View File

@@ -23,18 +23,13 @@ markRenderFunction <- function(uiFunc, renderFunc) {
useRenderFunction <- function(renderFunc) {
outputFunction <- attr(renderFunc, "outputFunc")
id <- createUniqueId(8, "out")
id <- createUniqueId(8)
o <- getDefaultReactiveDomain()$output
if (!is.null(o))
o[[id]] <- renderFunc
return(outputFunction(id))
}
#' @S3method as.tags shiny.render.function
as.tags.shiny.render.function <- function(x, ...) {
useRenderFunction(x)
}
#' Plot Output
#'
#' Renders a reactive plot that is suitable for assigning to an \code{output}
@@ -89,16 +84,7 @@ renderPlot <- function(expr, width='auto', height='auto', res=72, ...,
else
heightWrapper <- NULL
# If renderPlot isn't going to adapt to the height of the div, then the
# div needs to adapt to the height of renderPlot. By default, plotOutput
# sets the height to 400px, so to make it adapt we need to override it
# with NULL.
outputFunc <- if (identical(height, 'auto'))
plotOutput
else
function(outputId) plotOutput(outputId, height = NULL)
return(markRenderFunction(outputFunc, function(shinysession, name, ...) {
return(markRenderFunction(plotOutput, function(shinysession, name, ...) {
if (!is.null(widthWrapper))
width <- widthWrapper()
if (!is.null(heightWrapper))
@@ -125,11 +111,8 @@ renderPlot <- function(expr, width='auto', height='auto', res=72, ...,
plotFunc <- function() {
# Actually perform the plotting
result <- withVisible(func())
if (result$visible) {
# Use capture.output to squelch printing to the actual console; we
# are only interested in plot output
capture.output(print(result$value))
}
if (result$visible)
print(result$value)
# Now capture some graphics device info before we close it
usrCoords <- par('usr')
@@ -361,15 +344,14 @@ renderTable <- function(expr, ..., env=parent.frame(), quoted=FALSE, func=NULL)
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' @param func A function that may print output and/or return a printable R
#' object (deprecated; use \code{expr} instead).
#' @param width The value for \code{\link{options}('width')}.
#'
#' @seealso \code{\link{renderText}} for displaying the value returned from a
#' function, instead of the printed output.
#'
#' @example res/text-example.R
#'
#' @export
renderPrint <- function(expr, env = parent.frame(), quoted = FALSE, func = NULL,
width = getOption('width')) {
renderPrint <- function(expr, env=parent.frame(), quoted=FALSE, func=NULL) {
if (!is.null(func)) {
shinyDeprecated(msg="renderPrint: argument 'func' is deprecated. Please use 'expr' instead.")
} else {
@@ -377,9 +359,11 @@ renderPrint <- function(expr, env = parent.frame(), quoted = FALSE, func = NULL,
}
markRenderFunction(verbatimTextOutput, function() {
op <- options(width = width)
on.exit(options(op), add = TRUE)
paste(capture.output(func()), collapse = "\n")
return(paste(capture.output({
result <- withVisible(func())
if (result$visible)
print(result$value)
}), collapse="\n"))
})
}
@@ -423,6 +407,27 @@ renderText <- function(expr, env=parent.frame(), quoted=FALSE, func=NULL) {
})
}
#' @export
renderHTML <- function(expr, env=parent.frame(), quoted=FALSE) {
installExprFunction(expr, "func", env, quoted)
markRenderFunction(htmlOutput, function() {
result <- func()
if (is.null(result) || length(result) == 0)
return(NULL)
output <- list(
html = result,
deps = attr(result, "dependencies")
)
return(output)
})
}
#' UI Output
#'
#' \bold{Experimental feature.} Makes a reactive version of a function that
@@ -464,8 +469,7 @@ renderUI <- function(expr, env=parent.frame(), quoted=FALSE, func=NULL) {
result <- takeSingletons(result, shinysession$singletons, desingleton=FALSE)$ui
result <- surroundSingletons(result)
dependencies <- lapply(resolveDependencies(findDependencies(result)),
createWebDependency)
dependencies <- lapply(getNewestDeps(findDependencies(result)), createWebDependency)
names(dependencies) <- NULL
# renderTags returns a list with head, singletons, and html

View File

@@ -31,13 +31,13 @@ licenseLink <- function(licenseName) {
showcaseHead <- function() {
deps <- list(
htmlDependency("jqueryui", "1.10.4", c(href="shared/jqueryui/1.10.4"),
html_dependency("jqueryui", "1.10.4", "shared/jqueryui/1.10.4",
script = "jquery-ui.min.js"),
htmlDependency("showdown", "0.3.1", c(href="shared/showdown/compressed"),
html_dependency("showdown", "0.3.1", "shared/showdown/compressed",
script = "showdown.js"),
htmlDependency("font-awesome", "4.0.3", c(href="shared/font-awesome"),
html_dependency("font-awesome", "4.0.3", "shared/font-awesome",
stylesheet = "css/font-awesome.min.css"),
htmlDependency("highlight.js", "6.2", c(href="shared/highlight"),
html_dependency("highlight.js", "6.2", "shared/highlight",
script = "highlight.pack.js")
)
@@ -54,7 +54,7 @@ showcaseHead <- function() {
else ""
))
return(attachDependencies(html, deps))
return(attach_dependency(html, deps))
}
# Returns tags containing the application metadata (title and author) in

View File

@@ -33,7 +33,7 @@ animationOptions <- function(interval=1000,
# (www/shared/slider contains js, css, and img dependencies)
slider <- function(inputId, min, max, value, step = NULL, ...,
round=FALSE, format='#,##0.#####', locale='us',
ticks=TRUE, animate=FALSE, width=NULL) {
ticks=TRUE, animate=FALSE) {
# validate inputId
inputId <- as.character(inputId)
if (!is.character(inputId))
@@ -99,20 +99,20 @@ slider <- function(inputId, min, max, value, step = NULL, ...,
}
# build slider
dep <- htmlDependency("jslider", "1", c(href="shared/slider"),
dep <- html_dependency(name = "jslider", version = "1",
path = "shared/slider",
script = "js/jquery.slider.min.js",
stylesheet = "css/jquery.slider.min.css"
)
sliderFragment <- list(
attachDependencies(
attach_dependency(
tags$input(
id=inputId, type="slider",
name=inputId, value=paste(value, collapse=';'), class="jslider",
'data-from'=min, 'data-to'=max, 'data-step'=step,
'data-skin'='plastic', 'data-round'=round, 'data-locale'=locale,
'data-format'=format, 'data-scale'=ticks,
'data-smooth'=FALSE,
'data-width'=validateCssUnit(width)
'data-smooth'=FALSE
),
dep
)

637
R/tags.R Normal file
View File

@@ -0,0 +1,637 @@
htmlEscape <- local({
.htmlSpecials <- list(
`&` = '&amp;',
`<` = '&lt;',
`>` = '&gt;'
)
.htmlSpecialsPattern <- paste(names(.htmlSpecials), collapse='|')
.htmlSpecialsAttrib <- c(
.htmlSpecials,
`'` = '&#39;',
`"` = '&quot;',
`\r` = '&#13;',
`\n` = '&#10;'
)
.htmlSpecialsPatternAttrib <- paste(names(.htmlSpecialsAttrib), collapse='|')
function(text, attribute=TRUE) {
pattern <- if(attribute)
.htmlSpecialsPatternAttrib
else
.htmlSpecialsPattern
# Short circuit in the common case that there's nothing to escape
if (!any(grepl(pattern, text)))
return(text)
specials <- if(attribute)
.htmlSpecialsAttrib
else
.htmlSpecials
for (chr in names(specials)) {
text <- gsub(chr, specials[[chr]], text, fixed=TRUE)
}
return(text)
}
})
isTag <- function(x) {
inherits(x, "shiny.tag")
}
#' @export
print.shiny.tag <- function(x, ...) {
print(as.character(x), ...)
invisible(x)
}
# indent can be numeric to indicate an initial indent level,
# or FALSE to suppress
#' @export
format.shiny.tag <- function(x, ..., singletons = character(0), indent = 0) {
as.character(renderTags(x, singletons = singletons, indent = indent)$html)
}
#' @export
as.character.shiny.tag <- function(x, ...) {
renderTags(x)$html
}
#' @export
print.shiny.tag.list <- print.shiny.tag
#' @export
format.shiny.tag.list <- format.shiny.tag
#' @export
as.character.shiny.tag.list <- as.character.shiny.tag
#' @export
print.html <- function(x, ...) {
cat(x, "\n")
invisible(x)
}
#' @export
format.html <- function(x, ...) {
as.character(x)
}
normalizeText <- function(text) {
if (!is.null(attr(text, "html")))
text
else
htmlEscape(text, attribute=FALSE)
}
#' @rdname tag
#' @export
tagList <- function(...) {
lst <- list(...)
class(lst) <- c("shiny.tag.list", "list")
return(lst)
}
#' @rdname tag
#' @export
tagAppendAttributes <- function(tag, ...) {
tag$attribs <- c(tag$attribs, list(...))
tag
}
#' @rdname tag
#' @export
tagAppendChild <- function(tag, child) {
tag$children[[length(tag$children)+1]] <- child
tag
}
#' @rdname tag
#' @export
tagAppendChildren <- function(tag, ..., list = NULL) {
tag$children <- c(tag$children, c(list(...), list))
tag
}
#' @rdname tag
#' @export
tagSetChildren <- function(tag, ..., list = NULL) {
tag$children <- c(list(...), list)
tag
}
#' HTML Tag Object
#'
#' \code{tag()} creates an HTML tag definition. Note that all of the valid HTML5
#' tags are already defined in the \code{\link{tags}} environment so these
#' functions should only be used to generate additional tags.
#' \code{tagAppendChild()} and \code{tagList()} are for supporting package
#' authors who wish to create their own sets of tags; see the contents of
#' bootstrap.R for examples.
#' @param _tag_name HTML tag name
#' @param varArgs List of attributes and children of the element. Named list
#' items become attributes, and unnamed list items become children. Valid
#' children are tags, single-character character vectors (which become text
#' nodes), and raw HTML (see \code{\link{HTML}}). You can also pass lists that
#' contain tags, text nodes, and HTML.
#' @param tag A tag to append child elements to.
#' @param child A child element to append to a parent tag.
#' @param ... Unnamed items that comprise this list of tags.
#' @param list An optional list of elements. Can be used with or instead of the
#' \code{...} items.
#' @return An HTML tag object that can be rendered as HTML using
#' \code{\link{as.character}()}.
#' @export
#' @examples
#' tagList(tags$h1("Title"),
#' tags$h2("Header text"),
#' tags$p("Text here"))
#'
#' # Can also convert a regular list to a tagList (internal data structure isn't
#' # exactly the same, but when rendered to HTML, the output is the same).
#' x <- list(tags$h1("Title"),
#' tags$h2("Header text"),
#' tags$p("Text here"))
#' tagList(x)
tag <- function(`_tag_name`, varArgs) {
# Get arg names; if not a named list, use vector of empty strings
varArgsNames <- names(varArgs)
if (is.null(varArgsNames))
varArgsNames <- character(length=length(varArgs))
# Named arguments become attribs, dropping NULL values
named_idx <- nzchar(varArgsNames)
attribs <- dropNulls(varArgs[named_idx])
# Unnamed arguments are flattened and added as children.
# Use unname() to remove the names attribute from the list, which would
# consist of empty strings anyway.
children <- unname(varArgs[!named_idx])
# Return tag data structure
structure(
list(name = `_tag_name`,
attribs = attribs,
children = children),
class = "shiny.tag"
)
}
tagWrite <- function(tag, textWriter, indent=0, eol = "\n") {
if (length(tag) == 0)
return (NULL)
# optionally process a list of tags
if (!isTag(tag) && is.list(tag)) {
tag <- dropNullsOrEmpty(flattenTags(tag))
lapply(tag, tagWrite, textWriter, indent)
return (NULL)
}
nextIndent <- if (is.numeric(indent)) indent + 1 else indent
indent <- if (is.numeric(indent)) indent else 0
# compute indent text
indentText <- paste(rep(" ", indent*2), collapse="")
# Check if it's just text (may either be plain-text or HTML)
if (is.character(tag)) {
textWriter(paste(indentText, normalizeText(tag), eol, sep=""))
return (NULL)
}
# write tag name
textWriter(paste(indentText, "<", tag$name, sep=""))
# Convert all attribs to chars explicitly; prevents us from messing up factors
attribs <- lapply(tag$attribs, as.character)
# concatenate attributes
# split() is very slow, so avoid it if possible
if (anyDuplicated(names(attribs)))
attribs <- lapply(split(attribs, names(attribs)), paste, collapse = " ")
# write attributes
for (attrib in names(attribs)) {
attribValue <- attribs[[attrib]]
if (!is.na(attribValue)) {
if (is.logical(attribValue))
attribValue <- tolower(attribValue)
text <- htmlEscape(attribValue, attribute=TRUE)
textWriter(paste(" ", attrib,"=\"", text, "\"", sep=""))
}
else {
textWriter(paste(" ", attrib, sep=""))
}
}
# write any children
children <- dropNullsOrEmpty(flattenTags(tag$children))
if (length(children) > 0) {
textWriter(">")
# special case for a single child text node (skip newlines and indentation)
if ((length(children) == 1) && is.character(children[[1]]) ) {
textWriter(paste(normalizeText(children[[1]]), "</", tag$name, ">", eol,
sep=""))
}
else {
textWriter("\n")
for (child in children)
tagWrite(child, textWriter, nextIndent)
textWriter(paste(indentText, "</", tag$name, ">", eol, sep=""))
}
}
else {
# only self-close void elements
# (see: http://dev.w3.org/html5/spec/single-page.html#void-elements)
if (tag$name %in% c("area", "base", "br", "col", "command", "embed", "hr",
"img", "input", "keygen", "link", "meta", "param",
"source", "track", "wbr")) {
textWriter(paste("/>", eol, sep=""))
}
else {
textWriter(paste("></", tag$name, ">", eol, sep=""))
}
}
}
doRenderTags <- function(ui, indent = 0) {
# Render the body--the bodyHtml variable will be created
conn <- file(open="w+")
connWriter <- function(text) writeChar(text, conn, eos = NULL)
htmlResult <- tryCatch({
tagWrite(ui, connWriter, indent)
flush(conn)
readLines(conn)
},
finally = close(conn)
)
return(HTML(paste(htmlResult, collapse = "\n")))
}
renderTags <- function(ui, singletons = character(0), indent = 0) {
# Do singleton and head processing before rendering
singletonInfo <- takeSingletons(ui, singletons)
headInfo <- takeHeads(singletonInfo$ui)
deps <- getNewestDeps(findDependencies(singletonInfo$ui))
headIndent <- if (is.numeric(indent)) indent + 1 else indent
headHtml <- doRenderTags(headInfo$head, indent = headIndent)
bodyHtml <- doRenderTags(headInfo$ui, indent = indent)
return(list(head = headHtml,
singletons = singletonInfo$singletons,
dependencies = deps,
html = bodyHtml))
}
# Walk a tree of tag objects, rewriting objects according to func.
# preorder=TRUE means preorder tree traversal, that is, an object
# should be rewritten before its children.
rewriteTags <- function(ui, func, preorder) {
if (preorder)
ui <- func(ui)
if (isTag(ui)) {
ui$children[] <- lapply(ui$children, rewriteTags, func, preorder)
} else if (is.list(ui)) {
ui[] <- lapply(ui, rewriteTags, func, preorder)
}
if (!preorder)
ui <- func(ui)
return(ui)
}
# Preprocess a tag object by changing any singleton X into
# <!--SHINY.SINGLETON[sig]-->X'<!--/SHINY.SINGLETON[sig]-->
# where sig is the sha1 of X, and X' is X minus the singleton
# attribute.
#
# In the case of nested singletons, outer singletons are processed
# before inner singletons (otherwise the processing of inner
# singletons would cause the sha1 of the outer singletons to be
# different).
surroundSingletons <- local({
surroundSingleton <- function(uiObj) {
if (inherits(uiObj, "shiny.singleton")) {
sig <- digest(uiObj, "sha1")
class(uiObj) <- class(uiObj)[class(uiObj) != "shiny.singleton"]
return(tagList(
HTML(sprintf("<!--SHINY.SINGLETON[%s]-->", sig)),
uiObj,
HTML(sprintf("<!--/SHINY.SINGLETON[%s]-->", sig))
))
} else {
uiObj
}
}
function(ui) {
rewriteTags(ui, surroundSingleton, TRUE)
}
})
# Given a tag object, apply singleton logic (allow singleton objects
# to appear no more than once per signature) and return the processed
# HTML objects and also the list of known singletons.
takeSingletons <- function(ui, singletons=character(0), desingleton=TRUE) {
result <- rewriteTags(ui, function(uiObj) {
if (inherits(uiObj, "shiny.singleton")) {
sig <- digest(uiObj, "sha1")
if (sig %in% singletons)
return(NULL)
singletons <<- append(singletons, sig)
if (desingleton)
class(uiObj) <- class(uiObj)[class(uiObj) != "shiny.singleton"]
return(uiObj)
} else {
return(uiObj)
}
}, TRUE)
return(list(ui=result, singletons=singletons))
}
# Given a tag object, extract out any children of tags$head
# and return them separate from the body.
takeHeads <- function(ui) {
headItems <- list()
result <- rewriteTags(ui, function(uiObj) {
if (isTag(uiObj) && tolower(uiObj$name) == "head") {
headItems <<- append(headItems, uiObj$children)
return(NULL)
}
return(uiObj)
}, FALSE)
return(list(ui=result, head=headItems))
}
findDependencies <- function(ui) {
dep <- attr(ui, "html_dependency")
if (!is.null(dep) && inherits(dep, "html_dependency"))
dep <- list(dep)
children <- if (is.list(ui)) {
if (isTag(ui)) {
ui$children
} else {
ui
}
}
childDeps <- unlist(lapply(children, findDependencies), recursive = FALSE)
c(childDeps, if (!is.null(dep)) dep)
}
#' HTML Builder Functions
#'
#' Simple functions for constructing HTML documents.
#'
#' The \code{tags} environment contains convenience functions for all valid
#' HTML5 tags. To generate tags that are not part of the HTML5 specification,
#' you can use the \code{\link{tag}()} function.
#'
#' Dedicated functions are available for the most common HTML tags that do not
#' conflict with common R functions.
#'
#' The result from these functions is a tag object, which can be converted using
#' \code{\link{as.character}()}.
#'
#' @name builder
#' @param ... Attributes and children of the element. Named arguments become
#' attributes, and positional arguments become children. Valid children are
#' tags, single-character character vectors (which become text nodes), and raw
#' HTML (see \code{\link{HTML}}). You can also pass lists that contain tags,
#' text nodes, and HTML.
#' @export tags
#' @examples
#' doc <- tags$html(
#' tags$head(
#' tags$title('My first page')
#' ),
#' tags$body(
#' h1('My first heading'),
#' p('My first paragraph, with some ',
#' strong('bold'),
#' ' text.'),
#' div(id='myDiv', class='simpleDiv',
#' 'Here is a div with some attributes.')
#' )
#' )
#' cat(as.character(doc))
NULL
#' @rdname builder
#' @format NULL
#' @docType NULL
#' @keywords NULL
tags <- list(
a = function(...) tag("a", list(...)),
abbr = function(...) tag("abbr", list(...)),
address = function(...) tag("address", list(...)),
area = function(...) tag("area", list(...)),
article = function(...) tag("article", list(...)),
aside = function(...) tag("aside", list(...)),
audio = function(...) tag("audio", list(...)),
b = function(...) tag("b", list(...)),
base = function(...) tag("base", list(...)),
bdi = function(...) tag("bdi", list(...)),
bdo = function(...) tag("bdo", list(...)),
blockquote = function(...) tag("blockquote", list(...)),
body = function(...) tag("body", list(...)),
br = function(...) tag("br", list(...)),
button = function(...) tag("button", list(...)),
canvas = function(...) tag("canvas", list(...)),
caption = function(...) tag("caption", list(...)),
cite = function(...) tag("cite", list(...)),
code = function(...) tag("code", list(...)),
col = function(...) tag("col", list(...)),
colgroup = function(...) tag("colgroup", list(...)),
command = function(...) tag("command", list(...)),
data = function(...) tag("data", list(...)),
datalist = function(...) tag("datalist", list(...)),
dd = function(...) tag("dd", list(...)),
del = function(...) tag("del", list(...)),
details = function(...) tag("details", list(...)),
dfn = function(...) tag("dfn", list(...)),
div = function(...) tag("div", list(...)),
dl = function(...) tag("dl", list(...)),
dt = function(...) tag("dt", list(...)),
em = function(...) tag("em", list(...)),
embed = function(...) tag("embed", list(...)),
eventsource = function(...) tag("eventsource", list(...)),
fieldset = function(...) tag("fieldset", list(...)),
figcaption = function(...) tag("figcaption", list(...)),
figure = function(...) tag("figure", list(...)),
footer = function(...) tag("footer", list(...)),
form = function(...) tag("form", list(...)),
h1 = function(...) tag("h1", list(...)),
h2 = function(...) tag("h2", list(...)),
h3 = function(...) tag("h3", list(...)),
h4 = function(...) tag("h4", list(...)),
h5 = function(...) tag("h5", list(...)),
h6 = function(...) tag("h6", list(...)),
head = function(...) tag("head", list(...)),
header = function(...) tag("header", list(...)),
hgroup = function(...) tag("hgroup", list(...)),
hr = function(...) tag("hr", list(...)),
html = function(...) tag("html", list(...)),
i = function(...) tag("i", list(...)),
iframe = function(...) tag("iframe", list(...)),
img = function(...) tag("img", list(...)),
input = function(...) tag("input", list(...)),
ins = function(...) tag("ins", list(...)),
kbd = function(...) tag("kbd", list(...)),
keygen = function(...) tag("keygen", list(...)),
label = function(...) tag("label", list(...)),
legend = function(...) tag("legend", list(...)),
li = function(...) tag("li", list(...)),
link = function(...) tag("link", list(...)),
mark = function(...) tag("mark", list(...)),
map = function(...) tag("map", list(...)),
menu = function(...) tag("menu", list(...)),
meta = function(...) tag("meta", list(...)),
meter = function(...) tag("meter", list(...)),
nav = function(...) tag("nav", list(...)),
noscript = function(...) tag("noscript", list(...)),
object = function(...) tag("object", list(...)),
ol = function(...) tag("ol", list(...)),
optgroup = function(...) tag("optgroup", list(...)),
option = function(...) tag("option", list(...)),
output = function(...) tag("output", list(...)),
p = function(...) tag("p", list(...)),
param = function(...) tag("param", list(...)),
pre = function(...) tag("pre", list(...)),
progress = function(...) tag("progress", list(...)),
q = function(...) tag("q", list(...)),
ruby = function(...) tag("ruby", list(...)),
rp = function(...) tag("rp", list(...)),
rt = function(...) tag("rt", list(...)),
s = function(...) tag("s", list(...)),
samp = function(...) tag("samp", list(...)),
script = function(...) tag("script", list(...)),
section = function(...) tag("section", list(...)),
select = function(...) tag("select", list(...)),
small = function(...) tag("small", list(...)),
source = function(...) tag("source", list(...)),
span = function(...) tag("span", list(...)),
strong = function(...) tag("strong", list(...)),
style = function(...) tag("style", list(...)),
sub = function(...) tag("sub", list(...)),
summary = function(...) tag("summary", list(...)),
sup = function(...) tag("sup", list(...)),
table = function(...) tag("table", list(...)),
tbody = function(...) tag("tbody", list(...)),
td = function(...) tag("td", list(...)),
textarea = function(...) tag("textarea", list(...)),
tfoot = function(...) tag("tfoot", list(...)),
th = function(...) tag("th", list(...)),
thead = function(...) tag("thead", list(...)),
time = function(...) tag("time", list(...)),
title = function(...) tag("title", list(...)),
tr = function(...) tag("tr", list(...)),
track = function(...) tag("track", list(...)),
u = function(...) tag("u", list(...)),
ul = function(...) tag("ul", list(...)),
var = function(...) tag("var", list(...)),
video = function(...) tag("video", list(...)),
wbr = function(...) tag("wbr", list(...))
)
#' Mark Characters as HTML
#'
#' Marks the given text as HTML, which means the \link{tag} functions will know
#' not to perform HTML escaping on it.
#'
#' @param text The text value to mark with HTML
#' @param ... Any additional values to be converted to character and
#' concatenated together
#' @return The same value, but marked as HTML.
#'
#' @examples
#' el <- div(HTML("I like <u>turtles</u>"))
#' cat(as.character(el))
#'
#' @export
HTML <- function(text, ...) {
htmlText <- c(text, as.character(list(...)))
htmlText <- paste(htmlText, collapse=" ")
attr(htmlText, "html") <- TRUE
class(htmlText) <- c("html", "character")
htmlText
}
#' Evaluate an expression using the \code{tags}
#'
#' This function makes it simpler to write HTML-generating code. Instead of
#' needing to specify \code{tags} each time a tag function is used, as in
#' \code{tags$div()} and \code{tags$p()}, code inside \code{withTags} is
#' evaluated with \code{tags} searched first, so you can simply use
#' \code{div()} and \code{p()}.
#'
#' If your code uses an object which happens to have the same name as an
#' HTML tag function, such as \code{source()} or \code{summary()}, it will call
#' the tag function. To call the intended (non-tags function), specify the
#' namespace, as in \code{base::source()} or \code{base::summary()}.
#'
#' @param code A set of tags.
#'
#' @examples
#' # Using tags$ each time
#' tags$div(class = "myclass",
#' tags$h3("header"),
#' tags$p("text")
#' )
#'
#' # Equivalent to above, but using withTags
#' withTags(
#' div(class = "myclass",
#' h3("header"),
#' p("text")
#' )
#' )
#'
#'
#' @export
withTags <- function(code) {
eval(substitute(code), envir = as.list(tags), enclos = parent.frame())
}
# Given a list of tags, lists, and other items, return a flat list, where the
# items from the inner, nested lists are pulled to the top level, recursively.
flattenTags <- function(x) {
if (isTag(x)) {
# For tags, wrap them into a list (which will be unwrapped by caller)
list(x)
} else if (is.list(x)) {
if (length(x) == 0) {
# Empty lists are simply returned
x
} else {
# For items that are lists (but not tags), recurse
unlist(lapply(x, flattenTags), recursive = FALSE)
}
} else if (is.character(x)){
# This will preserve attributes if x is a character with attribute,
# like what HTML() produces
list(x)
} else if (is.function(x) && inherits(x, "shiny.render.function")) {
list(useRenderFunction(x))
} else {
# For other items, coerce to character and wrap them into a list (which
# will be unwrapped by caller). Note that this will strip attributes.
list(as.character(x))
}
}

View File

@@ -606,11 +606,7 @@ dataTablesJSON <- function(data, req) {
fdata <- data[i, , drop = FALSE] # filtered data
} else fdata <- data
fdata <- unname(as.matrix(fdata))
# WAT: toJSON(list(x = matrix(nrow = 0, ncol = 1))) => {"x": } (#299)
if (nrow(fdata) == 0) fdata <- list()
# WAT: toJSON(list(x = matrix(1:2))) => {x: [ [1], [2] ]}, however,
# toJSON(list(x = matrix(1))) => {x: [ 1 ]} (loss of dimension, #429)
if (all(dim(fdata) == 1)) fdata <- list(list(fdata[1, 1]))
res <- toJSON(list(
sEcho = as.integer(sEcho),
@@ -718,7 +714,7 @@ cachedFuncWithFile <- function(dir, file, func, case.sensitive = FALSE) {
now <- file.info(fname)$mtime
if (!identical(mtime, now)) {
value <<- func(fname, ...)
value <<- func(...)
mtime <<- now
}
value
@@ -729,7 +725,11 @@ cachedFuncWithFile <- function(dir, file, func, case.sensitive = FALSE) {
# calls, unless the file's mtime changes.
cachedSource <- function(dir, file, case.sensitive = FALSE) {
dir <- normalizePath(dir, mustWork=TRUE)
cachedFuncWithFile(dir, file, function(fname, ...) {
cachedFuncWithFile(dir, file, function(...) {
fname <- if (case.sensitive)
file.path(dir, file)
else
file.path.ci(dir, file)
if (file.exists(fname))
return(source(fname, ...))
else
@@ -800,8 +800,7 @@ columnToRowData <- function(data) {
#' @param ... A list of tests. Each test should equal \code{NULL} for success,
#' \code{FALSE} for silent failure, or a string for failure with an error
#' message.
#' @param errorClass A CSS class to apply. The actual CSS string will have
#' \code{shiny-output-error-} prepended to this value.
#' @param errorClass A CSS class to apply.
#' @export
#' @examples
#' # in ui.R
@@ -893,7 +892,7 @@ isTruthy <- function(x) {
stopWithCondition <- function(class, message) {
cond <- structure(
list(message = message),
class = c(class, 'shiny.silent.error', 'error', 'condition')
class = c(class, 'error', 'condition')
)
stop(cond)
}

View File

@@ -0,0 +1,10 @@
library(shiny)
library(rmdexamples)
shinyServer(function(input, output) {
output$gage <- renderHTML({
justgage("Foo", 5, 1, 10)
})
})

View File

@@ -0,0 +1,9 @@
library(shiny)
shinyUI(fluidPage(
titlePanel("Hello Shiny!"),
htmlOutput("gage")
))

View File

@@ -159,6 +159,8 @@ sd_section("Embedding",
"Functions that are intended for third-party packages that embed Shiny applications.",
c(
"shinyApp",
"maskReactiveContext"
"maskReactiveContext",
"knitr_methods",
"getProvidedHtmlDependencies"
)
)

View File

@@ -6,7 +6,7 @@ test_that("All man pages have an entry in staticdocs/index.r", {
return()
}
# Known not to be indexed
known_unindexed <- c("shiny-package", "knitr_methods")
known_unindexed <- c("shiny-package")
indexed_topics <- local({
result <- character(0)

432
inst/tests/test-tags.r Normal file
View File

@@ -0,0 +1,432 @@
context("tags")
test_that("Basic tag writing works", {
expect_equal(as.character(tagList("hi")), HTML("hi"))
expect_equal(
as.character(tagList("one", "two", tagList("three"))),
HTML("one\ntwo\nthree"))
expect_equal(
as.character(tags$b("one")),
HTML("<b>one</b>"))
expect_equal(
as.character(tags$b("one", "two")),
HTML("<b>\n one\n two\n</b>"))
expect_equal(
as.character(tagList(list("one"))),
HTML("one"))
expect_equal(
as.character(tagList(list(tagList("one")))),
HTML("one"))
expect_equal(
as.character(tagList(tags$br(), "one")),
HTML("<br/>\none"))
})
test_that("withTags works", {
output_tags <- tags$div(class = "myclass",
tags$h3("header"),
tags$p("text here")
)
output_withhtml <- withTags(
div(class = "myclass",
h3("header"),
p("text here")
)
)
expect_identical(output_tags, output_withhtml)
# Check that current environment is searched
x <- 100
expect_identical(tags$p(x), withTags(p(x)))
# Just to make sure, run it in a function, which has its own environment
foo <- function() {
y <- 100
withTags(p(y))
}
expect_identical(tags$p(100), foo())
})
test_that("HTML escaping in tags", {
# Regular text is escaped
expect_equivalent(format(div("<a&b>")), "<div>&lt;a&amp;b&gt;</div>")
# Text in HTML() isn't escaped
expect_equivalent(format(div(HTML("<a&b>"))), "<div><a&b></div>")
# Text in a property is escaped
expect_equivalent(format(div(class = "<a&b>", "text")),
'<div class="&lt;a&amp;b&gt;">text</div>')
# HTML() has no effect in a property like 'class'
expect_equivalent(format(div(class = HTML("<a&b>"), "text")),
'<div class="&lt;a&amp;b&gt;">text</div>')
})
test_that("Adding child tags", {
tag_list <- list(tags$p("tag1"), tags$b("tag2"), tags$i("tag3"))
# Creating nested tags by calling the tag$div function and passing a list
t1 <- tags$div(class="foo", tag_list)
expect_equal(length(t1$children), 1)
expect_equal(length(t1$children[[1]]), 3)
expect_equal(t1$children[[1]][[1]]$name, "p")
expect_equal(t1$children[[1]][[1]]$children[[1]], "tag1")
expect_equal(t1$children[[1]][[2]]$name, "b")
expect_equal(t1$children[[1]][[2]]$children[[1]], "tag2")
expect_equal(t1$children[[1]][[3]]$name, "i")
expect_equal(t1$children[[1]][[3]]$children[[1]], "tag3")
# div tag used as starting point for tests below
div_tag <- tags$div(class="foo")
# Appending each child
t2 <- tagAppendChild(div_tag, tag_list[[1]])
t2 <- tagAppendChild(t2, tag_list[[2]])
t2 <- tagAppendChild(t2, tag_list[[3]])
t2a <- do.call(tags$div, c(tag_list, class="foo"))
expect_identical(t2a, t2)
# tagSetChildren, using list argument
t2 <- tagSetChildren(div_tag, list = tag_list)
expect_identical(t2a, t2)
# tagSetChildren, using ... arguments
t2 <- tagSetChildren(div_tag, tag_list[[1]], tag_list[[2]], tag_list[[3]])
expect_identical(t2a, t2)
# tagSetChildren, using ... and list arguments
t2 <- tagSetChildren(div_tag, tag_list[[1]], list = tag_list[2:3])
expect_identical(t2a, t2)
# tagSetChildren overwrites existing children
t2 <- tagAppendChild(div_tag, p("should replace this tag"))
t2 <- tagSetChildren(div_tag, list = tag_list)
expect_identical(t2a, t2)
# tagAppendChildren, using list argument
t2 <- tagAppendChild(div_tag, tag_list[[1]])
t2 <- tagAppendChildren(t2, list = tag_list[2:3])
expect_identical(t2a, t2)
# tagAppendChildren, using ... arguments
t2 <- tagAppendChild(div_tag, tag_list[[1]])
t2 <- tagAppendChildren(t2, tag_list[[2]], tag_list[[3]])
expect_identical(t2a, t2)
# tagAppendChildren, using ... and list arguments
t2 <- tagAppendChild(div_tag, tag_list[[1]])
t2 <- tagAppendChildren(t2, tag_list[[2]], list = list(tag_list[[3]]))
expect_identical(t2a, t2)
# tagAppendChildren can start with no children
t2 <- tagAppendChildren(div_tag, list = tag_list)
expect_identical(t2a, t2)
# tagSetChildren preserves attributes
x <- tagSetChildren(div(), HTML("text"))
expect_identical(attr(x$children[[1]], "html"), TRUE)
# tagAppendChildren preserves attributes
x <- tagAppendChildren(div(), HTML("text"))
expect_identical(attr(x$children[[1]], "html"), TRUE)
})
test_that("Creating simple tags", {
# Empty tag
expect_identical(
div(),
structure(
list(name = "div", attribs = list(), children = list()),
.Names = c("name", "attribs", "children"),
class = "shiny.tag"
)
)
# Tag with text
expect_identical(
div("text"),
structure(
list(name = "div", attribs = list(), children = list("text")),
.Names = c("name", "attribs", "children"),
class = "shiny.tag"
)
)
# NULL attributes are dropped
expect_identical(
div(a = NULL, b = "value"),
div(b = "value")
)
# NULL children are dropped
expect_identical(
renderTags(div("foo", NULL, list(NULL, list(NULL, "bar"))))$html,
renderTags(div("foo", "bar"))$html
)
# Numbers are coerced to strings
expect_identical(
renderTags(div(1234))$html,
renderTags(div("1234"))$html
)
})
test_that("Creating nested tags", {
# Simple version
# Note that the $children list should not have a names attribute
expect_identical(
div(class="foo", list("a", "b")),
structure(
list(name = "div",
attribs = structure(list(class = "foo"), .Names = "class"),
children = list(list("a", "b"))),
.Names = c("name", "attribs", "children"),
class = "shiny.tag"
)
)
# More complex version
t1 <- withTags(
div(class = "foo",
p("child tag"),
list(
p("in-list child tag 1"),
"in-list character string",
p(),
p("in-list child tag 2")
),
"character string",
1234
)
)
# t1 should be identical to this data structure.
# The nested list should be flattened, and non-tag, non-strings should be
# converted to strings
t1_full <- structure(
list(
name = "div",
attribs = list(class = "foo"),
children = list(
structure(list(name = "p",
attribs = list(),
children = list("child tag")),
class = "shiny.tag"
),
structure(list(name = "p",
attribs = list(),
children = list("in-list child tag 1")),
class = "shiny.tag"
),
"in-list character string",
structure(list(name = "p",
attribs = list(),
children = list()),
class = "shiny.tag"
),
structure(list(name = "p",
attribs = list(),
children = list("in-list child tag 2")),
class = "shiny.tag"
),
"character string",
"1234"
)
),
class = "shiny.tag"
)
expect_identical(renderTags(t1)$html, renderTags(t1_full)$html)
})
test_that("Attributes are preserved", {
# HTML() adds an attribute to the data structure (note that this is
# different from the 'attribs' field in the list)
x <- HTML("<tag>&&</tag>")
expect_identical(attr(x, "html"), TRUE)
expect_equivalent(format(x), "<tag>&&</tag>")
# Make sure attributes are preserved when wrapped in other tags
x <- div(HTML("<tag>&&</tag>"))
expect_equivalent(x$children[[1]], HTML("<tag>&&</tag>"))
expect_identical(attr(x$children[[1]], "html"), TRUE)
expect_equivalent(format(x), "<div><tag>&&</tag></div>")
# Deeper nesting
x <- div(p(HTML("<tag>&&</tag>")))
expect_equivalent(x$children[[1]]$children[[1]], HTML("<tag>&&</tag>"))
expect_identical(attr(x$children[[1]]$children[[1]], "html"), TRUE)
expect_equivalent(format(x), "<div>\n <p><tag>&&</tag></p>\n</div>")
})
test_that("Flattening a list of tags", {
# Flatten a nested list
nested <- list(
"a1",
list(
"b1",
list("c1", "c2"),
list(),
"b2",
list("d1", "d2")
),
"a2"
)
flat <- list("a1", "b1", "c1", "c2", "b2", "d1", "d2", "a2")
expect_identical(flattenTags(nested), flat)
# no-op for flat lists
expect_identical(flattenTags(list(a="1", "b")), list(a="1", "b"))
# numbers are coerced to character
expect_identical(flattenTags(list(a=1, "b")), list(a="1", "b"))
# empty list results in empty list
expect_identical(flattenTags(list()), list())
# preserve attributes
nested <- list("txt1", list(structure("txt2", prop="prop2")))
flat <- list("txt1",
structure("txt2", prop="prop2"))
expect_identical(flattenTags(nested), flat)
})
test_that("Head and singleton behavior", {
result <- renderTags(tagList(
tags$head(singleton("hello"))
))
expect_identical(result$html, HTML(""))
expect_identical(result$head, HTML(" hello"))
expect_identical(result$singletons, "60eed8231e688bcba7c275c58dd2e3b4dacb61f0")
# Ensure that "hello" actually behaves like a singleton
result2 <- renderTags(tagList(
tags$head(singleton("hello"))
), singletons = result$singletons)
expect_identical(result$singletons, result2$singletons)
expect_identical(result2$head, HTML(""))
expect_identical(result2$html, HTML(""))
result3 <- renderTags(tagList(
tags$head(singleton("hello"), singleton("hello"))
))
expect_identical(result$singletons, result3$singletons)
expect_identical(result3$head, HTML(" hello"))
# Ensure that singleton can be applied to lists, not just tags
result4 <- renderTags(list(singleton(list("hello")), singleton(list("hello"))))
expect_identical(result4$singletons, "d7319e3f14167c4c056dd7aa0b274c83fe2291f6")
expect_identical(result4$html, renderTags(HTML("hello"))$html)
})
test_that("Factors are treated as characters, not numbers", {
myfactors <- factor(LETTERS[1:3])
expect_identical(
as.character(tags$option(value=myfactors[[1]], myfactors[[1]])),
HTML('<option value="A">A</option>')
)
expect_identical(
as.character(tags$option(value=myfactors[[1]], value='B', value=3, myfactors[[1]])),
HTML('<option value="A B 3">A</option>')
)
})
test_that("Unusual list contents are rendered correctly", {
expect_identical(renderTags(list(NULL)), renderTags(HTML("")))
expect_identical(renderTags(list(100)), renderTags(HTML("100")))
expect_identical(renderTags(list(list(100))), renderTags(HTML("100")))
expect_identical(renderTags(list(list())), renderTags(HTML("")))
expect_identical(renderTags(NULL), renderTags(HTML("")))
})
test_that("Low-level singleton manipulation methods", {
# Default arguments drop singleton duplicates and strips the
# singletons it keeps of the singleton bit
result1 <- takeSingletons(tags$div(
singleton(tags$head(tags$script("foo"))),
singleton(tags$head(tags$script("foo")))
))
expect_identical(result1$ui$children[[2]], NULL)
expect_false(is(result1$ui$children[[1]], "shiny.singleton"))
# desingleton=FALSE means drop duplicates but don't strip the
# singleton bit
result2 <- takeSingletons(tags$div(
singleton(tags$head(tags$script("foo"))),
singleton(tags$head(tags$script("foo")))
), desingleton=FALSE)
expect_identical(result2$ui$children[[2]], NULL)
expect_is(result2$ui$children[[1]], "shiny.singleton")
result3 <- surroundSingletons(tags$div(
singleton(tags$script("foo")),
singleton(tags$script("foo"))
))
expect_identical(
renderTags(result3)$html,
HTML("<div>
<!--SHINY.SINGLETON[58b302d493b50acb75e4a5606687cadccdf902d8]-->
<script>foo</script>
<!--/SHINY.SINGLETON[58b302d493b50acb75e4a5606687cadccdf902d8]-->
<!--SHINY.SINGLETON[58b302d493b50acb75e4a5606687cadccdf902d8]-->
<script>foo</script>
<!--/SHINY.SINGLETON[58b302d493b50acb75e4a5606687cadccdf902d8]-->
</div>")
)
})
test_that("Indenting can be controlled/suppressed", {
expect_identical(
renderTags(tags$div("a", "b"))$html,
HTML("<div>\n a\n b\n</div>")
)
expect_identical(
format(tags$div("a", "b")),
"<div>\n a\n b\n</div>"
)
expect_identical(
renderTags(tags$div("a", "b"), indent = 2)$html,
HTML(" <div>\n a\n b\n </div>")
)
expect_identical(
format(tags$div("a", "b"), indent = 2),
" <div>\n a\n b\n </div>"
)
expect_identical(
renderTags(tags$div("a", "b"), indent = FALSE)$html,
HTML("<div>\na\nb\n</div>")
)
expect_identical(
format(tags$div("a", "b"), indent = FALSE),
"<div>\na\nb\n</div>"
)
expect_identical(
renderTags(tagList(tags$div("a", "b")), indent = FALSE)$html,
HTML("<div>\na\nb\n</div>")
)
expect_identical(
format(tagList(tags$div("a", "b")), indent = FALSE),
"<div>\na\nb\n</div>"
)
})

View File

@@ -1,5 +1,5 @@
/**
* selectize.bootstrap2.css (v0.9.1) - Bootstrap 2 Theme
* selectize.bootstrap2.css (v0.8.0) - Bootstrap 2 Theme
* Copyright (c) 2013 Brian Reavis & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
@@ -13,337 +13,387 @@
*
* @author Brian Reavis <brian@thirdroute.com>
*/
.selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder {
visibility: visible !important;
background: #f2f2f2 !important;
background: rgba(0, 0, 0, 0.06) !important;
border: 0 none !important;
visibility: visible !important;
-webkit-box-shadow: inset 0 0 12px 4px #ffffff;
box-shadow: inset 0 0 12px 4px #ffffff;
box-shadow: inset 0 0 12px 4px #ffffff;
}
.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after {
content: '!';
visibility: hidden;
}
.selectize-control.plugin-drag_drop .ui-sortable-helper {
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
.selectize-dropdown-header {
position: relative;
padding: 3px 10px;
border-bottom: 1px solid #d0d0d0;
background: #f8f8f8;
border-bottom: 1px solid #d0d0d0;
-webkit-border-radius: 4px 4px 0 0;
-moz-border-radius: 4px 4px 0 0;
border-radius: 4px 4px 0 0;
-moz-border-radius: 4px 4px 0 0;
border-radius: 4px 4px 0 0;
}
.selectize-dropdown-header-close {
position: absolute;
right: 10px;
top: 50%;
right: 10px;
margin-top: -12px;
font-size: 20px !important;
line-height: 20px;
color: #333333;
opacity: 0.4;
margin-top: -12px;
line-height: 20px;
font-size: 20px !important;
}
.selectize-dropdown-header-close:hover {
color: #000000;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup {
border-right: 1px solid #f2f2f2;
border-top: 0 none;
float: left;
border-top: 0 none;
border-right: 1px solid #f2f2f2;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child {
border-right: 0 none;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup:before {
display: none;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup-header {
border-top: 0 none;
}
.selectize-control.plugin-remove_button [data-value] {
position: relative;
padding-right: 24px !important;
}
.selectize-control.plugin-remove_button [data-value] .remove {
z-index: 1;
/* fixes ie bug (see #392) */
position: absolute;
top: 0;
right: 0;
bottom: 0;
display: inline-block;
width: 17px;
text-align: center;
font-weight: bold;
padding: 1px 0 0 0;
font-size: 12px;
font-weight: bold;
color: inherit;
text-align: center;
text-decoration: none;
vertical-align: middle;
display: inline-block;
padding: 1px 0 0 0;
border-left: 1px solid #cccccc;
-webkit-border-radius: 0 2px 2px 0;
-moz-border-radius: 0 2px 2px 0;
border-radius: 0 2px 2px 0;
-moz-border-radius: 0 2px 2px 0;
border-radius: 0 2px 2px 0;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.selectize-control.plugin-remove_button [data-value] .remove:hover {
background: rgba(0, 0, 0, 0.05);
}
.selectize-control.plugin-remove_button [data-value].active .remove {
border-left-color: #0077b3;
}
.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover {
background: none;
}
.selectize-control.plugin-remove_button .disabled [data-value] .remove {
border-left-color: #e0e0e0;
}
.selectize-control {
position: relative;
}
.selectize-dropdown,
.selectize-input,
.selectize-input input {
color: #333333;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 20px;
-webkit-font-smoothing: inherit;
line-height: 20px;
color: #333333;
}
.selectize-input,
.selectize-control.single .selectize-input.input-active {
background: #ffffff;
display: inline-block;
cursor: text;
display: inline-block;
background: #ffffff;
}
.selectize-input {
border: 1px solid #d0d0d0;
padding: 7px 10px;
display: inline-block;
width: 100%;
overflow: hidden;
position: relative;
z-index: 1;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-box-shadow: none;
box-shadow: none;
display: inline-block;
width: 100%;
padding: 7px 10px;
overflow: hidden;
border: 1px solid #d0d0d0;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
-webkit-box-shadow: none;
box-shadow: none;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.selectize-control.multi .selectize-input.has-items {
padding: 5px 10px 2px;
}
.selectize-input.full {
background-color: #ffffff;
}
.selectize-input.disabled,
.selectize-input.disabled * {
cursor: default !important;
}
.selectize-input.focus {
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
}
.selectize-input.dropdown-active {
-webkit-border-radius: 4px 4px 0 0;
-moz-border-radius: 4px 4px 0 0;
border-radius: 4px 4px 0 0;
-moz-border-radius: 4px 4px 0 0;
border-radius: 4px 4px 0 0;
}
.selectize-input > * {
vertical-align: baseline;
display: -moz-inline-stack;
display: inline-block;
zoom: 1;
*display: inline;
vertical-align: baseline;
zoom: 1;
}
.selectize-control.multi .selectize-input > div {
cursor: pointer;
margin: 0 3px 3px 0;
padding: 1px 3px;
background: #e6e6e6;
margin: 0 3px 3px 0;
color: #333333;
cursor: pointer;
background: #e6e6e6;
border: 1px solid #cccccc;
}
.selectize-control.multi .selectize-input > div.active {
background: #0088cc;
color: #ffffff;
background: #0088cc;
border: 1px solid #0077b3;
}
.selectize-control.multi .selectize-input.disabled > div,
.selectize-control.multi .selectize-input.disabled > div.active {
color: #474747;
background: #fafafa;
border: 1px solid #e0e0e0;
}
.selectize-input > input {
padding: 0 !important;
min-height: 0 !important;
max-height: none !important;
max-width: 100% !important;
max-height: none !important;
min-height: 0 !important;
padding: 0 !important;
margin: 0 !important;
text-indent: 0 !important;
border: 0 none !important;
background: none !important;
line-height: inherit !important;
-webkit-user-select: auto !important;
text-indent: 0 !important;
background: none !important;
border: 0 none !important;
-webkit-box-shadow: none !important;
box-shadow: none !important;
}
.selectize-input > input::-ms-clear {
display: none;
box-shadow: none !important;
-webkit-user-select: auto !important;
}
.selectize-input > input:focus {
outline: none !important;
}
.selectize-input::after {
content: ' ';
display: block;
clear: left;
}
.selectize-input.dropdown-active::before {
content: ' ';
display: block;
}
.selectize-input.dropdown-active::before {
position: absolute;
background: #e5e5e5;
height: 1px;
right: 0;
bottom: 0;
left: 0;
right: 0;
display: block;
height: 1px;
background: #e5e5e5;
content: ' ';
}
.selectize-dropdown {
position: absolute;
z-index: 10;
border: 1px solid #d0d0d0;
background: #ffffff;
margin: -1px 0 0 0;
background: #ffffff;
border: 1px solid #d0d0d0;
border-top: 0 none;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-webkit-border-radius: 0 0 4px 4px;
-moz-border-radius: 0 0 4px 4px;
border-radius: 0 0 4px 4px;
-moz-border-radius: 0 0 4px 4px;
border-radius: 0 0 4px 4px;
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.selectize-dropdown [data-selectable] {
cursor: pointer;
overflow: hidden;
cursor: pointer;
}
.selectize-dropdown [data-selectable] .highlight {
background: rgba(255, 237, 40, 0.4);
-webkit-border-radius: 1px;
-moz-border-radius: 1px;
border-radius: 1px;
-moz-border-radius: 1px;
border-radius: 1px;
}
.selectize-dropdown [data-selectable],
.selectize-dropdown .optgroup-header {
padding: 3px 10px;
}
.selectize-dropdown .optgroup:first-child .optgroup-header {
border-top: 0 none;
}
.selectize-dropdown .optgroup-header {
color: #999999;
background: #ffffff;
cursor: default;
background: #ffffff;
}
.selectize-dropdown .active {
background-color: #0088cc;
color: #ffffff;
background-color: #0088cc;
}
.selectize-dropdown .active.create {
color: #ffffff;
}
.selectize-dropdown .create {
color: rgba(51, 51, 51, 0.5);
}
.selectize-dropdown-content {
overflow-y: auto;
overflow-x: hidden;
max-height: 200px;
overflow-x: hidden;
overflow-y: auto;
}
.selectize-control.single .selectize-input,
.selectize-control.single .selectize-input input {
cursor: pointer;
}
.selectize-control.single .selectize-input.input-active,
.selectize-control.single .selectize-input.input-active input {
cursor: text;
}
.selectize-control.single .selectize-input:after {
content: ' ';
display: block;
position: absolute;
top: 50%;
right: 15px;
margin-top: -3px;
display: block;
width: 0;
height: 0;
margin-top: -3px;
border-color: #000000 transparent transparent transparent;
border-style: solid;
border-width: 5px 5px 0 5px;
border-color: #000000 transparent transparent transparent;
content: ' ';
}
.selectize-control.single .selectize-input.dropdown-active:after {
margin-top: -4px;
border-width: 0 5px 5px 5px;
border-color: transparent transparent #000000 transparent;
border-width: 0 5px 5px 5px;
}
.selectize-control.rtl.single .selectize-input:after {
left: 15px;
right: auto;
left: 15px;
}
.selectize-control.rtl .selectize-input > input {
margin: 0 4px 0 -2px !important;
}
.selectize-control .selectize-input.disabled {
opacity: 0.5;
background-color: #ffffff;
opacity: 0.5;
}
.selectize-dropdown {
margin: 2px 0 0 0;
z-index: 1000;
margin: 2px 0 0 0;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 4px;
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
}
.selectize-dropdown .optgroup-header {
font-size: 11px;
font-weight: bold;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
text-transform: uppercase;
}
.selectize-dropdown .optgroup:first-child:before {
display: none;
}
.selectize-dropdown .optgroup:before {
content: ' ';
display: block;
*width: 100%;
height: 1px;
margin: 9px 1px;
*margin: -5px 0 5px;
margin-right: -10px;
margin-left: -10px;
overflow: hidden;
background-color: #e5e5e5;
border-bottom: 1px solid #ffffff;
margin-left: -10px;
margin-right: -10px;
content: ' ';
}
.selectize-dropdown [data-selectable].active {
background-color: #0081c2;
background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
@@ -354,26 +404,32 @@
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
}
.selectize-dropdown-content {
padding: 5px 0;
}
.selectize-dropdown-header {
padding: 6px 10px;
}
.selectize-input {
-webkit-transition: border linear .2s, box-shadow linear .2s;
-moz-transition: border linear .2s, box-shadow linear .2s;
-o-transition: border linear .2s, box-shadow linear .2s;
transition: border linear .2s, box-shadow linear .2s;
-webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
-moz-transition: border linear 0.2s, box-shadow linear 0.2s;
-o-transition: border linear 0.2s, box-shadow linear 0.2s;
transition: border linear 0.2s, box-shadow linear 0.2s;
}
.selectize-input.dropdown-active {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.selectize-input.dropdown-active::before {
display: none;
}
.selectize-input.input-active,
.selectize-input.input-active:hover,
.selectize-control.multi .selectize-input.focus {
@@ -381,30 +437,31 @@
border-color: rgba(82, 168, 236, 0.8) !important;
outline: 0 !important;
outline: thin dotted \9 !important;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6) !important;
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6) !important;
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6) !important;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6) !important;
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6) !important;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6) !important;
}
.selectize-control.single .selectize-input {
color: #333333;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
background-color: #f5f5f5;
*background-color: #e6e6e6;
background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
background-image: linear-gradient(to bottom, #ffffff, #e6e6e6);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);
border-color: #e6e6e6 #e6e6e6 #bfbfbf;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
*background-color: #e6e6e6;
/* Darken IE7 buttons by default so they stand out more given they won't have borders */
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
-webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
-moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
-moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
}
.selectize-control.single .selectize-input:hover,
.selectize-control.single .selectize-input:focus,
.selectize-control.single .selectize-input:active,
@@ -415,72 +472,79 @@
background-color: #e6e6e6;
*background-color: #d9d9d9;
}
.selectize-control.single .selectize-input:active,
.selectize-control.single .selectize-input.active {
background-color: #cccccc \9;
}
.selectize-control.single .selectize-input:hover {
color: #333333;
text-decoration: none;
background-position: 0 -15px;
-webkit-transition: background-position 0.1s linear;
-moz-transition: background-position 0.1s linear;
-o-transition: background-position 0.1s linear;
transition: background-position 0.1s linear;
-moz-transition: background-position 0.1s linear;
-o-transition: background-position 0.1s linear;
transition: background-position 0.1s linear;
}
.selectize-control.single .selectize-input.disabled {
background: #e6e6e6 !important;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
.selectize-control.multi .selectize-input {
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
}
.selectize-control.multi .selectize-input.has-items {
padding-left: 7px;
padding-right: 7px;
padding-left: 7px;
}
.selectize-control.multi .selectize-input > div {
color: #333333;
text-shadow: none;
background-color: #f5f5f5;
*background-color: #e6e6e6;
background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
background-image: linear-gradient(to bottom, #ffffff, #e6e6e6);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);
border: 1px solid #cccccc;
border-color: #e6e6e6 #e6e6e6 #bfbfbf;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
*background-color: #e6e6e6;
border: 1px solid #cccccc;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
-moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
-moz-border-radius: 4px;
border-radius: 4px;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
-moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
}
.selectize-control.multi .selectize-input > div.active {
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05);
-moz-box-shadow: 0 1px 2px rgba(0,0,0,.05);
box-shadow: 0 1px 2px rgba(0,0,0,.05);
color: #ffffff;
text-shadow: none;
background-color: #0081c2;
*background-color: #0088cc;
background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
background-image: -o-linear-gradient(top, #0088cc, #0077b3);
background-image: linear-gradient(to bottom, #0088cc, #0077b3);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
border: 1px solid #0088cc;
border-color: #0077b3 #0077b3 #004466;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
*background-color: #0088cc;
border: 1px solid #0088cc;
}
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
-moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}

File diff suppressed because one or more lines are too long

View File

@@ -1072,11 +1072,10 @@
this.renderError(el, err);
};
this.renderError = function(el, err) {
this.clearError(el);
if (err.message === '') {
// not really error, but we just need to wait (e.g. action buttons)
$(el).empty();
return;
return this.clearError(el);
}
var errClass = 'shiny-output-error';
if (err.type !== null) {
@@ -1285,7 +1284,7 @@
registerDependency(dep.name, dep.version);
var href = dep.src.href;
var path = dep.path;
var $head = $("head").first();
@@ -1299,14 +1298,14 @@
if (dep.stylesheet) {
var stylesheets = $.map(asArray(dep.stylesheet), function(stylesheet) {
return $("<link rel='stylesheet' type='text/css'>")
.attr("href", href + "/" + stylesheet);
.attr("href", path + "/" + stylesheet);
});
$head.append(stylesheets);
}
if (dep.script) {
var scripts = $.map(asArray(dep.script), function(scriptName) {
return $("<script>").attr("src", href + "/" + scriptName);
return $("<script>").attr("src", path + "/" + scriptName);
});
$head.append(scripts);
}
@@ -1722,9 +1721,7 @@
};
},
initialize: function(el) {
var $el = $(el);
$el.slider();
$el.next('span.jslider').css('width', $el.data('width'));
$(el).slider();
}
});
inputBindings.register(sliderInputBinding, 'shiny.sliderInput');
@@ -2210,7 +2207,6 @@
control.destroy();
control = $el.selectize(settings)[0].selectize;
}
$el.next('div.selectize-control').css('width', config.data('width'));
return control;
}
});

View File

@@ -1,3 +1,4 @@
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{HTML}
\alias{HTML}
\title{Mark Characters as HTML}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{absolutePanel}
\alias{absolutePanel}
\alias{fixedPanel}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{actionButton}
\alias{actionButton}
\alias{actionLink}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{addResourcePath}
\alias{addResourcePath}
\title{Resource Publishing}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{bootstrapPage}
\alias{basicPage}
\alias{bootstrapPage}

View File

@@ -1,4 +1,5 @@
\name{builder}
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{p}
\alias{a}
\alias{br}
\alias{builder}
@@ -20,8 +21,6 @@
\alias{tags}
\title{HTML Builder Functions}
\usage{
tags
p(...)
h1(...)
@@ -55,6 +54,8 @@ strong(...)
em(...)
hr(...)
tags
}
\arguments{
\item{...}{Attributes and children of the element. Named arguments become

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{checkboxGroupInput}
\alias{checkboxGroupInput}
\title{Checkbox Group Input Control}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{checkboxInput}
\alias{checkboxInput}
\title{Checkbox Input Control}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{column}
\alias{column}
\title{Create a column within a UI definition}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{conditionalPanel}
\alias{conditionalPanel}
\title{Conditional Panel}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{dateInput}
\alias{dateInput}
\title{Create date input}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{dateRangeInput}
\alias{dateRangeInput}
\title{Create date range input}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{getDefaultReactiveDomain}
\alias{domains}
\alias{getDefaultReactiveDomain}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{downloadButton}
\alias{downloadButton}
\alias{downloadLink}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{downloadHandler}
\alias{downloadHandler}
\title{File Downloads}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{exprToFunction}
\alias{exprToFunction}
\title{Convert an expression to a function}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{fileInput}
\alias{fileInput}
\title{File Upload Control}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{fixedPage}
\alias{fixedPage}
\alias{fixedRow}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{flowLayout}
\alias{flowLayout}
\title{Flow layout}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{fluidPage}
\alias{fluidPage}
\alias{fluidRow}

View File

@@ -0,0 +1,15 @@
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{getProvidedHtmlDependencies}
\alias{getProvidedHtmlDependencies}
\title{Return HTML dependencies provided by Shiny}
\usage{
getProvidedHtmlDependencies()
}
\value{
A list of objects of type \code{html_dependency}, one per dependency
}
\description{
By default, Shiny supplies some framework scripts when it renders a page.
\code{getProvidedHtmlDependencies} returns a list of those provided objects.
}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{headerPanel}
\alias{headerPanel}
\title{Create a header panel}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{helpText}
\alias{helpText}
\title{Create a help text element}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{htmlOutput}
\alias{htmlOutput}
\alias{uiOutput}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{icon}
\alias{icon}
\title{Create an icon}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{imageOutput}
\alias{imageOutput}
\title{Create a image output element}

View File

@@ -1,3 +1,4 @@
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{include}
\alias{include}
\alias{includeCSS}
@@ -25,7 +26,7 @@ includeScript(path, ...)
\item{...}{Any additional attributes to be applied to the generated tag.}
}
\description{
Load HTML, text, or rendered Markdown from a file and turn into HTML.
Include HTML, text, or rendered Markdown into a \link[=shinyUI]{Shiny UI}.
}
\details{
These functions provide a convenient way to include an extensive amount of

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{inputPanel}
\alias{inputPanel}
\title{Input panel}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{installExprFunction}
\alias{installExprFunction}
\title{Install an expression as a function}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{invalidateLater}
\alias{invalidateLater}
\title{Scheduled Invalidation}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{is.reactivevalues}
\alias{is.reactivevalues}
\title{Checks whether an object is a reactivevalues object}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{isolate}
\alias{isolate}
\title{Create a non-reactive scope for an expression}

View File

@@ -1,12 +1,18 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{knitr_methods}
\alias{knit_print.shiny.appobj}
\alias{knit_print.shiny.render.function}
\alias{knit_print.shiny.tag}
\alias{knit_print.shiny.tag.list}
\alias{knitr_methods}
\title{Knitr S3 methods}
\usage{
knit_print.shiny.appobj(x, ...)
knit_print.shiny.tag(x, ...)
knit_print.shiny.tag.list(x, ...)
knit_print.shiny.render.function(x, ...)
}
\arguments{

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{mainPanel}
\alias{mainPanel}
\title{Create a main panel}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{makeReactiveBinding}
\alias{makeReactiveBinding}
\title{Make a reactive variable}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{markRenderFunction}
\alias{markRenderFunction}
\title{Mark a function as a render function}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{maskReactiveContext}
\alias{maskReactiveContext}
\title{Evaluate an expression without a reactive context}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{navbarPage}
\alias{navbarMenu}
\alias{navbarPage}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{navlistPanel}
\alias{navlistPanel}
\title{Create a navigation list panel}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{numericInput}
\alias{numericInput}
\title{Create a numeric input control}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{observe}
\alias{observe}
\title{Create a reactive observer}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{outputOptions}
\alias{outputOptions}
\title{Set options for an output object.}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{pageWithSidebar}
\alias{pageWithSidebar}
\title{Create a page with a sidebar}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{parseQueryString}
\alias{parseQueryString}
\title{Parse a GET query string from a URL}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{plotOutput}
\alias{plotOutput}
\title{Create an plot output element}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{plotPNG}
\alias{plotPNG}
\title{Run a plotting function and save the output as a PNG}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{radioButtons}
\alias{radioButtons}
\title{Create radio buttons}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{reactive}
\alias{is.reactive}
\alias{reactive}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{reactiveFileReader}
\alias{reactiveFileReader}
\title{Reactive file reader}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{reactivePlot}
\alias{reactivePlot}
\title{Plot output (deprecated)}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{reactivePoll}
\alias{reactivePoll}
\title{Reactive polling}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{reactivePrint}
\alias{reactivePrint}
\title{Print output (deprecated)}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{reactiveTable}
\alias{reactiveTable}
\title{Table output (deprecated)}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{reactiveText}
\alias{reactiveText}
\title{Text output (deprecated)}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{reactiveTimer}
\alias{reactiveTimer}
\title{Timer}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{reactiveUI}
\alias{reactiveUI}
\title{UI output (deprecated)}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{reactiveValues}
\alias{reactiveValues}
\title{Create an object for storing reactive values}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{reactiveValuesToList}
\alias{reactiveValuesToList}
\title{Convert a reactivevalues object to a list}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{registerInputHandler}
\alias{registerInputHandler}
\title{Register an Input Handler}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{removeInputHandler}
\alias{removeInputHandler}
\title{Deregister an Input Handler}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{renderDataTable}
\alias{renderDataTable}
\title{Table output with the JavaScript library DataTables}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{renderImage}
\alias{renderImage}
\title{Image file output}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{renderPlot}
\alias{renderPlot}
\title{Plot Output}

View File

@@ -1,10 +1,9 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{renderPrint}
\alias{renderPrint}
\title{Printable Output}
\usage{
renderPrint(expr, env = parent.frame(), quoted = FALSE, func = NULL,
width = getOption("width"))
renderPrint(expr, env = parent.frame(), quoted = FALSE, func = NULL)
}
\arguments{
\item{expr}{An expression that may print output and/or return a printable R
@@ -15,9 +14,7 @@ object.}
\item{quoted}{Is \code{expr} a quoted expression (with \code{quote()})? This}
\item{func}{A function that may print output and/or return a printable R
object (deprecated; use \code{expr} instead).}
\item{width}{The value for \code{\link{options}('width')}.}
object (deprecated; use \code{expr} instead).}
}
\description{
Makes a reactive version of the given function that captures any printed

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{renderTable}
\alias{renderTable}
\title{Table Output}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{renderText}
\alias{renderText}
\title{Text Output}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{renderUI}
\alias{renderUI}
\title{UI Output}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{repeatable}
\alias{repeatable}
\title{Make a random number generator repeatable}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{runApp}
\alias{runApp}
\title{Run Shiny Application}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{runExample}
\alias{runExample}
\title{Run Shiny Example Applications}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{runGist}
\alias{runGist}
\title{Run a Shiny application from https://gist.github.com}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{runGitHub}
\alias{runGitHub}
\title{Run a Shiny application from a GitHub repository}

View File

@@ -1,4 +1,4 @@
% Generated by roxygen2 (4.0.1): do not edit by hand
% Generated by roxygen2 (4.0.0): do not edit by hand
\name{runUrl}
\alias{runUrl}
\title{Run a Shiny application from a URL}

Some files were not shown because too many files have changed in this diff Show More