Compare commits
123 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53c05128b3 | ||
|
|
99013f7998 | ||
|
|
fc396800db | ||
|
|
6d03ae57ac | ||
|
|
4a0aa57355 | ||
|
|
7db737494c | ||
|
|
b285501c44 | ||
|
|
2f9b29994f | ||
|
|
917434cb6b | ||
|
|
28a52bb658 | ||
|
|
82bc19374c | ||
|
|
0b23f30bb7 | ||
|
|
64a62d7aed | ||
|
|
de31cf8e7d | ||
|
|
3484f9afb3 | ||
|
|
81df0ff390 | ||
|
|
4268570166 | ||
|
|
ead508c0d0 | ||
|
|
f8e1be8565 | ||
|
|
360f1af32f | ||
|
|
ba4f3a1553 | ||
|
|
6ba9534da4 | ||
|
|
c16ef96754 | ||
|
|
e728491aa2 | ||
|
|
ce356fa266 | ||
|
|
5e46323ca3 | ||
|
|
0a7d047246 | ||
|
|
3fa534a3eb | ||
|
|
c6405f70d3 | ||
|
|
acae6c2c49 | ||
|
|
141fdc2197 | ||
|
|
a7ed8a006f | ||
|
|
b1a0ebd531 | ||
|
|
e8021acccd | ||
|
|
39b0da2a3f | ||
|
|
fd3d18f6c5 | ||
|
|
ecc27d1674 | ||
|
|
7d0514ab36 | ||
|
|
44c3024c00 | ||
|
|
253c92bab7 | ||
|
|
c10850118d | ||
|
|
4f017e9173 | ||
|
|
5ed46c82cb | ||
|
|
64391e906d | ||
|
|
47b4ee07ab | ||
|
|
3000cbf763 | ||
|
|
76b3d314a8 | ||
|
|
ba646de0ad | ||
|
|
395f746a05 | ||
|
|
f7e57cd398 | ||
|
|
3ea6d97ed2 | ||
|
|
affc0d8b67 | ||
|
|
c637e310e9 | ||
|
|
6ee7dcdd51 | ||
|
|
23470267fe | ||
|
|
4a92bb91df | ||
|
|
69522c422c | ||
|
|
bc5e3524eb | ||
|
|
479297fc35 | ||
|
|
516feafcfb | ||
|
|
a135c82ab5 | ||
|
|
10996f1cbd | ||
|
|
23b060e1f5 | ||
|
|
622ff3a256 | ||
|
|
5d457b6834 | ||
|
|
f10f76d127 | ||
|
|
58f3382daf | ||
|
|
0e1139446e | ||
|
|
f433216fae | ||
|
|
ed680baaac | ||
|
|
e0a9d908ed | ||
|
|
bfa4a46bd5 | ||
|
|
03f3ff991e | ||
|
|
619b4824f0 | ||
|
|
021af0186b | ||
|
|
d3caad8b8d | ||
|
|
ec6bec3326 | ||
|
|
dd54740d36 | ||
|
|
8f65156bda | ||
|
|
96c7df5afa | ||
|
|
0c19105fbf | ||
|
|
4145d83248 | ||
|
|
6490705e2a | ||
|
|
10d2432df5 | ||
|
|
815db72671 | ||
|
|
6d0ba61c54 | ||
|
|
5f61267f75 | ||
|
|
94ee42cebb | ||
|
|
b6795e5c63 | ||
|
|
ef85d063c2 | ||
|
|
59755971e5 | ||
|
|
c5ab831a87 | ||
|
|
6715dc2a5d | ||
|
|
af6de64ec0 | ||
|
|
1ac2448f90 | ||
|
|
b5f34b30d3 | ||
|
|
01f4e080df | ||
|
|
d55335e70b | ||
|
|
a8c1dc4bc6 | ||
|
|
2897059503 | ||
|
|
d491f9df5a | ||
|
|
bc40318e40 | ||
|
|
3935434f04 | ||
|
|
4cf1f2de94 | ||
|
|
73156c6780 | ||
|
|
bc52bafa8d | ||
|
|
5c9007b242 | ||
|
|
5857e3f75e | ||
|
|
e202831013 | ||
|
|
4cbbfccb6d | ||
|
|
21f3b1cf34 | ||
|
|
f7b384e9b6 | ||
|
|
1e6ab47ee4 | ||
|
|
78341ea2f1 | ||
|
|
3f8a4d4273 | ||
|
|
bae517c9f8 | ||
|
|
c88ccbf9bc | ||
|
|
5e40f5d509 | ||
|
|
46389131bc | ||
|
|
c6a344d0d9 | ||
|
|
bcc2c377a0 | ||
|
|
bb6afc847e | ||
|
|
e0193151db |
@@ -8,3 +8,4 @@
|
||||
^run\.R$
|
||||
^\.gitignore$
|
||||
^res$
|
||||
^tools$
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Package: shiny
|
||||
Type: Package
|
||||
Title: Web Application Framework for R
|
||||
Version: 0.5.0
|
||||
Version: 0.6.0.99
|
||||
Date: 2013-01-23
|
||||
Author: RStudio, Inc.
|
||||
Maintainer: Winston Chang <winston@rstudio.com>
|
||||
@@ -18,7 +18,7 @@ Imports:
|
||||
utils,
|
||||
datasets,
|
||||
methods,
|
||||
httpuv (>= 1.0.5),
|
||||
httpuv (>= 1.0.6.2),
|
||||
caTools,
|
||||
RJSONIO,
|
||||
xtable,
|
||||
@@ -31,11 +31,13 @@ URL: http://www.rstudio.com/shiny/
|
||||
BugReports: https://github.com/rstudio/shiny/issues
|
||||
Collate:
|
||||
'map.R'
|
||||
'priorityqueue.R'
|
||||
'utils.R'
|
||||
'tar.R'
|
||||
'timer.R'
|
||||
'tags.R'
|
||||
'cache.R'
|
||||
'graph.R'
|
||||
'react.R'
|
||||
'reactives.R'
|
||||
'fileupload.R'
|
||||
@@ -46,3 +48,4 @@ Collate:
|
||||
'bootstrap.R'
|
||||
'run-url.R'
|
||||
'imageutils.R'
|
||||
'update-input.R'
|
||||
|
||||
23
NAMESPACE
@@ -21,14 +21,18 @@ S3method(print,shiny.tag)
|
||||
S3method(print,shiny.tag.list)
|
||||
export(HTML)
|
||||
export(a)
|
||||
export(actionButton)
|
||||
export(addResourcePath)
|
||||
export(animationOptions)
|
||||
export(basicPage)
|
||||
export(bootstrapPage)
|
||||
export(br)
|
||||
export(checkboxGroupInput)
|
||||
export(checkboxInput)
|
||||
export(code)
|
||||
export(conditionalPanel)
|
||||
export(dateInput)
|
||||
export(dateRangeInput)
|
||||
export(div)
|
||||
export(downloadButton)
|
||||
export(downloadHandler)
|
||||
@@ -47,8 +51,10 @@ export(helpText)
|
||||
export(htmlOutput)
|
||||
export(imageOutput)
|
||||
export(img)
|
||||
export(includeCSS)
|
||||
export(includeHTML)
|
||||
export(includeMarkdown)
|
||||
export(includeScript)
|
||||
export(includeText)
|
||||
export(invalidateLater)
|
||||
export(isolate)
|
||||
@@ -87,10 +93,12 @@ export(runUrl)
|
||||
export(selectInput)
|
||||
export(shinyServer)
|
||||
export(shinyUI)
|
||||
export(showReactLog)
|
||||
export(sidebarPanel)
|
||||
export(singleton)
|
||||
export(sliderInput)
|
||||
export(span)
|
||||
export(stopApp)
|
||||
export(strong)
|
||||
export(submitButton)
|
||||
export(tabPanel)
|
||||
@@ -98,13 +106,28 @@ export(tableOutput)
|
||||
export(tabsetPanel)
|
||||
export(tag)
|
||||
export(tagAppendChild)
|
||||
export(tagAppendChildren)
|
||||
export(tagList)
|
||||
export(tagSetChildren)
|
||||
export(tags)
|
||||
export(textInput)
|
||||
export(textOutput)
|
||||
export(uiOutput)
|
||||
export(updateCheckboxGroupInput)
|
||||
export(updateCheckboxInput)
|
||||
export(updateDateInput)
|
||||
export(updateDateRangeInput)
|
||||
export(updateNumericInput)
|
||||
export(updateRadioButtons)
|
||||
export(updateSelectInput)
|
||||
export(updateSliderInput)
|
||||
export(updateTabsetPanel)
|
||||
export(updateTextInput)
|
||||
export(validateCssUnit)
|
||||
export(verbatimTextOutput)
|
||||
export(wellPanel)
|
||||
export(withTags)
|
||||
export(writeReactLog)
|
||||
import(RJSONIO)
|
||||
import(caTools)
|
||||
import(digest)
|
||||
|
||||
42
NEWS
@@ -1,3 +1,45 @@
|
||||
shiny 0.6.0.99
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
shiny 0.6.0
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
* `tabsetPanel()` can be directed to start with a specific tab selected.
|
||||
|
||||
* Fix bug where multiple file uploads with 3 or more files result in incorrect
|
||||
data.
|
||||
|
||||
* Add `withTags()` function.
|
||||
|
||||
* Add dateInput and dateRangeInput.
|
||||
|
||||
* `shinyServer()` now takes an optional `session` argument, which is used for
|
||||
communication with the session object.
|
||||
|
||||
* Add functions to update values of existing inputs on a page, instead of
|
||||
replacing them entirely.
|
||||
|
||||
* Allow listening on domain sockets.
|
||||
|
||||
* Added `actionButton()` to Shiny.
|
||||
|
||||
* The server can now send custom JSON messages to the client. On the client
|
||||
side, functions can be registered to handle these messages.
|
||||
|
||||
* Callbacks can be registered to be called at the end of a client session.
|
||||
|
||||
* Add ability to set priority of observers and outputs. Each priority level
|
||||
gets its own queue.
|
||||
|
||||
* Fix bug where the presence of a submit button would prevent sending of
|
||||
metadata until the button was clicked.
|
||||
|
||||
* `reactiveTimer()` and `invalidateLater()` by default no longer invalidate
|
||||
reactive objects after the client session has closed.
|
||||
|
||||
* Shiny apps can be run without a server.r and ui.r file.
|
||||
|
||||
shiny 0.5.0
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
429
R/bootstrap.R
@@ -4,9 +4,12 @@
|
||||
#' \href{http://getbootstrap.com}{Twitter Bootstrap}, and has no content in the
|
||||
#' page body (other than what you provide).
|
||||
#'
|
||||
#' This function is primarily intended for users who are proficient in HTML/CSS,
|
||||
#' and know how to lay out pages in Bootstrap. Most users should use template
|
||||
#' functions like \code{\link{pageWithSidebar}}.
|
||||
#' These functions are primarily intended for users who are proficient in
|
||||
#' HTML/CSS, and know how to lay out pages in Bootstrap. Most users should use
|
||||
#' template functions like \code{\link{pageWithSidebar}}.
|
||||
#'
|
||||
#' \code{basicPage} is the same as \code{bootstrapPage}, with an added
|
||||
#' \code{<div class="container-fluid">} wrapper to provide a little padding.
|
||||
#'
|
||||
#' @param ... The contents of the document body.
|
||||
#' @return A UI defintion that can be passed to the \link{shinyUI} function.
|
||||
@@ -59,6 +62,12 @@ bootstrapPage <- function(...) {
|
||||
)
|
||||
}
|
||||
|
||||
#' @rdname bootstrapPage
|
||||
#' @export
|
||||
basicPage <- function(...) {
|
||||
bootstrapPage(div(class="container-fluid", list(...)))
|
||||
}
|
||||
|
||||
#' Create a page with a sidebar
|
||||
#'
|
||||
#' Create a Shiny UI that contains a header with the application title, a
|
||||
@@ -246,12 +255,14 @@ conditionalPanel <- function(condition, ...) {
|
||||
#' @param value Initial value
|
||||
#' @return A text input control that can be added to a UI definition.
|
||||
#'
|
||||
#' @seealso \code{\link{updateTextInput}}
|
||||
#'
|
||||
#' @examples
|
||||
#' textInput("caption", "Caption:", "Data Summary")
|
||||
#' @export
|
||||
textInput <- function(inputId, label, value = "") {
|
||||
tagList(
|
||||
tags$label(label),
|
||||
tags$label(label, `for` = inputId),
|
||||
tags$input(id = inputId, type="text", value=value)
|
||||
)
|
||||
}
|
||||
@@ -267,6 +278,8 @@ textInput <- function(inputId, label, value = "") {
|
||||
#' @param max Maximum allowed value
|
||||
#' @param step Interval to use when stepping between min and max
|
||||
#' @return A numeric input control that can be added to a UI definition.
|
||||
#'
|
||||
#' @seealso \code{\link{updateNumericInput}}
|
||||
#'
|
||||
#' @examples
|
||||
#' numericInput("obs", "Observations:", 10,
|
||||
@@ -284,7 +297,7 @@ numericInput <- function(inputId, label, value, min = NA, max = NA, step = NA) {
|
||||
inputTag$attribs$step = step
|
||||
|
||||
tagList(
|
||||
tags$label(label),
|
||||
tags$label(label, `for` = inputId),
|
||||
inputTag
|
||||
)
|
||||
}
|
||||
@@ -350,7 +363,7 @@ fileInput <- function(inputId, label, multiple = FALSE, accept = NULL) {
|
||||
#' @param value Initial value (\code{TRUE} or \code{FALSE}).
|
||||
#' @return A checkbox control that can be added to a UI definition.
|
||||
#'
|
||||
#' @seealso \code{\link{checkboxGroupInput}}
|
||||
#' @seealso \code{\link{checkboxGroupInput}}, \code{\link{updateCheckboxInput}}
|
||||
#'
|
||||
#' @examples
|
||||
#' checkboxInput("outliers", "Show outliers", FALSE)
|
||||
@@ -359,7 +372,7 @@ checkboxInput <- function(inputId, label, value = FALSE) {
|
||||
inputTag <- tags$input(id = inputId, type="checkbox")
|
||||
if (!is.null(value) && value)
|
||||
inputTag$attribs$checked <- "checked"
|
||||
tags$label(class = "checkbox", inputTag, label)
|
||||
tags$label(class = "checkbox", `for` = inputId, inputTag, tags$span(label))
|
||||
}
|
||||
|
||||
|
||||
@@ -376,7 +389,7 @@ checkboxInput <- function(inputId, label, value = FALSE) {
|
||||
#' @param selected Names of items that should be initially selected, if any.
|
||||
#' @return A list of HTML elements that can be added to a UI definition.
|
||||
#'
|
||||
#' @seealso \code{\link{checkboxInput}}
|
||||
#' @seealso \code{\link{checkboxInput}}, \code{\link{updateCheckboxGroupInput}}
|
||||
#'
|
||||
#' @examples
|
||||
#' checkboxGroupInput("variable", "Variable:",
|
||||
@@ -388,23 +401,30 @@ checkboxInput <- function(inputId, label, value = FALSE) {
|
||||
checkboxGroupInput <- function(inputId, label, choices, selected = NULL) {
|
||||
# resolve names
|
||||
choices <- choicesWithNames(choices)
|
||||
|
||||
checkboxes <- list()
|
||||
for (choiceName in names(choices)) {
|
||||
|
||||
checkbox <- tags$input(name = inputId, type="checkbox",
|
||||
value = choices[[choiceName]])
|
||||
|
||||
if (choiceName %in% selected)
|
||||
checkbox$attribs$checked <- 'checked'
|
||||
|
||||
checkboxes[[length(checkboxes)+1]] <- checkbox
|
||||
checkboxes[[length(checkboxes)+1]] <- choiceName
|
||||
checkboxes[[length(checkboxes)+1]] <- tags$br()
|
||||
}
|
||||
|
||||
# Create tags for each of the options
|
||||
ids <- paste0(inputId, seq_along(choices))
|
||||
|
||||
checkboxes <- mapply(ids, choices, names(choices),
|
||||
SIMPLIFY = FALSE, USE.NAMES = FALSE,
|
||||
FUN = function(id, value, name) {
|
||||
inputTag <- tags$input(type = "checkbox",
|
||||
name = inputId,
|
||||
id = id,
|
||||
value = value)
|
||||
|
||||
if (name %in% selected)
|
||||
inputTag$attribs$checked <- "checked"
|
||||
|
||||
tags$label(class = "checkbox",
|
||||
inputTag,
|
||||
tags$span(name))
|
||||
}
|
||||
)
|
||||
|
||||
# return label and select tag
|
||||
tags$div(class='control-group',
|
||||
tags$div(id = inputId,
|
||||
class = "control-group shiny-input-checkboxgroup",
|
||||
controlLabel(inputId, label),
|
||||
checkboxes)
|
||||
}
|
||||
@@ -431,6 +451,8 @@ controlLabel <- function(controlName, label) {
|
||||
tags$label(class = "control-label", `for` = controlName, label)
|
||||
}
|
||||
|
||||
# Takes a vector or list, and adds names (same as the value) to any entries
|
||||
# without names.
|
||||
choicesWithNames <- function(choices) {
|
||||
# get choice names
|
||||
choiceNames <- names(choices)
|
||||
@@ -461,6 +483,8 @@ choicesWithNames <- function(choices) {
|
||||
#' @param multiple Is selection of multiple items allowed?
|
||||
#' @return A select list control that can be added to a UI definition.
|
||||
#'
|
||||
#' @seealso \code{\link{updateSelectInput}}
|
||||
#'
|
||||
#' @examples
|
||||
#' selectInput("variable", "Variable:",
|
||||
#' c("Cylinders" = "cyl",
|
||||
@@ -483,13 +507,22 @@ selectInput <- function(inputId,
|
||||
selectTag <- tags$select(id = inputId)
|
||||
if (multiple)
|
||||
selectTag$attribs$multiple <- "multiple"
|
||||
for (choiceName in names(choices)) {
|
||||
optionTag <- tags$option(value = choices[[choiceName]], choiceName)
|
||||
if (choiceName %in% selected)
|
||||
optionTag$attribs$selected = "selected"
|
||||
selectTag <- tagAppendChild(selectTag, optionTag)
|
||||
}
|
||||
|
||||
|
||||
# Create tags for each of the options
|
||||
optionTags <- mapply(choices, names(choices),
|
||||
SIMPLIFY = FALSE, USE.NAMES = FALSE,
|
||||
FUN = function(choice, name) {
|
||||
optionTag <- tags$option(value = choice, name)
|
||||
|
||||
if (name %in% selected)
|
||||
optionTag$attribs$selected = "selected"
|
||||
|
||||
optionTag
|
||||
}
|
||||
)
|
||||
|
||||
selectTag <- tagSetChildren(selectTag, list = optionTags)
|
||||
|
||||
# return label and select tag
|
||||
tagList(controlLabel(inputId, label), selectTag)
|
||||
}
|
||||
@@ -505,6 +538,8 @@ selectInput <- function(inputId,
|
||||
#' @param selected Name of initially selected item (if not specified then
|
||||
#' defaults to the first item)
|
||||
#' @return A set of radio buttons that can be added to a UI definition.
|
||||
#'
|
||||
#' @seealso \code{\link{updateRadioButtons}}
|
||||
#'
|
||||
#' @examples
|
||||
#' radioButtons("dist", "Distribution type:",
|
||||
@@ -521,27 +556,32 @@ radioButtons <- function(inputId, label, choices, selected = NULL) {
|
||||
if (is.null(selected))
|
||||
selected <- names(choices)[[1]]
|
||||
|
||||
# build list of radio button tags
|
||||
inputTags <- list()
|
||||
for (i in 1:length(choices)) {
|
||||
id <- paste(inputId, i, sep="")
|
||||
name <- names(choices)[[i]]
|
||||
value <- choices[[i]]
|
||||
inputTag <- tags$input(type = "radio",
|
||||
name = inputId,
|
||||
id = id,
|
||||
value = value)
|
||||
if (identical(name, selected))
|
||||
inputTag$attribs$checked = "checked"
|
||||
|
||||
labelTag <- tags$label(class = "radio")
|
||||
labelTag <- tagAppendChild(labelTag, inputTag)
|
||||
labelTag <- tagAppendChild(labelTag, name)
|
||||
inputTags[[length(inputTags) + 1]] <- labelTag
|
||||
}
|
||||
|
||||
tagList(tags$label(class = "control-label", label),
|
||||
inputTags)
|
||||
# Create tags for each of the options
|
||||
ids <- paste0(inputId, seq_along(choices))
|
||||
|
||||
inputTags <- mapply(ids, choices, names(choices),
|
||||
SIMPLIFY = FALSE, USE.NAMES = FALSE,
|
||||
FUN = function(id, value, name) {
|
||||
inputTag <- tags$input(type = "radio",
|
||||
name = inputId,
|
||||
id = id,
|
||||
value = value)
|
||||
|
||||
if (identical(name, selected))
|
||||
inputTag$attribs$checked = "checked"
|
||||
|
||||
# Put the label text in a span
|
||||
tags$label(class = "radio",
|
||||
inputTag,
|
||||
tags$span(name)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
tags$div(id = inputId,
|
||||
class = 'control-group shiny-input-radiogroup',
|
||||
tags$label(class = "control-label", `for` = inputId, label),
|
||||
inputTags)
|
||||
}
|
||||
|
||||
#' Create a submit button
|
||||
@@ -562,6 +602,21 @@ submitButton <- function(text = "Apply Changes") {
|
||||
)
|
||||
}
|
||||
|
||||
#' Action button
|
||||
#'
|
||||
#' Creates an action button whose value is initially zero, and increments by one
|
||||
#' each time it is pressed.
|
||||
#'
|
||||
#' @param inputId Specifies the input slot that will be used to access the
|
||||
#' value.
|
||||
#' @param label The contents of the button--usually a text label, but you could
|
||||
#' also use any other HTML, like an image.
|
||||
#'
|
||||
#' @export
|
||||
actionButton <- function(inputId, label) {
|
||||
tags$button(id=inputId, type="button", class="btn action-button", label)
|
||||
}
|
||||
|
||||
#' Slider Input Widget
|
||||
#'
|
||||
#' Constructs a slider widget to select a numeric value from a range.
|
||||
@@ -589,6 +644,8 @@ submitButton <- function(text = "Apply Changes") {
|
||||
#' @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{\link{animationOptions}}.
|
||||
#'
|
||||
#' @seealso \code{\link{updateSliderInput}}
|
||||
#'
|
||||
#' @details
|
||||
#'
|
||||
@@ -623,11 +680,243 @@ sliderInput <- function(inputId, label, min, max, value, step = NULL,
|
||||
}
|
||||
|
||||
# build slider
|
||||
tags$div(
|
||||
tagList(
|
||||
controlLabel(inputId, labelText),
|
||||
slider(inputId, min=min, max=max, value=value, step=step, round=round,
|
||||
locale=locale, format=format, ticks=ticks,
|
||||
animate=animate)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
#' Create date input
|
||||
#'
|
||||
#' Creates a text input which, when clicked on, brings up a calendar that
|
||||
#' the user can click on to select dates.
|
||||
#'
|
||||
#' The date \code{format} string specifies how the date will be displayed in
|
||||
#' the browser. It allows the following values:
|
||||
#'
|
||||
#' \itemize{
|
||||
#' \item \code{yy} Year without century (12)
|
||||
#' \item \code{yyyy} Year with century (2012)
|
||||
#' \item \code{mm} Month number, with leading zero (01-12)
|
||||
#' \item \code{m} Month number, without leading zero (01-12)
|
||||
#' \item \code{M} Abbreviated month name
|
||||
#' \item \code{MM} Full month name
|
||||
#' \item \code{dd} Day of month with leading zero
|
||||
#' \item \code{d} Day of month without leading zero
|
||||
#' \item \code{D} Abbreviated weekday name
|
||||
#' \item \code{DD} Full weekday name
|
||||
#' }
|
||||
#'
|
||||
#' @param inputId Input variable to assign the control's value to.
|
||||
#' @param label Display label for the control.
|
||||
#' @param value The starting date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format. If NULL (the default), will use the current
|
||||
#' date in the client's time zone.
|
||||
#' @param min The minimum allowed date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format.
|
||||
#' @param max The maximum allowed date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format.
|
||||
#' @param format The format of the date to display in the browser. Defaults to
|
||||
#' \code{"yyyy-mm-dd"}.
|
||||
#' @param startview The date range shown when the input object is first
|
||||
#' clicked. Can be "month" (the default), "year", or "decade".
|
||||
#' @param weekstart Which day is the start of the week. Should be an integer
|
||||
#' from 0 (Sunday) to 6 (Saturday).
|
||||
#' @param language The language used for month and day names. Default is "en".
|
||||
#' Other valid values include "bg", "ca", "cs", "da", "de", "el", "es", "fi",
|
||||
#' "fr", "he", "hr", "hu", "id", "is", "it", "ja", "kr", "lt", "lv", "ms",
|
||||
#' "nb", "nl", "pl", "pt", "pt", "ro", "rs", "rs-latin", "ru", "sk", "sl",
|
||||
#' "sv", "sw", "th", "tr", "uk", "zh-CN", and "zh-TW".
|
||||
#'
|
||||
#' @seealso \code{\link{dateRangeInput}}, \code{\link{updateDateInput}}
|
||||
#'
|
||||
#' @examples
|
||||
#' dateInput("date", "Date:", value = "2012-02-29")
|
||||
#'
|
||||
#' # Default value is the date in client's time zone
|
||||
#' dateInput("date", "Date:")
|
||||
#'
|
||||
#' # value is always yyyy-mm-dd, even if the display format is different
|
||||
#' dateInput("date", "Date:", value = "2012-02-29", format = "mm/dd/yy")
|
||||
#'
|
||||
#' # Pass in a Date object
|
||||
#' dateInput("date", "Date:", value = Sys.Date()-10)
|
||||
#'
|
||||
#' # Use different language and different first day of week
|
||||
#' dateInput("date", "Date:",
|
||||
#' language = "de",
|
||||
#' weekstart = 1)
|
||||
#'
|
||||
#' # Start with decade view instead of default month view
|
||||
#' dateInput("date", "Date:",
|
||||
#' startview = "decade")
|
||||
#'
|
||||
#' @export
|
||||
dateInput <- function(inputId, label, value = NULL, min = NULL, max = NULL,
|
||||
format = "yyyy-mm-dd", startview = "month", weekstart = 0, language = "en") {
|
||||
|
||||
# If value is a date object, convert it to a string with yyyy-mm-dd format
|
||||
# Same for min and max
|
||||
if (inherits(value, "Date")) value <- format(value, "%Y-%m-%d")
|
||||
if (inherits(min, "Date")) min <- format(min, "%Y-%m-%d")
|
||||
if (inherits(max, "Date")) max <- format(max, "%Y-%m-%d")
|
||||
|
||||
tagList(
|
||||
controlLabel(inputId, labelText),
|
||||
slider(inputId, min=min, max=max, value=value, step=step, round=round,
|
||||
locale=locale, format=format, ticks=ticks,
|
||||
animate=animate)
|
||||
singleton(tags$head(
|
||||
tags$script(src = "shared/datepicker/js/bootstrap-datepicker.min.js"),
|
||||
tags$link(rel = "stylesheet", type = "text/css",
|
||||
href = 'shared/datepicker/css/datepicker.css')
|
||||
)),
|
||||
tags$div(id = inputId,
|
||||
class = "shiny-date-input",
|
||||
|
||||
controlLabel(inputId, label),
|
||||
tags$input(type = "text",
|
||||
# datepicker class necessary for dropdown to display correctly
|
||||
class = "input-medium datepicker",
|
||||
`data-date-language` = language,
|
||||
`data-date-weekstart` = weekstart,
|
||||
`data-date-format` = format,
|
||||
`data-date-start-view` = startview,
|
||||
`data-min-date` = min,
|
||||
`data-max-date` = max,
|
||||
`data-initial-date` = value
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
#' Create date range input
|
||||
#'
|
||||
#' Creates a pair of text inputs which, when clicked on, bring up calendars that
|
||||
#' the user can click on to select dates.
|
||||
#'
|
||||
#' The date \code{format} string specifies how the date will be displayed in
|
||||
#' the browser. It allows the following values:
|
||||
#'
|
||||
#' \itemize{
|
||||
#' \item \code{yy} Year without century (12)
|
||||
#' \item \code{yyyy} Year with century (2012)
|
||||
#' \item \code{mm} Month number, with leading zero (01-12)
|
||||
#' \item \code{m} Month number, without leading zero (01-12)
|
||||
#' \item \code{M} Abbreviated month name
|
||||
#' \item \code{MM} Full month name
|
||||
#' \item \code{dd} Day of month with leading zero
|
||||
#' \item \code{d} Day of month without leading zero
|
||||
#' \item \code{D} Abbreviated weekday name
|
||||
#' \item \code{DD} Full weekday name
|
||||
#' }
|
||||
#'
|
||||
#' @param inputId Input variable to assign the control's value to.
|
||||
#' @param label Display label for the control.
|
||||
#' @param start The initial start date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format. If NULL (the default), will use the current
|
||||
#' date in the client's time zone.
|
||||
#' @param end The initial end date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format. If NULL (the default), will use the current
|
||||
#' date in the client's time zone.
|
||||
#' @param min The minimum allowed date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format.
|
||||
#' @param max The maximum allowed date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format.
|
||||
#' @param format The format of the date to display in the browser. Defaults to
|
||||
#' \code{"yyyy-mm-dd"}.
|
||||
#' @param startview The date range shown when the input object is first
|
||||
#' clicked. Can be "month" (the default), "year", or "decade".
|
||||
#' @param weekstart Which day is the start of the week. Should be an integer
|
||||
#' from 0 (Sunday) to 6 (Saturday).
|
||||
#' @param language The language used for month and day names. Default is "en".
|
||||
#' Other valid values include "bg", "ca", "cs", "da", "de", "el", "es", "fi",
|
||||
#' "fr", "he", "hr", "hu", "id", "is", "it", "ja", "kr", "lt", "lv", "ms",
|
||||
#' "nb", "nl", "pl", "pt", "pt", "ro", "rs", "rs-latin", "ru", "sk", "sl",
|
||||
#' "sv", "sw", "th", "tr", "uk", "zh-CN", and "zh-TW".
|
||||
#' @param separator String to display between the start and end input boxes.
|
||||
#'
|
||||
#' @seealso \code{\link{dateInput}}, \code{\link{updateDateRangeInput}}
|
||||
#'
|
||||
#' @examples
|
||||
#' dateRangeInput("daterange", "Date range:",
|
||||
#' start = "2001-01-01",
|
||||
#' end = "2010-12-31")
|
||||
#'
|
||||
#' # Default start and end is the current date in the client's time zone
|
||||
#' dateRangeInput("daterange", "Date range:")
|
||||
#'
|
||||
#' # start and end are always specified in yyyy-mm-dd, even if the display
|
||||
#' # format is different
|
||||
#' dateRangeInput("daterange", "Date range:",
|
||||
#' start = "2001-01-01",
|
||||
#' end = "2010-12-31",
|
||||
#' min = "2001-01-01",
|
||||
#' max = "2012-12-21",
|
||||
#' format = "mm/dd/yy",
|
||||
#' separator = " - ")
|
||||
#'
|
||||
#' # Pass in Date objects
|
||||
#' dateRangeInput("daterange", "Date range:",
|
||||
#' start = Sys.Date()-10,
|
||||
#' end = Sys.Date()+10)
|
||||
#'
|
||||
#' # Use different language and different first day of week
|
||||
#' dateRangeInput("daterange", "Date range:",
|
||||
#' language = "de",
|
||||
#' weekstart = 1)
|
||||
#'
|
||||
#' # Start with decade view instead of default month view
|
||||
#' dateRangeInput("daterange", "Date range:",
|
||||
#' startview = "decade")
|
||||
#'
|
||||
#' @export
|
||||
dateRangeInput <- function(inputId, label, start = NULL, end = NULL,
|
||||
min = NULL, max = NULL, format = "yyyy-mm-dd", startview = "month",
|
||||
weekstart = 0, language = "en", separator = " to ") {
|
||||
|
||||
# If start and end are date objects, convert to a string with yyyy-mm-dd format
|
||||
# Same for min and max
|
||||
if (inherits(start, "Date")) start <- format(start, "%Y-%m-%d")
|
||||
if (inherits(end, "Date")) end <- format(end, "%Y-%m-%d")
|
||||
if (inherits(min, "Date")) min <- format(min, "%Y-%m-%d")
|
||||
if (inherits(max, "Date")) max <- format(max, "%Y-%m-%d")
|
||||
|
||||
tagList(
|
||||
singleton(tags$head(
|
||||
tags$script(src = "shared/datepicker/js/bootstrap-datepicker.min.js"),
|
||||
tags$link(rel = "stylesheet", type = "text/css",
|
||||
href = 'shared/datepicker/css/datepicker.css')
|
||||
)),
|
||||
tags$div(id = inputId,
|
||||
# input-daterange class is needed for dropdown behavior
|
||||
class = "shiny-date-range-input input-daterange",
|
||||
|
||||
controlLabel(inputId, label),
|
||||
tags$input(class = "input-small",
|
||||
type = "text",
|
||||
`data-date-language` = language,
|
||||
`data-date-weekstart` = weekstart,
|
||||
`data-date-format` = format,
|
||||
`data-date-start-view` = startview,
|
||||
`data-min-date` = min,
|
||||
`data-max-date` = max,
|
||||
`data-initial-date` = start
|
||||
),
|
||||
HTML(separator),
|
||||
tags$input(class = "input-small",
|
||||
type = "text",
|
||||
`data-date-language` = language,
|
||||
`data-date-weekstart` = weekstart,
|
||||
`data-date-format` = format,
|
||||
`data-date-start-view` = startview,
|
||||
`data-min-date` = min,
|
||||
`data-max-date` = max,
|
||||
`data-initial-date` = end
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -642,6 +931,8 @@ sliderInput <- function(inputId, label, min, max, value, step = NULL,
|
||||
#' that this tab is selected. If omitted and \code{tabsetPanel} has an
|
||||
#' \code{id}, then the title will be used.
|
||||
#' @return A tab that can be passed to \code{\link{tabsetPanel}}
|
||||
#'
|
||||
#' @seealso \code{\link{tabsetPanel}}
|
||||
#'
|
||||
#' @examples
|
||||
#' # Show a tabset that includes a plot, summary, and
|
||||
@@ -668,8 +959,13 @@ tabPanel <- function(title, ..., value = NULL) {
|
||||
#' logic to determine which of the current tabs is active. The value will
|
||||
#' correspond to the \code{value} argument that is passed to
|
||||
#' \code{\link{tabPanel}}.
|
||||
#' @param selected The \code{value} (or, if none was supplied, the \code{title})
|
||||
#' of the tab that should be selected by default. If \code{NULL}, the first
|
||||
#' tab will be selected.
|
||||
#' @return A tabset that can be passed to \code{\link{mainPanel}}
|
||||
#'
|
||||
#' @seealso \code{\link{tabPanel}}, \code{\link{updateTabsetPanel}}
|
||||
#'
|
||||
#' @examples
|
||||
#' # Show a tabset that includes a plot, summary, and
|
||||
#' # table view of the generated distribution
|
||||
@@ -681,7 +977,7 @@ tabPanel <- function(title, ..., value = NULL) {
|
||||
#' )
|
||||
#' )
|
||||
#' @export
|
||||
tabsetPanel <- function(..., id = NULL) {
|
||||
tabsetPanel <- function(..., id = NULL, selected = NULL) {
|
||||
|
||||
# build tab-nav and tab-content divs
|
||||
tabs <- list(...)
|
||||
@@ -708,13 +1004,20 @@ tabsetPanel <- function(..., id = NULL) {
|
||||
`data-value` = tabValue,
|
||||
divTag$attribs$title))
|
||||
|
||||
# set the first tab as active
|
||||
if (firstTab) {
|
||||
if (is.null(tabValue)) {
|
||||
tabValue <- divTag$attribs$title
|
||||
}
|
||||
|
||||
# If appropriate, make this the selected tab
|
||||
if ((firstTab && is.null(selected)) ||
|
||||
(!is.null(selected) && identical(selected, tabValue))) {
|
||||
liTag$attribs$class <- "active"
|
||||
divTag$attribs$class <- "tab-pane active"
|
||||
firstTab = FALSE
|
||||
}
|
||||
|
||||
divTag$attribs$title <- NULL
|
||||
|
||||
# append the elements to our lists
|
||||
tabNavList <- tagAppendChild(tabNavList, liTag)
|
||||
tabContent <- tagAppendChild(tabContent, divTag)
|
||||
@@ -890,6 +1193,16 @@ downloadLink <- function(outputId, label="Download", class=NULL) {
|
||||
label)
|
||||
}
|
||||
|
||||
#' Validate proper CSS formatting of a unit
|
||||
#'
|
||||
#' @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.character(x) &&
|
||||
!grepl("^(auto|((\\.\\d+)|(\\d+(\\.\\d+)?))(%|in|cm|mm|em|ex|pt|pc|px))$", x)) {
|
||||
|
||||
@@ -71,7 +71,7 @@ CacheContext <- setRefClass(
|
||||
# If NULL or NA is given as the argument, then ui.R will re-execute next time.
|
||||
dependsOnFile <- function(filepath) {
|
||||
if (is.null(.currentCacheContext$cc))
|
||||
stop("addFileDependency was called at an unexpected time (no cache context found)")
|
||||
return()
|
||||
|
||||
if (is.null(filepath) || is.na(filepath))
|
||||
.currentCacheContext$cc$forceDirty()
|
||||
|
||||
@@ -35,6 +35,11 @@ FileUploadOperation <- setRefClass(
|
||||
initialize = function(parent, id, dir, fileInfos) {
|
||||
.parent <<- parent
|
||||
.id <<- id
|
||||
.files <<- data.frame(name=character(),
|
||||
size=numeric(),
|
||||
type=character(),
|
||||
datapath=character(),
|
||||
stringsAsFactors=FALSE)
|
||||
.dir <<- dir
|
||||
.pendingFileInfos <<- fileInfos
|
||||
},
|
||||
@@ -46,11 +51,11 @@ FileUploadOperation <- setRefClass(
|
||||
.currentFileInfo <<- file
|
||||
.pendingFileInfos <<- tail(.pendingFileInfos, -1)
|
||||
|
||||
filename <- file.path(.dir, as.character(length(.files)))
|
||||
filename <- file.path(.dir, as.character(length(.files$name)))
|
||||
row <- data.frame(name=file$name, size=file$size, type=file$type,
|
||||
datapath=filename, stringsAsFactors=FALSE)
|
||||
|
||||
if (length(.files) == 0)
|
||||
if (length(.files$name) == 0)
|
||||
.files <<- row
|
||||
else
|
||||
.files <<- rbind(.files, row)
|
||||
|
||||
73
R/graph.R
Normal file
@@ -0,0 +1,73 @@
|
||||
#' @export
|
||||
writeReactLog <- function(file=stdout()) {
|
||||
cat(RJSONIO::toJSON(.graphEnv$log, pretty=TRUE), file=file)
|
||||
}
|
||||
|
||||
#' @export
|
||||
showReactLog <- function() {
|
||||
browseURL(renderReactLog())
|
||||
}
|
||||
|
||||
renderReactLog <- function() {
|
||||
templateFile <- system.file('www/reactive-graph.html', package='shiny')
|
||||
html <- paste(readLines(templateFile, warn=FALSE), collapse='\r\n')
|
||||
tc <- textConnection(NULL, 'w')
|
||||
on.exit(close(tc))
|
||||
writeReactLog(tc)
|
||||
cat('\n', file=tc)
|
||||
flush(tc)
|
||||
html <- sub('__DATA__', paste(textConnectionValue(tc), collapse='\r\n'), html, fixed=TRUE)
|
||||
file <- tempfile(fileext = '.html')
|
||||
writeLines(html, file)
|
||||
return(file)
|
||||
}
|
||||
|
||||
.graphAppend <- function(logEntry) {
|
||||
if (isTRUE(getOption('shiny.reactlog', FALSE)))
|
||||
.graphEnv$log <- c(.graphEnv$log, list(logEntry))
|
||||
}
|
||||
|
||||
.graphDependsOn <- function(id, label) {
|
||||
if (isTRUE(getOption('shiny.reactlog', FALSE)))
|
||||
.graphAppend(list(action='dep', id=id, dependsOn=label))
|
||||
}
|
||||
|
||||
.graphDependsOnId <- function(id, dependee) {
|
||||
if (isTRUE(getOption('shiny.reactlog', FALSE)))
|
||||
.graphAppend(list(action='depId', id=id, dependsOn=dependee))
|
||||
}
|
||||
|
||||
.graphCreateContext <- function(id, label, type, prevId) {
|
||||
if (isTRUE(getOption('shiny.reactlog', FALSE)))
|
||||
.graphAppend(list(
|
||||
action='ctx', id=id, label=paste(label, collapse='\n'), type=type, prevId=prevId
|
||||
))
|
||||
}
|
||||
|
||||
.graphEnterContext <- function(id) {
|
||||
if (isTRUE(getOption('shiny.reactlog', FALSE)))
|
||||
.graphAppend(list(action='enter', id=id))
|
||||
}
|
||||
|
||||
.graphExitContext <- function(id) {
|
||||
if (isTRUE(getOption('shiny.reactlog', FALSE)))
|
||||
.graphAppend(list(action='exit', id=id))
|
||||
}
|
||||
|
||||
.graphValueChange <- function(label, value) {
|
||||
if (isTRUE(getOption('shiny.reactlog', FALSE))) {
|
||||
.graphAppend(list(
|
||||
action = 'valueChange',
|
||||
id = label,
|
||||
value = paste(capture.output(str(value)), collapse='\n')
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
.graphInvalidate <- function(id) {
|
||||
if (isTRUE(getOption('shiny.reactlog', FALSE)))
|
||||
.graphAppend(list(action='invalidate', id=id))
|
||||
}
|
||||
|
||||
.graphEnv <- new.env()
|
||||
.graphEnv$log <- list()
|
||||
@@ -11,6 +11,10 @@
|
||||
#' output. Notably, plain \code{png} output on Linux and Windows may not
|
||||
#' antialias some point shapes, resulting in poor quality output.
|
||||
#'
|
||||
#' In some cases, \code{Cairo()} provides output that looks worse than
|
||||
#' \code{png()}. To disable Cairo output for an app, use
|
||||
#' \code{options(shiny.usecairo=FALSE)}.
|
||||
#'
|
||||
#' @param func A function that generates a plot.
|
||||
#' @param filename The name of the output file. Defaults to a temp file with
|
||||
#' extension \code{.png}.
|
||||
@@ -30,9 +34,15 @@ plotPNG <- function(func, filename=tempfile(fileext='.png'),
|
||||
# Finally, if neither quartz nor Cairo, use png().
|
||||
if (capabilities("aqua")) {
|
||||
pngfun <- png
|
||||
} else if (nchar(system.file(package = "Cairo"))) {
|
||||
require(Cairo)
|
||||
pngfun <- CairoPNG
|
||||
} else if (getOption('shiny.usecairo', TRUE) &&
|
||||
nchar(system.file(package = "Cairo"))) {
|
||||
# Workaround for issue #140: Cairo ignores res and dpi settings. Need to
|
||||
# use regular png function.
|
||||
if (res == 72) {
|
||||
pngfun <- Cairo::CairoPNG
|
||||
} else {
|
||||
pngfun <- png
|
||||
}
|
||||
} else {
|
||||
pngfun <- png
|
||||
}
|
||||
|
||||
110
R/priorityqueue.R
Normal file
@@ -0,0 +1,110 @@
|
||||
# "...like a regular queue or stack data structure, but where additionally each
|
||||
# element has a "priority" associated with it. In a priority queue, an element
|
||||
# with high priority is served before an element with low priority. If two
|
||||
# elements have the same priority, they are served according to their order in
|
||||
# the queue." (http://en.wikipedia.org/wiki/Priority_queue)
|
||||
|
||||
PriorityQueue <- setRefClass(
|
||||
'PriorityQueue',
|
||||
fields = list(
|
||||
# Keys are priorities, values are subqueues (implemented as list)
|
||||
.itemsByPriority = 'Map',
|
||||
# Sorted vector (largest first)
|
||||
.priorities = 'numeric'
|
||||
),
|
||||
methods = list(
|
||||
# Enqueue an item, with the given priority level (must be integer). Higher
|
||||
# priority numbers are dequeued earlier than lower.
|
||||
enqueue = function(item, priority) {
|
||||
priority <- normalizePriority(priority)
|
||||
|
||||
if (!(priority %in% .priorities)) {
|
||||
.priorities <<- c(.priorities, priority)
|
||||
.priorities <<- sort(.priorities, decreasing=TRUE)
|
||||
.itemsByPriority$set(.key(priority), list(item))
|
||||
} else {
|
||||
.itemsByPriority$set(
|
||||
.key(priority),
|
||||
c(.itemsByPriority$get(.key(priority)), item)
|
||||
)
|
||||
}
|
||||
return(invisible())
|
||||
},
|
||||
# Retrieve a single item by 1) priority number (highest first) and then 2)
|
||||
# insertion order (first in, first out). If there are no items to be
|
||||
# dequeued, then NULL is returned. If it is necessary to distinguish between
|
||||
# a NULL value and the empty case, call isEmpty() before dequeue().
|
||||
dequeue = function() {
|
||||
if (length(.priorities) == 0)
|
||||
return(NULL)
|
||||
|
||||
maxPriority <- .priorities[[1]]
|
||||
items <- .itemsByPriority$get(.key(maxPriority))
|
||||
firstItem <- items[[1]]
|
||||
if (length(items) == 1) {
|
||||
# This is the last item at this priority. Remove both the list and the
|
||||
# priority level.
|
||||
.itemsByPriority$remove(.key(maxPriority))
|
||||
.priorities <<- .priorities[-1]
|
||||
} else {
|
||||
# There are still items at this priority. Remove the current item from
|
||||
# the list, and save it.
|
||||
items <- items[-1]
|
||||
.itemsByPriority$set(.key(maxPriority), items)
|
||||
}
|
||||
return(firstItem)
|
||||
},
|
||||
# Returns TRUE if no items are in the queue, otherwise FALSE.
|
||||
isEmpty = function() {
|
||||
length(.priorities) == 0
|
||||
},
|
||||
# Translates a priority integer to a character that is suitable for using as
|
||||
# a key.
|
||||
.key = function(priority) {
|
||||
sprintf('%a', priority)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
normalizePriority <- function(priority) {
|
||||
|
||||
if (is.null(priority))
|
||||
priority <- 0
|
||||
|
||||
# Cast integers to numeric to prevent any inconsistencies
|
||||
if (is.integer(priority))
|
||||
priority <- as.numeric(priority)
|
||||
|
||||
if (!is.numeric(priority))
|
||||
stop('priority must be an integer or numeric')
|
||||
|
||||
# Check length
|
||||
if (length(priority) == 0) {
|
||||
warning('Zero-length priority vector was passed; using 0')
|
||||
priority <- 0
|
||||
} else if (length(priority) > 1) {
|
||||
warning('Priority has length > 1 and only the first element will be used')
|
||||
priority <- priority[1]
|
||||
}
|
||||
|
||||
# NA == 0
|
||||
if (is.na(priority))
|
||||
priority <- 0
|
||||
|
||||
return(priority)
|
||||
}
|
||||
|
||||
# pq <- PriorityQueue$new()
|
||||
# pq$enqueue('a', 1)
|
||||
# pq$enqueue('b', 1L)
|
||||
# pq$enqueue('c', 1)
|
||||
# pq$enqueue('A', 2)
|
||||
# pq$enqueue('B', 2L)
|
||||
# pq$enqueue('C', 2)
|
||||
# pq$enqueue('d', 1)
|
||||
# pq$enqueue('e', 1L)
|
||||
# pq$enqueue('f', 1)
|
||||
# pq$enqueue('D', 2)
|
||||
# pq$enqueue('E', 2L)
|
||||
# pq$enqueue('F', 2)
|
||||
# # Expect ABCDEFabcdef
|
||||
26
R/react.R
@@ -8,16 +8,19 @@ Context <- setRefClass(
|
||||
.flushCallbacks = 'list'
|
||||
),
|
||||
methods = list(
|
||||
initialize = function(label='') {
|
||||
initialize = function(label='', type='other', prevId='') {
|
||||
id <<- .getReactiveEnvironment()$nextId()
|
||||
.invalidated <<- FALSE
|
||||
.invalidateCallbacks <<- list()
|
||||
.flushCallbacks <<- list()
|
||||
.label <<- label
|
||||
.graphCreateContext(id, label, type, prevId)
|
||||
},
|
||||
run = function(func) {
|
||||
"Run the provided function under this context."
|
||||
env <- .getReactiveEnvironment()
|
||||
.graphEnterContext(id)
|
||||
on.exit(.graphExitContext(id))
|
||||
env$runWith(.self, func)
|
||||
},
|
||||
invalidate = function() {
|
||||
@@ -27,6 +30,7 @@ Context <- setRefClass(
|
||||
return()
|
||||
.invalidated <<- TRUE
|
||||
|
||||
.graphInvalidate(id)
|
||||
lapply(.invalidateCallbacks, function(func) {
|
||||
func()
|
||||
})
|
||||
@@ -42,10 +46,10 @@ Context <- setRefClass(
|
||||
.invalidateCallbacks <<- c(.invalidateCallbacks, func)
|
||||
NULL
|
||||
},
|
||||
addPendingFlush = function() {
|
||||
addPendingFlush = function(priority) {
|
||||
"Tell the reactive environment that this context should be flushed the
|
||||
next time flushReact() called."
|
||||
.getReactiveEnvironment()$addPendingFlush(.self)
|
||||
.getReactiveEnvironment()$addPendingFlush(.self, priority)
|
||||
},
|
||||
onFlush = function(func) {
|
||||
"Register a function to be called when this context is flushed."
|
||||
@@ -71,14 +75,14 @@ ReactiveEnvironment <- setRefClass(
|
||||
fields = list(
|
||||
.currentContext = 'ANY',
|
||||
.nextId = 'integer',
|
||||
.pendingFlush = 'list',
|
||||
.pendingFlush = 'PriorityQueue',
|
||||
.inFlush = 'logical'
|
||||
),
|
||||
methods = list(
|
||||
initialize = function() {
|
||||
.currentContext <<- NULL
|
||||
.nextId <<- 0L
|
||||
.pendingFlush <<- list()
|
||||
.pendingFlush <<- PriorityQueue$new()
|
||||
.inFlush <<- FALSE
|
||||
},
|
||||
nextId = function() {
|
||||
@@ -86,10 +90,11 @@ ReactiveEnvironment <- setRefClass(
|
||||
return(as.character(.nextId))
|
||||
},
|
||||
currentContext = function() {
|
||||
if (is.null(.currentContext))
|
||||
if (is.null(.currentContext)) {
|
||||
stop('Operation not allowed without an active reactive context. ',
|
||||
'(You tried to do something that can only be done from inside a ',
|
||||
'reactive function.)')
|
||||
}
|
||||
return(.currentContext)
|
||||
},
|
||||
runWith = function(ctx, func) {
|
||||
@@ -98,8 +103,8 @@ ReactiveEnvironment <- setRefClass(
|
||||
on.exit(.currentContext <<- old.ctx)
|
||||
func()
|
||||
},
|
||||
addPendingFlush = function(ctx) {
|
||||
.pendingFlush <<- c(ctx, .pendingFlush)
|
||||
addPendingFlush = function(ctx, priority) {
|
||||
.pendingFlush$enqueue(ctx, priority)
|
||||
},
|
||||
flush = function() {
|
||||
# If already in a flush, don't start another one
|
||||
@@ -107,9 +112,8 @@ ReactiveEnvironment <- setRefClass(
|
||||
.inFlush <<- TRUE
|
||||
on.exit(.inFlush <<- FALSE)
|
||||
|
||||
while (length(.pendingFlush) > 0) {
|
||||
ctx <- .pendingFlush[[1]]
|
||||
.pendingFlush <<- .pendingFlush[-1]
|
||||
while (!.pendingFlush$isEmpty()) {
|
||||
ctx <- .pendingFlush$dequeue()
|
||||
ctx$executeFlushCallbacks()
|
||||
}
|
||||
}
|
||||
|
||||
203
R/reactives.R
@@ -4,13 +4,18 @@ Dependents <- setRefClass(
|
||||
.dependents = 'Map'
|
||||
),
|
||||
methods = list(
|
||||
register = function() {
|
||||
register = function(depId=NULL, depLabel=NULL) {
|
||||
ctx <- .getReactiveEnvironment()$currentContext()
|
||||
if (!.dependents$containsKey(ctx$id)) {
|
||||
.dependents$set(ctx$id, ctx)
|
||||
ctx$onInvalidate(function() {
|
||||
.dependents$remove(ctx$id)
|
||||
})
|
||||
|
||||
if (!is.null(depId) && nchar(depId) > 0)
|
||||
.graphDependsOnId(ctx$id, depId)
|
||||
if (!is.null(depLabel))
|
||||
.graphDependsOn(ctx$id, depLabel)
|
||||
}
|
||||
},
|
||||
invalidate = function() {
|
||||
@@ -31,6 +36,8 @@ Dependents <- setRefClass(
|
||||
ReactiveValues <- setRefClass(
|
||||
'ReactiveValues',
|
||||
fields = list(
|
||||
# For debug purposes
|
||||
.label = 'character',
|
||||
.values = 'environment',
|
||||
.dependents = 'environment',
|
||||
# Dependents for the list of all names, including hidden
|
||||
@@ -42,6 +49,8 @@ ReactiveValues <- setRefClass(
|
||||
),
|
||||
methods = list(
|
||||
initialize = function() {
|
||||
.label <<- paste('reactiveValues', runif(1, min=1000, max=9999),
|
||||
sep="")
|
||||
.values <<- new.env(parent=emptyenv())
|
||||
.dependents <<- new.env(parent=emptyenv())
|
||||
},
|
||||
@@ -49,6 +58,7 @@ ReactiveValues <- setRefClass(
|
||||
ctx <- .getReactiveEnvironment()$currentContext()
|
||||
dep.key <- paste(key, ':', ctx$id, sep='')
|
||||
if (!exists(dep.key, where=.dependents, inherits=FALSE)) {
|
||||
.graphDependsOn(ctx$id, sprintf('%s$%s', .label, key))
|
||||
assign(dep.key, ctx, pos=.dependents, inherits=FALSE)
|
||||
ctx$onInvalidate(function() {
|
||||
rm(list=dep.key, pos=.dependents, inherits=FALSE)
|
||||
@@ -76,8 +86,13 @@ ReactiveValues <- setRefClass(
|
||||
.allValuesDeps$invalidate()
|
||||
else
|
||||
.valuesDeps$invalidate()
|
||||
|
||||
|
||||
assign(key, value, pos=.values, inherits=FALSE)
|
||||
|
||||
.graphValueChange(sprintf('names(%s)', .label), ls(.values, all.names=TRUE))
|
||||
.graphValueChange(sprintf('%s (all)', .label), as.list(.values))
|
||||
.graphValueChange(sprintf('%s$%s', .label, key), value)
|
||||
|
||||
dep.keys <- objects(
|
||||
pos=.dependents,
|
||||
pattern=paste('^\\Q', key, ':', '\\E', '\\d+$', sep=''),
|
||||
@@ -99,16 +114,23 @@ ReactiveValues <- setRefClass(
|
||||
})
|
||||
},
|
||||
names = function() {
|
||||
.graphDependsOn(.getReactiveEnvironment()$currentContext()$id,
|
||||
sprintf('names(%s)', .label))
|
||||
.namesDeps$register()
|
||||
return(ls(.values, all.names=TRUE))
|
||||
},
|
||||
toList = function(all.names=FALSE) {
|
||||
.graphDependsOn(.getReactiveEnvironment()$currentContext()$id,
|
||||
sprintf('%s (all)', .label))
|
||||
if (all.names)
|
||||
.allValuesDeps$register()
|
||||
|
||||
.valuesDeps$register()
|
||||
|
||||
return(as.list(.values, all.names=all.names))
|
||||
},
|
||||
.setLabel = function(label) {
|
||||
.label <<- label
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -231,6 +253,11 @@ as.list.reactivevalues <- function(x, all.names=FALSE, ...) {
|
||||
reactiveValuesToList(x, all.names)
|
||||
}
|
||||
|
||||
# For debug purposes
|
||||
.setLabel <- function(x, label) {
|
||||
.subset2(x, 'impl')$.setLabel(label)
|
||||
}
|
||||
|
||||
#' Convert a reactivevalues object to a list
|
||||
#'
|
||||
#' This function does something similar to what you might \code{\link{as.list}}
|
||||
@@ -269,7 +296,8 @@ Observable <- setRefClass(
|
||||
.running = 'logical',
|
||||
.value = 'ANY',
|
||||
.visible = 'logical',
|
||||
.execCount = 'integer'
|
||||
.execCount = 'integer',
|
||||
.mostRecentCtxId = 'character'
|
||||
),
|
||||
methods = list(
|
||||
initialize = function(func, label=deparse(substitute(func))) {
|
||||
@@ -282,6 +310,7 @@ Observable <- setRefClass(
|
||||
.running <<- FALSE
|
||||
.label <<- label
|
||||
.execCount <<- 0L
|
||||
.mostRecentCtxId <<- ""
|
||||
},
|
||||
getValue = function() {
|
||||
.dependents$register()
|
||||
@@ -289,6 +318,8 @@ Observable <- setRefClass(
|
||||
if (.invalidated || .running) {
|
||||
.self$.updateValue()
|
||||
}
|
||||
|
||||
.graphDependsOnId(getCurrentContext()$id, .mostRecentCtxId)
|
||||
|
||||
if (identical(class(.value), 'try-error'))
|
||||
stop(attr(.value, 'condition'))
|
||||
@@ -299,7 +330,8 @@ Observable <- setRefClass(
|
||||
invisible(.value)
|
||||
},
|
||||
.updateValue = function() {
|
||||
ctx <- Context$new(.label)
|
||||
ctx <- Context$new(.label, type='observable', prevId=.mostRecentCtxId)
|
||||
.mostRecentCtxId <<- ctx$id
|
||||
ctx$onInvalidate(function() {
|
||||
.invalidated <<- TRUE
|
||||
.dependents$invalidate()
|
||||
@@ -369,7 +401,7 @@ Observable <- setRefClass(
|
||||
reactive <- function(x, env = parent.frame(), quoted = FALSE, label = NULL) {
|
||||
fun <- exprToFunction(x, env, quoted)
|
||||
if (is.null(label))
|
||||
label <- deparse(body(fun))
|
||||
label <- sprintf('reactive(%s)', paste(deparse(body(fun)), collapse='\n'))
|
||||
|
||||
Observable$new(fun, label)$getValue
|
||||
}
|
||||
@@ -391,28 +423,33 @@ Observer <- setRefClass(
|
||||
fields = list(
|
||||
.func = 'function',
|
||||
.label = 'character',
|
||||
.priority = 'numeric',
|
||||
.invalidateCallbacks = 'list',
|
||||
.execCount = 'integer',
|
||||
.onResume = 'function',
|
||||
.suspended = 'logical'
|
||||
.suspended = 'logical',
|
||||
.prevId = 'character'
|
||||
),
|
||||
methods = list(
|
||||
initialize = function(func, label, suspended = FALSE) {
|
||||
initialize = function(func, label, suspended = FALSE, priority = 0) {
|
||||
if (length(formals(func)) > 0)
|
||||
stop("Can't make an observer from a function that takes parameters; ",
|
||||
"only functions without parameters can be reactive.")
|
||||
|
||||
.func <<- func
|
||||
.label <<- label
|
||||
.priority <<- normalizePriority(priority)
|
||||
.execCount <<- 0L
|
||||
.suspended <<- suspended
|
||||
.onResume <<- function() NULL
|
||||
.prevId <<- ''
|
||||
|
||||
# Defer the first running of this until flushReact is called
|
||||
.createContext()$invalidate()
|
||||
},
|
||||
.createContext = function() {
|
||||
ctx <- Context$new(.label)
|
||||
ctx <- Context$new(.label, type='observer', prevId=.prevId)
|
||||
.prevId <<- ctx$id
|
||||
|
||||
ctx$onInvalidate(function() {
|
||||
lapply(.invalidateCallbacks, function(func) {
|
||||
@@ -421,7 +458,7 @@ Observer <- setRefClass(
|
||||
})
|
||||
|
||||
continue <- function() {
|
||||
ctx$addPendingFlush()
|
||||
ctx$addPendingFlush(.priority)
|
||||
}
|
||||
|
||||
if (.suspended == FALSE)
|
||||
@@ -441,15 +478,24 @@ Observer <- setRefClass(
|
||||
.execCount <<- .execCount + 1L
|
||||
ctx$run(.func)
|
||||
},
|
||||
onInvalidate = function(func) {
|
||||
"Register a function to run when this observer is invalidated"
|
||||
.invalidateCallbacks <<- c(.invalidateCallbacks, func)
|
||||
onInvalidate = function(callback) {
|
||||
"Register a callback function to run when this observer is invalidated.
|
||||
No arguments will be provided to the callback function when it is
|
||||
invoked."
|
||||
.invalidateCallbacks <<- c(.invalidateCallbacks, callback)
|
||||
},
|
||||
setPriority = function(priority = 0) {
|
||||
"Change this observer's priority. Note that if the observer is currently
|
||||
invalidated, then the change in priority will not take effect until the
|
||||
next invalidation--unless the observer is also currently suspended, in
|
||||
which case the priority change will be effective upon resume."
|
||||
.priority <<- normalizePriority(priority)
|
||||
},
|
||||
suspend = function() {
|
||||
"Causes this observer to stop scheduling flushes (re-executions) in
|
||||
response to invalidations. If the observer was invalidated prior to this
|
||||
call but it has not re-executed yet (because it waits until onFlush is
|
||||
called) then that re-execution will still occur, becasue the flush is
|
||||
called) then that re-execution will still occur, because the flush is
|
||||
already scheduled."
|
||||
.suspended <<- TRUE
|
||||
},
|
||||
@@ -469,10 +515,12 @@ Observer <- setRefClass(
|
||||
|
||||
#' Create a reactive observer
|
||||
#'
|
||||
#' Creates an observer from the given expression An observer is like a reactive
|
||||
#' Creates an observer from the given expression.
|
||||
#'
|
||||
#' An observer is like a reactive
|
||||
#' expression in that it can read reactive values and call reactive expressions, and
|
||||
#' will automatically re-execute when those dependencies change. But unlike
|
||||
#' reactive expression, it doesn't yield a result and can't be used as an input
|
||||
#' reactive expressions, it doesn't yield a result and can't be used as an input
|
||||
#' to other reactive expressions. Thus, observers are only useful for their side
|
||||
#' effects (for example, performing I/O).
|
||||
#'
|
||||
@@ -493,6 +541,36 @@ Observer <- setRefClass(
|
||||
#' @param label A label for the observer, useful for debugging.
|
||||
#' @param suspended If \code{TRUE}, start the observer in a suspended state.
|
||||
#' If \code{FALSE} (the default), start in a non-suspended state.
|
||||
#' @param priority An integer or numeric that controls the priority with which
|
||||
#' this observer should be executed. An observer with a given priority level
|
||||
#' will always execute sooner than all observers with a lower priority level.
|
||||
#' Positive, negative, and zero values are allowed.
|
||||
#' @return An observer reference class object. This object has the following
|
||||
#' methods:
|
||||
#' \describe{
|
||||
#' \item{\code{suspend()}}{
|
||||
#' Causes this observer to stop scheduling flushes (re-executions) in
|
||||
#' response to invalidations. If the observer was invalidated prior to
|
||||
#' this call but it has not re-executed yet then that re-execution will
|
||||
#' still occur, because the flush is already scheduled.
|
||||
#' }
|
||||
#' \item{\code{resume()}}{
|
||||
#' Causes this observer to start re-executing in response to
|
||||
#' invalidations. If the observer was invalidated while suspended, then it
|
||||
#' will schedule itself for re-execution.
|
||||
#' }
|
||||
#' \item{\code{setPriority(priority = 0)}}{
|
||||
#' Change this observer's priority. Note that if the observer is currently
|
||||
#' invalidated, then the change in priority will not take effect until the
|
||||
#' next invalidation--unless the observer is also currently suspended, in
|
||||
#' which case the priority change will be effective upon resume.
|
||||
#' }
|
||||
#' \item{\code{onInvalidate(callback)}}{
|
||||
#' Register a callback function to run when this observer is invalidated.
|
||||
#' No arguments will be provided to the callback function when it is
|
||||
#' invoked.
|
||||
#' }
|
||||
#' }
|
||||
#'
|
||||
#' @examples
|
||||
#' values <- reactiveValues(A=1)
|
||||
@@ -514,13 +592,14 @@ Observer <- setRefClass(
|
||||
#'
|
||||
#' @export
|
||||
observe <- function(x, env=parent.frame(), quoted=FALSE, label=NULL,
|
||||
suspended=FALSE) {
|
||||
suspended=FALSE, priority=0) {
|
||||
|
||||
fun <- exprToFunction(x, env, quoted)
|
||||
if (is.null(label))
|
||||
label <- deparse(body(fun))
|
||||
label <- sprintf('observe(%s)', paste(deparse(body(fun)), collapse='\n'))
|
||||
|
||||
invisible(Observer$new(fun, label=label, suspended=suspended))
|
||||
invisible(Observer$new(
|
||||
fun, label=label, suspended=suspended, priority=priority))
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -539,15 +618,58 @@ observe <- function(x, env=parent.frame(), quoted=FALSE, label=NULL,
|
||||
#' See \code{\link{invalidateLater}} as a safer and simpler alternative.
|
||||
#'
|
||||
#' @param intervalMs How often to fire, in milliseconds
|
||||
#' @param session A session object. This is needed to cancel any scheduled
|
||||
#' invalidations after a user has ended the session. If \code{NULL}, then
|
||||
#' this invalidation will not be tied to any session, and so it will still
|
||||
#' occur.
|
||||
#' @return A no-parameter function that can be called from a reactive context,
|
||||
#' in order to cause that context to be invalidated the next time the timer
|
||||
#' interval elapses. Calling the returned function also happens to yield the
|
||||
#' current time (as in \code{\link{Sys.time}}).
|
||||
#' @seealso invalidateLater
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' shinyServer(function(input, output, session) {
|
||||
#'
|
||||
#' # Anything that calls autoInvalidate will automatically invalidate
|
||||
#' # every 2 seconds.
|
||||
#' autoInvalidate <- reactiveTimer(2000, session)
|
||||
#'
|
||||
#' observe({
|
||||
#' # Invalidate and re-execute this reactive expression every time the
|
||||
#' # timer fires.
|
||||
#' autoInvalidate()
|
||||
#'
|
||||
#' # Do something each time this is invalidated.
|
||||
#' # The isolate() makes this observer _not_ get invalidated and re-executed
|
||||
#' # when input$n changes.
|
||||
#' print(paste("The value of input$n is", isolate(input$n)))
|
||||
#' })
|
||||
#'
|
||||
#' # Generate a new histogram each time the timer fires, but not when
|
||||
#' # input$n changes.
|
||||
#' output$plot <- renderPlot({
|
||||
#' autoInvalidate()
|
||||
#' hist(isolate(input$n))
|
||||
#' })
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
reactiveTimer <- function(intervalMs=1000) {
|
||||
reactiveTimer <- function(intervalMs=1000, session) {
|
||||
if (missing(session)) {
|
||||
warning("reactiveTimer should be passed a session object or NULL")
|
||||
session <- NULL
|
||||
}
|
||||
|
||||
dependents <- Map$new()
|
||||
timerCallbacks$schedule(intervalMs, function() {
|
||||
# Quit if the session is closed
|
||||
if (!is.null(session) && session$isClosed()) {
|
||||
return(invisible())
|
||||
}
|
||||
|
||||
timerCallbacks$schedule(intervalMs, sys.function())
|
||||
lapply(
|
||||
dependents$values(),
|
||||
@@ -574,10 +696,48 @@ reactiveTimer <- function(intervalMs=1000) {
|
||||
#' of milliseconds.
|
||||
#' @param millis Approximate milliseconds to wait before invalidating the
|
||||
#' current reactive context.
|
||||
#' @param session A session object. This is needed to cancel any scheduled
|
||||
#' invalidations after a user has ended the session. If \code{NULL}, then
|
||||
#' this invalidation will not be tied to any session, and so it will still
|
||||
#' occur.
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' shinyServer(function(input, output, session) {
|
||||
#'
|
||||
#' observe({
|
||||
#' # Re-execute this reactive expression after 1000 milliseconds
|
||||
#' invalidateLater(1000, session)
|
||||
#'
|
||||
#' # Do something each time this is invalidated.
|
||||
#' # The isolate() makes this observer _not_ get invalidated and re-executed
|
||||
#' # when input$n changes.
|
||||
#' print(paste("The value of input$n is", isolate(input$n)))
|
||||
#' })
|
||||
#'
|
||||
#' # Generate a new histogram at timed intervals, but not when
|
||||
#' # input$n changes.
|
||||
#' output$plot <- renderPlot({
|
||||
#' # Re-execute this reactive expression after 2000 milliseconds
|
||||
#' invalidateLater(2000, session)
|
||||
#' hist(isolate(input$n))
|
||||
#' })
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
invalidateLater <- function(millis) {
|
||||
invalidateLater <- function(millis, session) {
|
||||
if (missing(session)) {
|
||||
warning("invalidateLater should be passed a session object or NULL")
|
||||
session <- NULL
|
||||
}
|
||||
|
||||
ctx <- .getReactiveEnvironment()$currentContext()
|
||||
timerCallbacks$schedule(millis, function() {
|
||||
# Quit if the session is closed
|
||||
if (!is.null(session) && session$isClosed()) {
|
||||
return(invisible())
|
||||
}
|
||||
ctx$invalidate()
|
||||
})
|
||||
invisible()
|
||||
@@ -655,8 +815,9 @@ invalidateLater <- function(millis) {
|
||||
#'
|
||||
#' @export
|
||||
isolate <- function(expr) {
|
||||
ctx <- Context$new('[isolate]')
|
||||
ctx <- Context$new('[isolate]', type='isolate')
|
||||
ctx$run(function() {
|
||||
expr
|
||||
})
|
||||
ctx$invalidate()
|
||||
}
|
||||
|
||||
354
R/shiny.R
@@ -1,3 +1,16 @@
|
||||
#' Web Application Framework for R
|
||||
#'
|
||||
#' Shiny makes it incredibly easy to build interactive web applications with R.
|
||||
#' Automatic "reactive" binding between inputs and outputs and extensive
|
||||
#' pre-built widgets make it possible to build beautiful, responsive, and
|
||||
#' powerful applications with minimal effort.
|
||||
#'
|
||||
#' The Shiny tutorial at \url{http://rstudio.github.com/shiny/tutorial} explains
|
||||
#' the framework in depth, walks you through building a simple application, and
|
||||
#' includes extensive annotated examples.
|
||||
#'
|
||||
#' @name shiny-package
|
||||
#' @aliases shiny
|
||||
#' @docType package
|
||||
#' @import httpuv caTools RJSONIO xtable digest
|
||||
NULL
|
||||
@@ -18,24 +31,31 @@ ShinySession <- setRefClass(
|
||||
.websocket = 'ANY',
|
||||
.invalidatedOutputValues = 'Map',
|
||||
.invalidatedOutputErrors = 'Map',
|
||||
.inputMessageQueue = 'list', # A list of inputMessages to send when flushed
|
||||
.outputs = 'list', # Keeps track of all the output observer objects
|
||||
.outputOptions = 'list', # Options for each of the output observer objects
|
||||
.progressKeys = 'character',
|
||||
.fileUploadContext = 'FileUploadContext',
|
||||
.input = 'ReactiveValues', # Internal object for normal input sent from client
|
||||
.clientData = 'ReactiveValues', # Internal object for other data sent from the client
|
||||
.closedCallbacks = 'Callbacks',
|
||||
input = 'reactivevalues', # Externally-usable S3 wrapper object for .input
|
||||
output = 'ANY', # Externally-usable S3 wrapper object for .outputs
|
||||
clientData = 'reactivevalues', # Externally-usable S3 wrapper object for .clientData
|
||||
token = 'character', # Used to identify this instance in URLs
|
||||
files = 'Map', # For keeping track of files sent to client
|
||||
downloads = 'Map',
|
||||
closed = 'logical'
|
||||
closed = 'logical',
|
||||
session = 'list', # Object for the server app to access session stuff
|
||||
.workerId = 'character'
|
||||
),
|
||||
methods = list(
|
||||
initialize = function(websocket) {
|
||||
initialize = function(websocket, workerId) {
|
||||
.websocket <<- websocket
|
||||
.workerId <<- workerId
|
||||
.invalidatedOutputValues <<- Map$new()
|
||||
.invalidatedOutputErrors <<- Map$new()
|
||||
.inputMessageQueue <<- list()
|
||||
.progressKeys <<- character(0)
|
||||
closed <<- FALSE
|
||||
# TODO: Put file upload context in user/app-specific dir if possible
|
||||
@@ -45,17 +65,51 @@ ShinySession <- setRefClass(
|
||||
.clientData <<- ReactiveValues$new()
|
||||
|
||||
input <<- .createReactiveValues(.input, readonly=TRUE)
|
||||
.setLabel(input, 'input')
|
||||
clientData <<- .createReactiveValues(.clientData, readonly=TRUE)
|
||||
.setLabel(clientData, 'clientData')
|
||||
|
||||
output <<- .createOutputWriter(.self)
|
||||
|
||||
token <<- createUniqueId(16)
|
||||
.outputs <<- list()
|
||||
.outputOptions <<- list()
|
||||
|
||||
session <<- list(clientData = clientData,
|
||||
sendCustomMessage = .self$.sendCustomMessage,
|
||||
sendInputMessage = .self$.sendInputMessage,
|
||||
onSessionEnded = .self$onSessionEnded,
|
||||
isClosed = .self$isClosed,
|
||||
input = .self$input,
|
||||
output = .self$output)
|
||||
|
||||
.write(toJSON(list(config = list(
|
||||
workerId = .workerId,
|
||||
sessionId = token
|
||||
))))
|
||||
},
|
||||
onSessionEnded = function(callback) {
|
||||
"Registers the given callback to be invoked when the session is closed
|
||||
(i.e. the connection to the client has been severed). The return value
|
||||
is a function which unregisters the callback. If multiple callbacks are
|
||||
registered, the order in which they are invoked is not guaranteed."
|
||||
return(.closedCallbacks$register(callback))
|
||||
},
|
||||
close = function() {
|
||||
closed <<- TRUE
|
||||
for (output in .outputs) {
|
||||
output$suspend()
|
||||
}
|
||||
.closedCallbacks$invoke(onError=function(e) {
|
||||
warning(simpleWarning(
|
||||
paste("An error occurred in an onSessionEnded handler:",
|
||||
e$message),
|
||||
e$call
|
||||
))
|
||||
})
|
||||
},
|
||||
isClosed = function() {
|
||||
return(closed)
|
||||
},
|
||||
defineOutput = function(name, func, label) {
|
||||
"Binds an output generating function to this name. The function can either
|
||||
@@ -96,24 +150,26 @@ ShinySession <- setRefClass(
|
||||
}
|
||||
else
|
||||
.invalidatedOutputValues$set(name, value)
|
||||
}, label=label, suspended=TRUE)
|
||||
}, suspended=.shouldSuspend(name), label=sprintf('output$%s <- %s', name, paste(label, collapse='\n')))
|
||||
|
||||
obs$onInvalidate(function() {
|
||||
showProgress(name)
|
||||
})
|
||||
|
||||
.outputs[[name]] <<- obs
|
||||
# Default is to suspend when hidden
|
||||
.outputOptions[[name]][['suspendWhenHidden']] <<- TRUE
|
||||
if (is.null(.outputOptions[[name]]))
|
||||
.outputOptions[[name]] <<- list()
|
||||
}
|
||||
else {
|
||||
stop(paste("Unexpected", class(func), "output for", name))
|
||||
}
|
||||
},
|
||||
flushOutput = function() {
|
||||
|
||||
if (length(.progressKeys) == 0
|
||||
&& length(.invalidatedOutputValues) == 0
|
||||
&& length(.invalidatedOutputErrors) == 0) {
|
||||
&& length(.invalidatedOutputErrors) == 0
|
||||
&& length(.inputMessageQueue) == 0) {
|
||||
return(invisible())
|
||||
}
|
||||
|
||||
@@ -123,9 +179,12 @@ ShinySession <- setRefClass(
|
||||
.invalidatedOutputValues <<- Map$new()
|
||||
errors <- .invalidatedOutputErrors
|
||||
.invalidatedOutputErrors <<- Map$new()
|
||||
inputMessages <- .inputMessageQueue
|
||||
.inputMessageQueue <<- list()
|
||||
|
||||
json <- toJSON(list(errors=as.list(errors),
|
||||
values=as.list(values)))
|
||||
values=as.list(values),
|
||||
inputMessages=inputMessages))
|
||||
|
||||
.write(json)
|
||||
},
|
||||
@@ -178,6 +237,17 @@ ShinySession <- setRefClass(
|
||||
return()
|
||||
.write(toJSON(list(response=list(tag=requestMsg$tag, error=error))))
|
||||
},
|
||||
.sendCustomMessage = function(type, message) {
|
||||
data <- list()
|
||||
data[[type]] <- message
|
||||
.write(toJSON(list(custom=data)))
|
||||
},
|
||||
.sendInputMessage = function(inputId, message) {
|
||||
data <- list(id = inputId, message = message)
|
||||
|
||||
# Add to input message queue
|
||||
.inputMessageQueue[[length(.inputMessageQueue) + 1]] <<- data
|
||||
},
|
||||
.write = function(json) {
|
||||
if (getOption('shiny.trace', FALSE))
|
||||
message('SEND ',
|
||||
@@ -202,7 +272,9 @@ ShinySession <- setRefClass(
|
||||
|
||||
jobId <- .fileUploadContext$createUploadOperation(fileInfos)
|
||||
return(list(jobId=jobId,
|
||||
uploadUrl=paste('session', token, 'upload', jobId, sep='/')))
|
||||
uploadUrl=paste('session', token, 'upload',
|
||||
paste(jobId, "?w=", .workerId,sep=""),
|
||||
sep='/')))
|
||||
},
|
||||
`@uploadEnd` = function(jobId, inputId) {
|
||||
fileData <- .fileUploadContext$getUploadOperation(jobId)$finish()
|
||||
@@ -321,9 +393,10 @@ ShinySession <- setRefClass(
|
||||
"Creates an entry in the file map for the data, and returns a URL pointing
|
||||
to the file."
|
||||
files$set(name, list(data=data, contentType=contentType))
|
||||
return(sprintf('session/%s/file/%s?%s',
|
||||
return(sprintf('session/%s/file/%s?w=%s&r=%s',
|
||||
URLencode(token, TRUE),
|
||||
URLencode(name, TRUE),
|
||||
.workerId,
|
||||
createUniqueId(8)))
|
||||
},
|
||||
# Send a file to the client
|
||||
@@ -336,7 +409,7 @@ ShinySession <- setRefClass(
|
||||
return(NULL)
|
||||
|
||||
fileData <- readBin(file, 'raw', n=bytes)
|
||||
|
||||
|
||||
if (isTRUE(.clientData$.values$allowDataUriScheme)) {
|
||||
b64 <- base64encode(fileData)
|
||||
return(paste('data:', contentType, ';base64,', b64, sep=''))
|
||||
@@ -349,29 +422,42 @@ ShinySession <- setRefClass(
|
||||
downloads$set(name, list(filename = filename,
|
||||
contentType = contentType,
|
||||
func = func))
|
||||
return(sprintf('session/%s/download/%s',
|
||||
return(sprintf('session/%s/download/%s?w=%s',
|
||||
URLencode(token, TRUE),
|
||||
URLencode(name, TRUE)))
|
||||
URLencode(name, TRUE),
|
||||
.workerId))
|
||||
},
|
||||
.getOutputOption = function(outputName, propertyName, defaultValue) {
|
||||
opts <- .outputOptions[[outputName]]
|
||||
if (is.null(opts))
|
||||
return(defaultValue)
|
||||
result <- opts[[propertyName]]
|
||||
if (is.null(result))
|
||||
return(defaultValue)
|
||||
return(result)
|
||||
},
|
||||
.shouldSuspend = function(name) {
|
||||
# Find corresponding hidden state clientData variable, with the format
|
||||
# "output_foo_hidden". (It comes from .clientdata_output_foo_hidden
|
||||
# on the JS side)
|
||||
# Some tricky stuff: instead of accessing names using input$names(),
|
||||
# get the names directly via input$.values, to avoid triggering reactivity.
|
||||
# Need to handle cases where the output object isn't actually used
|
||||
# in the web page; in these cases, there's no output_foo_hidden flag,
|
||||
# and hidden should be TRUE. In other words, NULL and TRUE should map to
|
||||
# TRUE, FALSE should map to FALSE.
|
||||
hidden <- .clientData$.values[[paste("output_", name, "_hidden",
|
||||
sep="")]]
|
||||
if (is.null(hidden)) hidden <- TRUE
|
||||
|
||||
return(hidden && .getOutputOption(name, 'suspendWhenHidden', TRUE))
|
||||
},
|
||||
# This function suspends observers for hidden outputs and resumes observers
|
||||
# for un-hidden outputs.
|
||||
manageHiddenOutputs = function() {
|
||||
# Find hidden state for each output, and suspend/resume accordingly
|
||||
for (outputName in names(.outputs)) {
|
||||
# Find corresponding hidden state clientData variable, with the format
|
||||
# "output_foo_hidden". (It comes from .clientdata_output_foo_hidden
|
||||
# on the JS side)
|
||||
# Some tricky stuff: instead of accessing names using input$names(),
|
||||
# get the names directly via input$.values, to avoid triggering reactivity.
|
||||
# Need to handle cases where the output object isn't actually used
|
||||
# in the web page; in these cases, there's no output_foo_hidden flag,
|
||||
# and hidden should be TRUE. In other words, NULL and TRUE should map to
|
||||
# TRUE, FALSE should map to FALSE.
|
||||
hidden <- .clientData$.values[[paste("output_", outputName, "_hidden",
|
||||
sep="")]]
|
||||
if (is.null(hidden)) hidden <- TRUE
|
||||
|
||||
if (hidden && .outputOptions[[outputName]][['suspendWhenHidden']]) {
|
||||
if (.shouldSuspend(outputName)) {
|
||||
.outputs[[outputName]]$suspend()
|
||||
} else {
|
||||
.outputs[[outputName]]$resume()
|
||||
@@ -407,7 +493,7 @@ ShinySession <- setRefClass(
|
||||
return(.outputOptions[[name]])
|
||||
|
||||
# Set the appropriate option
|
||||
validOpts <- "suspendWhenHidden"
|
||||
validOpts <- c("suspendWhenHidden", "priority")
|
||||
for (optname in names(opts)) {
|
||||
if (! optname %in% validOpts)
|
||||
stop(optname, " is not a valid option")
|
||||
@@ -419,6 +505,10 @@ ShinySession <- setRefClass(
|
||||
if ("suspendWhenHidden" %in% names(opts)) {
|
||||
manageHiddenOutputs()
|
||||
}
|
||||
|
||||
if ("priority" %in% names(opts)) {
|
||||
.outputs[[name]]$setPriority(opts[['priority']])
|
||||
}
|
||||
|
||||
invisible()
|
||||
}
|
||||
@@ -464,6 +554,8 @@ ShinySession <- setRefClass(
|
||||
#' will be suspended (not execute) when it is hidden on the web page. When
|
||||
#' \code{FALSE}, the output object will not suspend when hidden, and if it
|
||||
#' was already hidden and suspended, then it will resume immediately.
|
||||
#' \item priority. The priority level of the output object. Queued outputs
|
||||
#' with higher priority values will execute before those with lower values.
|
||||
#' }
|
||||
#'
|
||||
#' @examples
|
||||
@@ -473,6 +565,9 @@ ShinySession <- setRefClass(
|
||||
#'
|
||||
#' # Disable suspend for output$myplot
|
||||
#' outputOptions(output, "myplot", suspendWhenHidden = FALSE)
|
||||
#'
|
||||
#' # Change priority for output$myplot
|
||||
#' outputOptions(output, "myplot", priority = 10)
|
||||
#'
|
||||
#' # Get the list of options for output$myplot
|
||||
#' outputOptions(output, "myplot")
|
||||
@@ -568,6 +663,20 @@ joinHandlers <- function(handlers) {
|
||||
}
|
||||
}
|
||||
|
||||
reactLogHandler <- function(req) {
|
||||
if (!identical(req$PATH_INFO, '/reactlog'))
|
||||
return(NULL)
|
||||
|
||||
if (!getOption('shiny.reactlog', FALSE)) {
|
||||
return(NULL)
|
||||
}
|
||||
|
||||
return(httpResponse(
|
||||
status=200,
|
||||
content=list(file=renderReactLog(), owned=TRUE)
|
||||
))
|
||||
}
|
||||
|
||||
sessionHandler <- function(req) {
|
||||
path <- req$PATH_INFO
|
||||
if (is.null(path))
|
||||
@@ -765,7 +874,9 @@ resourcePathHandler <- function(req) {
|
||||
#'
|
||||
#' The server function will be called when each client (web browser) first loads
|
||||
#' the Shiny application's page. It must take an \code{input} and an
|
||||
#' \code{output} parameter. Any return value will be ignored.
|
||||
#' \code{output} parameter. Any return value will be ignored. It also takes an
|
||||
#' optional \code{session} parameter, which is used when greater control is
|
||||
#' needed.
|
||||
#'
|
||||
#' See the \href{http://rstudio.github.com/shiny/tutorial/}{tutorial} for more
|
||||
#' on how to write a server function.
|
||||
@@ -774,7 +885,7 @@ resourcePathHandler <- function(req) {
|
||||
#' \dontrun{
|
||||
#' # A very simple Shiny app that takes a message from the user
|
||||
#' # and outputs an uppercase version of it.
|
||||
#' shinyServer(function(input, output) {
|
||||
#' shinyServer(function(input, output, session) {
|
||||
#' output$uppercase <- renderText({
|
||||
#' toupper(input$message)
|
||||
#' })
|
||||
@@ -844,10 +955,7 @@ file.path.ci <- function(dir, name) {
|
||||
|
||||
# Instantiates the app in the current working directory.
|
||||
# port - The TCP port that the application should listen on.
|
||||
startApp <- function(port=8101L) {
|
||||
|
||||
sys.www.root <- system.file('www', package='shiny')
|
||||
|
||||
startAppDir <- function(port=8101L, workerId) {
|
||||
globalR <- file.path.ci(getwd(), 'global.R')
|
||||
uiR <- file.path.ci(getwd(), 'ui.R')
|
||||
serverR <- file.path.ci(getwd(), 'server.R')
|
||||
@@ -862,14 +970,56 @@ startApp <- function(port=8101L) {
|
||||
source(globalR, local=FALSE)
|
||||
|
||||
shinyServer(NULL)
|
||||
serverFileTimestamp <- NULL
|
||||
local({
|
||||
serverFileTimestamp <<- file.info(serverR)$mtime
|
||||
source(serverR, local=new.env(parent=.GlobalEnv))
|
||||
if (is.null(.globals$server))
|
||||
stop("No server was defined in server.R")
|
||||
})
|
||||
serverFunc <- .globals$server
|
||||
serverFileTimestamp <- file.info(serverR)$mtime
|
||||
local(source(serverR, local=new.env(parent=.GlobalEnv)))
|
||||
if (is.null(.globals$server))
|
||||
stop("No server was defined in server.R")
|
||||
|
||||
serverFuncSource <- function() {
|
||||
# Check if server.R has changed, and if so, reload
|
||||
mtime <- file.info(serverR)$mtime
|
||||
if (!identical(mtime, serverFileTimestamp)) {
|
||||
shinyServer(NULL)
|
||||
serverFileTimestamp <<- mtime
|
||||
local(source(serverR, local=new.env(parent=.GlobalEnv)))
|
||||
if (is.null(.globals$server))
|
||||
stop("No server was defined in server.R")
|
||||
}
|
||||
return(.globals$server)
|
||||
}
|
||||
|
||||
startApp(
|
||||
c(dynamicHandler(uiR), wwwDir),
|
||||
serverFuncSource,
|
||||
port,
|
||||
workerId
|
||||
)
|
||||
}
|
||||
|
||||
startAppObj <- function(ui, serverFunc, port, workerId) {
|
||||
uiHandler <- function(req) {
|
||||
if (!identical(req$REQUEST_METHOD, 'GET'))
|
||||
return(NULL)
|
||||
|
||||
if (req$PATH_INFO != '/')
|
||||
return(NULL)
|
||||
|
||||
textConn <- textConnection(NULL, "w")
|
||||
on.exit(close(textConn))
|
||||
|
||||
renderPage(ui, textConn)
|
||||
html <- paste(textConnectionValue(textConn), collapse='\n')
|
||||
return(httpResponse(200, content=html))
|
||||
}
|
||||
|
||||
startApp(uiHandler,
|
||||
function() { serverFunc },
|
||||
port, workerId)
|
||||
}
|
||||
|
||||
startApp <- function(httpHandlers, serverFuncSource, port, workerId) {
|
||||
|
||||
sys.www.root <- system.file('www', package='shiny')
|
||||
|
||||
httpuvCallbacks <- list(
|
||||
onHeaders = function(req) {
|
||||
@@ -895,12 +1045,12 @@ startApp <- function(port=8101L) {
|
||||
}
|
||||
},
|
||||
call = httpServer(c(sessionHandler,
|
||||
dynamicHandler(uiR),
|
||||
wwwDir,
|
||||
httpHandlers,
|
||||
sys.www.root,
|
||||
resourcePathHandler)),
|
||||
resourcePathHandler,
|
||||
reactLogHandler)),
|
||||
onWSOpen = function(ws) {
|
||||
shinysession <- ShinySession$new(ws)
|
||||
shinysession <- ShinySession$new(ws, workerId)
|
||||
appsByToken$set(shinysession$token, shinysession)
|
||||
|
||||
ws$onMessage(function(binary, msg) {
|
||||
@@ -936,6 +1086,11 @@ startApp <- function(port=8101L) {
|
||||
splitName[[2]],
|
||||
matrix = unpackMatrix(val),
|
||||
number = ifelse(is.null(val), NA, val),
|
||||
date = {
|
||||
# First replace NULLs with NA, then convert to Date vector
|
||||
datelist <- ifelse(lapply(val, is.null), NA, val)
|
||||
as.Date(unlist(datelist))
|
||||
},
|
||||
stop('Unknown type specified for ', name)
|
||||
)
|
||||
}
|
||||
@@ -956,18 +1111,7 @@ startApp <- function(port=8101L) {
|
||||
msg$method,
|
||||
init = {
|
||||
|
||||
# Check if server.R has changed, and if so, reload
|
||||
mtime <- file.info(serverR)$mtime
|
||||
if (!identical(mtime, serverFileTimestamp)) {
|
||||
shinyServer(NULL)
|
||||
local({
|
||||
serverFileTimestamp <<- mtime
|
||||
source(serverR, local=new.env(parent=.GlobalEnv))
|
||||
if (is.null(.globals$server))
|
||||
stop("No server was defined in server.R")
|
||||
})
|
||||
serverFunc <<- .globals$server
|
||||
}
|
||||
serverFunc <- serverFuncSource()
|
||||
|
||||
shinysession$manageInputs(msg$data)
|
||||
local({
|
||||
@@ -975,10 +1119,14 @@ startApp <- function(port=8101L) {
|
||||
input=shinysession$input,
|
||||
output=.createOutputWriter(shinysession))
|
||||
|
||||
# The clientData argument is optional; check if it exists
|
||||
# The clientData and session arguments are optional; check if
|
||||
# each exists
|
||||
if ('clientData' %in% names(formals(serverFunc)))
|
||||
args$clientData <- shinysession$clientData
|
||||
|
||||
if ('session' %in% names(formals(serverFunc)))
|
||||
args$session <- shinysession$session
|
||||
|
||||
do.call(serverFunc, args)
|
||||
})
|
||||
},
|
||||
@@ -1002,9 +1150,14 @@ startApp <- function(port=8101L) {
|
||||
}
|
||||
)
|
||||
|
||||
message('\n', 'Listening on port ', port)
|
||||
|
||||
return(startServer("0.0.0.0", port, httpuvCallbacks))
|
||||
if (is.numeric(port) || is.integer(port)) {
|
||||
message('\n', 'Listening on port ', port)
|
||||
return(startServer("0.0.0.0", port, httpuvCallbacks))
|
||||
} else if (is.character(port)) {
|
||||
message('\n', 'Listening on domain socket ', port)
|
||||
mask <- attr(port, 'mask')
|
||||
return(startPipeServer(port, mask, httpuvCallbacks))
|
||||
}
|
||||
}
|
||||
|
||||
# NOTE: we de-roxygenized this comment because the function isn't exported
|
||||
@@ -1048,17 +1201,40 @@ serviceApp <- function(ws_env) {
|
||||
#' @param launch.browser If true, the system's default web browser will be
|
||||
#' launched automatically after the app is started. Defaults to true in
|
||||
#' interactive sessions only.
|
||||
#'
|
||||
#' @param workerId Can generally be ignored. Exists to help some editions of
|
||||
#' Shiny Server Pro route requests to the correct process.
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' # Start app in the current working directory
|
||||
#' runApp()
|
||||
#'
|
||||
#' # Start app in a subdirectory called myapp
|
||||
#' runApp("myapp")
|
||||
#'
|
||||
#'
|
||||
#' # Apps can be run without a server.r and ui.r file
|
||||
#' runApp(list(
|
||||
#' ui = bootstrapPage(
|
||||
#' numericInput('n', 'Number of obs', 100),
|
||||
#' plotOutput('plot')
|
||||
#' ),
|
||||
#' server = function(input, output) {
|
||||
#' output$plot <- renderPlot({ hist(runif(input$n)) })
|
||||
#' }
|
||||
#' ))
|
||||
#' }
|
||||
#' @export
|
||||
runApp <- function(appDir=getwd(),
|
||||
port=8100L,
|
||||
launch.browser=getOption('shiny.launch.browser',
|
||||
interactive())) {
|
||||
interactive()),
|
||||
workerId="") {
|
||||
|
||||
# Make warnings print immediately
|
||||
ops <- options(warn = 1)
|
||||
on.exit(options(ops))
|
||||
|
||||
|
||||
if (nzchar(Sys.getenv('SHINY_PORT'))) {
|
||||
# If SHINY_PORT is set, we're running under Shiny Server. Check the version
|
||||
# to make sure it is compatible. Older versions of Shiny Server don't set
|
||||
@@ -1071,24 +1247,30 @@ runApp <- function(appDir=getwd(),
|
||||
}
|
||||
}
|
||||
|
||||
orig.wd <- getwd()
|
||||
setwd(appDir)
|
||||
on.exit(setwd(orig.wd), add = TRUE)
|
||||
|
||||
require(shiny)
|
||||
|
||||
if (is.character(appDir)) {
|
||||
orig.wd <- getwd()
|
||||
setwd(appDir)
|
||||
on.exit(setwd(orig.wd), add = TRUE)
|
||||
server <- startAppDir(port=port, workerId)
|
||||
} else {
|
||||
server <- startAppObj(appDir$ui, appDir$server, port=port, workerId)
|
||||
}
|
||||
|
||||
server <- startApp(port=port)
|
||||
on.exit({
|
||||
stopServer(server)
|
||||
}, add = TRUE)
|
||||
|
||||
if (launch.browser) {
|
||||
if (launch.browser && !is.character(port)) {
|
||||
appUrl <- paste("http://localhost:", port, sep="")
|
||||
utils::browseURL(appUrl)
|
||||
}
|
||||
|
||||
.globals$retval <- NULL
|
||||
.globals$stopped <- FALSE
|
||||
tryCatch(
|
||||
while (TRUE) {
|
||||
while (!.globals$stopped) {
|
||||
serviceApp()
|
||||
Sys.sleep(0.001)
|
||||
},
|
||||
@@ -1096,6 +1278,23 @@ runApp <- function(appDir=getwd(),
|
||||
timerCallbacks$clear()
|
||||
}
|
||||
)
|
||||
|
||||
return(.globals$retval)
|
||||
}
|
||||
|
||||
#' Stop the currently running Shiny app
|
||||
#'
|
||||
#' Stops the currently running Shiny app, returning control to the caller of
|
||||
#' \code{\link{runApp}}.
|
||||
#'
|
||||
#' @param returnValue The value that should be returned from
|
||||
#' \code{\link{runApp}}.
|
||||
#'
|
||||
#' @export
|
||||
stopApp <- function(returnValue = NULL) {
|
||||
.globals$retval <- returnValue
|
||||
.globals$stopped <- TRUE
|
||||
httpuv::interrupt()
|
||||
}
|
||||
|
||||
#' Run Shiny Example Applications
|
||||
@@ -1109,7 +1308,18 @@ runApp <- function(appDir=getwd(),
|
||||
#' @param launch.browser If true, the system's default web browser will be
|
||||
#' launched automatically after the app is started. Defaults to true in
|
||||
#' interactive sessions only.
|
||||
#'
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' # List all available examples
|
||||
#' runExample()
|
||||
#'
|
||||
#' # Run one of the examples
|
||||
#' runExample("01_hello")
|
||||
#'
|
||||
#' # Print the directory containing the code for all examples
|
||||
#' system.file("examples", package="shiny")
|
||||
#' }
|
||||
#' @export
|
||||
runExample <- function(example=NA,
|
||||
port=8100L,
|
||||
|
||||
47
R/shinyui.R
@@ -47,6 +47,28 @@ strong <- function(...) tags$strong(...)
|
||||
#' @export
|
||||
em <- function(...) tags$em(...)
|
||||
|
||||
#' 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
|
||||
#' @export
|
||||
includeHTML <- function(path) {
|
||||
dependsOnFile(path)
|
||||
@@ -54,13 +76,15 @@ includeHTML <- function(path) {
|
||||
return(HTML(paste(lines, collapse='\r\n')))
|
||||
}
|
||||
|
||||
#' @rdname include
|
||||
#' @export
|
||||
includeText <- function(path) {
|
||||
dependsOnFile(path)
|
||||
lines <- readLines(path, warn=FALSE, encoding='UTF-8')
|
||||
return(HTML(paste(lines, collapse='\r\n')))
|
||||
return(paste(lines, collapse='\r\n'))
|
||||
}
|
||||
|
||||
#' @rdname include
|
||||
#' @export
|
||||
includeMarkdown <- function(path) {
|
||||
if (!require(markdown))
|
||||
@@ -72,6 +96,27 @@ includeMarkdown <- function(path) {
|
||||
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')), ...))
|
||||
}
|
||||
|
||||
|
||||
#' Include Content Only Once
|
||||
#'
|
||||
|
||||
@@ -11,6 +11,9 @@ suppressPackageStartupMessages({
|
||||
#' The corresponding HTML output tag should be \code{div} or \code{img} and have
|
||||
#' the CSS class name \code{shiny-plot-output}.
|
||||
#'
|
||||
#' @seealso For more details on how the plots are generated, and how to control
|
||||
#' the output, see \code{\link{plotPNG}}.
|
||||
#'
|
||||
#' @param expr An expression that generates a plot.
|
||||
#' @param width The width of the rendered plot, in pixels; or \code{'auto'} to
|
||||
#' use the \code{offsetWidth} of the HTML element that is bound to this plot.
|
||||
@@ -109,6 +112,9 @@ renderPlot <- function(expr, width='auto', height='auto', res=72, ...,
|
||||
#' The corresponding HTML output tag should be \code{div} or \code{img} and have
|
||||
#' the CSS class name \code{shiny-image-output}.
|
||||
#'
|
||||
#' @seealso For more details on how the images are generated, and how to control
|
||||
#' the output, see \code{\link{plotPNG}}.
|
||||
#'
|
||||
#' @param expr An expression that returns a list.
|
||||
#' @param env The environment in which to evaluate \code{expr}.
|
||||
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
|
||||
|
||||
@@ -43,7 +43,7 @@ slider <- function(inputId, min, max, value, step = NULL, ...,
|
||||
|
||||
# validate numeric inputs
|
||||
if (!is.numeric(value) || !is.numeric(min) || !is.numeric(max))
|
||||
stop("min, max, amd value must all be numeric values")
|
||||
stop("min, max, and value must all be numeric values")
|
||||
else if (min(value) < min)
|
||||
stop(paste("slider initial value", value,
|
||||
"is less than the specified minimum"))
|
||||
|
||||
372
R/tags.R
@@ -94,70 +94,41 @@ tagAppendChild <- function(tag, child) {
|
||||
tag
|
||||
}
|
||||
|
||||
#' @export
|
||||
tagAppendChildren <- function(tag, ..., list = NULL) {
|
||||
tag$children <- c(tag$children, c(list(...), list))
|
||||
tag
|
||||
}
|
||||
|
||||
#' @export
|
||||
tagSetChildren <- function(tag, ..., list = NULL) {
|
||||
tag$children <- c(list(...), list)
|
||||
tag
|
||||
}
|
||||
|
||||
#' @export
|
||||
tag <- function(`_tag_name`, varArgs) {
|
||||
|
||||
# create basic tag data structure
|
||||
tag <- list()
|
||||
class(tag) <- "shiny.tag"
|
||||
tag$name <- `_tag_name`
|
||||
tag$attribs <- list()
|
||||
tag$children <- list()
|
||||
|
||||
# process 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])
|
||||
|
||||
if (length(varArgsNames) > 0) {
|
||||
for (i in 1:length(varArgsNames)) {
|
||||
# save name and value
|
||||
name <- varArgsNames[[i]]
|
||||
value <- varArgs[[i]]
|
||||
|
||||
# process attribs
|
||||
if (nzchar(name))
|
||||
tag$attribs[[name]] <- value
|
||||
|
||||
# process child tags
|
||||
else if (isTag(value)) {
|
||||
tag$children[[length(tag$children)+1]] <- value
|
||||
}
|
||||
|
||||
# recursively process lists of children
|
||||
else if (is.list(value)) {
|
||||
|
||||
tagAppendChildren <- function(tag, children) {
|
||||
for(child in children) {
|
||||
if (isTag(child))
|
||||
tag <- tagAppendChild(tag, child)
|
||||
else if (is.list(child))
|
||||
tag <- tagAppendChildren(tag, child)
|
||||
else if (is.character(child))
|
||||
tag <- tagAppendChild(tag, child)
|
||||
else
|
||||
tag <- tagAppendChild(tag, as.character(child))
|
||||
}
|
||||
return (tag)
|
||||
}
|
||||
|
||||
tag <- tagAppendChildren(tag, value)
|
||||
}
|
||||
|
||||
# add text
|
||||
else if (is.character(value)) {
|
||||
tag <- tagAppendChild(tag, value)
|
||||
}
|
||||
|
||||
# everything else treated as text
|
||||
else {
|
||||
tag <- tagAppendChild(tag, as.character(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# return the tag
|
||||
return (tag)
|
||||
# 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 <- flattenTags(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, context = NULL, eol = "\n") {
|
||||
@@ -231,117 +202,118 @@ tagWrite <- function(tag, textWriter, indent=0, context = NULL, eol = "\n") {
|
||||
|
||||
# environment used to store all available tags
|
||||
#' @export
|
||||
tags <- new.env()
|
||||
tags$a <- function(...) tag("a", list(...))
|
||||
tags$abbr <- function(...) tag("abbr", list(...))
|
||||
tags$address <- function(...) tag("address", list(...))
|
||||
tags$area <- function(...) tag("area", list(...))
|
||||
tags$article <- function(...) tag("article", list(...))
|
||||
tags$aside <- function(...) tag("aside", list(...))
|
||||
tags$audio <- function(...) tag("audio", list(...))
|
||||
tags$b <- function(...) tag("b", list(...))
|
||||
tags$base <- function(...) tag("base", list(...))
|
||||
tags$bdi <- function(...) tag("bdi", list(...))
|
||||
tags$bdo <- function(...) tag("bdo", list(...))
|
||||
tags$blockquote <- function(...) tag("blockquote", list(...))
|
||||
tags$body <- function(...) tag("body", list(...))
|
||||
tags$br <- function(...) tag("br", list(...))
|
||||
tags$button <- function(...) tag("button", list(...))
|
||||
tags$canvas <- function(...) tag("canvas", list(...))
|
||||
tags$caption <- function(...) tag("caption", list(...))
|
||||
tags$cite <- function(...) tag("cite", list(...))
|
||||
tags$code <- function(...) tag("code", list(...))
|
||||
tags$col <- function(...) tag("col", list(...))
|
||||
tags$colgroup <- function(...) tag("colgroup", list(...))
|
||||
tags$command <- function(...) tag("command", list(...))
|
||||
tags$data <- function(...) tag("data", list(...))
|
||||
tags$datalist <- function(...) tag("datalist", list(...))
|
||||
tags$dd <- function(...) tag("dd", list(...))
|
||||
tags$del <- function(...) tag("del", list(...))
|
||||
tags$details <- function(...) tag("details", list(...))
|
||||
tags$dfn <- function(...) tag("dfn", list(...))
|
||||
tags$div <- function(...) tag("div", list(...))
|
||||
tags$dl <- function(...) tag("dl", list(...))
|
||||
tags$dt <- function(...) tag("dt", list(...))
|
||||
tags$em <- function(...) tag("em", list(...))
|
||||
tags$embed <- function(...) tag("embed", list(...))
|
||||
tags$eventsource <- function(...) tag("eventsource", list(...))
|
||||
tags$fieldset <- function(...) tag("fieldset", list(...))
|
||||
tags$figcaption <- function(...) tag("figcaption", list(...))
|
||||
tags$figure <- function(...) tag("figure", list(...))
|
||||
tags$footer <- function(...) tag("footer", list(...))
|
||||
tags$form <- function(...) tag("form", list(...))
|
||||
tags$h1 <- function(...) tag("h1", list(...))
|
||||
tags$h2 <- function(...) tag("h2", list(...))
|
||||
tags$h3 <- function(...) tag("h3", list(...))
|
||||
tags$h4 <- function(...) tag("h4", list(...))
|
||||
tags$h5 <- function(...) tag("h5", list(...))
|
||||
tags$h6 <- function(...) tag("h6", list(...))
|
||||
tags$head <- function(...) tag("head", list(...))
|
||||
tags$header <- function(...) tag("header", list(...))
|
||||
tags$hgroup <- function(...) tag("hgroup", list(...))
|
||||
tags$hr <- function(...) tag("hr", list(...))
|
||||
tags$html <- function(...) tag("html", list(...))
|
||||
tags$i <- function(...) tag("i", list(...))
|
||||
tags$iframe <- function(...) tag("iframe", list(...))
|
||||
tags$img <- function(...) tag("img", list(...))
|
||||
tags$input <- function(...) tag("input", list(...))
|
||||
tags$ins <- function(...) tag("ins", list(...))
|
||||
tags$kbd <- function(...) tag("kbd", list(...))
|
||||
tags$keygen <- function(...) tag("keygen", list(...))
|
||||
tags$label <- function(...) tag("label", list(...))
|
||||
tags$legend <- function(...) tag("legend", list(...))
|
||||
tags$li <- function(...) tag("li", list(...))
|
||||
tags$link <- function(...) tag("link", list(...))
|
||||
tags$mark <- function(...) tag("mark", list(...))
|
||||
tags$map <- function(...) tag("map", list(...))
|
||||
tags$menu <- function(...) tag("menu", list(...))
|
||||
tags$meta <- function(...) tag("meta", list(...))
|
||||
tags$meter <- function(...) tag("meter", list(...))
|
||||
tags$nav <- function(...) tag("nav", list(...))
|
||||
tags$noscript <- function(...) tag("noscript", list(...))
|
||||
tags$object <- function(...) tag("object", list(...))
|
||||
tags$ol <- function(...) tag("ol", list(...))
|
||||
tags$optgroup <- function(...) tag("optgroup", list(...))
|
||||
tags$option <- function(...) tag("option", list(...))
|
||||
tags$output <- function(...) tag("output", list(...))
|
||||
tags$p <- function(...) tag("p", list(...))
|
||||
tags$param <- function(...) tag("param", list(...))
|
||||
tags$pre <- function(...) tag("pre", list(...))
|
||||
tags$progress <- function(...) tag("progress", list(...))
|
||||
tags$q <- function(...) tag("q", list(...))
|
||||
tags$ruby <- function(...) tag("ruby", list(...))
|
||||
tags$rp <- function(...) tag("rp", list(...))
|
||||
tags$rt <- function(...) tag("rt", list(...))
|
||||
tags$s <- function(...) tag("s", list(...))
|
||||
tags$samp <- function(...) tag("samp", list(...))
|
||||
tags$script <- function(...) tag("script", list(...))
|
||||
tags$section <- function(...) tag("section", list(...))
|
||||
tags$select <- function(...) tag("select", list(...))
|
||||
tags$small <- function(...) tag("small", list(...))
|
||||
tags$source <- function(...) tag("source", list(...))
|
||||
tags$span <- function(...) tag("span", list(...))
|
||||
tags$strong <- function(...) tag("strong", list(...))
|
||||
tags$style <- function(...) tag("style", list(...))
|
||||
tags$sub <- function(...) tag("sub", list(...))
|
||||
tags$summary <- function(...) tag("summary", list(...))
|
||||
tags$sup <- function(...) tag("sup", list(...))
|
||||
tags$table <- function(...) tag("table", list(...))
|
||||
tags$tbody <- function(...) tag("tbody", list(...))
|
||||
tags$td <- function(...) tag("td", list(...))
|
||||
tags$textarea <- function(...) tag("textarea", list(...))
|
||||
tags$tfoot <- function(...) tag("tfoot", list(...))
|
||||
tags$th <- function(...) tag("th", list(...))
|
||||
tags$thead <- function(...) tag("thead", list(...))
|
||||
tags$time <- function(...) tag("time", list(...))
|
||||
tags$title <- function(...) tag("title", list(...))
|
||||
tags$tr <- function(...) tag("tr", list(...))
|
||||
tags$track <- function(...) tag("track", list(...))
|
||||
tags$u <- function(...) tag("u", list(...))
|
||||
tags$ul <- function(...) tag("ul", list(...))
|
||||
tags$var <- function(...) tag("var", list(...))
|
||||
tags$video <- function(...) tag("video", list(...))
|
||||
tags$wbr <- function(...) tag("wbr", list(...))
|
||||
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
|
||||
#'
|
||||
@@ -365,4 +337,66 @@ HTML <- function(text, ...) {
|
||||
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 {
|
||||
# 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))
|
||||
}
|
||||
}
|
||||
|
||||
405
R/update-input.R
Normal file
@@ -0,0 +1,405 @@
|
||||
#' Change the value of a text input on the client
|
||||
#'
|
||||
#' @template update-input
|
||||
#' @param value The value to set for the input object.
|
||||
#'
|
||||
#' @seealso \code{\link{textInput}}
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' shinyServer(function(input, output, session) {
|
||||
#'
|
||||
#' observe({
|
||||
#' # We'll use the input$controller variable multiple times, so save it as x
|
||||
#' # for convenience.
|
||||
#' x <- input$controller
|
||||
#'
|
||||
#' # This will change the value of input$inText, based on x
|
||||
#' updateTextInput(session, "inText", value = paste("New text", x))
|
||||
#'
|
||||
#' # Can also set the label, this time for input$inText2
|
||||
#' updateTextInput(session, "inText2",
|
||||
#' label = paste("New label", x),
|
||||
#' value = paste("New text", x))
|
||||
#' })
|
||||
#' })
|
||||
#' }
|
||||
#' @export
|
||||
updateTextInput <- function(session, inputId, label = NULL, value = NULL) {
|
||||
message <- dropNulls(list(label=label, value=value))
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
|
||||
|
||||
#' Change the value of a checkbox input on the client
|
||||
#'
|
||||
#' @template update-input
|
||||
#' @param value The value to set for the input object.
|
||||
#'
|
||||
#' @seealso \code{\link{checkboxInput}}
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' shinyServer(function(input, output, session) {
|
||||
#'
|
||||
#' observe({
|
||||
#' # TRUE if input$controller is even, FALSE otherwise.
|
||||
#' x_even <- input$controller %% 2 == 0
|
||||
#'
|
||||
#' updateCheckboxInput(session, "inCheckbox", value = x_even)
|
||||
#' })
|
||||
#' })
|
||||
#' }
|
||||
#' @export
|
||||
updateCheckboxInput <- updateTextInput
|
||||
|
||||
|
||||
#' Change the value of a slider input on the client
|
||||
#'
|
||||
#' @template update-input
|
||||
#' @param value The value to set for the input object.
|
||||
#'
|
||||
#' @seealso \code{\link{sliderInput}}
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' shinyServer(function(input, output, session) {
|
||||
#'
|
||||
#' observe({
|
||||
#' # We'll use the input$controller variable multiple times, so save it as x
|
||||
#' # for convenience.
|
||||
#' x <- input$controller
|
||||
#'
|
||||
#' # Similar to number and text. only label and value can be set for slider
|
||||
#' updateSliderInput(session, "inSlider",
|
||||
#' label = paste("Slider label", x),
|
||||
#' value = x)
|
||||
#'
|
||||
#' # For sliders that pick out a range, pass in a vector of 2 values.
|
||||
#' updateSliderInput(session, "inSlider2", value = c(x-1, x+1))
|
||||
#'
|
||||
#' # An NA means to not change that value (the low or high one)
|
||||
#' updateSliderInput(session, "inSlider3", value = c(NA, x+2))
|
||||
#' })
|
||||
#' })
|
||||
#' }
|
||||
#' @export
|
||||
updateSliderInput <- updateTextInput
|
||||
|
||||
#' Change the value of a date input on the client
|
||||
#'
|
||||
#' @template update-input
|
||||
#' @param value The desired date value. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format.
|
||||
#' @param min The minimum allowed date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format.
|
||||
#' @param max The maximum allowed date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format.
|
||||
#'
|
||||
#' @seealso \code{\link{dateInput}}
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' shinyServer(function(input, output, session) {
|
||||
#'
|
||||
#' observe({
|
||||
#' # We'll use the input$controller variable multiple times, so save it as x
|
||||
#' # for convenience.
|
||||
#' x <- input$controller
|
||||
#'
|
||||
#' updateDateInput(session, "inDate",
|
||||
#' label = paste("Date label", x),
|
||||
#' value = paste("2013-04-", x, sep=""),
|
||||
#' min = paste("2013-04-", x-1, sep=""),
|
||||
#' max = paste("2013-04-", x+1, sep="")
|
||||
#' )
|
||||
#' })
|
||||
#' })
|
||||
#' }
|
||||
#' @export
|
||||
updateDateInput <- function(session, inputId, label = NULL, value = NULL,
|
||||
min = NULL, max = NULL) {
|
||||
|
||||
# If value is a date object, convert it to a string with yyyy-mm-dd format
|
||||
# Same for min and max
|
||||
if (inherits(value, "Date")) value <- format(value, "%Y-%m-%d")
|
||||
if (inherits(min, "Date")) min <- format(min, "%Y-%m-%d")
|
||||
if (inherits(max, "Date")) max <- format(max, "%Y-%m-%d")
|
||||
|
||||
message <- dropNulls(list(label=label, value=value, min=min, max=max))
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
|
||||
|
||||
#' Change the start and end values of a date range input on the client
|
||||
#'
|
||||
#' @template update-input
|
||||
#' @param start The start date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format.
|
||||
#' @param end The end date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format.
|
||||
#' @param min The minimum allowed date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format.
|
||||
#' @param max The maximum allowed date. Either a Date object, or a string in
|
||||
#' \code{yyyy-mm-dd} format.
|
||||
#'
|
||||
#' @seealso \code{\link{dateRangeInput}}
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' shinyServer(function(input, output, session) {
|
||||
#'
|
||||
#' observe({
|
||||
#' # We'll use the input$controller variable multiple times, so save it as x
|
||||
#' # for convenience.
|
||||
#' x <- input$controller
|
||||
#'
|
||||
#' updateDateRangeInput(session, "inDateRange",
|
||||
#' label = paste("Date range label", x),
|
||||
#' start = paste("2013-01-", x, sep=""))
|
||||
#' end = paste("2013-12-", x, sep=""))
|
||||
#' })
|
||||
#' })
|
||||
#' }
|
||||
#' @export
|
||||
updateDateRangeInput <- function(session, inputId, label = NULL,
|
||||
start = NULL, end = NULL, min = NULL, max = NULL) {
|
||||
|
||||
# Make sure start and end are strings, not date objects. This is for
|
||||
# consistency across different locales.
|
||||
if (inherits(start, "Date")) start <- format(start, '%Y-%m-%d')
|
||||
if (inherits(end, "Date")) end <- format(end, '%Y-%m-%d')
|
||||
if (inherits(min, "Date")) min <- format(min, '%Y-%m-%d')
|
||||
if (inherits(max, "Date")) max <- format(max, '%Y-%m-%d')
|
||||
|
||||
message <- dropNulls(list(
|
||||
label = label,
|
||||
value = c(start, end),
|
||||
min = min,
|
||||
max = max
|
||||
))
|
||||
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
|
||||
#' Change the selected tab on the client
|
||||
#'
|
||||
#' @param session The \code{session} object passed to function given to
|
||||
#' \code{shinyServer}.
|
||||
#' @param inputId The id of the tabset panel object.
|
||||
#' @param selected The name of the tab to make active.
|
||||
#'
|
||||
#' @seealso \code{\link{tabsetPanel}}
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' shinyServer(function(input, output, session) {
|
||||
#'
|
||||
#' observe({
|
||||
#' # TRUE if input$controller is even, FALSE otherwise.
|
||||
#' x_even <- input$controller %% 2 == 0
|
||||
#'
|
||||
#' # Change the selected tab.
|
||||
#' # Note that the tabsetPanel must have been created with an 'id' argument
|
||||
#' if (x_even) {
|
||||
#' updateTabsetPanel(session, "inTabset", selected = "panel2")
|
||||
#' } else {
|
||||
#' updateTabsetPanel(session, "inTabset", selected = "panel1")
|
||||
#' }
|
||||
#' })
|
||||
#' })
|
||||
#' }
|
||||
#' @export
|
||||
updateTabsetPanel <- function(session, inputId, selected = NULL) {
|
||||
message <- dropNulls(list(value = selected))
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
|
||||
|
||||
#' Change the value of a number input on the client
|
||||
#'
|
||||
#' @template update-input
|
||||
#' @param value The value to set for the input object.
|
||||
#' @param min Minimum value.
|
||||
#' @param max Maximum value.
|
||||
#' @param step Step size.
|
||||
#'
|
||||
#' @seealso \code{\link{numericInput}}
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' shinyServer(function(input, output, session) {
|
||||
#'
|
||||
#' observe({
|
||||
#' # We'll use the input$controller variable multiple times, so save it as x
|
||||
#' # for convenience.
|
||||
#' x <- input$controller
|
||||
#'
|
||||
#' updateNumericInput(session, "inNumber", value = x)
|
||||
#'
|
||||
#' updateNumericInput(session, "inNumber2",
|
||||
#' label = paste("Number label ", x),
|
||||
#' value = x, min = x-10, max = x+10, step = 5)
|
||||
#' })
|
||||
#' })
|
||||
#' }
|
||||
#' @export
|
||||
updateNumericInput <- function(session, inputId, label = NULL, value = NULL,
|
||||
min = NULL, max = NULL, step = NULL) {
|
||||
|
||||
message <- dropNulls(list(label=label, value=value, min=min, max=max, step=step))
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
|
||||
|
||||
#' Change the value of a checkbox group input on the client
|
||||
#'
|
||||
#' @template update-input
|
||||
#' @param choices A named vector or named list of options. For each item, the
|
||||
#' name will be used as the label, and the value will be used as the value.
|
||||
#' @param selected A vector or list of options which will be selected.
|
||||
#'
|
||||
#' @seealso \code{\link{checkboxGroupInput}}
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' shinyServer(function(input, output, session) {
|
||||
#'
|
||||
#' observe({
|
||||
#' # We'll use the input$controller variable multiple times, so save it as x
|
||||
#' # for convenience.
|
||||
#' x <- input$controller
|
||||
#'
|
||||
#' # Create a list of new options, where the name of the items is something
|
||||
#' # like 'option label x 1', and the values are 'option-x-1'.
|
||||
#' cb_options <- list()
|
||||
#' cb_options[[sprintf("option label %d 1", x)]] <- sprintf("option-%d-1", x)
|
||||
#' cb_options[[sprintf("option label %d 2", x)]] <- sprintf("option-%d-2", x)
|
||||
#'
|
||||
#' # Change values for input$inCheckboxGroup
|
||||
#' updateCheckboxGroupInput(session, "inCheckboxGroup", choices = cb_options)
|
||||
#'
|
||||
#' # Can also set the label and select items
|
||||
#' updateCheckboxGroupInput(session, "inCheckboxGroup2",
|
||||
#' label = paste("checkboxgroup label", x),
|
||||
#' choices = cb_options,
|
||||
#' selected = sprintf("option label %d 2", x)
|
||||
#' )
|
||||
#' })
|
||||
#' })
|
||||
#' }
|
||||
#' @export
|
||||
updateCheckboxGroupInput <- function(session, inputId, label = NULL,
|
||||
choices = NULL, selected = NULL) {
|
||||
|
||||
choices <- choicesWithNames(choices)
|
||||
|
||||
options <- mapply(choices, names(choices),
|
||||
SIMPLIFY = FALSE, USE.NAMES = FALSE,
|
||||
FUN = function(value, name) {
|
||||
list(value = value,
|
||||
label = name,
|
||||
checked = name %in% selected)
|
||||
}
|
||||
)
|
||||
|
||||
message <- dropNulls(list(label = label, options = options))
|
||||
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
|
||||
|
||||
#' Change the value of a radio input on the client
|
||||
#'
|
||||
#' @template update-input
|
||||
#' @param choices A named vector or named list of options. For each item, the
|
||||
#' name will be used as the label, and the value will be used as the value.
|
||||
#' @param selected A vector or list of options which will be selected.
|
||||
#'
|
||||
#' @seealso \code{\link{radioButtons}}
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' shinyServer(function(input, output, session) {
|
||||
#'
|
||||
#' observe({
|
||||
#' # We'll use the input$controller variable multiple times, so save it as x
|
||||
#' # for convenience.
|
||||
#' x <- input$controller
|
||||
#'
|
||||
#' r_options <- list()
|
||||
#' r_options[[sprintf("option label %d 1", x)]] <- sprintf("option-%d-1", x)
|
||||
#' r_options[[sprintf("option label %d 2", x)]] <- sprintf("option-%d-2", x)
|
||||
#'
|
||||
#' # Change values for input$inRadio
|
||||
#' updateRadioButtons(session, "inRadio", choices = r_options)
|
||||
#'
|
||||
#' # Can also set the label and select an item
|
||||
#' updateRadioButtons(session, "inRadio2",
|
||||
#' label = paste("Radio label", x),
|
||||
#' choices = r_options,
|
||||
#' selected = sprintf("option label %d 2", x)
|
||||
#' )
|
||||
#' })
|
||||
#' })
|
||||
#' }
|
||||
#' @export
|
||||
updateRadioButtons <- updateCheckboxGroupInput
|
||||
|
||||
|
||||
#' Change the value of a select input on the client
|
||||
#'
|
||||
#' @template update-input
|
||||
#' @param choices A named vector or named list of options. For each item, the
|
||||
#' name will be used as the label, and the value will be used as the value.
|
||||
#' @param selected A vector or list of options which will be selected.
|
||||
#'
|
||||
#' @seealso \code{\link{selectInput}}
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' shinyServer(function(input, output, session) {
|
||||
#'
|
||||
#' observe({
|
||||
#' # We'll use the input$controller variable multiple times, so save it as x
|
||||
#' # for convenience.
|
||||
#' x <- input$controller
|
||||
#'
|
||||
#' # Create a list of new options, where the name of the items is something
|
||||
#' # like 'option label x 1', and the values are 'option-x-1'.
|
||||
#' s_options <- list()
|
||||
#' s_options[[sprintf("option label %d 1", x)]] <- sprintf("option-%d-1", x)
|
||||
#' s_options[[sprintf("option label %d 2", x)]] <- sprintf("option-%d-2", x)
|
||||
#'
|
||||
#' # Change values for input$inSelect
|
||||
#' updateSelectInput(session, "inSelect", choices = s_options)
|
||||
#'
|
||||
#' # Can also set the label and select an item (or more than one if it's a
|
||||
#' # multi-select)
|
||||
#' updateSelectInput(session, "inSelect2",
|
||||
#' label = paste("Select label", x),
|
||||
#' choices = s_options,
|
||||
#' selected = sprintf("option label %d 2", x)
|
||||
#' )
|
||||
#' })
|
||||
#' })
|
||||
#' }
|
||||
#' @export
|
||||
updateSelectInput <- function(session, inputId, label = NULL, choices = NULL,
|
||||
selected = NULL) {
|
||||
|
||||
choices <- choicesWithNames(choices)
|
||||
|
||||
options <- mapply(choices, names(choices),
|
||||
SIMPLIFY = FALSE, USE.NAMES = FALSE,
|
||||
FUN = function(value, name) {
|
||||
list(value = value,
|
||||
label = name,
|
||||
selected = name %in% selected)
|
||||
}
|
||||
)
|
||||
|
||||
message <- dropNulls(list(label = label, options = options))
|
||||
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
42
R/utils.R
@@ -55,6 +55,11 @@ repeatable <- function(rngfunc, seed = runif(1, 0, .Machine$integer.max)) {
|
||||
paste(x, y, sep='')
|
||||
}
|
||||
|
||||
# Given a vector or list, drop all the NULL items in it
|
||||
dropNulls <- function(x) {
|
||||
x[!vapply(x, is.null, FUN.VALUE=logical(1))]
|
||||
}
|
||||
|
||||
knownContentTypes <- Map$new()
|
||||
knownContentTypes$mset(
|
||||
html='text/html; charset=UTF-8',
|
||||
@@ -266,3 +271,40 @@ shinyDeprecated <- function(new=NULL, msg=NULL,
|
||||
# Similar to .Deprecated(), but print a message instead of warning
|
||||
message(msg)
|
||||
}
|
||||
|
||||
Callbacks <- setRefClass(
|
||||
'Callbacks',
|
||||
fields = list(
|
||||
.nextId = 'integer',
|
||||
.callbacks = 'Map'
|
||||
),
|
||||
methods = list(
|
||||
initialize = function() {
|
||||
.nextId <<- as.integer(.Machine$integer.max)
|
||||
},
|
||||
register = function(callback) {
|
||||
id <- as.character(.nextId)
|
||||
.nextId <<- .nextId - 1L
|
||||
.callbacks$set(id, callback)
|
||||
return(function() {
|
||||
.callbacks$remove(id)
|
||||
})
|
||||
},
|
||||
invoke = function(..., onError=NULL) {
|
||||
for (callback in .callbacks$values()) {
|
||||
tryCatch(
|
||||
do.call(callback, list(...)),
|
||||
error = function(e) {
|
||||
if (is.null(onError))
|
||||
stop(e)
|
||||
else
|
||||
onError(e)
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
count = function() {
|
||||
.callbacks$size()
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -4,6 +4,7 @@ these components are included below):
|
||||
|
||||
- jQuery
|
||||
- Bootstrap
|
||||
- bootstrap-datepicker, from https://github.com/eternicode/bootstrap-datepicker
|
||||
- jslider
|
||||
|
||||
|
||||
@@ -33,7 +34,7 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
Bootstrap License
|
||||
Bootstrap and bootstrap-datepicker License
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Apache License
|
||||
|
||||
95
inst/tests-js/SpecRunner.html
Normal file
@@ -0,0 +1,95 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Jasmine Spec Runner</title>
|
||||
|
||||
<link rel="shortcut icon" type="image/png" href="lib/jasmine-1.3.1/jasmine_favicon.png">
|
||||
<link rel="stylesheet" type="text/css" href="lib/jasmine-1.3.1/jasmine.css">
|
||||
<script type="text/javascript" src="lib/jasmine-1.3.1/jasmine.js"></script>
|
||||
<script type="text/javascript" src="lib/jasmine-1.3.1/jasmine-html.js"></script>
|
||||
|
||||
<!-- include source files here... -->
|
||||
|
||||
<!-- All of these includes are copied out of the HTML file generated by
|
||||
shinyUI() -->
|
||||
<script src="../www/shared/jquery.js" type="text/javascript"></script>
|
||||
<script src="../www/shared/shiny.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../www/shared/shiny.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="../www/shared/slider/css/jquery.slider.min.css"/>
|
||||
<script src="../www/shared/slider/js/jquery.slider.min.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../www/shared/bootstrap/css/bootstrap.min.css"/>
|
||||
<script src="../www/shared/bootstrap/js/bootstrap.min.js"></script>
|
||||
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"/> -->
|
||||
<link rel="stylesheet" type="text/css" href="../www/shared/bootstrap/css/bootstrap-responsive.min.css"/>
|
||||
|
||||
<script src="../www/shared/datepicker/js/bootstrap-datepicker.min.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../www/shared/datepicker/css/datepicker.css"/>
|
||||
|
||||
<script src="../www/shared/bootstrap-daterangepicker/date.js"></script>
|
||||
<script src="../www/shared/bootstrap-daterangepicker/daterangepicker.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../www/shared/bootstrap-daterangepicker/daterangepicker.css"/>
|
||||
|
||||
<!-- include spec files here... -->
|
||||
<!-- <script type="text/javascript" src="spec/SpecHelper.js"></script>
|
||||
<script type="text/javascript" src="spec/PlayerSpec.js"></script>
|
||||
-->
|
||||
<script type="text/javascript" src="spec/inputBindingSpec.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
var jasmineEnv = jasmine.getEnv();
|
||||
jasmineEnv.updateInterval = 1000;
|
||||
|
||||
var htmlReporter = new jasmine.HtmlReporter();
|
||||
|
||||
jasmineEnv.addReporter(htmlReporter);
|
||||
|
||||
jasmineEnv.specFilter = function(spec) {
|
||||
return htmlReporter.specFilter(spec);
|
||||
};
|
||||
|
||||
// var currentWindowOnload = window.onload;
|
||||
|
||||
// window.onload = function() {
|
||||
// if (currentWindowOnload) {
|
||||
// currentWindowOnload();
|
||||
// }
|
||||
// execJasmine();
|
||||
// };
|
||||
|
||||
// Add a slight delay before running tests, so that Shiny has time to
|
||||
// do setup stuff.
|
||||
$(document).ready(function() {
|
||||
setTimeout(function() {
|
||||
execJasmine();
|
||||
},
|
||||
50
|
||||
)
|
||||
});
|
||||
|
||||
|
||||
function execJasmine() {
|
||||
jasmineEnv.execute();
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
|
||||
// Clear the Shiny disconnected gray screen shortly after loading
|
||||
$(document).ready(function() {
|
||||
setTimeout(function() {
|
||||
$('body').removeClass('disconnected');
|
||||
},
|
||||
100
|
||||
)
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
2
inst/tests-js/fixtures/textInputBinding.html
Normal file
@@ -0,0 +1,2 @@
|
||||
<label>Text input:</label>
|
||||
<input id="in_text" type="text" value="starting value"/>
|
||||
20
inst/tests-js/lib/jasmine-1.3.1/MIT.LICENSE
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2008-2011 Pivotal Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
681
inst/tests-js/lib/jasmine-1.3.1/jasmine-html.js
Normal file
@@ -0,0 +1,681 @@
|
||||
jasmine.HtmlReporterHelpers = {};
|
||||
|
||||
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
|
||||
var el = document.createElement(type);
|
||||
|
||||
for (var i = 2; i < arguments.length; i++) {
|
||||
var child = arguments[i];
|
||||
|
||||
if (typeof child === 'string') {
|
||||
el.appendChild(document.createTextNode(child));
|
||||
} else {
|
||||
if (child) {
|
||||
el.appendChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var attr in attrs) {
|
||||
if (attr == "className") {
|
||||
el[attr] = attrs[attr];
|
||||
} else {
|
||||
el.setAttribute(attr, attrs[attr]);
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
|
||||
var results = child.results();
|
||||
var status = results.passed() ? 'passed' : 'failed';
|
||||
if (results.skipped) {
|
||||
status = 'skipped';
|
||||
}
|
||||
|
||||
return status;
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
|
||||
var parentDiv = this.dom.summary;
|
||||
var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
|
||||
var parent = child[parentSuite];
|
||||
|
||||
if (parent) {
|
||||
if (typeof this.views.suites[parent.id] == 'undefined') {
|
||||
this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
|
||||
}
|
||||
parentDiv = this.views.suites[parent.id].element;
|
||||
}
|
||||
|
||||
parentDiv.appendChild(childElement);
|
||||
};
|
||||
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
|
||||
for(var fn in jasmine.HtmlReporterHelpers) {
|
||||
ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter = function(_doc) {
|
||||
var self = this;
|
||||
var doc = _doc || window.document;
|
||||
|
||||
var reporterView;
|
||||
|
||||
var dom = {};
|
||||
|
||||
// Jasmine Reporter Public Interface
|
||||
self.logRunningSpecs = false;
|
||||
|
||||
self.reportRunnerStarting = function(runner) {
|
||||
var specs = runner.specs() || [];
|
||||
|
||||
if (specs.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
createReporterDom(runner.env.versionString());
|
||||
doc.body.appendChild(dom.reporter);
|
||||
setExceptionHandling();
|
||||
|
||||
reporterView = new jasmine.HtmlReporter.ReporterView(dom);
|
||||
reporterView.addSpecs(specs, self.specFilter);
|
||||
};
|
||||
|
||||
self.reportRunnerResults = function(runner) {
|
||||
reporterView && reporterView.complete();
|
||||
};
|
||||
|
||||
self.reportSuiteResults = function(suite) {
|
||||
reporterView.suiteComplete(suite);
|
||||
};
|
||||
|
||||
self.reportSpecStarting = function(spec) {
|
||||
if (self.logRunningSpecs) {
|
||||
self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
|
||||
}
|
||||
};
|
||||
|
||||
self.reportSpecResults = function(spec) {
|
||||
reporterView.specComplete(spec);
|
||||
};
|
||||
|
||||
self.log = function() {
|
||||
var console = jasmine.getGlobal().console;
|
||||
if (console && console.log) {
|
||||
if (console.log.apply) {
|
||||
console.log.apply(console, arguments);
|
||||
} else {
|
||||
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.specFilter = function(spec) {
|
||||
if (!focusedSpecName()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return spec.getFullName().indexOf(focusedSpecName()) === 0;
|
||||
};
|
||||
|
||||
return self;
|
||||
|
||||
function focusedSpecName() {
|
||||
var specName;
|
||||
|
||||
(function memoizeFocusedSpec() {
|
||||
if (specName) {
|
||||
return;
|
||||
}
|
||||
|
||||
var paramMap = [];
|
||||
var params = jasmine.HtmlReporter.parameters(doc);
|
||||
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
var p = params[i].split('=');
|
||||
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
|
||||
}
|
||||
|
||||
specName = paramMap.spec;
|
||||
})();
|
||||
|
||||
return specName;
|
||||
}
|
||||
|
||||
function createReporterDom(version) {
|
||||
dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
|
||||
dom.banner = self.createDom('div', { className: 'banner' },
|
||||
self.createDom('span', { className: 'title' }, "Jasmine "),
|
||||
self.createDom('span', { className: 'version' }, version)),
|
||||
|
||||
dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
|
||||
dom.alert = self.createDom('div', {className: 'alert'},
|
||||
self.createDom('span', { className: 'exceptions' },
|
||||
self.createDom('label', { className: 'label', 'for': 'no_try_catch' }, 'No try/catch'),
|
||||
self.createDom('input', { id: 'no_try_catch', type: 'checkbox' }))),
|
||||
dom.results = self.createDom('div', {className: 'results'},
|
||||
dom.summary = self.createDom('div', { className: 'summary' }),
|
||||
dom.details = self.createDom('div', { id: 'details' }))
|
||||
);
|
||||
}
|
||||
|
||||
function noTryCatch() {
|
||||
return window.location.search.match(/catch=false/);
|
||||
}
|
||||
|
||||
function searchWithCatch() {
|
||||
var params = jasmine.HtmlReporter.parameters(window.document);
|
||||
var removed = false;
|
||||
var i = 0;
|
||||
|
||||
while (!removed && i < params.length) {
|
||||
if (params[i].match(/catch=/)) {
|
||||
params.splice(i, 1);
|
||||
removed = true;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (jasmine.CATCH_EXCEPTIONS) {
|
||||
params.push("catch=false");
|
||||
}
|
||||
|
||||
return params.join("&");
|
||||
}
|
||||
|
||||
function setExceptionHandling() {
|
||||
var chxCatch = document.getElementById('no_try_catch');
|
||||
|
||||
if (noTryCatch()) {
|
||||
chxCatch.setAttribute('checked', true);
|
||||
jasmine.CATCH_EXCEPTIONS = false;
|
||||
}
|
||||
chxCatch.onclick = function() {
|
||||
window.location.search = searchWithCatch();
|
||||
};
|
||||
}
|
||||
};
|
||||
jasmine.HtmlReporter.parameters = function(doc) {
|
||||
var paramStr = doc.location.search.substring(1);
|
||||
var params = [];
|
||||
|
||||
if (paramStr.length > 0) {
|
||||
params = paramStr.split('&');
|
||||
}
|
||||
return params;
|
||||
}
|
||||
jasmine.HtmlReporter.sectionLink = function(sectionName) {
|
||||
var link = '?';
|
||||
var params = [];
|
||||
|
||||
if (sectionName) {
|
||||
params.push('spec=' + encodeURIComponent(sectionName));
|
||||
}
|
||||
if (!jasmine.CATCH_EXCEPTIONS) {
|
||||
params.push("catch=false");
|
||||
}
|
||||
if (params.length > 0) {
|
||||
link += params.join("&");
|
||||
}
|
||||
|
||||
return link;
|
||||
};
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);
|
||||
jasmine.HtmlReporter.ReporterView = function(dom) {
|
||||
this.startedAt = new Date();
|
||||
this.runningSpecCount = 0;
|
||||
this.completeSpecCount = 0;
|
||||
this.passedCount = 0;
|
||||
this.failedCount = 0;
|
||||
this.skippedCount = 0;
|
||||
|
||||
this.createResultsMenu = function() {
|
||||
this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
|
||||
this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
|
||||
' | ',
|
||||
this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
|
||||
|
||||
this.summaryMenuItem.onclick = function() {
|
||||
dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
|
||||
};
|
||||
|
||||
this.detailsMenuItem.onclick = function() {
|
||||
showDetails();
|
||||
};
|
||||
};
|
||||
|
||||
this.addSpecs = function(specs, specFilter) {
|
||||
this.totalSpecCount = specs.length;
|
||||
|
||||
this.views = {
|
||||
specs: {},
|
||||
suites: {}
|
||||
};
|
||||
|
||||
for (var i = 0; i < specs.length; i++) {
|
||||
var spec = specs[i];
|
||||
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
|
||||
if (specFilter(spec)) {
|
||||
this.runningSpecCount++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.specComplete = function(spec) {
|
||||
this.completeSpecCount++;
|
||||
|
||||
if (isUndefined(this.views.specs[spec.id])) {
|
||||
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
|
||||
}
|
||||
|
||||
var specView = this.views.specs[spec.id];
|
||||
|
||||
switch (specView.status()) {
|
||||
case 'passed':
|
||||
this.passedCount++;
|
||||
break;
|
||||
|
||||
case 'failed':
|
||||
this.failedCount++;
|
||||
break;
|
||||
|
||||
case 'skipped':
|
||||
this.skippedCount++;
|
||||
break;
|
||||
}
|
||||
|
||||
specView.refresh();
|
||||
this.refresh();
|
||||
};
|
||||
|
||||
this.suiteComplete = function(suite) {
|
||||
var suiteView = this.views.suites[suite.id];
|
||||
if (isUndefined(suiteView)) {
|
||||
return;
|
||||
}
|
||||
suiteView.refresh();
|
||||
};
|
||||
|
||||
this.refresh = function() {
|
||||
|
||||
if (isUndefined(this.resultsMenu)) {
|
||||
this.createResultsMenu();
|
||||
}
|
||||
|
||||
// currently running UI
|
||||
if (isUndefined(this.runningAlert)) {
|
||||
this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "runningAlert bar" });
|
||||
dom.alert.appendChild(this.runningAlert);
|
||||
}
|
||||
this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
|
||||
|
||||
// skipped specs UI
|
||||
if (isUndefined(this.skippedAlert)) {
|
||||
this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "skippedAlert bar" });
|
||||
}
|
||||
|
||||
this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
|
||||
|
||||
if (this.skippedCount === 1 && isDefined(dom.alert)) {
|
||||
dom.alert.appendChild(this.skippedAlert);
|
||||
}
|
||||
|
||||
// passing specs UI
|
||||
if (isUndefined(this.passedAlert)) {
|
||||
this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: "passingAlert bar" });
|
||||
}
|
||||
this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
|
||||
|
||||
// failing specs UI
|
||||
if (isUndefined(this.failedAlert)) {
|
||||
this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
|
||||
}
|
||||
this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
|
||||
|
||||
if (this.failedCount === 1 && isDefined(dom.alert)) {
|
||||
dom.alert.appendChild(this.failedAlert);
|
||||
dom.alert.appendChild(this.resultsMenu);
|
||||
}
|
||||
|
||||
// summary info
|
||||
this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
|
||||
this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
|
||||
};
|
||||
|
||||
this.complete = function() {
|
||||
dom.alert.removeChild(this.runningAlert);
|
||||
|
||||
this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
|
||||
|
||||
if (this.failedCount === 0) {
|
||||
dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
|
||||
} else {
|
||||
showDetails();
|
||||
}
|
||||
|
||||
dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
|
||||
};
|
||||
|
||||
return this;
|
||||
|
||||
function showDetails() {
|
||||
if (dom.reporter.className.search(/showDetails/) === -1) {
|
||||
dom.reporter.className += " showDetails";
|
||||
}
|
||||
}
|
||||
|
||||
function isUndefined(obj) {
|
||||
return typeof obj === 'undefined';
|
||||
}
|
||||
|
||||
function isDefined(obj) {
|
||||
return !isUndefined(obj);
|
||||
}
|
||||
|
||||
function specPluralizedFor(count) {
|
||||
var str = count + " spec";
|
||||
if (count > 1) {
|
||||
str += "s"
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
|
||||
|
||||
|
||||
jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
|
||||
this.spec = spec;
|
||||
this.dom = dom;
|
||||
this.views = views;
|
||||
|
||||
this.symbol = this.createDom('li', { className: 'pending' });
|
||||
this.dom.symbolSummary.appendChild(this.symbol);
|
||||
|
||||
this.summary = this.createDom('div', { className: 'specSummary' },
|
||||
this.createDom('a', {
|
||||
className: 'description',
|
||||
href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()),
|
||||
title: this.spec.getFullName()
|
||||
}, this.spec.description)
|
||||
);
|
||||
|
||||
this.detail = this.createDom('div', { className: 'specDetail' },
|
||||
this.createDom('a', {
|
||||
className: 'description',
|
||||
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
|
||||
title: this.spec.getFullName()
|
||||
}, this.spec.getFullName())
|
||||
);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.status = function() {
|
||||
return this.getSpecStatus(this.spec);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
|
||||
this.symbol.className = this.status();
|
||||
|
||||
switch (this.status()) {
|
||||
case 'skipped':
|
||||
break;
|
||||
|
||||
case 'passed':
|
||||
this.appendSummaryToSuiteDiv();
|
||||
break;
|
||||
|
||||
case 'failed':
|
||||
this.appendSummaryToSuiteDiv();
|
||||
this.appendFailureDetail();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
|
||||
this.summary.className += ' ' + this.status();
|
||||
this.appendToSummary(this.spec, this.summary);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
|
||||
this.detail.className += ' ' + this.status();
|
||||
|
||||
var resultItems = this.spec.results().getItems();
|
||||
var messagesDiv = this.createDom('div', { className: 'messages' });
|
||||
|
||||
for (var i = 0; i < resultItems.length; i++) {
|
||||
var result = resultItems[i];
|
||||
|
||||
if (result.type == 'log') {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
|
||||
} else if (result.type == 'expect' && result.passed && !result.passed()) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
|
||||
|
||||
if (result.trace.stack) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (messagesDiv.childNodes.length > 0) {
|
||||
this.detail.appendChild(messagesDiv);
|
||||
this.dom.details.appendChild(this.detail);
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
|
||||
this.suite = suite;
|
||||
this.dom = dom;
|
||||
this.views = views;
|
||||
|
||||
this.element = this.createDom('div', { className: 'suite' },
|
||||
this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description)
|
||||
);
|
||||
|
||||
this.appendToSummary(this.suite, this.element);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SuiteView.prototype.status = function() {
|
||||
return this.getSpecStatus(this.suite);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
|
||||
this.element.className += " " + this.status();
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
|
||||
|
||||
/* @deprecated Use jasmine.HtmlReporter instead
|
||||
*/
|
||||
jasmine.TrivialReporter = function(doc) {
|
||||
this.document = doc || document;
|
||||
this.suiteDivs = {};
|
||||
this.logRunningSpecs = false;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
|
||||
var el = document.createElement(type);
|
||||
|
||||
for (var i = 2; i < arguments.length; i++) {
|
||||
var child = arguments[i];
|
||||
|
||||
if (typeof child === 'string') {
|
||||
el.appendChild(document.createTextNode(child));
|
||||
} else {
|
||||
if (child) { el.appendChild(child); }
|
||||
}
|
||||
}
|
||||
|
||||
for (var attr in attrs) {
|
||||
if (attr == "className") {
|
||||
el[attr] = attrs[attr];
|
||||
} else {
|
||||
el.setAttribute(attr, attrs[attr]);
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
|
||||
var showPassed, showSkipped;
|
||||
|
||||
this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
|
||||
this.createDom('div', { className: 'banner' },
|
||||
this.createDom('div', { className: 'logo' },
|
||||
this.createDom('span', { className: 'title' }, "Jasmine"),
|
||||
this.createDom('span', { className: 'version' }, runner.env.versionString())),
|
||||
this.createDom('div', { className: 'options' },
|
||||
"Show ",
|
||||
showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
|
||||
this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
|
||||
showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
|
||||
this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
|
||||
)
|
||||
),
|
||||
|
||||
this.runnerDiv = this.createDom('div', { className: 'runner running' },
|
||||
this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
|
||||
this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
|
||||
this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
|
||||
);
|
||||
|
||||
this.document.body.appendChild(this.outerDiv);
|
||||
|
||||
var suites = runner.suites();
|
||||
for (var i = 0; i < suites.length; i++) {
|
||||
var suite = suites[i];
|
||||
var suiteDiv = this.createDom('div', { className: 'suite' },
|
||||
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
|
||||
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
|
||||
this.suiteDivs[suite.id] = suiteDiv;
|
||||
var parentDiv = this.outerDiv;
|
||||
if (suite.parentSuite) {
|
||||
parentDiv = this.suiteDivs[suite.parentSuite.id];
|
||||
}
|
||||
parentDiv.appendChild(suiteDiv);
|
||||
}
|
||||
|
||||
this.startedAt = new Date();
|
||||
|
||||
var self = this;
|
||||
showPassed.onclick = function(evt) {
|
||||
if (showPassed.checked) {
|
||||
self.outerDiv.className += ' show-passed';
|
||||
} else {
|
||||
self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
|
||||
}
|
||||
};
|
||||
|
||||
showSkipped.onclick = function(evt) {
|
||||
if (showSkipped.checked) {
|
||||
self.outerDiv.className += ' show-skipped';
|
||||
} else {
|
||||
self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
|
||||
var results = runner.results();
|
||||
var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
|
||||
this.runnerDiv.setAttribute("class", className);
|
||||
//do it twice for IE
|
||||
this.runnerDiv.setAttribute("className", className);
|
||||
var specs = runner.specs();
|
||||
var specCount = 0;
|
||||
for (var i = 0; i < specs.length; i++) {
|
||||
if (this.specFilter(specs[i])) {
|
||||
specCount++;
|
||||
}
|
||||
}
|
||||
var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
|
||||
message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
|
||||
this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
|
||||
|
||||
this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
|
||||
var results = suite.results();
|
||||
var status = results.passed() ? 'passed' : 'failed';
|
||||
if (results.totalCount === 0) { // todo: change this to check results.skipped
|
||||
status = 'skipped';
|
||||
}
|
||||
this.suiteDivs[suite.id].className += " " + status;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
|
||||
if (this.logRunningSpecs) {
|
||||
this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
|
||||
var results = spec.results();
|
||||
var status = results.passed() ? 'passed' : 'failed';
|
||||
if (results.skipped) {
|
||||
status = 'skipped';
|
||||
}
|
||||
var specDiv = this.createDom('div', { className: 'spec ' + status },
|
||||
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
|
||||
this.createDom('a', {
|
||||
className: 'description',
|
||||
href: '?spec=' + encodeURIComponent(spec.getFullName()),
|
||||
title: spec.getFullName()
|
||||
}, spec.description));
|
||||
|
||||
|
||||
var resultItems = results.getItems();
|
||||
var messagesDiv = this.createDom('div', { className: 'messages' });
|
||||
for (var i = 0; i < resultItems.length; i++) {
|
||||
var result = resultItems[i];
|
||||
|
||||
if (result.type == 'log') {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
|
||||
} else if (result.type == 'expect' && result.passed && !result.passed()) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
|
||||
|
||||
if (result.trace.stack) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (messagesDiv.childNodes.length > 0) {
|
||||
specDiv.appendChild(messagesDiv);
|
||||
}
|
||||
|
||||
this.suiteDivs[spec.suite.id].appendChild(specDiv);
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.log = function() {
|
||||
var console = jasmine.getGlobal().console;
|
||||
if (console && console.log) {
|
||||
if (console.log.apply) {
|
||||
console.log.apply(console, arguments);
|
||||
} else {
|
||||
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.getLocation = function() {
|
||||
return this.document.location;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.specFilter = function(spec) {
|
||||
var paramMap = {};
|
||||
var params = this.getLocation().search.substring(1).split('&');
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
var p = params[i].split('=');
|
||||
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
|
||||
}
|
||||
|
||||
if (!paramMap.spec) {
|
||||
return true;
|
||||
}
|
||||
return spec.getFullName().indexOf(paramMap.spec) === 0;
|
||||
};
|
||||
82
inst/tests-js/lib/jasmine-1.3.1/jasmine.css
Normal file
@@ -0,0 +1,82 @@
|
||||
body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
|
||||
|
||||
#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
|
||||
#HTMLReporter a { text-decoration: none; }
|
||||
#HTMLReporter a:hover { text-decoration: underline; }
|
||||
#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
|
||||
#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
|
||||
#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
|
||||
#HTMLReporter .version { color: #aaaaaa; }
|
||||
#HTMLReporter .banner { margin-top: 14px; }
|
||||
#HTMLReporter .duration { color: #aaaaaa; float: right; }
|
||||
#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
|
||||
#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
|
||||
#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
|
||||
#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
|
||||
#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
|
||||
#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
|
||||
#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
|
||||
#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
|
||||
#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
|
||||
#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
|
||||
#HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
|
||||
#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
|
||||
#HTMLReporter .runningAlert { background-color: #666666; }
|
||||
#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
|
||||
#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
|
||||
#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
|
||||
#HTMLReporter .passingAlert { background-color: #a6b779; }
|
||||
#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
|
||||
#HTMLReporter .failingAlert { background-color: #cf867e; }
|
||||
#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
|
||||
#HTMLReporter .results { margin-top: 14px; }
|
||||
#HTMLReporter #details { display: none; }
|
||||
#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
|
||||
#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
|
||||
#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
|
||||
#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
|
||||
#HTMLReporter.showDetails .summary { display: none; }
|
||||
#HTMLReporter.showDetails #details { display: block; }
|
||||
#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
|
||||
#HTMLReporter .summary { margin-top: 14px; }
|
||||
#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
|
||||
#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
|
||||
#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
|
||||
#HTMLReporter .description + .suite { margin-top: 0; }
|
||||
#HTMLReporter .suite { margin-top: 14px; }
|
||||
#HTMLReporter .suite a { color: #333333; }
|
||||
#HTMLReporter #details .specDetail { margin-bottom: 28px; }
|
||||
#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
|
||||
#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
|
||||
#HTMLReporter .resultMessage span.result { display: block; }
|
||||
#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
|
||||
|
||||
#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
|
||||
#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
|
||||
#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
|
||||
#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
|
||||
#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
|
||||
#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
|
||||
#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
|
||||
#TrivialReporter .runner.running { background-color: yellow; }
|
||||
#TrivialReporter .options { text-align: right; font-size: .8em; }
|
||||
#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
|
||||
#TrivialReporter .suite .suite { margin: 5px; }
|
||||
#TrivialReporter .suite.passed { background-color: #dfd; }
|
||||
#TrivialReporter .suite.failed { background-color: #fdd; }
|
||||
#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
|
||||
#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
|
||||
#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
|
||||
#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
|
||||
#TrivialReporter .spec.skipped { background-color: #bbb; }
|
||||
#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
|
||||
#TrivialReporter .passed { background-color: #cfc; display: none; }
|
||||
#TrivialReporter .failed { background-color: #fbb; }
|
||||
#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
|
||||
#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
|
||||
#TrivialReporter .resultMessage .mismatch { color: black; }
|
||||
#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
|
||||
#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
|
||||
#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
|
||||
#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
|
||||
#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }
|
||||
2600
inst/tests-js/lib/jasmine-1.3.1/jasmine.js
Normal file
1624
inst/tests-js/spec/inputBindingSpec.js
Normal file
@@ -15,3 +15,46 @@ test_that("CSS unit validation", {
|
||||
# Numbers should return string with "px"
|
||||
expect_equal(validateCssUnit(100), "100px")
|
||||
})
|
||||
|
||||
|
||||
test_that("Repeated names for selectInput and radioButtons choices", {
|
||||
# These test might be a bit too closely tied to the exact structure of the
|
||||
# tag object, but they get the job done for now.
|
||||
|
||||
# Select input
|
||||
x <- selectInput('id','label', choices = c(a='x1', a='x2', b='x3'))
|
||||
choices <- x[[2]]$children
|
||||
|
||||
expect_equal(choices[[1]]$children[[1]], 'a')
|
||||
expect_equal(choices[[1]]$attribs$value, 'x1')
|
||||
expect_equal(choices[[1]]$attribs$selected, 'selected')
|
||||
|
||||
expect_equal(choices[[2]]$children[[1]], 'a')
|
||||
expect_equal(choices[[2]]$attribs$value, 'x2')
|
||||
# This one actually should be NULL, but with the syntax of selectInput, it
|
||||
# must be 'selected'.
|
||||
expect_equal(choices[[2]]$attribs$selected, 'selected')
|
||||
|
||||
expect_equal(choices[[3]]$children[[1]], 'b')
|
||||
expect_equal(choices[[3]]$attribs$value, 'x3')
|
||||
expect_equal(choices[[3]]$attribs$selected, NULL)
|
||||
|
||||
|
||||
# Radio buttons
|
||||
x <- radioButtons('id','label', choices = c(a='x1', a='x2', b='x3'))
|
||||
choices <- x$children
|
||||
|
||||
expect_equal(choices[[2]]$children[[2]]$children[[1]], 'a')
|
||||
expect_equal(choices[[2]]$children[[1]]$attribs$value, 'x1')
|
||||
expect_equal(choices[[2]]$children[[1]]$attribs$checked, 'checked')
|
||||
|
||||
expect_equal(choices[[3]]$children[[2]]$children[[1]], 'a')
|
||||
expect_equal(choices[[3]]$children[[1]]$attribs$value, 'x2')
|
||||
# This one actually should be NULL, but with the syntax of radioButtons, it
|
||||
# must be 'checked'.
|
||||
expect_equal(choices[[3]]$children[[1]]$attribs$checked, 'checked')
|
||||
|
||||
expect_equal(choices[[4]]$children[[2]]$children[[1]], 'b')
|
||||
expect_equal(choices[[4]]$children[[1]]$attribs$value, 'x3')
|
||||
expect_equal(choices[[4]]$children[[1]]$attribs$checked, NULL)
|
||||
})
|
||||
|
||||
@@ -650,3 +650,16 @@ test_that("observe() accepts quoted and unquoted expressions", {
|
||||
flushReact()
|
||||
expect_identical(parent.env(inside_env), this_env)
|
||||
})
|
||||
|
||||
test_that("Observer priorities are respected", {
|
||||
results <- c()
|
||||
observe(results <<- c(results, 10), priority=10)
|
||||
observe(results <<- c(results, 30), priority=30)
|
||||
observe(results <<- c(results, 20), priority=20L)
|
||||
observe(results <<- c(results, 21), priority=20)
|
||||
observe(results <<- c(results, 22), priority=20L)
|
||||
|
||||
flushReact()
|
||||
|
||||
expect_identical(results, c(30, 20, 21, 22, 10))
|
||||
})
|
||||
|
||||
@@ -21,3 +21,280 @@ test_that("Basic tag writing works", {
|
||||
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><a&b></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="<a&b>">text</div>')
|
||||
|
||||
# HTML() has no effect in a property like 'class'
|
||||
expect_equivalent(format(div(class = HTML("<a&b>"), "text")),
|
||||
'<div class="<a&b>">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), 3)
|
||||
expect_equal(t1$children[[1]]$name, "p")
|
||||
expect_equal(t1$children[[1]]$children[[1]], "tag1")
|
||||
expect_equal(t1$children[[2]]$name, "b")
|
||||
expect_equal(t1$children[[2]]$children[[1]], "tag2")
|
||||
expect_equal(t1$children[[3]]$name, "i")
|
||||
expect_equal(t1$children[[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]])
|
||||
expect_identical(t1, t2)
|
||||
|
||||
|
||||
# tagSetChildren, using list argument
|
||||
t2 <- tagSetChildren(div_tag, list = tag_list)
|
||||
expect_identical(t1, t2)
|
||||
|
||||
# tagSetChildren, using ... arguments
|
||||
t2 <- tagSetChildren(div_tag, tag_list[[1]], tag_list[[2]], tag_list[[3]])
|
||||
expect_identical(t1, t2)
|
||||
|
||||
# tagSetChildren, using ... and list arguments
|
||||
t2 <- tagSetChildren(div_tag, tag_list[[1]], list = tag_list[2:3])
|
||||
expect_identical(t1, t2)
|
||||
|
||||
# tagSetChildren overwrites existing children
|
||||
t2 <- tagAppendChild(div_tag, p("should replace this tag"))
|
||||
t2 <- tagSetChildren(div_tag, list = tag_list)
|
||||
expect_identical(t1, t2)
|
||||
|
||||
|
||||
# tagAppendChildren, using list argument
|
||||
t2 <- tagAppendChild(div_tag, tag_list[[1]])
|
||||
t2 <- tagAppendChildren(t2, list = tag_list[2:3])
|
||||
expect_identical(t1, t2)
|
||||
|
||||
# tagAppendChildren, using ... arguments
|
||||
t2 <- tagAppendChild(div_tag, tag_list[[1]])
|
||||
t2 <- tagAppendChildren(t2, tag_list[[2]], tag_list[[3]])
|
||||
expect_identical(t1, 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(t1, t2)
|
||||
|
||||
# tagAppendChildren can start with no children
|
||||
t2 <- tagAppendChildren(div_tag, list = tag_list)
|
||||
expect_identical(t1, 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")
|
||||
)
|
||||
|
||||
# Numbers are coerced to strings
|
||||
expect_identical(
|
||||
div(1234),
|
||||
structure(
|
||||
list(name = "div", attribs = list(), children = list("1234")),
|
||||
.Names = c("name", "attribs", "children"),
|
||||
class = "shiny.tag"
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
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("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(t1, t1_full)
|
||||
})
|
||||
|
||||
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]], "<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]], "<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)
|
||||
})
|
||||
|
||||
570
inst/www/reactive-graph.html
Normal file
@@ -0,0 +1,570 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
|
||||
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
|
||||
<link href='http://fonts.googleapis.com/css?family=Source+Sans+Pro:200,400,600' rel='stylesheet' type='text/css'>
|
||||
<style type="text/css">
|
||||
html, body {
|
||||
font-family: 'Source Sans Pro', sans-serif;
|
||||
font-weight: 400;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
div {
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-o-user-select: none;
|
||||
cursor: default;
|
||||
}
|
||||
#instructions, #ended {
|
||||
position: relative;
|
||||
font-weight: 200;
|
||||
color: #444;
|
||||
top: 20px;
|
||||
font-size: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
#ended strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
svg {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.node {
|
||||
cursor: pointer;
|
||||
}
|
||||
.node text {
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
font-weight: normal;
|
||||
text-anchor: start;
|
||||
fill: #999;
|
||||
user-select: none;
|
||||
transition: fill 0.75s ease;
|
||||
}
|
||||
.node.running text {
|
||||
fill: black;
|
||||
}
|
||||
.node.changed text {
|
||||
fill: red;
|
||||
}
|
||||
.node text tspan {
|
||||
white-space: pre;
|
||||
}
|
||||
.node path {
|
||||
fill: white;
|
||||
stroke: #777;
|
||||
stroke-width: 7.5px;
|
||||
transition: fill 0.75s ease;
|
||||
}
|
||||
.node.observer path {
|
||||
}
|
||||
.node.observable path {
|
||||
}
|
||||
.node.value path {
|
||||
}
|
||||
.node.invalidated path {
|
||||
fill: #E0E0E0;
|
||||
/*fill: url(#diagonalHatch);*/
|
||||
}
|
||||
.node.running path {
|
||||
fill: #61B97E;
|
||||
}
|
||||
#legend {
|
||||
font-size: 22px;
|
||||
position: fixed;
|
||||
bottom: 10px;
|
||||
right: 20px;
|
||||
}
|
||||
.color {
|
||||
display: inline-block;
|
||||
border: 1px solid #777;
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
}
|
||||
.color.normal {
|
||||
background-color: #white;
|
||||
}
|
||||
.color.invalidated {
|
||||
background-color: #E0E0E0;
|
||||
}
|
||||
.color.running {
|
||||
background-color: #61B97E;
|
||||
}
|
||||
#triangle {
|
||||
fill: #CCC;
|
||||
}
|
||||
.link {
|
||||
fill: none;
|
||||
stroke: #CCC;
|
||||
stroke-width: 0.5px;
|
||||
}
|
||||
#description {
|
||||
position: fixed;
|
||||
width: 300px;
|
||||
left: 630px;
|
||||
top: 36px;
|
||||
height: auto;
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var log = [
|
||||
{ "action" : "valueChange", "id" : "names(input)", "value" : " chr \"dataset\"" },
|
||||
{ "action" : "valueChange", "id" : "input (all)", "value" : "List of 1\n $ dataset: chr \"rock\"" },
|
||||
{ "action" : "valueChange", "id" : "input$dataset", "value" : " chr \"rock\"" },
|
||||
{ "action" : "valueChange", "id" : "names(input)", "value" : " chr [1:2] \"caption\" \"dataset\"" },
|
||||
{ "action" : "valueChange", "id" : "input (all)", "value" : "List of 2\n $ caption: chr \"Data Summary\"\n $ dataset: chr \"rock\"" },
|
||||
{ "action" : "valueChange", "id" : "input$caption", "value" : " chr \"Data Summary\"" },
|
||||
{ "action" : "valueChange", "id" : "names(input)", "value" : " chr [1:3] \"caption\" \"dataset\" \"obs\"" },
|
||||
{ "action" : "valueChange", "id" : "input (all)", "value" : "List of 3\n $ caption: chr \"Data Summary\"\n $ obs : num 10\n $ dataset: chr \"rock\"" },
|
||||
{ "action" : "valueChange", "id" : "input$obs", "value" : " num 10" },
|
||||
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr \"output_caption_hidden\"" },
|
||||
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 1\n $ output_caption_hidden: logi FALSE" },
|
||||
{ "action" : "valueChange", "id" : "clientData$output_caption_hidden", "value" : " logi FALSE" },
|
||||
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:2] \"output_caption_hidden\" \"output_summary_hidden\"" },
|
||||
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 2\n $ output_caption_hidden: logi FALSE\n $ output_summary_hidden: logi FALSE" },
|
||||
{ "action" : "valueChange", "id" : "clientData$output_summary_hidden", "value" : " logi FALSE" },
|
||||
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:3] \"output_caption_hidden\" \"output_summary_hidden\" \"output_view_hidden\"" },
|
||||
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 3\n $ output_view_hidden : logi FALSE\n $ output_caption_hidden: logi FALSE\n $ output_summary_hidden: logi FALSE" },
|
||||
{ "action" : "valueChange", "id" : "clientData$output_view_hidden", "value" : " logi FALSE" },
|
||||
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:4] \"output_caption_hidden\" \"output_summary_hidden\" \"output_view_hidden\" ..." },
|
||||
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 4\n $ output_view_hidden : logi FALSE\n $ output_caption_hidden: logi FALSE\n $ pixelratio : num 2\n $ output_summary_hidden: logi FALSE" },
|
||||
{ "action" : "valueChange", "id" : "clientData$pixelratio", "value" : " num 2" },
|
||||
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:5] \"output_caption_hidden\" \"output_summary_hidden\" \"output_view_hidden\" ..." },
|
||||
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 5\n $ output_view_hidden : logi FALSE\n $ output_caption_hidden: logi FALSE\n $ pixelratio : num 2\n $ output_summary_hidden: logi FALSE\n $ url_protocol : chr \"http:\"" },
|
||||
{ "action" : "valueChange", "id" : "clientData$url_protocol", "value" : " chr \"http:\"" },
|
||||
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:6] \"output_caption_hidden\" \"output_summary_hidden\" \"output_view_hidden\" ..." },
|
||||
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 6\n $ output_view_hidden : logi FALSE\n $ output_caption_hidden: logi FALSE\n $ pixelratio : num 2\n $ url_hostname : chr \"localhost\"\n $ output_summary_hidden: logi FALSE\n $ url_protocol : chr \"http:\"" },
|
||||
{ "action" : "valueChange", "id" : "clientData$url_hostname", "value" : " chr \"localhost\"" },
|
||||
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:7] \"output_caption_hidden\" \"output_summary_hidden\" \"output_view_hidden\" ..." },
|
||||
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 7\n $ output_view_hidden : logi FALSE\n $ url_port : chr \"8100\"\n $ output_caption_hidden: logi FALSE\n $ pixelratio : num 2\n $ url_hostname : chr \"localhost\"\n $ output_summary_hidden: logi FALSE\n $ url_protocol : chr \"http:\"" },
|
||||
{ "action" : "valueChange", "id" : "clientData$url_port", "value" : " chr \"8100\"" },
|
||||
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:8] \"output_caption_hidden\" \"output_summary_hidden\" \"output_view_hidden\" ..." },
|
||||
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 8\n $ url_pathname : chr \"/\"\n $ output_view_hidden : logi FALSE\n $ url_port : chr \"8100\"\n $ output_caption_hidden: logi FALSE\n $ pixelratio : num 2\n $ url_hostname : chr \"localhost\"\n $ output_summary_hidden: logi FALSE\n $ url_protocol : chr \"http:\"" },
|
||||
{ "action" : "valueChange", "id" : "clientData$url_pathname", "value" : " chr \"/\"" },
|
||||
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:9] \"output_caption_hidden\" \"output_summary_hidden\" \"output_view_hidden\" ..." },
|
||||
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 9\n $ url_pathname : chr \"/\"\n $ output_view_hidden : logi FALSE\n $ url_port : chr \"8100\"\n $ output_caption_hidden: logi FALSE\n $ pixelratio : num 2\n $ url_hostname : chr \"localhost\"\n $ output_summary_hidden: logi FALSE\n $ url_search : chr \"\"\n $ url_protocol : chr \"http:\"" },
|
||||
{ "action" : "valueChange", "id" : "clientData$url_search", "value" : " chr \"\"" },
|
||||
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:10] \"output_caption_hidden\" \"output_summary_hidden\" \"output_view_hidden\" ..." },
|
||||
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 10\n $ url_pathname : chr \"/\"\n $ output_view_hidden : logi FALSE\n $ url_port : chr \"8100\"\n $ output_caption_hidden: logi FALSE\n $ url_hash_initial : chr \"\"\n $ pixelratio : num 2\n $ url_hostname : chr \"localhost\"\n $ output_summary_hidden: logi FALSE\n $ url_search : chr \"\"\n $ url_protocol : chr \"http:\"" },
|
||||
{ "action" : "valueChange", "id" : "clientData$url_hash_initial", "value" : " chr \"\"" },
|
||||
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:11] \"allowDataUriScheme\" \"output_caption_hidden\" \"output_summary_hidden\" ..." },
|
||||
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 11\n $ allowDataUriScheme : logi TRUE\n $ url_pathname : chr \"/\"\n $ output_view_hidden : logi FALSE\n $ url_port : chr \"8100\"\n $ output_caption_hidden: logi FALSE\n $ url_hash_initial : chr \"\"\n $ pixelratio : num 2\n $ url_hostname : chr \"localhost\"\n $ output_summary_hidden: logi FALSE\n $ url_search : chr \"\"\n $ url_protocol : chr \"http:\"" },
|
||||
{ "action" : "valueChange", "id" : "clientData$allowDataUriScheme", "value" : " logi TRUE" },
|
||||
{ "action" : "ctx", "id" : "1", "label" : "output$caption <- renderText({ \n input$caption\n})", "type" : "observer", "prevId" : "" },
|
||||
{ "action" : "invalidate", "id" : "1" },
|
||||
{ "action" : "ctx", "id" : "2", "label" : "output$summary <- renderPrint({ \n dataset <- datasetInput()\n summary(dataset)\n})", "type" : "observer", "prevId" : "" },
|
||||
{ "action" : "invalidate", "id" : "2" },
|
||||
{ "action" : "ctx", "id" : "3", "label" : "output$view <- renderTable({ \n head(datasetInput(), n = input$obs)\n})", "type" : "observer", "prevId" : "" },
|
||||
{ "action" : "invalidate", "id" : "3" },
|
||||
{ "action" : "ctx", "id" : "4", "label" : "output$caption <- renderText({ \n input$caption\n})", "type" : "observer", "prevId" : "1" },
|
||||
{ "action" : "enter", "id" : "4" },
|
||||
{ "action" : "dep", "id" : "4", "dependsOn" : "input$caption" },
|
||||
{ "action" : "exit", "id" : "4" },
|
||||
{ "action" : "ctx", "id" : "5", "label" : "output$summary <- renderPrint({ \n dataset <- datasetInput()\n summary(dataset)\n})", "type" : "observer", "prevId" : "2" },
|
||||
{ "action" : "enter", "id" : "5" },
|
||||
{ "action" : "ctx", "id" : "6", "label" : "reactive({ \n switch(input$dataset, rock = rock, pressure = pressure, cars = cars)\n})", "type" : "observable", "prevId" : "" },
|
||||
{ "action" : "enter", "id" : "6" },
|
||||
{ "action" : "dep", "id" : "6", "dependsOn" : "input$dataset" },
|
||||
{ "action" : "exit", "id" : "6" },
|
||||
{ "action" : "depId", "id" : "5", "dependsOn" : "6" },
|
||||
{ "action" : "exit", "id" : "5" },
|
||||
{ "action" : "ctx", "id" : "7", "label" : "output$view <- renderTable({ \n head(datasetInput(), n = input$obs)\n})", "type" : "observer", "prevId" : "3" },
|
||||
{ "action" : "enter", "id" : "7" },
|
||||
{ "action" : "depId", "id" : "7", "dependsOn" : "6" },
|
||||
{ "action" : "dep", "id" : "7", "dependsOn" : "input$obs" },
|
||||
{ "action" : "exit", "id" : "7" },
|
||||
{ "action" : "valueChange", "id" : "names(input)", "value" : " chr [1:3] \"caption\" \"dataset\" \"obs\"" },
|
||||
{ "action" : "valueChange", "id" : "input (all)", "value" : "List of 3\n $ caption: chr \"Pressure Summary\"\n $ obs : num 10\n $ dataset: chr \"rock\"" },
|
||||
{ "action" : "valueChange", "id" : "input$caption", "value" : " chr \"Pressure Summary\"" },
|
||||
{ "action" : "invalidate", "id" : "4" },
|
||||
{ "action" : "ctx", "id" : "8", "label" : "output$caption <- renderText({ \n input$caption\n})", "type" : "observer", "prevId" : "4" },
|
||||
{ "action" : "enter", "id" : "8" },
|
||||
{ "action" : "dep", "id" : "8", "dependsOn" : "input$caption" },
|
||||
{ "action" : "exit", "id" : "8" },
|
||||
{ "action" : "valueChange", "id" : "names(input)", "value" : " chr [1:3] \"caption\" \"dataset\" \"obs\"" },
|
||||
{ "action" : "valueChange", "id" : "input (all)", "value" : "List of 3\n $ caption: chr \"Pressure Summary\"\n $ obs : num 10\n $ dataset: chr \"pressure\"" },
|
||||
{ "action" : "valueChange", "id" : "input$dataset", "value" : " chr \"pressure\"" },
|
||||
{ "action" : "invalidate", "id" : "6" },
|
||||
{ "action" : "invalidate", "id" : "5" },
|
||||
{ "action" : "invalidate", "id" : "7" },
|
||||
{ "action" : "ctx", "id" : "9", "label" : "output$summary <- renderPrint({ \n dataset <- datasetInput()\n summary(dataset)\n})", "type" : "observer", "prevId" : "5" },
|
||||
{ "action" : "enter", "id" : "9" },
|
||||
{ "action" : "ctx", "id" : "10", "label" : "reactive({ \n switch(input$dataset, rock = rock, pressure = pressure, cars = cars)\n})", "type" : "observable", "prevId" : "6" },
|
||||
{ "action" : "enter", "id" : "10" },
|
||||
{ "action" : "dep", "id" : "10", "dependsOn" : "input$dataset" },
|
||||
{ "action" : "exit", "id" : "10" },
|
||||
{ "action" : "depId", "id" : "9", "dependsOn" : "10" },
|
||||
{ "action" : "exit", "id" : "9" },
|
||||
{ "action" : "ctx", "id" : "11", "label" : "output$view <- renderTable({ \n head(datasetInput(), n = input$obs)\n})", "type" : "observer", "prevId" : "7" },
|
||||
{ "action" : "enter", "id" : "11" },
|
||||
{ "action" : "depId", "id" : "11", "dependsOn" : "10" },
|
||||
{ "action" : "dep", "id" : "11", "dependsOn" : "input$obs" },
|
||||
{ "action" : "exit", "id" : "11" },
|
||||
{ "action" : "valueChange", "id" : "names(input)", "value" : " chr [1:3] \"caption\" \"dataset\" \"obs\"" },
|
||||
{ "action" : "valueChange", "id" : "input (all)", "value" : "List of 3\n $ caption: chr \"Pressure Summary\"\n $ obs : num 15\n $ dataset: chr \"pressure\"" },
|
||||
{ "action" : "valueChange", "id" : "input$obs", "value" : " num 15" },
|
||||
{ "action" : "invalidate", "id" : "11" },
|
||||
{ "action" : "ctx", "id" : "12", "label" : "output$view <- renderTable({ \n head(datasetInput(), n = input$obs)\n})", "type" : "observer", "prevId" : "11" },
|
||||
{ "action" : "enter", "id" : "12" },
|
||||
{ "action" : "depId", "id" : "12", "dependsOn" : "10" },
|
||||
{ "action" : "dep", "id" : "12", "dependsOn" : "input$obs" },
|
||||
{ "action" : "exit", "id" : "12" }
|
||||
];
|
||||
try {
|
||||
log = __DATA__;
|
||||
} catch (e) {}
|
||||
|
||||
var nodes = {};
|
||||
var nodeList = [];
|
||||
var nodeSelection = null;
|
||||
var links = [];
|
||||
var linkSelection = null;
|
||||
|
||||
var node, link; // d3 selections
|
||||
|
||||
var MAX_LINES = 6;
|
||||
|
||||
var force = d3.layout.force()
|
||||
.charge(-100)
|
||||
.nodes(nodeList)
|
||||
.links(links);
|
||||
force.on('tick', onTick);
|
||||
|
||||
function pathDataForNode(node) {
|
||||
/*
|
||||
d="m 58,2 c -75,0 -75,100 0,100 l 60,0 l 50,-50 l -50,-50 Z"
|
||||
d="m 58,2 c -75,0 -75,100 0,100 l 100,0 l 0,-100 Z"
|
||||
d="m 2,0 l 0,100 l 100,0 l 50,-50 l -50,-50 Z"
|
||||
*/
|
||||
switch (node.type) {
|
||||
case 'observer':
|
||||
return 'M -25,-50 c -75,0 -75,100 0,100 l 100,0 l 0,-100 Z';
|
||||
case 'observable':
|
||||
return 'M -25,-50 c -75,0 -75,100 0,100 l 60,0 l 50,-50 l -50,-50 Z';
|
||||
case 'value':
|
||||
return 'M -50,-50 l 0,100 l 100,0 l 50,-50 l -50,-50 Z';
|
||||
}
|
||||
}
|
||||
|
||||
function getSourceCoords(node) {
|
||||
switch (node.type) {
|
||||
case 'observer':
|
||||
case 'observable':
|
||||
return {x: node.x - 5, y: node.y};
|
||||
default:
|
||||
return {x: node.x, y: node.y};
|
||||
}
|
||||
}
|
||||
|
||||
function getTargetCoords(node) {
|
||||
switch (node.type) {
|
||||
case 'observable':
|
||||
return {x: node.x + 7, y: node.y};
|
||||
case 'value':
|
||||
return {x: node.x + 8, y: node.y};
|
||||
default:
|
||||
return {x: node.x, y: node.y};
|
||||
}
|
||||
}
|
||||
|
||||
function multilineTextNode(node) {
|
||||
var MAX_LINES = 6;
|
||||
var fade = false;
|
||||
var el = d3.select(this);
|
||||
var lines = el.text().split('\n');
|
||||
if (lines.length > MAX_LINES) {
|
||||
lines.splice(MAX_LINES);
|
||||
fade = true;
|
||||
}
|
||||
el.text('');
|
||||
var tspan = el.selectAll('tspan').data(lines);
|
||||
tspan.enter().append('tspan');
|
||||
tspan
|
||||
.attr('x', 8)
|
||||
.attr('dy', function(line, i) { return i > 0 ? '1em' : 0})
|
||||
.attr('opacity', function(line, i) {
|
||||
if (!fade)
|
||||
return 1;
|
||||
return Math.min(1, (MAX_LINES - i) * 0.25 - 0.15);
|
||||
})
|
||||
.text(function(line) { return line; });
|
||||
}
|
||||
|
||||
function update() {
|
||||
force.size([document.documentElement.clientWidth / 4,
|
||||
document.documentElement.clientHeight / 4]);
|
||||
|
||||
var layoutDirty = true;
|
||||
|
||||
node = d3.select('#nodes').selectAll('.node').data(nodeList);
|
||||
//layoutDirty = layoutDirty || !node.enter().empty() || !node.exit().empty();
|
||||
var newG = node.enter().append('g')
|
||||
.attr('class', function(n) {return 'node ' + n.type;})
|
||||
.attr('r', 5)
|
||||
// don't show until next tick
|
||||
.style('display', 'none')
|
||||
.on('mousedown', function() {
|
||||
d3.event.stopPropagation();
|
||||
})
|
||||
.on('mouseover', function(n) {
|
||||
$('#description').text(n.label);
|
||||
})
|
||||
.on('mouseout', function(d, i) {
|
||||
$('#description').html('');
|
||||
})
|
||||
.call(force.drag);
|
||||
newG.append('path')
|
||||
.attr('transform', 'scale(0.08)')
|
||||
.attr('stroke', 'black')
|
||||
.attr('stroke-width', 4)
|
||||
.attr('fill', 'white')
|
||||
.attr('d', pathDataForNode);
|
||||
newG.append('text')
|
||||
.attr('x', 3)
|
||||
.attr('y', 0)
|
||||
.attr('font-size', 2.5)
|
||||
.attr('transform', function(n) {
|
||||
if (n.type !== 'observer')
|
||||
return 'translate(1.5, 0)';
|
||||
else
|
||||
return null;
|
||||
})
|
||||
node.exit().remove();
|
||||
node
|
||||
.classed('invalidated', function(n) { return n.invalidated; })
|
||||
.classed('running', function(n) { return n.running; })
|
||||
.classed('changed', function(n) { return n.changed; })
|
||||
.attr('fill', function(n) {
|
||||
if (n.invalidated)
|
||||
return "url(#diagonalHatch)";
|
||||
else
|
||||
return null;
|
||||
});
|
||||
var tspan = node.selectAll('text').filter(function(n) {
|
||||
// This filter is used to disregard all nodes whose labels have
|
||||
// not changed since the last time we updated them.
|
||||
var changed = n.label !== this.label;
|
||||
this.label = n.label;
|
||||
return changed;
|
||||
}).selectAll('tspan')
|
||||
.data(function(n) {
|
||||
var lines = n.label.split('\n');
|
||||
if (lines.length > MAX_LINES) {
|
||||
lines.splice(MAX_LINES);
|
||||
}
|
||||
return lines;
|
||||
});
|
||||
tspan.enter().append('tspan');
|
||||
tspan.exit().remove();
|
||||
tspan
|
||||
.attr('x', 8)
|
||||
.attr('dy', function(line, i) { return i > 0 ? '1em' : 0})
|
||||
.attr('opacity', function(line, i) {
|
||||
return Math.min(1, (MAX_LINES - i) * 0.25 - 0.15);
|
||||
})
|
||||
.text(function(line) { return line; });
|
||||
|
||||
link = d3.select('#links').selectAll('.link').data(links);
|
||||
//layoutDirty = layoutDirty || !link.enter().empty() || !link.exit().empty();
|
||||
link.enter().append('path')
|
||||
.attr('class', 'link')
|
||||
.attr('marker-mid', 'url(#triangle)');
|
||||
link.exit().remove();
|
||||
|
||||
if (layoutDirty) {
|
||||
force
|
||||
.nodes(nodeList.filter(function(n) {return !n.hide;}))
|
||||
.start();
|
||||
layoutDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onTick() {
|
||||
node
|
||||
.style('display', null)
|
||||
.attr('transform', function(n) {
|
||||
return 'translate(' + n.x + ' ' + n.y + ')';
|
||||
});
|
||||
link
|
||||
.attr('d', function(link) {
|
||||
var source = getSourceCoords(link.source);
|
||||
var target = getTargetCoords(link.target)
|
||||
var mid = {
|
||||
x: (source.x + target.x) / 2,
|
||||
y: (source.y + target.y) / 2
|
||||
}
|
||||
return 'M' + source.x + ',' + source.y +
|
||||
' L' + mid.x + ',' + mid.y +
|
||||
' L' + target.x + ',' + target.y;
|
||||
});
|
||||
}
|
||||
|
||||
function createNode(data) {
|
||||
var node;
|
||||
if (!data.prevId) {
|
||||
node = {
|
||||
label: data.label,
|
||||
type: data.type,
|
||||
hide: data.hide
|
||||
};
|
||||
nodes[data.id] = node;
|
||||
if (!node.hide)
|
||||
nodeList.push(node);
|
||||
} else {
|
||||
node = nodes[data.prevId];
|
||||
delete nodes[data.prevId];
|
||||
nodes[data.id] = node;
|
||||
node.label = data.label;
|
||||
node.invalidated = false;
|
||||
}
|
||||
}
|
||||
|
||||
var callbacks = {
|
||||
ctx: function(data) {
|
||||
createNode(data);
|
||||
return true;
|
||||
},
|
||||
dep: function(data) {
|
||||
var dependsOn = nodes[data.dependsOn];
|
||||
if (!dependsOn) {
|
||||
createNode({id: data.dependsOn, label: data.dependsOn, type: 'value'});
|
||||
dependsOn = nodes[data.dependsOn];
|
||||
}
|
||||
if (dependsOn.hide) {
|
||||
dependsOn.hide = false;
|
||||
nodeList.push(dependsOn);
|
||||
}
|
||||
links.push({
|
||||
source: nodes[data.id],
|
||||
target: nodes[data.dependsOn]
|
||||
});
|
||||
},
|
||||
depId: function(data) {
|
||||
links.push({
|
||||
source: nodes[data.id],
|
||||
target: nodes[data.dependsOn]
|
||||
});
|
||||
},
|
||||
invalidate: function(data) {
|
||||
var node = nodes[data.id];
|
||||
node.invalidated = true;
|
||||
links = links.filter(function(link) {
|
||||
return link.source !== node;
|
||||
});
|
||||
},
|
||||
valueChange: function(data) {
|
||||
var existed = !!nodes[data.id];
|
||||
createNode({
|
||||
id: data.id,
|
||||
label: data.id + ' = ' + data.value,
|
||||
type: 'value',
|
||||
prevId: nodes[data.id] ? data.id : null,
|
||||
hide: existed ? nodes[data.id].hide : true
|
||||
});
|
||||
if (!existed || nodes[data.id].hide)
|
||||
return true;
|
||||
nodes[data.id].changed = true;
|
||||
executeBeforeNextCommand.push(function() {
|
||||
nodes[data.id].changed = false;
|
||||
});
|
||||
},
|
||||
enter: function(data) {
|
||||
var node = nodes[data.id];
|
||||
node.running = true;
|
||||
},
|
||||
exit: function(data) {
|
||||
var node = nodes[data.id];
|
||||
node.running = false;
|
||||
}
|
||||
};
|
||||
|
||||
function processMessage(data) {
|
||||
console.log(JSON.stringify(data));
|
||||
if (!callbacks.hasOwnProperty(data.action))
|
||||
throw new Error('Unknown action ' + data.action);
|
||||
var result = callbacks[data.action].call(callbacks, data);
|
||||
update();
|
||||
return result;
|
||||
}
|
||||
|
||||
var executeBeforeNextCommand = [];
|
||||
function doNext() {
|
||||
while (executeBeforeNextCommand.length)
|
||||
executeBeforeNextCommand.shift()();
|
||||
while (log.length)
|
||||
if (!processMessage(log.shift()))
|
||||
break;
|
||||
if (!log.length)
|
||||
$('#ended').fadeIn(1500);
|
||||
}
|
||||
|
||||
function zoom() {
|
||||
var scale = d3.event.scale;
|
||||
var x = d3.event.translate[0];
|
||||
var y = d3.event.translate[1];
|
||||
d3.select('#viz').attr('transform', 'scale(' + scale + ') translate(' + x/scale + ' ' + y/scale + ')');
|
||||
}
|
||||
$(function() {
|
||||
d3.select('svg').call(d3.behavior.zoom().scale(4).on('zoom', zoom));
|
||||
$(document.body).on('keydown', function(e) {
|
||||
if (e.which === 39 || e.which === 32)
|
||||
doNext();
|
||||
if (e.which === 35) {
|
||||
while (log.length) {
|
||||
doNext();
|
||||
}
|
||||
}
|
||||
});
|
||||
doNext();
|
||||
executeBeforeNextCommand.push(function() {
|
||||
$('#instructions').fadeOut(1000);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<body>
|
||||
<svg>
|
||||
<defs>
|
||||
<marker id="triangle"
|
||||
viewBox="0 0 10 10"
|
||||
refX="5" refY="5"
|
||||
markerWidth="6"
|
||||
markerHeight="6"
|
||||
orient="auto">
|
||||
<path d="M 10 0 L 0 5 L 10 10 z" />
|
||||
</marker>
|
||||
<pattern id="diagonalHatch" patternUnits="userSpaceOnUse" width="1" height="1">
|
||||
<path stroke="black" stroke-width="0.25" fill="none"
|
||||
d="M-1,1 l2,-2
|
||||
M0,4 l4,-4
|
||||
M3,5 l2,-2" />
|
||||
</pattern>
|
||||
</defs>
|
||||
<g id="viz" transform="scale(4)">
|
||||
<g id="links"></g>
|
||||
<g id="nodes"></g>
|
||||
</g>
|
||||
</svg>
|
||||
<div id="instructions">
|
||||
Press right-arrow to advance
|
||||
</div>
|
||||
<div id="ended" style="display: none;">
|
||||
<strong>You’ve reached the end</strong><br/>Reload the page to start over
|
||||
</div>
|
||||
<div id="legend">
|
||||
<div class="color normal"></div> Normal<br/>
|
||||
<div class="color invalidated"></div> Invalidated<br/>
|
||||
<div class="color running"></div> Running<br/>
|
||||
</div>
|
||||
<br/>
|
||||
<pre id="description"><br/></pre>
|
||||
</body>
|
||||
</html>
|
||||
442
inst/www/shared/datepicker/css/datepicker.css
Normal file
@@ -0,0 +1,442 @@
|
||||
/*!
|
||||
* Datepicker for Bootstrap
|
||||
*
|
||||
* Copyright 2012 Stefan Petre
|
||||
* Improvements by Andrew Rowls
|
||||
* Licensed under the Apache License v2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*/
|
||||
.datepicker {
|
||||
padding: 4px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
direction: ltr;
|
||||
/*.dow {
|
||||
border-top: 1px solid #ddd !important;
|
||||
}*/
|
||||
|
||||
}
|
||||
.datepicker-inline {
|
||||
width: 220px;
|
||||
}
|
||||
.datepicker.datepicker-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
.datepicker.datepicker-rtl table tr td span {
|
||||
float: right;
|
||||
}
|
||||
.datepicker-dropdown {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.datepicker-dropdown:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
border-left: 7px solid transparent;
|
||||
border-right: 7px solid transparent;
|
||||
border-bottom: 7px solid #ccc;
|
||||
border-bottom-color: rgba(0, 0, 0, 0.2);
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
left: 6px;
|
||||
}
|
||||
.datepicker-dropdown:after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid #ffffff;
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
left: 7px;
|
||||
}
|
||||
.datepicker > div {
|
||||
display: none;
|
||||
}
|
||||
.datepicker.days div.datepicker-days {
|
||||
display: block;
|
||||
}
|
||||
.datepicker.months div.datepicker-months {
|
||||
display: block;
|
||||
}
|
||||
.datepicker.years div.datepicker-years {
|
||||
display: block;
|
||||
}
|
||||
.datepicker table {
|
||||
margin: 0;
|
||||
}
|
||||
.datepicker td,
|
||||
.datepicker th {
|
||||
text-align: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
}
|
||||
.table-striped .datepicker table tr td,
|
||||
.table-striped .datepicker table tr th {
|
||||
background-color: transparent;
|
||||
}
|
||||
.datepicker table tr td.day:hover {
|
||||
background: #eeeeee;
|
||||
cursor: pointer;
|
||||
}
|
||||
.datepicker table tr td.old,
|
||||
.datepicker table tr td.new {
|
||||
color: #999999;
|
||||
}
|
||||
.datepicker table tr td.disabled,
|
||||
.datepicker table tr td.disabled:hover {
|
||||
background: none;
|
||||
color: #999999;
|
||||
cursor: default;
|
||||
}
|
||||
.datepicker table tr td.today,
|
||||
.datepicker table tr td.today:hover,
|
||||
.datepicker table tr td.today.disabled,
|
||||
.datepicker table tr td.today.disabled:hover {
|
||||
background-color: #fde19a;
|
||||
background-image: -moz-linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-image: -ms-linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a));
|
||||
background-image: -webkit-linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-image: -o-linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-image: linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0);
|
||||
border-color: #fdf59a #fdf59a #fbed50;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #000 !important;
|
||||
}
|
||||
.datepicker table tr td.today:hover,
|
||||
.datepicker table tr td.today:hover:hover,
|
||||
.datepicker table tr td.today.disabled:hover,
|
||||
.datepicker table tr td.today.disabled:hover:hover,
|
||||
.datepicker table tr td.today:active,
|
||||
.datepicker table tr td.today:hover:active,
|
||||
.datepicker table tr td.today.disabled:active,
|
||||
.datepicker table tr td.today.disabled:hover:active,
|
||||
.datepicker table tr td.today.active,
|
||||
.datepicker table tr td.today:hover.active,
|
||||
.datepicker table tr td.today.disabled.active,
|
||||
.datepicker table tr td.today.disabled:hover.active,
|
||||
.datepicker table tr td.today.disabled,
|
||||
.datepicker table tr td.today:hover.disabled,
|
||||
.datepicker table tr td.today.disabled.disabled,
|
||||
.datepicker table tr td.today.disabled:hover.disabled,
|
||||
.datepicker table tr td.today[disabled],
|
||||
.datepicker table tr td.today:hover[disabled],
|
||||
.datepicker table tr td.today.disabled[disabled],
|
||||
.datepicker table tr td.today.disabled:hover[disabled] {
|
||||
background-color: #fdf59a;
|
||||
}
|
||||
.datepicker table tr td.today:active,
|
||||
.datepicker table tr td.today:hover:active,
|
||||
.datepicker table tr td.today.disabled:active,
|
||||
.datepicker table tr td.today.disabled:hover:active,
|
||||
.datepicker table tr td.today.active,
|
||||
.datepicker table tr td.today:hover.active,
|
||||
.datepicker table tr td.today.disabled.active,
|
||||
.datepicker table tr td.today.disabled:hover.active {
|
||||
background-color: #fbf069 \9;
|
||||
}
|
||||
.datepicker table tr td.range,
|
||||
.datepicker table tr td.range:hover,
|
||||
.datepicker table tr td.range.disabled,
|
||||
.datepicker table tr td.range.disabled:hover {
|
||||
background: #eeeeee;
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.datepicker table tr td.range.today,
|
||||
.datepicker table tr td.range.today:hover,
|
||||
.datepicker table tr td.range.today.disabled,
|
||||
.datepicker table tr td.range.today.disabled:hover {
|
||||
background-color: #f3d17a;
|
||||
background-image: -moz-linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-image: -ms-linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f3c17a), to(#f3e97a));
|
||||
background-image: -webkit-linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-image: -o-linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-image: linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f3c17a', endColorstr='#f3e97a', GradientType=0);
|
||||
border-color: #f3e97a #f3e97a #edde34;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.datepicker table tr td.range.today:hover,
|
||||
.datepicker table tr td.range.today:hover:hover,
|
||||
.datepicker table tr td.range.today.disabled:hover,
|
||||
.datepicker table tr td.range.today.disabled:hover:hover,
|
||||
.datepicker table tr td.range.today:active,
|
||||
.datepicker table tr td.range.today:hover:active,
|
||||
.datepicker table tr td.range.today.disabled:active,
|
||||
.datepicker table tr td.range.today.disabled:hover:active,
|
||||
.datepicker table tr td.range.today.active,
|
||||
.datepicker table tr td.range.today:hover.active,
|
||||
.datepicker table tr td.range.today.disabled.active,
|
||||
.datepicker table tr td.range.today.disabled:hover.active,
|
||||
.datepicker table tr td.range.today.disabled,
|
||||
.datepicker table tr td.range.today:hover.disabled,
|
||||
.datepicker table tr td.range.today.disabled.disabled,
|
||||
.datepicker table tr td.range.today.disabled:hover.disabled,
|
||||
.datepicker table tr td.range.today[disabled],
|
||||
.datepicker table tr td.range.today:hover[disabled],
|
||||
.datepicker table tr td.range.today.disabled[disabled],
|
||||
.datepicker table tr td.range.today.disabled:hover[disabled] {
|
||||
background-color: #f3e97a;
|
||||
}
|
||||
.datepicker table tr td.range.today:active,
|
||||
.datepicker table tr td.range.today:hover:active,
|
||||
.datepicker table tr td.range.today.disabled:active,
|
||||
.datepicker table tr td.range.today.disabled:hover:active,
|
||||
.datepicker table tr td.range.today.active,
|
||||
.datepicker table tr td.range.today:hover.active,
|
||||
.datepicker table tr td.range.today.disabled.active,
|
||||
.datepicker table tr td.range.today.disabled:hover.active {
|
||||
background-color: #efe24b \9;
|
||||
}
|
||||
.datepicker table tr td.selected,
|
||||
.datepicker table tr td.selected:hover,
|
||||
.datepicker table tr td.selected.disabled,
|
||||
.datepicker table tr td.selected.disabled:hover {
|
||||
background-color: #9e9e9e;
|
||||
background-image: -moz-linear-gradient(top, #b3b3b3, #808080);
|
||||
background-image: -ms-linear-gradient(top, #b3b3b3, #808080);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#b3b3b3), to(#808080));
|
||||
background-image: -webkit-linear-gradient(top, #b3b3b3, #808080);
|
||||
background-image: -o-linear-gradient(top, #b3b3b3, #808080);
|
||||
background-image: linear-gradient(top, #b3b3b3, #808080);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b3b3b3', endColorstr='#808080', GradientType=0);
|
||||
border-color: #808080 #808080 #595959;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td.selected:hover,
|
||||
.datepicker table tr td.selected:hover:hover,
|
||||
.datepicker table tr td.selected.disabled:hover,
|
||||
.datepicker table tr td.selected.disabled:hover:hover,
|
||||
.datepicker table tr td.selected:active,
|
||||
.datepicker table tr td.selected:hover:active,
|
||||
.datepicker table tr td.selected.disabled:active,
|
||||
.datepicker table tr td.selected.disabled:hover:active,
|
||||
.datepicker table tr td.selected.active,
|
||||
.datepicker table tr td.selected:hover.active,
|
||||
.datepicker table tr td.selected.disabled.active,
|
||||
.datepicker table tr td.selected.disabled:hover.active,
|
||||
.datepicker table tr td.selected.disabled,
|
||||
.datepicker table tr td.selected:hover.disabled,
|
||||
.datepicker table tr td.selected.disabled.disabled,
|
||||
.datepicker table tr td.selected.disabled:hover.disabled,
|
||||
.datepicker table tr td.selected[disabled],
|
||||
.datepicker table tr td.selected:hover[disabled],
|
||||
.datepicker table tr td.selected.disabled[disabled],
|
||||
.datepicker table tr td.selected.disabled:hover[disabled] {
|
||||
background-color: #808080;
|
||||
}
|
||||
.datepicker table tr td.selected:active,
|
||||
.datepicker table tr td.selected:hover:active,
|
||||
.datepicker table tr td.selected.disabled:active,
|
||||
.datepicker table tr td.selected.disabled:hover:active,
|
||||
.datepicker table tr td.selected.active,
|
||||
.datepicker table tr td.selected:hover.active,
|
||||
.datepicker table tr td.selected.disabled.active,
|
||||
.datepicker table tr td.selected.disabled:hover.active {
|
||||
background-color: #666666 \9;
|
||||
}
|
||||
.datepicker table tr td.active,
|
||||
.datepicker table tr td.active:hover,
|
||||
.datepicker table tr td.active.disabled,
|
||||
.datepicker table tr td.active.disabled:hover {
|
||||
background-color: #006dcc;
|
||||
background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
|
||||
background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -o-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: linear-gradient(top, #0088cc, #0044cc);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
|
||||
border-color: #0044cc #0044cc #002a80;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td.active:hover,
|
||||
.datepicker table tr td.active:hover:hover,
|
||||
.datepicker table tr td.active.disabled:hover,
|
||||
.datepicker table tr td.active.disabled:hover:hover,
|
||||
.datepicker table tr td.active:active,
|
||||
.datepicker table tr td.active:hover:active,
|
||||
.datepicker table tr td.active.disabled:active,
|
||||
.datepicker table tr td.active.disabled:hover:active,
|
||||
.datepicker table tr td.active.active,
|
||||
.datepicker table tr td.active:hover.active,
|
||||
.datepicker table tr td.active.disabled.active,
|
||||
.datepicker table tr td.active.disabled:hover.active,
|
||||
.datepicker table tr td.active.disabled,
|
||||
.datepicker table tr td.active:hover.disabled,
|
||||
.datepicker table tr td.active.disabled.disabled,
|
||||
.datepicker table tr td.active.disabled:hover.disabled,
|
||||
.datepicker table tr td.active[disabled],
|
||||
.datepicker table tr td.active:hover[disabled],
|
||||
.datepicker table tr td.active.disabled[disabled],
|
||||
.datepicker table tr td.active.disabled:hover[disabled] {
|
||||
background-color: #0044cc;
|
||||
}
|
||||
.datepicker table tr td.active:active,
|
||||
.datepicker table tr td.active:hover:active,
|
||||
.datepicker table tr td.active.disabled:active,
|
||||
.datepicker table tr td.active.disabled:hover:active,
|
||||
.datepicker table tr td.active.active,
|
||||
.datepicker table tr td.active:hover.active,
|
||||
.datepicker table tr td.active.disabled.active,
|
||||
.datepicker table tr td.active.disabled:hover.active {
|
||||
background-color: #003399 \9;
|
||||
}
|
||||
.datepicker table tr td span {
|
||||
display: block;
|
||||
width: 23%;
|
||||
height: 54px;
|
||||
line-height: 54px;
|
||||
float: left;
|
||||
margin: 1%;
|
||||
cursor: pointer;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.datepicker table tr td span:hover {
|
||||
background: #eeeeee;
|
||||
}
|
||||
.datepicker table tr td span.disabled,
|
||||
.datepicker table tr td span.disabled:hover {
|
||||
background: none;
|
||||
color: #999999;
|
||||
cursor: default;
|
||||
}
|
||||
.datepicker table tr td span.active,
|
||||
.datepicker table tr td span.active:hover,
|
||||
.datepicker table tr td span.active.disabled,
|
||||
.datepicker table tr td span.active.disabled:hover {
|
||||
background-color: #006dcc;
|
||||
background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
|
||||
background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -o-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: linear-gradient(top, #0088cc, #0044cc);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
|
||||
border-color: #0044cc #0044cc #002a80;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td span.active:hover,
|
||||
.datepicker table tr td span.active:hover:hover,
|
||||
.datepicker table tr td span.active.disabled:hover,
|
||||
.datepicker table tr td span.active.disabled:hover:hover,
|
||||
.datepicker table tr td span.active:active,
|
||||
.datepicker table tr td span.active:hover:active,
|
||||
.datepicker table tr td span.active.disabled:active,
|
||||
.datepicker table tr td span.active.disabled:hover:active,
|
||||
.datepicker table tr td span.active.active,
|
||||
.datepicker table tr td span.active:hover.active,
|
||||
.datepicker table tr td span.active.disabled.active,
|
||||
.datepicker table tr td span.active.disabled:hover.active,
|
||||
.datepicker table tr td span.active.disabled,
|
||||
.datepicker table tr td span.active:hover.disabled,
|
||||
.datepicker table tr td span.active.disabled.disabled,
|
||||
.datepicker table tr td span.active.disabled:hover.disabled,
|
||||
.datepicker table tr td span.active[disabled],
|
||||
.datepicker table tr td span.active:hover[disabled],
|
||||
.datepicker table tr td span.active.disabled[disabled],
|
||||
.datepicker table tr td span.active.disabled:hover[disabled] {
|
||||
background-color: #0044cc;
|
||||
}
|
||||
.datepicker table tr td span.active:active,
|
||||
.datepicker table tr td span.active:hover:active,
|
||||
.datepicker table tr td span.active.disabled:active,
|
||||
.datepicker table tr td span.active.disabled:hover:active,
|
||||
.datepicker table tr td span.active.active,
|
||||
.datepicker table tr td span.active:hover.active,
|
||||
.datepicker table tr td span.active.disabled.active,
|
||||
.datepicker table tr td span.active.disabled:hover.active {
|
||||
background-color: #003399 \9;
|
||||
}
|
||||
.datepicker table tr td span.old {
|
||||
color: #999999;
|
||||
}
|
||||
.datepicker th.datepicker-switch {
|
||||
width: 145px;
|
||||
}
|
||||
.datepicker thead tr:first-child th,
|
||||
.datepicker tfoot tr:first-child th {
|
||||
cursor: pointer;
|
||||
}
|
||||
.datepicker thead tr:first-child th:hover,
|
||||
.datepicker tfoot tr:first-child th:hover {
|
||||
background: #eeeeee;
|
||||
}
|
||||
.datepicker .cw {
|
||||
font-size: 10px;
|
||||
width: 12px;
|
||||
padding: 0 2px 0 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.datepicker thead tr:first-child th.cw {
|
||||
cursor: default;
|
||||
background-color: transparent;
|
||||
}
|
||||
.input-append.date .add-on i,
|
||||
.input-prepend.date .add-on i {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.input-daterange input {
|
||||
text-align: center;
|
||||
}
|
||||
.input-daterange input:first-child {
|
||||
-webkit-border-radius: 3px 0 0 3px;
|
||||
-moz-border-radius: 3px 0 0 3px;
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
.input-daterange input:last-child {
|
||||
-webkit-border-radius: 0 3px 3px 0;
|
||||
-moz-border-radius: 0 3px 3px 0;
|
||||
border-radius: 0 3px 3px 0;
|
||||
}
|
||||
.input-daterange .add-on {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
min-width: 16px;
|
||||
height: 18px;
|
||||
padding: 4px 5px;
|
||||
font-weight: normal;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
text-shadow: 0 1px 0 #ffffff;
|
||||
vertical-align: middle;
|
||||
background-color: #eeeeee;
|
||||
border: 1px solid #ccc;
|
||||
margin-left: -5px;
|
||||
margin-right: -5px;
|
||||
}
|
||||
1177
inst/www/shared/datepicker/js/bootstrap-datepicker.js
vendored
Normal file
2
inst/www/shared/datepicker/js/bootstrap-datepicker.min.js
vendored
Normal file
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.bg.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Bulgarian translation for bootstrap-datepicker
|
||||
* Apostol Apostolov <apostol.s.apostolov@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['bg'] = {
|
||||
days: ["Неделя", "Понеделник", "Вторник", "Сряда", "Четвъртък", "Петък", "Събота", "Неделя"],
|
||||
daysShort: ["Нед", "Пон", "Вто", "Сря", "Чет", "Пет", "Съб", "Нед"],
|
||||
daysMin: ["Н", "П", "В", "С", "Ч", "П", "С", "Н"],
|
||||
months: ["Януари", "Февруари", "Март", "Април", "Май", "Юни", "Юли", "Август", "Септември", "Октомври", "Ноември", "Декември"],
|
||||
monthsShort: ["Ян", "Фев", "Мар", "Апр", "Май", "Юни", "Юли", "Авг", "Сеп", "Окт", "Ное", "Дек"],
|
||||
today: "днес"
|
||||
};
|
||||
}(jQuery));
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.ca.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Catalan translation for bootstrap-datepicker
|
||||
* J. Garcia <jogaco.en@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['ca'] = {
|
||||
days: ["Diumenge", "Dilluns", "Dimarts", "Dimecres", "Dijous", "Divendres", "Dissabte", "Diumenge"],
|
||||
daysShort: ["Diu", "Dil", "Dmt", "Dmc", "Dij", "Div", "Dis", "Diu"],
|
||||
daysMin: ["dg", "dl", "dt", "dc", "dj", "dv", "ds", "dg"],
|
||||
months: ["Gener", "Febrer", "Març", "Abril", "Maig", "Juny", "Juliol", "Agost", "Setembre", "Octubre", "Novembre", "Desembre"],
|
||||
monthsShort: ["Gen", "Feb", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Oct", "Nov", "Des"],
|
||||
today: "Avui"
|
||||
};
|
||||
}(jQuery));
|
||||
15
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.cs.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Czech translation for bootstrap-datepicker
|
||||
* Matěj Koubík <matej@koubik.name>
|
||||
* Fixes by Michal Remiš <michal.remis@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['cs'] = {
|
||||
days: ["Neděle", "Pondělí", "Úterý", "Středa", "Čtvrtek", "Pátek", "Sobota", "Neděle"],
|
||||
daysShort: ["Ned", "Pon", "Úte", "Stř", "Čtv", "Pát", "Sob", "Ned"],
|
||||
daysMin: ["Ne", "Po", "Út", "St", "Čt", "Pá", "So", "Ne"],
|
||||
months: ["Leden", "Únor", "Březen", "Duben", "Květen", "Červen", "Červenec", "Srpen", "Září", "Říjen", "Listopad", "Prosinec"],
|
||||
monthsShort: ["Led", "Úno", "Bře", "Dub", "Kvě", "Čer", "Čnc", "Srp", "Zář", "Říj", "Lis", "Pro"],
|
||||
today: "Dnes"
|
||||
};
|
||||
}(jQuery));
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.da.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Danish translation for bootstrap-datepicker
|
||||
* Christian Pedersen <http://github.com/chripede>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['da'] = {
|
||||
days: ["Søndag", "Mandag", "Tirsdag", "Onsdag", "Torsdag", "Fredag", "Lørdag", "Søndag"],
|
||||
daysShort: ["Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør", "Søn"],
|
||||
daysMin: ["Sø", "Ma", "Ti", "On", "To", "Fr", "Lø", "Sø"],
|
||||
months: ["Januar", "Februar", "Marts", "April", "Maj", "Juni", "Juli", "August", "September", "Oktober", "November", "December"],
|
||||
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"],
|
||||
today: "I Dag"
|
||||
};
|
||||
}(jQuery));
|
||||
16
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.de.js
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* German translation for bootstrap-datepicker
|
||||
* Sam Zurcher <sam@orelias.ch>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['de'] = {
|
||||
days: ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"],
|
||||
daysShort: ["Son", "Mon", "Die", "Mit", "Don", "Fre", "Sam", "Son"],
|
||||
daysMin: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"],
|
||||
months: ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"],
|
||||
monthsShort: ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"],
|
||||
today: "Heute",
|
||||
weekStart: 1,
|
||||
format: "dd.mm.yyyy"
|
||||
};
|
||||
}(jQuery));
|
||||
13
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.el.js
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Greek translation for bootstrap-datepicker
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['el'] = {
|
||||
days: ["Κυριακή", "Δευτέρα", "Τρίτη", "Τετάρτη", "Πέμπτη", "Παρασκευή", "Σάββατο", "Κυριακή"],
|
||||
daysShort: ["Κυρ", "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ", "Κυρ"],
|
||||
daysMin: ["Κυ", "Δε", "Τρ", "Τε", "Πε", "Πα", "Σα", "Κυ"],
|
||||
months: ["Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος"],
|
||||
monthsShort: ["Ιαν", "Φεβ", "Μαρ", "Απρ", "Μάι", "Ιουν", "Ιουλ", "Αυγ", "Σεπ", "Οκτ", "Νοε", "Δεκ"],
|
||||
today: "Σήμερα"
|
||||
};
|
||||
}(jQuery));
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.es.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Spanish translation for bootstrap-datepicker
|
||||
* Bruno Bonamin <bruno.bonamin@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['es'] = {
|
||||
days: ["Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo"],
|
||||
daysShort: ["Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb", "Dom"],
|
||||
daysMin: ["Do", "Lu", "Ma", "Mi", "Ju", "Vi", "Sa", "Do"],
|
||||
months: ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"],
|
||||
monthsShort: ["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"],
|
||||
today: "Hoy"
|
||||
};
|
||||
}(jQuery));
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.fi.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Finnish translation for bootstrap-datepicker
|
||||
* Jaakko Salonen <https://github.com/jsalonen>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['fi'] = {
|
||||
days: ["sunnuntai", "maanantai", "tiistai", "keskiviikko", "torstai", "perjantai", "lauantai", "sunnuntai"],
|
||||
daysShort: ["sun", "maa", "tii", "kes", "tor", "per", "lau", "sun"],
|
||||
daysMin: ["su", "ma", "ti", "ke", "to", "pe", "la", "su"],
|
||||
months: ["tammikuu", "helmikuu", "maaliskuu", "huhtikuu", "toukokuu", "kesäkuu", "heinäkuu", "elokuu", "syyskuu", "lokakuu", "marraskuu", "joulukuu"],
|
||||
monthsShort: ["tam", "hel", "maa", "huh", "tou", "kes", "hei", "elo", "syy", "lok", "mar", "jou"],
|
||||
today: "tänään"
|
||||
};
|
||||
}(jQuery));
|
||||
16
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.fr.js
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* French translation for bootstrap-datepicker
|
||||
* Nico Mollet <nico.mollet@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['fr'] = {
|
||||
days: ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche"],
|
||||
daysShort: ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam", "Dim"],
|
||||
daysMin: ["D", "L", "Ma", "Me", "J", "V", "S", "D"],
|
||||
months: ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"],
|
||||
monthsShort: ["Jan", "Fev", "Mar", "Avr", "Mai", "Jui", "Jul", "Aou", "Sep", "Oct", "Nov", "Dec"],
|
||||
today: "Aujourd'hui",
|
||||
weekStart: 1,
|
||||
format: "dd/mm/yyyy"
|
||||
};
|
||||
}(jQuery));
|
||||
15
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.he.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Hebrew translation for bootstrap-datepicker
|
||||
* Sagie Maoz <sagie@maoz.info>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['he'] = {
|
||||
days: ["ראשון", "שני", "שלישי", "רביעי", "חמישי", "שישי", "שבת", "ראשון"],
|
||||
daysShort: ["א", "ב", "ג", "ד", "ה", "ו", "ש", "א"],
|
||||
daysMin: ["א", "ב", "ג", "ד", "ה", "ו", "ש", "א"],
|
||||
months: ["ינואר", "פברואר", "מרץ", "אפריל", "מאי", "יוני", "יולי", "אוגוסט", "ספטמבר", "אוקטובר", "נובמבר", "דצמבר"],
|
||||
monthsShort: ["ינו", "פבר", "מרץ", "אפר", "מאי", "יונ", "יול", "אוג", "ספט", "אוק", "נוב", "דצמ"],
|
||||
today: "היום",
|
||||
rtl: true
|
||||
};
|
||||
}(jQuery));
|
||||
13
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.hr.js
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Croatian localisation
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['hr'] = {
|
||||
days: ["Nedjelja", "Ponedjelja", "Utorak", "Srijeda", "Četrtak", "Petak", "Subota", "Nedjelja"],
|
||||
daysShort: ["Ned", "Pon", "Uto", "Srr", "Čet", "Pet", "Sub", "Ned"],
|
||||
daysMin: ["Ne", "Po", "Ut", "Sr", "Če", "Pe", "Su", "Ne"],
|
||||
months: ["Siječanj", "Veljača", "Ožujak", "Travanj", "Svibanj", "Lipanj", "Srpanj", "Kolovoz", "Rujan", "Listopad", "Studeni", "Prosinac"],
|
||||
monthsShort: ["Sije", "Velj", "Ožu", "Tra", "Svi", "Lip", "Jul", "Kol", "Ruj", "Lis", "Stu", "Pro"],
|
||||
today: "Danas"
|
||||
};
|
||||
}(jQuery));
|
||||
16
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.hu.js
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Hungarian translation for bootstrap-datepicker
|
||||
* Sotus László <lacisan@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['hu'] = {
|
||||
days: ["Vasárnap", "Hétfő", "Kedd", "Szerda", "Csütörtök", "Péntek", "Szombat", "Vasárnap"],
|
||||
daysShort: ["Vas", "Hét", "Ked", "Sze", "Csü", "Pén", "Szo", "Vas"],
|
||||
daysMin: ["Va", "Hé", "Ke", "Sz", "Cs", "Pé", "Sz", "Va"],
|
||||
months: ["Január", "Február", "Március", "Április", "Május", "Június", "Július", "Augusztus", "Szeptember", "Október", "November", "December"],
|
||||
monthsShort: ["Jan", "Feb", "Már", "Ápr", "Máj", "Jún", "Júl", "Aug", "Sze", "Okt", "Nov", "Dec"],
|
||||
today: "Ma",
|
||||
weekStart: 1,
|
||||
format: "yyyy.mm.dd"
|
||||
};
|
||||
}(jQuery));
|
||||
13
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.id.js
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Bahasa translation for bootstrap-datepicker
|
||||
* Azwar Akbar <azwar.akbar@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['id'] = {
|
||||
days: ["Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu", "Minggu"],
|
||||
daysShort: ["Mgu", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab", "Mgu"],
|
||||
daysMin: ["Mg", "Sn", "Sl", "Ra", "Ka", "Ju", "Sa", "Mg"],
|
||||
months: ["Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember"],
|
||||
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Ags", "Sep", "Okt", "Nov", "Des"]
|
||||
};
|
||||
}(jQuery));
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.is.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Icelandic translation for bootstrap-datepicker
|
||||
* Hinrik Örn Sigurðsson <hinrik.sig@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['is'] = {
|
||||
days: ["Sunnudagur", "Mánudagur", "Þriðjudagur", "Miðvikudagur", "Fimmtudagur", "Föstudagur", "Laugardagur", "Sunnudagur"],
|
||||
daysShort: ["Sun", "Mán", "Þri", "Mið", "Fim", "Fös", "Lau", "Sun"],
|
||||
daysMin: ["Su", "Má", "Þr", "Mi", "Fi", "Fö", "La", "Su"],
|
||||
months: ["Janúar", "Febrúar", "Mars", "Apríl", "Maí", "Júní", "Júlí", "Ágúst", "September", "Október", "Nóvember", "Desember"],
|
||||
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maí", "Jún", "Júl", "Ágú", "Sep", "Okt", "Nóv", "Des"],
|
||||
today: "Í Dag"
|
||||
};
|
||||
}(jQuery));
|
||||
16
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.it.js
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Italian translation for bootstrap-datepicker
|
||||
* Enrico Rubboli <rubboli@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['it'] = {
|
||||
days: ["Domenica", "Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì", "Sabato", "Domenica"],
|
||||
daysShort: ["Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab", "Dom"],
|
||||
daysMin: ["Do", "Lu", "Ma", "Me", "Gi", "Ve", "Sa", "Do"],
|
||||
months: ["Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"],
|
||||
monthsShort: ["Gen", "Feb", "Mar", "Apr", "Mag", "Giu", "Lug", "Ago", "Set", "Ott", "Nov", "Dic"],
|
||||
today: "Oggi",
|
||||
weekStart: 1,
|
||||
format: "dd/mm/yyyy"
|
||||
};
|
||||
}(jQuery));
|
||||
15
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.ja.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Japanese translation for bootstrap-datepicker
|
||||
* Norio Suzuki <https://github.com/suzuki/>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['ja'] = {
|
||||
days: ["日曜", "月曜", "火曜", "水曜", "木曜", "金曜", "土曜", "日曜"],
|
||||
daysShort: ["日", "月", "火", "水", "木", "金", "土", "日"],
|
||||
daysMin: ["日", "月", "火", "水", "木", "金", "土", "日"],
|
||||
months: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
|
||||
monthsShort: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
|
||||
today: "今日",
|
||||
format: "yyyy/mm/dd"
|
||||
};
|
||||
}(jQuery));
|
||||
13
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.kr.js
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Korean translation for bootstrap-datepicker
|
||||
* Gu Youn <http://github.com/guyoun>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['kr'] = {
|
||||
days: ["일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일", "일요일"],
|
||||
daysShort: ["일", "월", "화", "수", "목", "금", "토", "일"],
|
||||
daysMin: ["일", "월", "화", "수", "목", "금", "토", "일"],
|
||||
months: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"],
|
||||
monthsShort: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"]
|
||||
};
|
||||
}(jQuery));
|
||||
16
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.lt.js
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Lithuanian translation for bootstrap-datepicker
|
||||
* Šarūnas Gliebus <ssharunas@yahoo.co.uk>
|
||||
*/
|
||||
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['lt'] = {
|
||||
days: ["Sekmadienis", "Pirmadienis", "Antradienis", "Trečiadienis", "Ketvirtadienis", "Penktadienis", "Šeštadienis", "Sekmadienis"],
|
||||
daysShort: ["S", "Pr", "A", "T", "K", "Pn", "Š", "S"],
|
||||
daysMin: ["Sk", "Pr", "An", "Tr", "Ke", "Pn", "Št", "Sk"],
|
||||
months: ["Sausis", "Vasaris", "Kovas", "Balandis", "Gegužė", "Birželis", "Liepa", "Rugpjūtis", "Rugsėjis", "Spalis", "Lapkritis", "Gruodis"],
|
||||
monthsShort: ["Sau", "Vas", "Kov", "Bal", "Geg", "Bir", "Lie", "Rugp", "Rugs", "Spa", "Lap", "Gru"],
|
||||
today: "Šiandien",
|
||||
weekStart: 1
|
||||
};
|
||||
}(jQuery));
|
||||
16
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.lv.js
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Latvian translation for bootstrap-datepicker
|
||||
* Artis Avotins <artis@apit.lv>
|
||||
*/
|
||||
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['lv'] = {
|
||||
days: ["Svētdiena", "Pirmdiena", "Otrdiena", "Trešdiena", "Ceturtdiena", "Piektdiena", "Sestdiena", "Svētdiena"],
|
||||
daysShort: ["Sv", "P", "O", "T", "C", "Pk", "S", "Sv"],
|
||||
daysMin: ["Sv", "Pr", "Ot", "Tr", "Ce", "Pk", "St", "Sv"],
|
||||
months: ["Janvāris", "Februāris", "Marts", "Aprīlis", "Maijs", "Jūnijs", "Jūlijs", "Augusts", "Septembris", "Oktobris", "Novembris", "Decembris"],
|
||||
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mai", "Jūn", "Jūl", "Aug", "Sep", "Okt", "Nov", "Dec."],
|
||||
today: "Šodien",
|
||||
weekStart: 1
|
||||
};
|
||||
}(jQuery));
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.ms.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Malay translation for bootstrap-datepicker
|
||||
* Ateman Faiz <noorulfaiz@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['ms'] = {
|
||||
days: ["Ahad", "Isnin", "Selasa", "Rabu", "Khamis", "Jumaat", "Sabtu", "Ahad"],
|
||||
daysShort: ["Aha", "Isn", "Sel", "Rab", "Kha", "Jum", "Sab", "Aha"],
|
||||
daysMin: ["Ah", "Is", "Se", "Ra", "Kh", "Ju", "Sa", "Ah"],
|
||||
months: ["Januari", "Februari", "Mac", "April", "Mei", "Jun", "Julai", "Ogos", "September", "Oktober", "November", "Disember"],
|
||||
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Ogo", "Sep", "Okt", "Nov", "Dis"],
|
||||
today: "Hari Ini"
|
||||
};
|
||||
}(jQuery));
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.nb.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Norwegian (bokmål) translation for bootstrap-datepicker
|
||||
* Fredrik Sundmyhr <http://github.com/fsundmyhr>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['nb'] = {
|
||||
days: ["Søndag", "Mandag", "Tirsdag", "Onsdag", "Torsdag", "Fredag", "Lørdag", "Søndag"],
|
||||
daysShort: ["Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør", "Søn"],
|
||||
daysMin: ["Sø", "Ma", "Ti", "On", "To", "Fr", "Lø", "Sø"],
|
||||
months: ["Januar", "Februar", "Mars", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Desember"],
|
||||
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Des"],
|
||||
today: "I Dag"
|
||||
};
|
||||
}(jQuery));
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.nl.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Dutch translation for bootstrap-datepicker
|
||||
* Reinier Goltstein <mrgoltstein@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['nl'] = {
|
||||
days: ["Zondag", "Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrijdag", "Zaterdag", "Zondag"],
|
||||
daysShort: ["Zo", "Ma", "Di", "Wo", "Do", "Vr", "Za", "Zo"],
|
||||
daysMin: ["Zo", "Ma", "Di", "Wo", "Do", "Vr", "Za", "Zo"],
|
||||
months: ["Januari", "Februari", "Maart", "April", "Mei", "Juni", "Juli", "Augustus", "September", "Oktober", "November", "December"],
|
||||
monthsShort: ["Jan", "Feb", "Mrt", "Apr", "Mei", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"],
|
||||
today: "Vandaag"
|
||||
};
|
||||
}(jQuery));
|
||||
15
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.pl.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Polish translation for bootstrap-datepicker
|
||||
* Robert <rtpm@gazeta.pl>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['pl'] = {
|
||||
days: ["Niedziela", "Poniedziałek", "Wtorek", "Środa", "Czwartek", "Piątek", "Sobota", "Niedziela"],
|
||||
daysShort: ["Nie", "Pn", "Wt", "Śr", "Czw", "Pt", "So", "Nie"],
|
||||
daysMin: ["N", "Pn", "Wt", "Śr", "Cz", "Pt", "So", "N"],
|
||||
months: ["Styczeń", "Luty", "Marzec", "Kwiecień", "Maj", "Czerwiec", "Lipiec", "Sierpień", "Wrzesień", "Październik", "Listopad", "Grudzień"],
|
||||
monthsShort: ["Sty", "Lu", "Mar", "Kw", "Maj", "Cze", "Lip", "Sie", "Wrz", "Pa", "Lis", "Gru"],
|
||||
today: "Dzisiaj",
|
||||
weekStart: 1
|
||||
};
|
||||
}(jQuery));
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.pt-BR.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Brazilian translation for bootstrap-datepicker
|
||||
* Cauan Cabral <cauan@radig.com.br>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['pt-BR'] = {
|
||||
days: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado", "Domingo"],
|
||||
daysShort: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb", "Dom"],
|
||||
daysMin: ["Do", "Se", "Te", "Qu", "Qu", "Se", "Sa", "Do"],
|
||||
months: ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"],
|
||||
monthsShort: ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"],
|
||||
today: "Hoje"
|
||||
};
|
||||
}(jQuery));
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.pt.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Portuguese translation for bootstrap-datepicker
|
||||
* Original code: Cauan Cabral <cauan@radig.com.br>
|
||||
* Tiago Melo <tiago.blackcode@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['pt'] = {
|
||||
days: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado", "Domingo"],
|
||||
daysShort: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb", "Dom"],
|
||||
daysMin: ["Do", "Se", "Te", "Qu", "Qu", "Se", "Sa", "Do"],
|
||||
months: ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"],
|
||||
monthsShort: ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"]
|
||||
};
|
||||
}(jQuery));
|
||||
15
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.ro.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Romanian translation for bootstrap-datepicker
|
||||
* Cristian Vasile <cristi.mie@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['ro'] = {
|
||||
days: ["Duminică", "Luni", "Marţi", "Miercuri", "Joi", "Vineri", "Sâmbătă", "Duminică"],
|
||||
daysShort: ["Dum", "Lun", "Mar", "Mie", "Joi", "Vin", "Sâm", "Dum"],
|
||||
daysMin: ["Du", "Lu", "Ma", "Mi", "Jo", "Vi", "Sâ", "Du"],
|
||||
months: ["Ianuarie", "Februarie", "Martie", "Aprilie", "Mai", "Iunie", "Iulie", "August", "Septembrie", "Octombrie", "Noiembrie", "Decembrie"],
|
||||
monthsShort: ["Ian", "Feb", "Mar", "Apr", "Mai", "Iun", "Iul", "Aug", "Sep", "Oct", "Nov", "Dec"],
|
||||
today: "Astăzi",
|
||||
weekStart: 1
|
||||
};
|
||||
}(jQuery));
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.rs-latin.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Serbian latin translation for bootstrap-datepicker
|
||||
* Bojan Milosavlević <milboj@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['rs'] = {
|
||||
days: ["Nedelja","Ponedeljak", "Utorak", "Sreda", "Četvrtak", "Petak", "Subota", "Nedelja"],
|
||||
daysShort: ["Ned", "Pon", "Uto", "Sre", "Čet", "Pet", "Sub", "Ned"],
|
||||
daysMin: ["N", "Po", "U", "Sr", "Č", "Pe", "Su", "N"],
|
||||
months: ["Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar"],
|
||||
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Avg", "Sep", "Okt", "Nov", "Dec"],
|
||||
today: "Danas"
|
||||
};
|
||||
}(jQuery));
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.rs.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Serbian cyrillic translation for bootstrap-datepicker
|
||||
* Bojan Milosavlević <milboj@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['rs'] = {
|
||||
days: ["Недеља","Понедељак", "Уторак", "Среда", "Четвртак", "Петак", "Субота", "Недеља"],
|
||||
daysShort: ["Нед", "Пон", "Уто", "Сре", "Чет", "Пет", "Суб", "Нед"],
|
||||
daysMin: ["Н", "По", "У", "Ср", "Ч", "Пе", "Су", "Н"],
|
||||
months: ["Јануар", "Фебруар", "Март", "Април", "Мај", "Јун", "Јул", "Август", "Септембар", "Октобар", "Новембар", "Децембар"],
|
||||
monthsShort: ["Јан", "Феб", "Мар", "Апр", "Мај", "Јун", "Јул", "Авг", "Сеп", "Окт", "Нов", "Дец"],
|
||||
today: "Данас"
|
||||
};
|
||||
}(jQuery));
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.ru.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Russian translation for bootstrap-datepicker
|
||||
* Victor Taranenko <darwin@snowdale.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['ru'] = {
|
||||
days: ["Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота", "Воскресенье"],
|
||||
daysShort: ["Вск", "Пнд", "Втр", "Срд", "Чтв", "Птн", "Суб", "Вск"],
|
||||
daysMin: ["Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"],
|
||||
months: ["Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"],
|
||||
monthsShort: ["Янв", "Фев", "Мар", "Апр", "Май", "Июн", "Июл", "Авг", "Сен", "Окт", "Ноя", "Дек"],
|
||||
today: "Сегодня"
|
||||
};
|
||||
}(jQuery));
|
||||
15
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.sk.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Slovak translation for bootstrap-datepicker
|
||||
* Marek Lichtner <marek@licht.sk>
|
||||
* Fixes by Michal Remiš <michal.remis@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates["sk"] = {
|
||||
days: ["Nedeľa", "Pondelok", "Utorok", "Streda", "Štvrtok", "Piatok", "Sobota", "Nedeľa"],
|
||||
daysShort: ["Ned", "Pon", "Uto", "Str", "Štv", "Pia", "Sob", "Ned"],
|
||||
daysMin: ["Ne", "Po", "Ut", "St", "Št", "Pia", "So", "Ne"],
|
||||
months: ["Január", "Február", "Marec", "Apríl", "Máj", "Jún", "Júl", "August", "September", "Október", "November", "December"],
|
||||
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Máj", "Jún", "Júl", "Aug", "Sep", "Okt", "Nov", "Dec"],
|
||||
today: "Dnes"
|
||||
};
|
||||
}(jQuery));
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.sl.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Slovene translation for bootstrap-datepicker
|
||||
* Gregor Rudolf <gregor.rudolf@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['sl'] = {
|
||||
days: ["Nedelja", "Ponedeljek", "Torek", "Sreda", "Četrtek", "Petek", "Sobota", "Nedelja"],
|
||||
daysShort: ["Ned", "Pon", "Tor", "Sre", "Čet", "Pet", "Sob", "Ned"],
|
||||
daysMin: ["Ne", "Po", "To", "Sr", "Če", "Pe", "So", "Ne"],
|
||||
months: ["Januar", "Februar", "Marec", "April", "Maj", "Junij", "Julij", "Avgust", "September", "Oktober", "November", "December"],
|
||||
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Avg", "Sep", "Okt", "Nov", "Dec"],
|
||||
today: "Danes"
|
||||
};
|
||||
}(jQuery));
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.sv.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Swedish translation for bootstrap-datepicker
|
||||
* Patrik Ragnarsson <patrik@starkast.net>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['sv'] = {
|
||||
days: ["Söndag", "Måndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "Lördag", "Söndag"],
|
||||
daysShort: ["Sön", "Mån", "Tis", "Ons", "Tor", "Fre", "Lör", "Sön"],
|
||||
daysMin: ["Sö", "Må", "Ti", "On", "To", "Fr", "Lö", "Sö"],
|
||||
months: ["Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December"],
|
||||
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"],
|
||||
today: "I Dag"
|
||||
};
|
||||
}(jQuery));
|
||||
15
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.sw.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Swahili translation for bootstrap-datepicker
|
||||
* Edwin Mugendi <https://github.com/edwinmugendi>
|
||||
* Source: http://scriptsource.org/cms/scripts/page.php?item_id=entry_detail&uid=xnfaqyzcku
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['sw'] = {
|
||||
days: ["Jumapili", "Jumatatu", "Jumanne", "Jumatano", "Alhamisi", "Ijumaa", "Jumamosi", "Jumapili"],
|
||||
daysShort: ["J2", "J3", "J4", "J5", "Alh", "Ij", "J1", "J2"],
|
||||
daysMin: ["2", "3", "4", "5", "A", "I", "1", "2"],
|
||||
months: ["Januari", "Februari", "Machi", "Aprili", "Mei", "Juni", "Julai", "Agosti", "Septemba", "Oktoba", "Novemba", "Desemba"],
|
||||
monthsShort: ["Jan", "Feb", "Mac", "Apr", "Mei", "Jun", "Jul", "Ago", "Sep", "Okt", "Nov", "Des"],
|
||||
today: "Leo"
|
||||
};
|
||||
}(jQuery));
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.th.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Thai translation for bootstrap-datepicker
|
||||
* Suchau Jiraprapot <seroz24@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['th'] = {
|
||||
days: ["อาทิตย์", "จันทร์", "อังคาร", "พุธ", "พฤหัส", "ศุกร์", "เสาร์", "อาทิตย์"],
|
||||
daysShort: ["อา", "จ", "อ", "พ", "พฤ", "ศ", "ส", "อา"],
|
||||
daysMin: ["อา", "จ", "อ", "พ", "พฤ", "ศ", "ส", "อา"],
|
||||
months: ["มกราคม", "กุมภาพันธ์", "มีนาคม", "เมษายน", "พฤษภาคม", "มิถุนายน", "กรกฎาคม", "สิงหาคม", "กันยายน", "ตุลาคม", "พฤศจิกายน", "ธันวาคม"],
|
||||
monthsShort: ["ม.ค.", "ก.พ.", "มี.ค.", "เม.ย.", "พ.ค.", "มิ.ย.", "ก.ค.", "ส.ค.", "ก.ย.", "ต.ค.", "พ.ย.", "ธ.ค."],
|
||||
today: "วันนี้"
|
||||
};
|
||||
}(jQuery));
|
||||
15
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.tr.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Turkish translation for bootstrap-datepicker
|
||||
* Serkan Algur <kaisercrazy_2@hotmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['tr'] = {
|
||||
days: ["Pazar", "Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi", "Pazar"],
|
||||
daysShort: ["Pz", "Pzt", "Sal", "Çrş", "Prş", "Cu", "Cts", "Pz"],
|
||||
daysMin: ["Pz", "Pzt", "Sa", "Çr", "Pr", "Cu", "Ct", "Pz"],
|
||||
months: ["Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"],
|
||||
monthsShort: ["Oca", "Şub", "Mar", "Nis", "May", "Haz", "Tem", "Ağu", "Eyl", "Eki", "Kas", "Ara"],
|
||||
today: "Bugün"
|
||||
};
|
||||
}(jQuery));
|
||||
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.uk.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Ukrainian translation for bootstrap-datepicker
|
||||
* Andrey Vityuk <andrey [dot] vityuk [at] gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['uk'] = {
|
||||
days: ["Неділя", "Понеділок", "Вівторок", "Середа", "Четвер", "П'ятниця", "Субота", "Неділя"],
|
||||
daysShort: ["Нед", "Пнд", "Втр", "Срд", "Чтв", "Птн", "Суб", "Нед"],
|
||||
daysMin: ["Нд", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Нд"],
|
||||
months: ["Січень", "Лютий", "Березень", "Квітень", "Травень", "Червень", "Липень", "Серпень", "Вересень", "Жовтень", "Листопад", "Грудень"],
|
||||
monthsShort: ["Січ", "Лют", "Бер", "Кві", "Тра", "Чер", "Лип", "Сер", "Вер", "Жов", "Лис", "Гру"],
|
||||
today: "Сьогодні"
|
||||
};
|
||||
}(jQuery));
|
||||
14
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.zh-CN.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Simplified Chinese translation for bootstrap-datepicker
|
||||
* Yuan Cheung <advanimal@gmail.com>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['zh-CN'] = {
|
||||
days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"],
|
||||
daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六", "周日"],
|
||||
daysMin: ["日", "一", "二", "三", "四", "五", "六", "日"],
|
||||
months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
|
||||
monthsShort: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
|
||||
today: "今日"
|
||||
};
|
||||
}(jQuery));
|
||||
13
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.zh-TW.js
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Traditional Chinese translation for bootstrap-datepicker
|
||||
* Rung-Sheng Jang <daniel@i-trend.co.cc>
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['zh-TW'] = {
|
||||
days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"],
|
||||
daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六", "周日"],
|
||||
daysMin: ["日", "一", "二", "三", "四", "五", "六", "日"],
|
||||
months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
|
||||
monthsShort: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"]
|
||||
};
|
||||
}(jQuery));
|
||||
230
inst/www/shared/datepicker/less/datepicker.less
Normal file
@@ -0,0 +1,230 @@
|
||||
/*!
|
||||
* Datepicker for Bootstrap
|
||||
*
|
||||
* Copyright 2012 Stefan Petre
|
||||
* Improvements by Andrew Rowls
|
||||
* Licensed under the Apache License v2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
.datepicker {
|
||||
padding: 4px;
|
||||
.border-radius(4px);
|
||||
&-inline {
|
||||
width: 220px;
|
||||
}
|
||||
direction: ltr;
|
||||
&&-rtl {
|
||||
direction: rtl;
|
||||
table tr td span {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
&-dropdown {
|
||||
top: 0;
|
||||
left: 0;
|
||||
&:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
border-left: 7px solid transparent;
|
||||
border-right: 7px solid transparent;
|
||||
border-bottom: 7px solid #ccc;
|
||||
border-bottom-color: rgba(0,0,0,.2);
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
left: 6px;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid @white;
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
left: 7px;
|
||||
}
|
||||
}
|
||||
>div {
|
||||
display: none;
|
||||
}
|
||||
&.days div.datepicker-days {
|
||||
display: block;
|
||||
}
|
||||
&.months div.datepicker-months {
|
||||
display: block;
|
||||
}
|
||||
&.years div.datepicker-years {
|
||||
display: block;
|
||||
}
|
||||
table{
|
||||
margin: 0;
|
||||
}
|
||||
td,
|
||||
th{
|
||||
text-align: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
.border-radius(4px);
|
||||
|
||||
border: none;
|
||||
}
|
||||
// Inline display inside a table presents some problems with
|
||||
// border and background colors.
|
||||
.table-striped & table tr {
|
||||
td, th {
|
||||
background-color:transparent;
|
||||
}
|
||||
}
|
||||
table tr td {
|
||||
&.day:hover {
|
||||
background: @grayLighter;
|
||||
cursor: pointer;
|
||||
}
|
||||
&.old,
|
||||
&.new {
|
||||
color: @grayLight;
|
||||
}
|
||||
&.disabled,
|
||||
&.disabled:hover {
|
||||
background: none;
|
||||
color: @grayLight;
|
||||
cursor: default;
|
||||
}
|
||||
&.today,
|
||||
&.today:hover,
|
||||
&.today.disabled,
|
||||
&.today.disabled:hover {
|
||||
@todayBackground: lighten(@orange, 30%);
|
||||
.buttonBackground(@todayBackground, spin(@todayBackground, 20));
|
||||
color: #000 !important;
|
||||
}
|
||||
&.range,
|
||||
&.range:hover,
|
||||
&.range.disabled,
|
||||
&.range.disabled:hover {
|
||||
background:@grayLighter;
|
||||
.border-radius(0);
|
||||
}
|
||||
&.range.today,
|
||||
&.range.today:hover,
|
||||
&.range.today.disabled,
|
||||
&.range.today.disabled:hover {
|
||||
@todayBackground: mix(@orange, @grayLighter);
|
||||
.buttonBackground(@todayBackground, spin(@todayBackground, 20));
|
||||
.border-radius(0);
|
||||
}
|
||||
&.selected,
|
||||
&.selected:hover,
|
||||
&.selected.disabled,
|
||||
&.selected.disabled:hover {
|
||||
.buttonBackground(lighten(@grayLight, 10), darken(@grayLight, 10));
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0,0,0,.25);
|
||||
}
|
||||
&.active,
|
||||
&.active:hover,
|
||||
&.active.disabled,
|
||||
&.active.disabled:hover {
|
||||
.buttonBackground(@btnPrimaryBackground, spin(@btnPrimaryBackground, 20));
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0,0,0,.25);
|
||||
}
|
||||
span {
|
||||
display: block;
|
||||
width: 23%;
|
||||
height: 54px;
|
||||
line-height: 54px;
|
||||
float: left;
|
||||
margin: 1%;
|
||||
cursor: pointer;
|
||||
.border-radius(4px);
|
||||
&:hover {
|
||||
background: @grayLighter;
|
||||
}
|
||||
&.disabled,
|
||||
&.disabled:hover {
|
||||
background:none;
|
||||
color: @grayLight;
|
||||
cursor: default;
|
||||
}
|
||||
&.active,
|
||||
&.active:hover,
|
||||
&.active.disabled,
|
||||
&.active.disabled:hover {
|
||||
.buttonBackground(@btnPrimaryBackground, spin(@btnPrimaryBackground, 20));
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0,0,0,.25);
|
||||
}
|
||||
&.old {
|
||||
color: @grayLight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
th.datepicker-switch {
|
||||
width: 145px;
|
||||
}
|
||||
|
||||
thead tr:first-child th,
|
||||
tfoot tr:first-child th {
|
||||
cursor: pointer;
|
||||
&:hover{
|
||||
background: @grayLighter;
|
||||
}
|
||||
}
|
||||
/*.dow {
|
||||
border-top: 1px solid #ddd !important;
|
||||
}*/
|
||||
|
||||
// Basic styling for calendar-week cells
|
||||
.cw {
|
||||
font-size: 10px;
|
||||
width: 12px;
|
||||
padding: 0 2px 0 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
thead tr:first-child th.cw {
|
||||
cursor: default;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
.input-append,
|
||||
.input-prepend {
|
||||
&.date {
|
||||
.add-on i {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.input-daterange {
|
||||
input {
|
||||
text-align:center;
|
||||
}
|
||||
input:first-child {
|
||||
.border-radius(3px 0 0 3px);
|
||||
}
|
||||
input:last-child {
|
||||
.border-radius(0 3px 3px 0);
|
||||
}
|
||||
.add-on {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
min-width: 16px;
|
||||
height: @baseLineHeight;
|
||||
padding: 4px 5px;
|
||||
font-weight: normal;
|
||||
line-height: @baseLineHeight;
|
||||
text-align: center;
|
||||
text-shadow: 0 1px 0 @white;
|
||||
vertical-align: middle;
|
||||
background-color: @grayLighter;
|
||||
border: 1px solid #ccc;
|
||||
margin-left:-5px;
|
||||
margin-right:-5px;
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1001 B After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 999 B After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 4.9 KiB |
16
man-roxygen/update-input.R
Normal file
@@ -0,0 +1,16 @@
|
||||
#' @details
|
||||
#' The input updater functions send a message to the client, telling it to
|
||||
#' change the settings of an input object. The messages are collected and sent
|
||||
#' after all the observers (including outputs) have finished running.
|
||||
#'
|
||||
#' The syntax of these functions is similar to the functions that created the
|
||||
#' inputs in the first place. For example, \code{\link{numericInput}()} and
|
||||
#' \code{updateNumericInput()} take a similar set of arguments.
|
||||
#'
|
||||
#' Any arguments with NULL values will be ignored; they will not result in any
|
||||
#' changes to the input object on the client.
|
||||
#'
|
||||
#' @param session The \code{session} object passed to function given to
|
||||
#' \code{shinyServer}.
|
||||
#' @param inputId The id of the input object.
|
||||
#' @param label The label to set for the input object.
|
||||
19
man/actionButton.Rd
Normal file
@@ -0,0 +1,19 @@
|
||||
\name{actionButton}
|
||||
\alias{actionButton}
|
||||
\title{Action button}
|
||||
\usage{
|
||||
actionButton(inputId, label)
|
||||
}
|
||||
\arguments{
|
||||
\item{inputId}{Specifies the input slot that will be used
|
||||
to access the value.}
|
||||
|
||||
\item{label}{The contents of the button--usually a text
|
||||
label, but you could also use any other HTML, like an
|
||||
image.}
|
||||
}
|
||||
\description{
|
||||
Creates an action button whose value is initially zero,
|
||||
and increments by one each time it is pressed.
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
\name{bootstrapPage}
|
||||
\alias{basicPage}
|
||||
\alias{bootstrapPage}
|
||||
\title{Create a Twitter Bootstrap page}
|
||||
\usage{
|
||||
bootstrapPage(...)
|
||||
|
||||
basicPage(...)
|
||||
}
|
||||
\arguments{
|
||||
\item{...}{The contents of the document body.}
|
||||
@@ -18,9 +21,13 @@
|
||||
provide).
|
||||
}
|
||||
\details{
|
||||
This function is primarily intended for users who are
|
||||
These functions are primarily intended for users who are
|
||||
proficient in HTML/CSS, and know how to lay out pages in
|
||||
Bootstrap. Most users should use template functions like
|
||||
\code{\link{pageWithSidebar}}.
|
||||
|
||||
\code{basicPage} is the same as \code{bootstrapPage},
|
||||
with an added \code{<div class="container-fluid">}
|
||||
wrapper to provide a little padding.
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ checkboxGroupInput("variable", "Variable:",
|
||||
"Gears" = "gear"))
|
||||
}
|
||||
\seealso{
|
||||
\code{\link{checkboxInput}}
|
||||
\code{\link{checkboxInput}},
|
||||
\code{\link{updateCheckboxGroupInput}}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
checkboxInput("outliers", "Show outliers", FALSE)
|
||||
}
|
||||
\seealso{
|
||||
\code{\link{checkboxGroupInput}}
|
||||
\code{\link{checkboxGroupInput}},
|
||||
\code{\link{updateCheckboxInput}}
|
||||
}
|
||||
|
||||
|
||||
88
man/dateInput.Rd
Normal file
@@ -0,0 +1,88 @@
|
||||
\name{dateInput}
|
||||
\alias{dateInput}
|
||||
\title{Create date input}
|
||||
\usage{
|
||||
dateInput(inputId, label, value = NULL, min = NULL,
|
||||
max = NULL, format = "yyyy-mm-dd", startview = "month",
|
||||
weekstart = 0, language = "en")
|
||||
}
|
||||
\arguments{
|
||||
\item{inputId}{Input variable to assign the control's
|
||||
value to.}
|
||||
|
||||
\item{label}{Display label for the control.}
|
||||
|
||||
\item{value}{The starting date. Either a Date object, or
|
||||
a string in \code{yyyy-mm-dd} format. If NULL (the
|
||||
default), will use the current date in the client's time
|
||||
zone.}
|
||||
|
||||
\item{min}{The minimum allowed date. Either a Date
|
||||
object, or a string in \code{yyyy-mm-dd} format.}
|
||||
|
||||
\item{max}{The maximum allowed date. Either a Date
|
||||
object, or a string in \code{yyyy-mm-dd} format.}
|
||||
|
||||
\item{format}{The format of the date to display in the
|
||||
browser. Defaults to \code{"yyyy-mm-dd"}.}
|
||||
|
||||
\item{startview}{The date range shown when the input
|
||||
object is first clicked. Can be "month" (the default),
|
||||
"year", or "decade".}
|
||||
|
||||
\item{weekstart}{Which day is the start of the week.
|
||||
Should be an integer from 0 (Sunday) to 6 (Saturday).}
|
||||
|
||||
\item{language}{The language used for month and day
|
||||
names. Default is "en". Other valid values include "bg",
|
||||
"ca", "cs", "da", "de", "el", "es", "fi", "fr", "he",
|
||||
"hr", "hu", "id", "is", "it", "ja", "kr", "lt", "lv",
|
||||
"ms", "nb", "nl", "pl", "pt", "pt", "ro", "rs",
|
||||
"rs-latin", "ru", "sk", "sl", "sv", "sw", "th", "tr",
|
||||
"uk", "zh-CN", and "zh-TW".}
|
||||
}
|
||||
\description{
|
||||
Creates a text input which, when clicked on, brings up a
|
||||
calendar that the user can click on to select dates.
|
||||
}
|
||||
\details{
|
||||
The date \code{format} string specifies how the date will
|
||||
be displayed in the browser. It allows the following
|
||||
values:
|
||||
|
||||
\itemize{ \item \code{yy} Year without century (12) \item
|
||||
\code{yyyy} Year with century (2012) \item \code{mm}
|
||||
Month number, with leading zero (01-12) \item \code{m}
|
||||
Month number, without leading zero (01-12) \item \code{M}
|
||||
Abbreviated month name \item \code{MM} Full month name
|
||||
\item \code{dd} Day of month with leading zero \item
|
||||
\code{d} Day of month without leading zero \item \code{D}
|
||||
Abbreviated weekday name \item \code{DD} Full weekday
|
||||
name }
|
||||
}
|
||||
\examples{
|
||||
dateInput("date", "Date:", value = "2012-02-29")
|
||||
|
||||
# Default value is the date in client's time zone
|
||||
dateInput("date", "Date:")
|
||||
|
||||
# value is always yyyy-mm-dd, even if the display format is different
|
||||
dateInput("date", "Date:", value = "2012-02-29", format = "mm/dd/yy")
|
||||
|
||||
# Pass in a Date object
|
||||
dateInput("date", "Date:", value = Sys.Date()-10)
|
||||
|
||||
# Use different language and different first day of week
|
||||
dateInput("date", "Date:",
|
||||
language = "de",
|
||||
weekstart = 1)
|
||||
|
||||
# Start with decade view instead of default month view
|
||||
dateInput("date", "Date:",
|
||||
startview = "decade")
|
||||
}
|
||||
\seealso{
|
||||
\code{\link{dateRangeInput}},
|
||||
\code{\link{updateDateInput}}
|
||||
}
|
||||
|
||||
109
man/dateRangeInput.Rd
Normal file
@@ -0,0 +1,109 @@
|
||||
\name{dateRangeInput}
|
||||
\alias{dateRangeInput}
|
||||
\title{Create date range input}
|
||||
\usage{
|
||||
dateRangeInput(inputId, label, start = NULL, end = NULL,
|
||||
min = NULL, max = NULL, format = "yyyy-mm-dd",
|
||||
startview = "month", weekstart = 0, language = "en",
|
||||
separator = " to ")
|
||||
}
|
||||
\arguments{
|
||||
\item{inputId}{Input variable to assign the control's
|
||||
value to.}
|
||||
|
||||
\item{label}{Display label for the control.}
|
||||
|
||||
\item{start}{The initial start date. Either a Date
|
||||
object, or a string in \code{yyyy-mm-dd} format. If NULL
|
||||
(the default), will use the current date in the client's
|
||||
time zone.}
|
||||
|
||||
\item{end}{The initial end date. Either a Date object, or
|
||||
a string in \code{yyyy-mm-dd} format. If NULL (the
|
||||
default), will use the current date in the client's time
|
||||
zone.}
|
||||
|
||||
\item{min}{The minimum allowed date. Either a Date
|
||||
object, or a string in \code{yyyy-mm-dd} format.}
|
||||
|
||||
\item{max}{The maximum allowed date. Either a Date
|
||||
object, or a string in \code{yyyy-mm-dd} format.}
|
||||
|
||||
\item{format}{The format of the date to display in the
|
||||
browser. Defaults to \code{"yyyy-mm-dd"}.}
|
||||
|
||||
\item{startview}{The date range shown when the input
|
||||
object is first clicked. Can be "month" (the default),
|
||||
"year", or "decade".}
|
||||
|
||||
\item{weekstart}{Which day is the start of the week.
|
||||
Should be an integer from 0 (Sunday) to 6 (Saturday).}
|
||||
|
||||
\item{language}{The language used for month and day
|
||||
names. Default is "en". Other valid values include "bg",
|
||||
"ca", "cs", "da", "de", "el", "es", "fi", "fr", "he",
|
||||
"hr", "hu", "id", "is", "it", "ja", "kr", "lt", "lv",
|
||||
"ms", "nb", "nl", "pl", "pt", "pt", "ro", "rs",
|
||||
"rs-latin", "ru", "sk", "sl", "sv", "sw", "th", "tr",
|
||||
"uk", "zh-CN", and "zh-TW".}
|
||||
|
||||
\item{separator}{String to display between the start and
|
||||
end input boxes.}
|
||||
}
|
||||
\description{
|
||||
Creates a pair of text inputs which, when clicked on,
|
||||
bring up calendars that the user can click on to select
|
||||
dates.
|
||||
}
|
||||
\details{
|
||||
The date \code{format} string specifies how the date will
|
||||
be displayed in the browser. It allows the following
|
||||
values:
|
||||
|
||||
\itemize{ \item \code{yy} Year without century (12) \item
|
||||
\code{yyyy} Year with century (2012) \item \code{mm}
|
||||
Month number, with leading zero (01-12) \item \code{m}
|
||||
Month number, without leading zero (01-12) \item \code{M}
|
||||
Abbreviated month name \item \code{MM} Full month name
|
||||
\item \code{dd} Day of month with leading zero \item
|
||||
\code{d} Day of month without leading zero \item \code{D}
|
||||
Abbreviated weekday name \item \code{DD} Full weekday
|
||||
name }
|
||||
}
|
||||
\examples{
|
||||
dateRangeInput("daterange", "Date range:",
|
||||
start = "2001-01-01",
|
||||
end = "2010-12-31")
|
||||
|
||||
# Default start and end is the current date in the client's time zone
|
||||
dateRangeInput("daterange", "Date range:")
|
||||
|
||||
# start and end are always specified in yyyy-mm-dd, even if the display
|
||||
# format is different
|
||||
dateRangeInput("daterange", "Date range:",
|
||||
start = "2001-01-01",
|
||||
end = "2010-12-31",
|
||||
min = "2001-01-01",
|
||||
max = "2012-12-21",
|
||||
format = "mm/dd/yy",
|
||||
separator = " - ")
|
||||
|
||||
# Pass in Date objects
|
||||
dateRangeInput("daterange", "Date range:",
|
||||
start = Sys.Date()-10,
|
||||
end = Sys.Date()+10)
|
||||
|
||||
# Use different language and different first day of week
|
||||
dateRangeInput("daterange", "Date range:",
|
||||
language = "de",
|
||||
weekstart = 1)
|
||||
|
||||
# Start with decade view instead of default month view
|
||||
dateRangeInput("daterange", "Date range:",
|
||||
startview = "decade")
|
||||
}
|
||||
\seealso{
|
||||
\code{\link{dateInput}},
|
||||
\code{\link{updateDateRangeInput}}
|
||||
}
|
||||
|
||||
@@ -1,54 +1,49 @@
|
||||
\name{includeHTML}
|
||||
\alias{includeCSS}
|
||||
\alias{includeHTML}
|
||||
\alias{includeText}
|
||||
\alias{includeMarkdown}
|
||||
|
||||
\alias{includeScript}
|
||||
\alias{includeText}
|
||||
\title{Include Content From a File}
|
||||
\usage{
|
||||
includeHTML(path)
|
||||
|
||||
includeText(path)
|
||||
|
||||
includeMarkdown(path)
|
||||
|
||||
includeCSS(path, ...)
|
||||
|
||||
includeScript(path, ...)
|
||||
}
|
||||
|
||||
\title{Include Content From a File}
|
||||
|
||||
\arguments{
|
||||
\item{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.
|
||||
}
|
||||
\item{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.}
|
||||
|
||||
\item{...}{Any additional attributes to be applied to the
|
||||
generated tag.}
|
||||
}
|
||||
\description{
|
||||
Include HTML, text, or rendered Markdown into a \link[=shinyUI]{Shiny UI}.
|
||||
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 HTML, textual, or Markdown content, rather than using a large literal R
|
||||
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.
|
||||
\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.
|
||||
|
||||
The \code{includeMarkdown} function requires the
|
||||
\code{markdown} package.
|
||||
}
|
||||
\note{
|
||||
The \code{includeMarkdown} function requires the \code{markdown} package.
|
||||
}
|
||||
\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))
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,43 @@
|
||||
\alias{invalidateLater}
|
||||
\title{Scheduled Invalidation}
|
||||
\usage{
|
||||
invalidateLater(millis)
|
||||
invalidateLater(millis, session)
|
||||
}
|
||||
\arguments{
|
||||
\item{millis}{Approximate milliseconds to wait before
|
||||
invalidating the current reactive context.}
|
||||
|
||||
\item{session}{A session object. This is needed to cancel
|
||||
any scheduled invalidations after a user has ended the
|
||||
session. If \code{NULL}, then this invalidation will not
|
||||
be tied to any session, and so it will still occur.}
|
||||
}
|
||||
\description{
|
||||
Schedules the current reactive context to be invalidated
|
||||
in the given number of milliseconds.
|
||||
}
|
||||
\examples{
|
||||
\dontrun{
|
||||
shinyServer(function(input, output, session) {
|
||||
|
||||
observe({
|
||||
# Re-execute this reactive expression after 1000 milliseconds
|
||||
invalidateLater(1000, session)
|
||||
|
||||
# Do something each time this is invalidated.
|
||||
# The isolate() makes this observer _not_ get invalidated and re-executed
|
||||
# when input$n changes.
|
||||
print(paste("The value of input$n is", isolate(input$n)))
|
||||
})
|
||||
|
||||
# Generate a new histogram at timed intervals, but not when
|
||||
# input$n changes.
|
||||
output$plot <- renderPlot({
|
||||
# Re-execute this reactive expression after 2000 milliseconds
|
||||
invalidateLater(2000, session)
|
||||
hist(isolate(input$n))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,4 +31,7 @@
|
||||
numericInput("obs", "Observations:", 10,
|
||||
min = 1, max = 100)
|
||||
}
|
||||
\seealso{
|
||||
\code{\link{updateNumericInput}}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
\title{Create a reactive observer}
|
||||
\usage{
|
||||
observe(x, env = parent.frame(), quoted = FALSE,
|
||||
label = NULL, suspended = FALSE)
|
||||
label = NULL, suspended = FALSE, priority = 0)
|
||||
}
|
||||
\arguments{
|
||||
\item{x}{An expression (quoted or unquoted). Any return
|
||||
@@ -25,18 +25,48 @@
|
||||
\item{suspended}{If \code{TRUE}, start the observer in a
|
||||
suspended state. If \code{FALSE} (the default), start in
|
||||
a non-suspended state.}
|
||||
|
||||
\item{priority}{An integer or numeric that controls the
|
||||
priority with which this observer should be executed. An
|
||||
observer with a given priority level will always execute
|
||||
sooner than all observers with a lower priority level.
|
||||
Positive, negative, and zero values are allowed.}
|
||||
}
|
||||
\value{
|
||||
An observer reference class object. This object has the
|
||||
following methods: \describe{ \item{\code{suspend()}}{
|
||||
Causes this observer to stop scheduling flushes
|
||||
(re-executions) in response to invalidations. If the
|
||||
observer was invalidated prior to this call but it has
|
||||
not re-executed yet then that re-execution will still
|
||||
occur, because the flush is already scheduled. }
|
||||
\item{\code{resume()}}{ Causes this observer to start
|
||||
re-executing in response to invalidations. If the
|
||||
observer was invalidated while suspended, then it will
|
||||
schedule itself for re-execution. }
|
||||
\item{\code{setPriority(priority = 0)}}{ Change this
|
||||
observer's priority. Note that if the observer is
|
||||
currently invalidated, then the change in priority will
|
||||
not take effect until the next invalidation--unless the
|
||||
observer is also currently suspended, in which case the
|
||||
priority change will be effective upon resume. }
|
||||
\item{\code{onInvalidate(callback)}}{ Register a callback
|
||||
function to run when this observer is invalidated. No
|
||||
arguments will be provided to the callback function when
|
||||
it is invoked. } }
|
||||
}
|
||||
\description{
|
||||
Creates an observer from the given expression An observer
|
||||
is like a reactive expression in that it can read
|
||||
reactive values and call reactive expressions, and will
|
||||
automatically re-execute when those dependencies change.
|
||||
But unlike reactive expression, it doesn't yield a result
|
||||
and can't be used as an input to other reactive
|
||||
expressions. Thus, observers are only useful for their
|
||||
side effects (for example, performing I/O).
|
||||
Creates an observer from the given expression.
|
||||
}
|
||||
\details{
|
||||
An observer is like a reactive expression in that it can
|
||||
read reactive values and call reactive expressions, and
|
||||
will automatically re-execute when those dependencies
|
||||
change. But unlike reactive expressions, it doesn't yield
|
||||
a result and can't be used as an input to other reactive
|
||||
expressions. Thus, observers are only useful for their
|
||||
side effects (for example, performing I/O).
|
||||
|
||||
Another contrast between reactive expressions and
|
||||
observers is their execution strategy. Reactive
|
||||
expressions use lazy evaluation; that is, when their
|
||||
|
||||
@@ -19,7 +19,10 @@
|
||||
execute) when it is hidden on the web page. When
|
||||
\code{FALSE}, the output object will not suspend when
|
||||
hidden, and if it was already hidden and suspended, then
|
||||
it will resume immediately. }
|
||||
it will resume immediately. \item priority. The priority
|
||||
level of the output object. Queued outputs with higher
|
||||
priority values will execute before those with lower
|
||||
values. }
|
||||
}
|
||||
\examples{
|
||||
\dontrun{
|
||||
@@ -29,6 +32,9 @@ outputOptions(output)
|
||||
# Disable suspend for output$myplot
|
||||
outputOptions(output, "myplot", suspendWhenHidden = FALSE)
|
||||
|
||||
# Change priority for output$myplot
|
||||
outputOptions(output, "myplot", priority = 10)
|
||||
|
||||
# Get the list of options for output$myplot
|
||||
outputOptions(output, "myplot")
|
||||
}
|
||||
|
||||
@@ -39,5 +39,9 @@
|
||||
of output. Notably, plain \code{png} output on Linux and
|
||||
Windows may not antialias some point shapes, resulting in
|
||||
poor quality output.
|
||||
|
||||
In some cases, \code{Cairo()} provides output that looks
|
||||
worse than \code{png()}. To disable Cairo output for an
|
||||
app, use \code{options(shiny.usecairo=FALSE)}.
|
||||
}
|
||||
|
||||
|
||||
@@ -32,4 +32,7 @@ radioButtons("dist", "Distribution type:",
|
||||
"Log-normal" = "lnorm",
|
||||
"Exponential" = "exp"))
|
||||
}
|
||||
\seealso{
|
||||
\code{\link{updateRadioButtons}}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,15 @@
|
||||
\alias{reactiveTimer}
|
||||
\title{Timer}
|
||||
\usage{
|
||||
reactiveTimer(intervalMs = 1000)
|
||||
reactiveTimer(intervalMs = 1000, session)
|
||||
}
|
||||
\arguments{
|
||||
\item{intervalMs}{How often to fire, in milliseconds}
|
||||
|
||||
\item{session}{A session object. This is needed to cancel
|
||||
any scheduled invalidations after a user has ended the
|
||||
session. If \code{NULL}, then this invalidation will not
|
||||
be tied to any session, and so it will still occur.}
|
||||
}
|
||||
\value{
|
||||
A no-parameter function that can be called from a
|
||||
@@ -29,6 +34,34 @@
|
||||
See \code{\link{invalidateLater}} as a safer and simpler
|
||||
alternative.
|
||||
}
|
||||
\examples{
|
||||
\dontrun{
|
||||
shinyServer(function(input, output, session) {
|
||||
|
||||
# Anything that calls autoInvalidate will automatically invalidate
|
||||
# every 2 seconds.
|
||||
autoInvalidate <- reactiveTimer(2000, session)
|
||||
|
||||
observe({
|
||||
# Invalidate and re-execute this reactive expression every time the
|
||||
# timer fires.
|
||||
autoInvalidate()
|
||||
|
||||
# Do something each time this is invalidated.
|
||||
# The isolate() makes this observer _not_ get invalidated and re-executed
|
||||
# when input$n changes.
|
||||
print(paste("The value of input$n is", isolate(input$n)))
|
||||
})
|
||||
|
||||
# Generate a new histogram each time the timer fires, but not when
|
||||
# input$n changes.
|
||||
output$plot <- renderPlot({
|
||||
autoInvalidate()
|
||||
hist(isolate(input$n))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
\seealso{
|
||||
invalidateLater
|
||||
}
|
||||
|
||||
@@ -99,4 +99,8 @@ shinyServer(function(input, output, clientData) {
|
||||
|
||||
}
|
||||
}
|
||||
\seealso{
|
||||
For more details on how the images are generated, and how
|
||||
to control the output, see \code{\link{plotPNG}}.
|
||||
}
|
||||
|
||||
|
||||
@@ -51,4 +51,8 @@
|
||||
\code{img} and have the CSS class name
|
||||
\code{shiny-plot-output}.
|
||||
}
|
||||
\seealso{
|
||||
For more details on how the plots are generated, and how
|
||||
to control the output, see \code{\link{plotPNG}}.
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
\title{Run Shiny Application}
|
||||
\usage{
|
||||
runApp(appDir = getwd(), port = 8100L,
|
||||
launch.browser = getOption("shiny.launch.browser", interactive()))
|
||||
launch.browser = getOption("shiny.launch.browser", interactive()),
|
||||
workerId = "")
|
||||
}
|
||||
\arguments{
|
||||
\item{appDir}{The directory of the application. Should
|
||||
@@ -17,10 +18,35 @@
|
||||
\item{launch.browser}{If true, the system's default web
|
||||
browser will be launched automatically after the app is
|
||||
started. Defaults to true in interactive sessions only.}
|
||||
|
||||
\item{workerId}{Can generally be ignored. Exists to help
|
||||
some editions of Shiny Server Pro route requests to the
|
||||
correct process.}
|
||||
}
|
||||
\description{
|
||||
Runs a Shiny application. This function normally does not
|
||||
return; interrupt R to stop the application (usually by
|
||||
pressing Ctrl+C or Esc).
|
||||
}
|
||||
\examples{
|
||||
\dontrun{
|
||||
# Start app in the current working directory
|
||||
runApp()
|
||||
|
||||
# Start app in a subdirectory called myapp
|
||||
runApp("myapp")
|
||||
|
||||
|
||||
# Apps can be run without a server.r and ui.r file
|
||||
runApp(list(
|
||||
ui = bootstrapPage(
|
||||
numericInput('n', 'Number of obs', 100),
|
||||
plotOutput('plot')
|
||||
),
|
||||
server = function(input, output) {
|
||||
output$plot <- renderPlot({ hist(runif(input$n)) })
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,4 +20,16 @@
|
||||
Launch Shiny example applications, and optionally, your
|
||||
system's web browser.
|
||||
}
|
||||
\examples{
|
||||
\dontrun{
|
||||
# List all available examples
|
||||
runExample()
|
||||
|
||||
# Run one of the examples
|
||||
runExample("01_hello")
|
||||
|
||||
# Print the directory containing the code for all examples
|
||||
system.file("examples", package="shiny")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,4 +37,7 @@ selectInput("variable", "Variable:",
|
||||
"Transmission" = "am",
|
||||
"Gears" = "gear"))
|
||||
}
|
||||
\seealso{
|
||||
\code{\link{updateSelectInput}}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,41 +1,20 @@
|
||||
\name{shiny-package}
|
||||
\alias{shiny-package}
|
||||
\alias{shiny}
|
||||
\docType{package}
|
||||
\title{
|
||||
Web Application Framework for R
|
||||
}
|
||||
\name{shiny-package}
|
||||
\alias{shiny}
|
||||
\alias{shiny-package}
|
||||
\title{Web Application Framework for R}
|
||||
\description{
|
||||
Shiny makes it incredibly easy to build interactive web
|
||||
applications with R. Automatic "reactive" binding between inputs and
|
||||
outputs and extensive pre-built widgets make it possible to build
|
||||
beautiful, responsive, and powerful applications with minimal effort.
|
||||
|
||||
The Shiny tutorial at \url{http://rstudio.github.com/shiny/tutorial}
|
||||
explains the framework in-depth, walks you through
|
||||
building a simple application, and includes extensive annotated
|
||||
examples.
|
||||
Shiny makes it incredibly easy to build interactive web
|
||||
applications with R. Automatic "reactive" binding between
|
||||
inputs and outputs and extensive pre-built widgets make
|
||||
it possible to build beautiful, responsive, and powerful
|
||||
applications with minimal effort.
|
||||
}
|
||||
\details{
|
||||
\tabular{ll}{
|
||||
Package: \tab shiny\cr
|
||||
Type: \tab Package\cr
|
||||
Version: \tab 0.1.0\cr
|
||||
Date: \tab 2012-07-28\cr
|
||||
License: \tab GPL-3\cr
|
||||
Depends: \tab R (>= 2.14.1), methods, websockets (>= 1.1.4), caTools, RJSONIO, xtable\cr
|
||||
Imports: \tab stats, tools, utils, datasets\cr
|
||||
URL: \tab https://github.com/rstudio/shiny, http://rstudio.github.com/shiny/tutorial\cr
|
||||
BugReports: \tab https://github.com/rstudio/shiny/issues\cr
|
||||
The Shiny tutorial at
|
||||
\url{http://rstudio.github.com/shiny/tutorial} explains
|
||||
the framework in depth, walks you through building a
|
||||
simple application, and includes extensive annotated
|
||||
examples.
|
||||
}
|
||||
|
||||
}
|
||||
\author{
|
||||
RStudio, Inc.
|
||||
|
||||
Maintainer: Joe Cheng <joe@rstudio.org>
|
||||
}
|
||||
|
||||
|
||||
\keyword{ package }
|
||||
|
||||
|
||||