mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-12 16:38:06 -05:00
Compare commits
23 Commits
v0.14.1
...
barbara/rb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e71e8db0a2 | ||
|
|
6cc2f875b0 | ||
|
|
bcbc6b8b92 | ||
|
|
e133290c57 | ||
|
|
1429b0677e | ||
|
|
d03ee36647 | ||
|
|
6e5880c642 | ||
|
|
fa93cffafb | ||
|
|
ce9af0fb57 | ||
|
|
95700d8d51 | ||
|
|
fb15e98519 | ||
|
|
3054cb7971 | ||
|
|
f84587cf5a | ||
|
|
538f38f314 | ||
|
|
06578349c7 | ||
|
|
a807476171 | ||
|
|
7aacf9ca89 | ||
|
|
50dae5fb83 | ||
|
|
0853c425fe | ||
|
|
edcc676693 | ||
|
|
c8a742a121 | ||
|
|
ee14a7e15f | ||
|
|
e1eaccf409 |
@@ -1,7 +1,7 @@
|
||||
Package: shiny
|
||||
Type: Package
|
||||
Title: Web Application Framework for R
|
||||
Version: 0.14.1
|
||||
Version: 0.14.1.9001
|
||||
Authors@R: c(
|
||||
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@rstudio.com"),
|
||||
person("Joe", "Cheng", role = "aut", email = "joe@rstudio.com"),
|
||||
|
||||
@@ -38,6 +38,7 @@ export(actionButton)
|
||||
export(actionLink)
|
||||
export(addResourcePath)
|
||||
export(animationOptions)
|
||||
export(applyInputHandlers)
|
||||
export(as.shiny.appobj)
|
||||
export(basicPage)
|
||||
export(bookmarkButton)
|
||||
|
||||
23
NEWS.md
23
NEWS.md
@@ -1,3 +1,26 @@
|
||||
shiny 0.14.1.9001
|
||||
============
|
||||
|
||||
## Full changelog
|
||||
|
||||
### Minor new features and improvements
|
||||
|
||||
* HTML markup can now be used in the choices' names for radio buttons. ([#1439](https://github.com/rstudio/shiny/pull/1439))
|
||||
|
||||
* Added a `fade` argument to `modalDialog()` -- setting it to `FALSE` will remove the usual fade-in animation for that modal window. ([#1414](https://github.com/rstudio/shiny/pull/1414))
|
||||
|
||||
* Exported function to apply input handlers to input values. This can be used for testing Shiny applications. ([#1421](https://github.com/rstudio/shiny/pull/1421))
|
||||
|
||||
* Fixed a "duplicate binding" error that occurred in some edge cases involving `insertUI` and nested `uiOutput`. ([#1402](https://github.com/rstudio/shiny/pull/1402))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed [#1427](https://github.com/rstudio/shiny/issues/1427): make sure that modals do not close incorrectly when an element inside them is triggered as hidden. ([#1430](https://github.com/rstudio/shiny/pull/1430))
|
||||
|
||||
* Fixed [#1404](https://github.com/rstudio/shiny/issues/1404): stack trace tests were not compatible with the byte-code compiler in R-devel, which now tracks source references.
|
||||
|
||||
* `sliderInputBinding.setValue()` now sends a slider's value immediately, instead of waiting for the usual 250ms debounce delay. ([#1429](https://github.com/rstudio/shiny/pull/1429))
|
||||
|
||||
shiny 0.14.1
|
||||
============
|
||||
|
||||
|
||||
4
R/app.R
4
R/app.R
@@ -231,13 +231,13 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
|
||||
# ignored when checking extensions. If any changes are detected, all connected
|
||||
# Shiny sessions are reloaded.
|
||||
#
|
||||
# Use option(shiny.autoreload = TRUE) to enable this behavior. Since monitoring
|
||||
# Use options(shiny.autoreload = TRUE) to enable this behavior. Since monitoring
|
||||
# for changes is expensive (we are polling for mtimes here, nothing fancy) this
|
||||
# feature is intended only for development.
|
||||
#
|
||||
# You can customize the file patterns Shiny will monitor by setting the
|
||||
# shiny.autoreload.pattern option. For example, to monitor only ui.R:
|
||||
# option(shiny.autoreload.pattern = glob2rx("ui.R"))
|
||||
# options(shiny.autoreload.pattern = glob2rx("ui.R"))
|
||||
#
|
||||
# The return value is a function that halts monitoring when called.
|
||||
initAutoReloadMonitor <- function(dir) {
|
||||
|
||||
@@ -76,7 +76,7 @@ getCallNames <- function(calls) {
|
||||
}
|
||||
|
||||
getLocs <- function(calls) {
|
||||
sapply(calls, function(call) {
|
||||
vapply(calls, function(call) {
|
||||
srcref <- attr(call, "srcref", exact = TRUE)
|
||||
if (!is.null(srcref)) {
|
||||
srcfile <- attr(srcref, "srcfile", exact = TRUE)
|
||||
@@ -86,7 +86,7 @@ getLocs <- function(calls) {
|
||||
}
|
||||
}
|
||||
return("")
|
||||
})
|
||||
}, character(1))
|
||||
}
|
||||
|
||||
#' @details \code{captureStackTraces} runs the given \code{expr} and if any
|
||||
|
||||
@@ -51,10 +51,10 @@ generateOptions <- function(inputId, choices, selected, inline, type = 'checkbox
|
||||
# If inline, there's no wrapper div, and the label needs a class like
|
||||
# checkbox-inline.
|
||||
if (inline) {
|
||||
tags$label(class = paste0(type, "-inline"), inputTag, tags$span(name))
|
||||
tags$label(class = paste0(type, "-inline"), inputTag, tags$span(HTML(name)))
|
||||
} else {
|
||||
tags$div(class = type,
|
||||
tags$label(inputTag, tags$span(name))
|
||||
tags$label(inputTag, tags$span(HTML(name)))
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -43,6 +43,8 @@ removeModal <- function(session = getDefaultReactiveDomain()) {
|
||||
#' \code{FALSE} (the default), the modal dialog can't be dismissed in those
|
||||
#' ways; instead it must be dismissed by clicking on the dismiss button, or
|
||||
#' from a call to \code{\link{removeModal}} on the server.
|
||||
#' @param fade If \code{FALSE}, the modal dialog will have no fade-in animation
|
||||
#' (it will simply appear rather than fade in to view).
|
||||
#'
|
||||
#' @examples
|
||||
#' if (interactive()) {
|
||||
@@ -143,11 +145,12 @@ removeModal <- function(session = getDefaultReactiveDomain()) {
|
||||
#' }
|
||||
#' @export
|
||||
modalDialog <- function(..., title = NULL, footer = modalButton("Dismiss"),
|
||||
size = c("m", "s", "l"), easyClose = FALSE) {
|
||||
size = c("m", "s", "l"), easyClose = FALSE, fade = TRUE) {
|
||||
|
||||
size <- match.arg(size)
|
||||
|
||||
div(id = "shiny-modal", class = "modal fade", tabindex = "-1",
|
||||
cls <- if (fade) "modal fade" else "modal"
|
||||
div(id = "shiny-modal", class = cls, tabindex = "-1",
|
||||
`data-backdrop` = if (!easyClose) "static",
|
||||
`data-keyboard` = if (!easyClose) "false",
|
||||
|
||||
|
||||
@@ -71,6 +71,72 @@ removeInputHandler <- function(type){
|
||||
inputHandlers$remove(type)
|
||||
}
|
||||
|
||||
|
||||
# Apply input handler to a single input value
|
||||
applyInputHandler <- function(name, val) {
|
||||
splitName <- strsplit(name, ':')[[1]]
|
||||
if (length(splitName) > 1) {
|
||||
if (!inputHandlers$containsKey(splitName[[2]])) {
|
||||
# No input handler registered for this type
|
||||
stop("No handler registered for type ", name)
|
||||
}
|
||||
|
||||
inputName <- splitName[[1]]
|
||||
|
||||
# Get the function for processing this type of input
|
||||
inputHandler <- inputHandlers$get(splitName[[2]])
|
||||
|
||||
return(inputHandler(val, shinysession, inputName))
|
||||
|
||||
} else if (is.list(val) && is.null(names(val))) {
|
||||
return(unlist(val, recursive = TRUE))
|
||||
} else {
|
||||
return(val)
|
||||
}
|
||||
}
|
||||
|
||||
#' Apply input handlers to raw input values
|
||||
#'
|
||||
#' The purpose of this function is to make it possible for external packages to
|
||||
#' test Shiny inputs. It takes a named list of raw input values, applies input
|
||||
#' handlers to those values, and then returns a named list of the processed
|
||||
#' values.
|
||||
#'
|
||||
#' The raw input values should be in a named list. Some values may have names
|
||||
#' like \code{"x:shiny.date"}. This function would apply the \code{"shiny.date"}
|
||||
#' input handler to the value, and then rename the result to \code{"x"}, in the
|
||||
#' output.
|
||||
#'
|
||||
#' @param inputs A named list of input values.
|
||||
#'
|
||||
#' @seealso registerInputHandler
|
||||
#'
|
||||
#' @examples
|
||||
#' applyInputHandlers(list(
|
||||
#' "m1" = list(list(1, 2), list(3, 4)),
|
||||
#' "m2:shiny.matrix" = list(list(1, 2), list(3, 4)),
|
||||
#'
|
||||
#' "d1" = "2016-01-01",
|
||||
#' "d2:shiny.date" = "2016-01-01", # Date object
|
||||
#'
|
||||
#' "n1" = NULL,
|
||||
#' "n2:shiny.number" = NULL # Converts to NA
|
||||
#' ))
|
||||
#' @export
|
||||
applyInputHandlers <- function(inputs) {
|
||||
inputs <- mapply(applyInputHandler, names(inputs), inputs, SIMPLIFY = FALSE)
|
||||
|
||||
# Convert names like "button1:shiny.action" to "button1"
|
||||
names(inputs) <- vapply(
|
||||
names(inputs),
|
||||
function(name) { strsplit(name, ":")[[1]][1] },
|
||||
FUN.VALUE = character(1)
|
||||
)
|
||||
|
||||
inputs
|
||||
}
|
||||
|
||||
|
||||
# Takes a list-of-lists and returns a matrix. The lists
|
||||
# must all be the same length. NULL is replaced by NA.
|
||||
registerInputHandler("shiny.matrix", function(data, ...) {
|
||||
|
||||
33
R/server.R
33
R/server.R
@@ -247,38 +247,7 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
|
||||
|
||||
withRestoreContext(shinysession$restoreContext, {
|
||||
|
||||
unpackInput <- function(name, val) {
|
||||
splitName <- strsplit(name, ':')[[1]]
|
||||
if (length(splitName) > 1) {
|
||||
if (!inputHandlers$containsKey(splitName[[2]])) {
|
||||
# No input handler registered for this type
|
||||
stop("No handler registered for type ", name)
|
||||
}
|
||||
|
||||
inputName <- splitName[[1]]
|
||||
|
||||
# Get the function for processing this type of input
|
||||
inputHandler <- inputHandlers$get(splitName[[2]])
|
||||
|
||||
return(inputHandler(val, shinysession, inputName))
|
||||
|
||||
} else if (is.list(val) && is.null(names(val))) {
|
||||
return(unlist(val, recursive = TRUE))
|
||||
} else {
|
||||
return(val)
|
||||
}
|
||||
}
|
||||
|
||||
msg$data <- mapply(unpackInput, names(msg$data), msg$data,
|
||||
SIMPLIFY = FALSE)
|
||||
|
||||
# Convert names like "button1:shiny.action" to "button1"
|
||||
names(msg$data) <- vapply(
|
||||
names(msg$data),
|
||||
function(name) { strsplit(name, ":")[[1]][1] },
|
||||
FUN.VALUE = character(1)
|
||||
)
|
||||
|
||||
msg$data <- applyInputHandlers(msg$data)
|
||||
|
||||
switch(
|
||||
msg$method,
|
||||
|
||||
@@ -53,10 +53,10 @@ NULL
|
||||
#'
|
||||
#' You can customize the file patterns Shiny will monitor by setting the
|
||||
#' shiny.autoreload.pattern option. For example, to monitor only ui.R:
|
||||
#' \code{option(shiny.autoreload.pattern = glob2rx("ui.R"))}
|
||||
#' \code{options(shiny.autoreload.pattern = glob2rx("ui.R"))}
|
||||
#'
|
||||
#' The default polling interval is 500 milliseconds. You can change this
|
||||
#' by setting e.g. \code{option(shiny.autoreload.interval = 2000)} (every
|
||||
#' by setting e.g. \code{options(shiny.autoreload.interval = 2000)} (every
|
||||
#' two seconds).}
|
||||
#' \item{shiny.reactlog}{If \code{TRUE}, enable logging of reactive events,
|
||||
#' which can be viewed later with the \code{\link{showReactLog}} function.
|
||||
|
||||
@@ -184,6 +184,7 @@ sd_section("Utility functions",
|
||||
"safeError",
|
||||
"onFlush",
|
||||
"restoreInput",
|
||||
"applyInputHandlers",
|
||||
"exprToFunction",
|
||||
"installExprFunction",
|
||||
"parseQueryString",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
|
||||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Source file: ../srcjs/_start.js
|
||||
@@ -1361,7 +1361,7 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
var fadeDuration = 250;
|
||||
|
||||
function show() {
|
||||
var _ref = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
|
||||
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
||||
|
||||
var _ref$html = _ref.html;
|
||||
var html = _ref$html === undefined ? '' : _ref$html;
|
||||
@@ -1520,7 +1520,7 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
// content is non-Bootstrap. Bootstrap modals require some special handling,
|
||||
// which is coded in here.
|
||||
show: function show() {
|
||||
var _ref2 = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
|
||||
var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
||||
|
||||
var _ref2$html = _ref2.html;
|
||||
var html = _ref2$html === undefined ? '' : _ref2$html;
|
||||
@@ -1529,7 +1529,7 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
|
||||
|
||||
// If there was an existing Bootstrap modal, then there will be a modal-
|
||||
// backdrop div that was added outside of the modal wrapper, and it must be
|
||||
// backdrop div that was added outside of the modal wrapper, and it must be
|
||||
// removed; otherwise there can be multiple of these divs.
|
||||
$('.modal-backdrop').remove();
|
||||
|
||||
@@ -1541,9 +1541,11 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
|
||||
// If the wrapper's content is a Bootstrap modal, then when the inner
|
||||
// modal is hidden, remove the entire thing, including wrapper.
|
||||
$modal.on('hidden.bs.modal', function () {
|
||||
exports.unbindAll($modal);
|
||||
$modal.remove();
|
||||
$modal.on('hidden.bs.modal', function (e) {
|
||||
if (e.target === $("#shiny-modal")[0]) {
|
||||
exports.unbindAll($modal);
|
||||
$modal.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3075,7 +3077,7 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
// inputs/outputs. `content` can be null, a string, or an object with
|
||||
// properties 'html' and 'deps'.
|
||||
exports.renderContent = function (el, content) {
|
||||
var where = arguments.length <= 2 || arguments[2] === undefined ? "replace" : arguments[2];
|
||||
var where = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "replace";
|
||||
|
||||
exports.unbindAll(el);
|
||||
|
||||
@@ -3112,7 +3114,7 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
|
||||
// Render HTML in a DOM element, inserting singletons into head as needed
|
||||
exports.renderHtml = function (html, el, dependencies) {
|
||||
var where = arguments.length <= 3 || arguments[3] === undefined ? 'replace' : arguments[3];
|
||||
var where = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'replace';
|
||||
|
||||
renderDependencies(dependencies);
|
||||
return singletons.renderHtml(html, el, where);
|
||||
@@ -3414,6 +3416,10 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
this.getValue = function (el) {
|
||||
throw "Not implemented";
|
||||
};
|
||||
|
||||
// The callback method takes one argument, whose value is boolean. If true,
|
||||
// allow deferred (debounce or throttle) sending depending on the value of
|
||||
// getRatePolicy. If false, send value immediately.
|
||||
this.subscribe = function (el, callback) {};
|
||||
this.unsubscribe = function (el) {};
|
||||
|
||||
@@ -3646,18 +3652,25 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
}
|
||||
},
|
||||
setValue: function setValue(el, value) {
|
||||
var slider = $(el).data('ionRangeSlider');
|
||||
var $el = $(el);
|
||||
var slider = $el.data('ionRangeSlider');
|
||||
|
||||
if (this._numValues(el) == 2 && value instanceof Array) {
|
||||
slider.update({ from: value[0], to: value[1] });
|
||||
} else {
|
||||
slider.update({ from: value });
|
||||
$el.data('immediate', true);
|
||||
try {
|
||||
if (this._numValues(el) == 2 && value instanceof Array) {
|
||||
slider.update({ from: value[0], to: value[1] });
|
||||
} else {
|
||||
slider.update({ from: value });
|
||||
}
|
||||
|
||||
forceIonSliderUpdate(slider);
|
||||
} finally {
|
||||
$el.data('immediate', false);
|
||||
}
|
||||
forceIonSliderUpdate(slider);
|
||||
},
|
||||
subscribe: function subscribe(el, callback) {
|
||||
$(el).on('change.sliderInputBinding', function (event) {
|
||||
callback(!$(el).data('updating') && !$(el).data('animating'));
|
||||
callback(!$(el).data('immediate') && !$(el).data('animating'));
|
||||
});
|
||||
},
|
||||
unsubscribe: function unsubscribe(el) {
|
||||
@@ -3682,12 +3695,12 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
|
||||
if (data.hasOwnProperty('label')) $el.parent().find('label[for="' + $escape(el.id) + '"]').text(data.label);
|
||||
|
||||
$el.data('updating', true);
|
||||
$el.data('immediate', true);
|
||||
try {
|
||||
slider.update(msg);
|
||||
forceIonSliderUpdate(slider);
|
||||
} finally {
|
||||
$el.data('updating', false);
|
||||
$el.data('immediate', false);
|
||||
}
|
||||
},
|
||||
getRatePolicy: function getRatePolicy() {
|
||||
@@ -4031,8 +4044,8 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
|
||||
return [formatDateUTC(start), formatDateUTC(end)];
|
||||
},
|
||||
// value must be an array of unambiguous strings like '2001-01-01', or
|
||||
// Date objects.
|
||||
// value must be an object, with optional fields `start` and `end`. These
|
||||
// should be unambiguous strings like '2001-01-01', or Date objects.
|
||||
setValue: function setValue(el, value) {
|
||||
if (!(value instanceof Object)) {
|
||||
return;
|
||||
@@ -4848,7 +4861,7 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
var shinyapp = exports.shinyapp = new ShinyApp();
|
||||
|
||||
function bindOutputs() {
|
||||
var scope = arguments.length <= 0 || arguments[0] === undefined ? document : arguments[0];
|
||||
var scope = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : document;
|
||||
|
||||
scope = $(scope);
|
||||
|
||||
@@ -4864,6 +4877,11 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
// Check if ID is falsy
|
||||
if (!id) continue;
|
||||
|
||||
// In some uncommon cases, elements that are later in the
|
||||
// matches array can be removed from the document by earlier
|
||||
// iterations. See https://github.com/rstudio/shiny/issues/1399
|
||||
if (!$.contains(document, el)) continue;
|
||||
|
||||
var $el = $(el);
|
||||
if ($el.hasClass('shiny-bound-output')) {
|
||||
// Already bound; can happen with nested uiOutput (bindAll
|
||||
@@ -4889,8 +4907,8 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
}
|
||||
|
||||
function unbindOutputs() {
|
||||
var scope = arguments.length <= 0 || arguments[0] === undefined ? document : arguments[0];
|
||||
var includeSelf = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
|
||||
var scope = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : document;
|
||||
var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
||||
|
||||
var outputs = $(scope).find('.shiny-bound-output');
|
||||
|
||||
@@ -4952,7 +4970,7 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
}
|
||||
|
||||
function bindInputs() {
|
||||
var scope = arguments.length <= 0 || arguments[0] === undefined ? document : arguments[0];
|
||||
var scope = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : document;
|
||||
|
||||
var bindings = inputBindings.getBindings();
|
||||
|
||||
@@ -5010,8 +5028,8 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
}
|
||||
|
||||
function unbindInputs() {
|
||||
var scope = arguments.length <= 0 || arguments[0] === undefined ? document : arguments[0];
|
||||
var includeSelf = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
|
||||
var scope = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : document;
|
||||
var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
||||
|
||||
var inputs = $(scope).find('.shiny-bound-input');
|
||||
|
||||
@@ -5040,7 +5058,7 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
return bindInputs(scope);
|
||||
}
|
||||
function unbindAll(scope) {
|
||||
var includeSelf = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
|
||||
var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
||||
|
||||
unbindInputs(scope, includeSelf);
|
||||
unbindOutputs(scope, includeSelf);
|
||||
@@ -5065,7 +5083,7 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
// Calls .initialize() for all of the input objects in all input bindings,
|
||||
// in the given scope.
|
||||
function initializeInputs() {
|
||||
var scope = arguments.length <= 0 || arguments[0] === undefined ? document : arguments[0];
|
||||
var scope = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : document;
|
||||
|
||||
var bindings = inputBindings.getBindings();
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
8
inst/www/shared/shiny.min.js
vendored
8
inst/www/shared/shiny.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
39
man/applyInputHandlers.Rd
Normal file
39
man/applyInputHandlers.Rd
Normal file
@@ -0,0 +1,39 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/server-input-handlers.R
|
||||
\name{applyInputHandlers}
|
||||
\alias{applyInputHandlers}
|
||||
\title{Apply input handlers to raw input values}
|
||||
\usage{
|
||||
applyInputHandlers(inputs)
|
||||
}
|
||||
\arguments{
|
||||
\item{inputs}{A named list of input values.}
|
||||
}
|
||||
\description{
|
||||
The purpose of this function is to make it possible for external packages to
|
||||
test Shiny inputs. It takes a named list of raw input values, applies input
|
||||
handlers to those values, and then returns a named list of the processed
|
||||
values.
|
||||
}
|
||||
\details{
|
||||
The raw input values should be in a named list. Some values may have names
|
||||
like \code{"x:shiny.date"}. This function would apply the \code{"shiny.date"}
|
||||
input handler to the value, and then rename the result to \code{"x"}, in the
|
||||
output.
|
||||
}
|
||||
\examples{
|
||||
applyInputHandlers(list(
|
||||
"m1" = list(list(1, 2), list(3, 4)),
|
||||
"m2:shiny.matrix" = list(list(1, 2), list(3, 4)),
|
||||
|
||||
"d1" = "2016-01-01",
|
||||
"d2:shiny.date" = "2016-01-01", # Date object
|
||||
|
||||
"n1" = NULL,
|
||||
"n2:shiny.number" = NULL # Converts to NA
|
||||
))
|
||||
}
|
||||
\seealso{
|
||||
registerInputHandler
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
\title{Create a modal dialog UI}
|
||||
\usage{
|
||||
modalDialog(..., title = NULL, footer = modalButton("Dismiss"),
|
||||
size = c("m", "s", "l"), easyClose = FALSE)
|
||||
size = c("m", "s", "l"), easyClose = FALSE, fade = TRUE)
|
||||
}
|
||||
\arguments{
|
||||
\item{...}{UI elements for the body of the modal dialog box.}
|
||||
@@ -22,6 +22,9 @@ clicking outside the dialog box, or be pressing the Escape key. If
|
||||
\code{FALSE} (the default), the modal dialog can't be dismissed in those
|
||||
ways; instead it must be dismissed by clicking on the dismiss button, or
|
||||
from a call to \code{\link{removeModal}} on the server.}
|
||||
|
||||
\item{fade}{If \code{FALSE}, the modal dialog will have no fade-in animation
|
||||
(it will simply appear rather than fade in to view).}
|
||||
}
|
||||
\description{
|
||||
This creates the UI for a modal dialog, using Bootstrap's modal class. Modals
|
||||
|
||||
@@ -27,10 +27,10 @@ Since monitoring for changes is expensive (we simply poll for last
|
||||
|
||||
You can customize the file patterns Shiny will monitor by setting the
|
||||
shiny.autoreload.pattern option. For example, to monitor only ui.R:
|
||||
\code{option(shiny.autoreload.pattern = glob2rx("ui.R"))}
|
||||
\code{options(shiny.autoreload.pattern = glob2rx("ui.R"))}
|
||||
|
||||
The default polling interval is 500 milliseconds. You can change this
|
||||
by setting e.g. \code{option(shiny.autoreload.interval = 2000)} (every
|
||||
by setting e.g. \code{options(shiny.autoreload.interval = 2000)} (every
|
||||
two seconds).}
|
||||
\item{shiny.reactlog}{If \code{TRUE}, enable logging of reactive events,
|
||||
which can be viewed later with the \code{\link{showReactLog}} function.
|
||||
|
||||
@@ -18,6 +18,12 @@ function initShiny() {
|
||||
if (!id)
|
||||
continue;
|
||||
|
||||
// In some uncommon cases, elements that are later in the
|
||||
// matches array can be removed from the document by earlier
|
||||
// iterations. See https://github.com/rstudio/shiny/issues/1399
|
||||
if (!$.contains(document, el))
|
||||
continue;
|
||||
|
||||
var $el = $(el);
|
||||
if ($el.hasClass('shiny-bound-output')) {
|
||||
// Already bound; can happen with nested uiOutput (bindAll
|
||||
|
||||
@@ -14,6 +14,10 @@ this.getId = function(el) {
|
||||
// to deserialize the JSON correctly
|
||||
this.getType = function() { return false; };
|
||||
this.getValue = function(el) { throw "Not implemented"; };
|
||||
|
||||
// The callback method takes one argument, whose value is boolean. If true,
|
||||
// allow deferred (debounce or throttle) sending depending on the value of
|
||||
// getRatePolicy. If false, send value immediately.
|
||||
this.subscribe = function(el, callback) { };
|
||||
this.unsubscribe = function(el) { };
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ $.extend(dateRangeInputBinding, dateInputBinding, {
|
||||
|
||||
return [formatDateUTC(start), formatDateUTC(end)];
|
||||
},
|
||||
// value must be an array of unambiguous strings like '2001-01-01', or
|
||||
// Date objects.
|
||||
// value must be an object, with optional fields `start` and `end`. These
|
||||
// should be unambiguous strings like '2001-01-01', or Date objects.
|
||||
setValue: function(el, value) {
|
||||
if (!(value instanceof Object)) {
|
||||
return;
|
||||
|
||||
@@ -53,18 +53,25 @@ $.extend(sliderInputBinding, textInputBinding, {
|
||||
|
||||
},
|
||||
setValue: function(el, value) {
|
||||
var slider = $(el).data('ionRangeSlider');
|
||||
var $el = $(el);
|
||||
var slider = $el.data('ionRangeSlider');
|
||||
|
||||
if (this._numValues(el) == 2 && value instanceof Array) {
|
||||
slider.update({ from: value[0], to: value[1] });
|
||||
} else {
|
||||
slider.update({ from: value });
|
||||
$el.data('immediate', true);
|
||||
try {
|
||||
if (this._numValues(el) == 2 && value instanceof Array) {
|
||||
slider.update({ from: value[0], to: value[1] });
|
||||
} else {
|
||||
slider.update({ from: value });
|
||||
}
|
||||
|
||||
forceIonSliderUpdate(slider);
|
||||
} finally {
|
||||
$el.data('immediate', false);
|
||||
}
|
||||
forceIonSliderUpdate(slider);
|
||||
},
|
||||
subscribe: function(el, callback) {
|
||||
$(el).on('change.sliderInputBinding', function(event) {
|
||||
callback(!$(el).data('updating') && !$(el).data('animating'));
|
||||
callback(!$(el).data('immediate') && !$(el).data('animating'));
|
||||
});
|
||||
},
|
||||
unsubscribe: function(el) {
|
||||
@@ -90,12 +97,12 @@ $.extend(sliderInputBinding, textInputBinding, {
|
||||
if (data.hasOwnProperty('label'))
|
||||
$el.parent().find('label[for="' + $escape(el.id) + '"]').text(data.label);
|
||||
|
||||
$el.data('updating', true);
|
||||
$el.data('immediate', true);
|
||||
try {
|
||||
slider.update(msg);
|
||||
forceIonSliderUpdate(slider);
|
||||
} finally {
|
||||
$el.data('updating', false);
|
||||
$el.data('immediate', false);
|
||||
}
|
||||
},
|
||||
getRatePolicy: function() {
|
||||
|
||||
@@ -7,7 +7,7 @@ exports.modal = {
|
||||
show: function({ html='', deps=[] } = {}) {
|
||||
|
||||
// If there was an existing Bootstrap modal, then there will be a modal-
|
||||
// backdrop div that was added outside of the modal wrapper, and it must be
|
||||
// backdrop div that was added outside of the modal wrapper, and it must be
|
||||
// removed; otherwise there can be multiple of these divs.
|
||||
$('.modal-backdrop').remove();
|
||||
|
||||
@@ -19,9 +19,11 @@ exports.modal = {
|
||||
|
||||
// If the wrapper's content is a Bootstrap modal, then when the inner
|
||||
// modal is hidden, remove the entire thing, including wrapper.
|
||||
$modal.on('hidden.bs.modal', function() {
|
||||
exports.unbindAll($modal);
|
||||
$modal.remove();
|
||||
$modal.on('hidden.bs.modal', function(e) {
|
||||
if (e.target === $("#shiny-modal")[0]) {
|
||||
exports.unbindAll($modal);
|
||||
$modal.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -33,15 +33,15 @@ test_that("Repeated names for selectInput and radioButtons choices", {
|
||||
x <- radioButtons('id','label', choices = c(a='x1', a='x2', b='x3'))
|
||||
choices <- x$children
|
||||
|
||||
expect_equal(choices[[2]]$children[[1]][[1]]$children[[1]]$children[[2]]$children[[1]], 'a')
|
||||
expect_equal(choices[[2]]$children[[1]][[1]]$children[[1]]$children[[2]]$children[[1]], HTML('a'))
|
||||
expect_equal(choices[[2]]$children[[1]][[1]]$children[[1]]$children[[1]]$attribs$value, 'x1')
|
||||
expect_equal(choices[[2]]$children[[1]][[1]]$children[[1]]$children[[1]]$attribs$checked, 'checked')
|
||||
|
||||
expect_equal(choices[[2]]$children[[1]][[2]]$children[[1]]$children[[2]]$children[[1]], 'a')
|
||||
expect_equal(choices[[2]]$children[[1]][[2]]$children[[1]]$children[[2]]$children[[1]], HTML('a'))
|
||||
expect_equal(choices[[2]]$children[[1]][[2]]$children[[1]]$children[[1]]$attribs$value, 'x2')
|
||||
expect_equal(choices[[2]]$children[[1]][[2]]$children[[1]]$children[[1]]$attribs$checked, NULL)
|
||||
|
||||
expect_equal(choices[[2]]$children[[1]][[3]]$children[[1]]$children[[2]]$children[[1]], 'b')
|
||||
expect_equal(choices[[2]]$children[[1]][[3]]$children[[1]]$children[[2]]$children[[1]], HTML('b'))
|
||||
expect_equal(choices[[2]]$children[[1]][[3]]$children[[1]]$children[[1]]$attribs$value, 'x3')
|
||||
expect_equal(choices[[2]]$children[[1]][[3]]$children[[1]]$children[[1]]$attribs$checked, NULL)
|
||||
})
|
||||
|
||||
@@ -13,7 +13,15 @@ causeError <- function(full) {
|
||||
B()
|
||||
})
|
||||
|
||||
res <- try(captureStackTraces(isolate(renderTable({C()}, server = FALSE)())),
|
||||
res <- try({
|
||||
captureStackTraces({
|
||||
isolate({
|
||||
renderTable({
|
||||
C()
|
||||
}, server = FALSE)()
|
||||
})
|
||||
})
|
||||
},
|
||||
silent = TRUE)
|
||||
cond <- attr(res, "condition", exact = TRUE)
|
||||
|
||||
@@ -50,7 +58,7 @@ test_that("integration tests", {
|
||||
"isolate", "withCallingHandlers", "captureStackTraces", "doTryCatch",
|
||||
"tryCatchOne", "tryCatchList", "tryCatch", "try"))
|
||||
expect_equal(nzchar(df$loc), c(TRUE, TRUE, TRUE, FALSE, TRUE,
|
||||
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
|
||||
FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE,
|
||||
FALSE, FALSE))
|
||||
|
||||
df <- causeError(full = TRUE)
|
||||
@@ -72,8 +80,8 @@ test_that("integration tests", {
|
||||
"tryCatch", "try"))
|
||||
expect_equal(nzchar(df$loc), c(FALSE, FALSE, FALSE, TRUE,
|
||||
TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
|
||||
FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE,
|
||||
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
|
||||
FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE,
|
||||
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE,
|
||||
FALSE, FALSE, FALSE, FALSE))
|
||||
})
|
||||
|
||||
|
||||
@@ -1,30 +1,49 @@
|
||||
This directory contains build tools for Shiny.
|
||||
|
||||
|
||||
## Grunt
|
||||
## JavaScript build tools
|
||||
|
||||
Grunt is a build tool that runs on node.js. In Shiny, it is used for concatenating, minifying, and linting Javascript code.
|
||||
|
||||
### Installing Grunt
|
||||
### First-time setup
|
||||
|
||||
Grunt requires Node.js and npm (the Node.js package manager). Installation of these programs differs across platforms and is generally pretty easy, so I won't include instructions here.
|
||||
Shiny's JavaScript build tools use Node.js, along with [yarn](https://yarnpkg.com/) to manage the JavaScript packages.
|
||||
|
||||
Once node and npm are installed, install grunt:
|
||||
Installation of Node.js differs across platforms and is generally pretty easy, so I won't include instructions here.
|
||||
|
||||
There are a number of ways to [install yarn](https://yarnpkg.com/en/docs/install), but if you already have npm installed, you can simply run:
|
||||
|
||||
```
|
||||
sudo npm install --global yarn
|
||||
```
|
||||
|
||||
Then, in this directory (tools/), run the following to install the packages:
|
||||
|
||||
```
|
||||
yarn
|
||||
```
|
||||
|
||||
If in the future you want to upgrade or add a package, run:
|
||||
|
||||
```
|
||||
yarn add --dev [packagename]
|
||||
```
|
||||
|
||||
If someone else updates the package.json and/or yarn.lock files, simply run `yarn` to update your packages.
|
||||
|
||||
For information about upgrading or installing new packages, see the [yarn workflow documentation](https://yarnpkg.com/en/docs/yarn-workflow).
|
||||
|
||||
|
||||
### Grunt
|
||||
|
||||
Grunt is a build tool that runs on node.js and will be installed. In Shiny, it is used for concatenating, minifying, and linting Javascript code.
|
||||
|
||||
#### Installing Grunt
|
||||
|
||||
```
|
||||
# Install grunt command line tool globally
|
||||
sudo npm install -g grunt-cli
|
||||
|
||||
# Install grunt plus modules for this project
|
||||
npm install
|
||||
|
||||
# To update modules in the future
|
||||
npm update
|
||||
sudo yarn global add grunt-cli
|
||||
```
|
||||
|
||||
Note: The `package.json` file contains a reference to `estraverse-fb`. This is needed only because the current version of ESLint has a [bug](https://github.com/eslint/eslint/issues/5476). At some point in the future, it can be removed.
|
||||
|
||||
|
||||
### Using Grunt
|
||||
|
||||
To run all default grunt tasks (concatenation, minification, and jshint), simply go into the `tools` directory and run:
|
||||
@@ -58,7 +77,7 @@ Updating web libraries
|
||||
|
||||
To update the version of babel-polyfill:
|
||||
|
||||
* Check if there is a newer version available by running `npm outdated babel-polyfill`. (If there's no output, then you have the latest version.)
|
||||
* Run `npm install babel-polyfill --save-dev --save-exact`.
|
||||
* Check if there is a newer version available by running `yarn outdated babel-polyfill`. (If there's no output, then you have the latest version.)
|
||||
* Run `yarn add --dev babel-polyfill --exact`.
|
||||
* Edit R/shinyui.R. The `renderPage` function has an `htmlDependency` for
|
||||
`babel-polyfill`. Update this to the new version number.
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
"babel-polyfill": "6.7.2",
|
||||
"babel-preset-es2015": "^6.6.0",
|
||||
"eslint-stylish-mapped": "^1.0.0",
|
||||
"estraverse-fb": "^1.3.1",
|
||||
"grunt": "~1.0.0",
|
||||
"grunt-babel": "^6.0.0",
|
||||
"grunt-contrib-clean": "^1.0.0",
|
||||
|
||||
2067
tools/yarn.lock
Normal file
2067
tools/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user