Compare commits

...

45 Commits

Author SHA1 Message Date
cpsievert
4671eccd84 devtools::document() (GitHub Actions) 2022-01-20 19:46:41 +00:00
Carson
6c0cf4f7e5 code review 2022-01-20 13:41:31 -06:00
cpsievert
dc7d4b46d8 devtools::document() (GitHub Actions) 2022-01-20 18:31:22 +00:00
Carson Sievert
509acb62b0 Update R/bootstrap-deprecated.R 2022-01-20 12:24:32 -06:00
Carson
d05b2c305e roxygenize 2022-01-20 12:24:32 -06:00
Carson
a562e286d0 Improve organization of function reference 2022-01-20 12:24:32 -06:00
Carson
a40903c7e3 Improve documentation surrounding fluidPage/fixedRow/bootstrapPage and fluidRow/column 2022-01-20 12:24:32 -06:00
Carson
1faefe66fa Soft deprecate fixedRow() and basicPage() 2022-01-20 12:24:32 -06:00
Carson Sievert
fc7e237000 Pass args from knit_print.shiny.render.function() down to it's use of the knit_print() generic (#3569)
Co-authored-by: cpsievert <cpsievert@users.noreply.github.com>
Co-authored-by: Barret Schloerke <barret@rstudio.com>
Co-authored-by: schloerke <schloerke@users.noreply.github.com>
2022-01-11 12:21:18 -06:00
Winston Chang
de8134742d Update NEWS 2022-01-11 10:41:19 -06:00
Winston Chang
f814034835 Merge pull request #3570 from romainfrancois/shinyActionButtonValue 2022-01-11 10:38:28 -06:00
Romain Francois
6d9fad29f3 put the extension class on the left.
from: https://github.com/tidyverse/dplyr/issues/6154
2022-01-11 12:49:48 +01:00
Lionel Henry
313ae9044d Handle chained errors (#3567)
Closes tidyverse/dplyr#5552
Part of #3566
2022-01-10 18:33:14 -08:00
Barret Schloerke
9389160af0 Cache the reactlog version found. Remove mustWork argument to system_file(). (#3554) 2021-12-08 15:30:37 -05:00
Barret Schloerke
6a7ffeff68 master -> main; use shiny-workflows (#3535) 2021-11-19 17:50:57 -05:00
Winston Chang
bc6ff57cb7 Remove old unused JS files (#3547) 2021-11-17 22:01:19 -06:00
Carson Sievert
b52b9e4520 Some UI-related speed improvements (#3541) 2021-11-17 16:25:27 -06:00
Winston Chang
fb71ab6146 Editor configuration improvements 2021-11-16 15:38:21 -06:00
Carson Sievert
d8c7a634ff Fix failing tabPanel() test broken by testthat 3.1.0 (#3543) 2021-11-05 16:45:16 -05:00
Winston Chang
396dd2632e Merge pull request #3537 from rstudio/htmldep 2021-11-05 15:56:09 -05:00
Winston Chang
c11875a5f0 Fix appending to list 2021-11-05 15:52:14 -05:00
Winston Chang
2e599faf1f Fix comment 2021-11-05 15:47:23 -05:00
Winston Chang
a5a8385420 Use all_files=FALSE for html dependencies
This commit sets all_files=FALSE for html dependencies in www/shared.
2021-11-04 11:24:46 -05:00
Winston Chang
33ed698e5b Remove “file =“ 2021-11-04 11:15:01 -05:00
Winston Chang
ed547fdf40 Rebuild JS files 2021-11-03 21:28:15 -05:00
Winston Chang
0b1c35c92b Use .map() and .forEeach() instead of for loops 2021-11-03 21:28:11 -05:00
Winston Chang
d304bdf333 Rename HtmlDepSimplified to HtmlDepNormalized 2021-11-03 20:54:53 -05:00
Winston Chang
a9255e6b12 Extract jquery-ui dependency into function 2021-11-03 20:54:53 -05:00
Winston Chang
45429fb798 Streamline code for adding scripts and attachments 2021-11-03 13:56:37 -05:00
Winston Chang
1206d1d3ba Use array of objects to represent meta tags 2021-11-03 13:56:37 -05:00
Winston Chang
af44a447a1 Fix type specification of HTML dependency meta values 2021-11-03 11:40:49 -05:00
Winston Chang
d7fb6d1793 Fixes for strict null checks 2021-11-03 11:40:49 -05:00
Winston Chang
cb0083adb2 Update VSCode editor settings 2021-11-03 11:40:49 -05:00
Winston Chang
77bae68f26 Update NEWS 2021-11-03 11:40:48 -05:00
Winston Chang
e9f8b4d552 Use htmlDependencies with ‘src=file’ for showcase mode 2021-11-03 11:39:28 -05:00
Winston Chang
aee6b74cfb Minor cleanups 2021-11-03 11:39:28 -05:00
Winston Chang
29b6b03297 Extract restyle code into separate code path 2021-11-03 11:39:28 -05:00
Winston Chang
b5ebd8a645 Fix attachment handling and add more specific types 2021-11-03 11:39:28 -05:00
Winston Chang
356ba8c5a1 Add simplifyHtmlDependency() function 2021-11-03 11:39:28 -05:00
Winston Chang
5aa5cb1794 Make dep.src.href field optional 2021-11-03 11:39:28 -05:00
Winston Chang
09c609e417 Bump version to 1.7.1.9001 2021-11-03 11:39:28 -05:00
Winston Chang
10e7d11846 Allow array of attributes for stylesheets 2021-11-03 11:39:28 -05:00
Winston Chang
4e442312a7 Serve HTML dependencies from dynamic paths 2021-11-03 11:39:28 -05:00
Winston Chang
8ea97df3f2 Fix getid access of data-input-id attribute (#3538) 2021-11-03 11:38:54 -05:00
Winston Chang
a8c14dab96 Bump version to 1.7.1.9000 2021-10-04 11:09:26 -05:00
109 changed files with 1478 additions and 9282 deletions

12
.github/shiny-workflows/routine.sh vendored Normal file
View File

@@ -0,0 +1,12 @@
#!/bin/bash -e
. ./tools/documentation/checkDocsCurrent.sh
echo "Updating package.json version to match DESCRIPTION Version"
Rscript ./tools/updatePackageJsonVersion.R
if [ -n "$(git status --porcelain package.json)" ]
then
yarn build
git add ./inst package.json && git commit -m 'Sync package version (GitHub Actions)' || echo "No package version to commit"
else
echo "No package version difference detected; package.json is current."
fi

View File

@@ -1,139 +1,23 @@
# NOTE: This workflow is overkill for most R packages
# check-standard.yaml is likely a better choice
# usethis::use_github_action("check-standard") will install it.
# Workflow derived from https://github.com/rstudio/shiny-workflows
#
# For help debugging build failures open an issue on the RStudio community with the 'github-actions' tag.
# https://community.rstudio.com/new-topic?category=Package%20development&tags=github-actions
# NOTE: This Shiny team GHA workflow is overkill for most R packages.
# For most R packages it is better to use https://github.com/r-lib/actions
on:
push:
branches:
- main
- master
branches: [main, rc-**]
pull_request:
branches:
- main
- master
- rc-v**
branches: [main]
schedule:
- cron: '0 5 * * 1' # every monday
name: R-CMD-check
name: Package checks
jobs:
rversions:
name: R Versions
runs-on: ubuntu-latest
outputs:
devel: ${{ steps.devel.outputs.installed-r-version }}
release: ${{ steps.release.outputs.installed-r-version }}
oldrel1: ${{ steps.oldrel1.outputs.installed-r-version }}
oldrel2: ${{ steps.oldrel2.outputs.installed-r-version }}
oldrel3: ${{ steps.oldrel3.outputs.installed-r-version }}
oldrel4: ${{ steps.oldrel4.outputs.installed-r-version }}
steps:
- { name: devel, uses: r-lib/actions/setup-r@master, id: devel, with: { r-version: devel, install-r: false }}
- { name: release, uses: r-lib/actions/setup-r@master, id: release, with: { r-version: release, install-r: false }}
- { name: oldrel/1, uses: r-lib/actions/setup-r@master, id: oldrel1, with: { r-version: oldrel/1, install-r: false }}
- { name: oldrel/2, uses: r-lib/actions/setup-r@master, id: oldrel2, with: { r-version: oldrel/2, install-r: false }}
- { name: oldrel/3, uses: r-lib/actions/setup-r@master, id: oldrel3, with: { r-version: oldrel/3, install-r: false }}
- { name: oldrel/4, uses: r-lib/actions/setup-r@master, id: oldrel4, with: { r-version: oldrel/4, install-r: false }}
website:
uses: rstudio/shiny-workflows/.github/workflows/website.yaml@v1
routine:
uses: rstudio/shiny-workflows/.github/workflows/routine.yaml@v1
with:
node-version: "14.x"
R-CMD-check:
runs-on: ${{ matrix.config.os }}
name: ${{ matrix.config.os }} (${{ matrix.config.r }})
needs:
- rversions
strategy:
fail-fast: false
matrix:
config:
- {os: macOS-latest, r: '${{ needs.rversions.outputs.release }}'}
- {os: windows-latest, r: '${{ needs.rversions.outputs.release }}'}
- {os: windows-latest, r: '3.6'}
- {os: ubuntu-20.04, r: '${{ needs.rversions.outputs.devel }}', http-user-agent: "release" }
- {os: ubuntu-20.04, r: '${{ needs.rversions.outputs.release }}'}
- {os: ubuntu-20.04, r: '${{ needs.rversions.outputs.oldrel1 }}'}
- {os: ubuntu-20.04, r: '${{ needs.rversions.outputs.oldrel2 }}'}
- {os: ubuntu-20.04, r: '${{ needs.rversions.outputs.oldrel3 }}'}
- {os: ubuntu-20.04, r: '${{ needs.rversions.outputs.oldrel4 }}'}
- {os: ubuntu-18.04, r: '${{ needs.rversions.outputs.devel }}', http-user-agent: "release" }
- {os: ubuntu-18.04, r: '${{ needs.rversions.outputs.release }}'}
- {os: ubuntu-18.04, r: '${{ needs.rversions.outputs.oldrel1 }}'}
- {os: ubuntu-18.04, r: '${{ needs.rversions.outputs.oldrel2 }}'}
- {os: ubuntu-18.04, r: '${{ needs.rversions.outputs.oldrel3 }}'}
- {os: ubuntu-18.04, r: '${{ needs.rversions.outputs.oldrel4 }}'}
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
steps:
# https://github.com/actions/checkout/issues/135
- name: Set git to use LF
if: runner.os == 'Windows'
run: |
git config --system core.autocrlf false
git config --system core.eol lf
- uses: actions/checkout@v2
- uses: r-lib/actions/setup-r@v1
id: install-r
with:
r-version: ${{ matrix.config.r }}
http-user-agent: ${{ matrix.config.http-user-agent }}
use-public-rspm: true
- uses: r-lib/actions/setup-pandoc@v1
- uses: r-lib/actions/setup-r-dependencies@v1
with:
extra-packages: rcmdcheck
# xquartz and cairo are needed for Cairo package.
# harfbuzz and fribidi are needed for textshaping package.
- name: Mac systemdeps
if: runner.os == 'macOS'
run: |
brew install --cask xquartz
brew install cairo
brew install harfbuzz fribidi
- name: Find PhantomJS path
id: phantomjs
run: |
echo "::set-output name=path::$(Rscript -e 'cat(shinytest:::phantom_paths()[[1]])')"
- name: Cache PhantomJS
uses: actions/cache@v2
with:
path: ${{ steps.phantomjs.outputs.path }}
key: ${{ matrix.config.os }}-phantomjs
restore-keys: ${{ matrix.config.os }}-phantomjs
- name: Install PhantomJS
run: >
Rscript
-e "if (!shinytest::dependenciesInstalled()) shinytest::installDependencies()"
- name: Check
env:
_R_CHECK_CRAN_INCOMING_: false
_R_CHECK_FORCE_SUGGESTS_: ${{ matrix.config.r != 'devel' }}
run: |
options(crayon.enabled = TRUE)
rcmdcheck::rcmdcheck(args = c("--no-manual", "--as-cran"), error_on = "warning", check_dir = "check")
shell: Rscript {0}
- name: Show testthat output
if: always()
run: find check -name 'testthat.Rout*' -exec cat '{}' \; || true
shell: bash
- name: Upload check results
if: failure()
uses: actions/upload-artifact@main
with:
name: ${{ matrix.config.os }}-r${{ matrix.config.r }}-results
path: check
uses: rstudio/shiny-workflows/.github/workflows/R-CMD-check.yaml@v1

View File

@@ -1,171 +0,0 @@
on:
push:
branches:
- master
- ghactions
pull_request:
branches:
- master
name: Rituals
jobs:
rituals:
name: Rituals
runs-on: ${{ matrix.config.os }}
strategy:
fail-fast: false
matrix:
config:
- { os: ubuntu-20.04, r: 'release', node: "14.x", rspm: "https://packagemanager.rstudio.com/all/__linux__/focal/latest"}
env:
R_REMOTES_NO_ERRORS_FROM_WARNINGS: true
RSPM: ${{ matrix.config.rspm }}
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v1
- uses: r-lib/actions/pr-fetch@master
name: Git Pull (PR)
if: github.event_name == 'pull_request'
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- uses: r-lib/actions/setup-r@master
id: install-r
with:
r-version: ${{ matrix.config.r }}
- uses: r-lib/actions/setup-pandoc@master
- name: Git Config
run: |
git config user.name "${GITHUB_ACTOR}"
git config user.email "${GITHUB_ACTOR}@users.noreply.github.com"
- name: Install pak and query dependencies
shell: Rscript {0}
run: |
install.packages("pak", repos = "https://r-lib.github.io/p/pak/dev/")
saveRDS(pak::pkg_deps_tree("local::.", dependencies = TRUE), ".github/r-depends.rds")
- name: Cache R packages
uses: actions/cache@v2
with:
path: ${{ env.R_LIBS_USER }}
key: ${{ matrix.config.os }}-${{ steps.install-r.outputs.installed-r-version }}-1-${{ hashFiles('.github/r-depends.rds') }}
restore-keys: ${{ matrix.config.os }}-${{ steps.install-r.outputs.installed-r-version }}-1-
- name: Install system dependencies
# if: runner.os == 'Linux'
shell: Rscript {0}
run: |
pak::local_system_requirements(execute = TRUE)
- name: Install dependencies
shell: Rscript {0}
run: |
pak::local_install_dev_deps(upgrade = TRUE)
- name: Session info
shell: Rscript {0}
run: |
options(width = 100)
pak::pkg_install("sessioninfo")
pkgs <- installed.packages()[, "Package"]
sessioninfo::session_info(pkgs, include_base = TRUE)
- name: Url redirects
# only perform if in an RC branch (`rc-vX.Y.Z`)
if: ${{ github.event_name == 'push' && contains(github.ref, '/rc-v') }}
run: |
Rscript -e 'pak::pkg_install("r-lib/urlchecker"); urlchecker::url_update()'
# throw an error if man files were updated
if [ -n "$(git status --porcelain man)" ]
then
git status --porcelain
>&2 echo "Updated links found in files above"
>&2 echo 'Run `urlchecker::url_update()` to fix links locally'
exit 1
fi
# Add locally changed urls
git add .
git commit -m 'Update links (GitHub Actions)' || echo "No link changes to commit"
- name: Document
run: |
Rscript -e 'pak::pkg_install("devtools")'
Rscript -e 'devtools::document()'
git add man/\* NAMESPACE
git commit -m 'Document (GitHub Actions)' || echo "No documentation changes to commit"
- name: Check documentation
run: |
./tools/documentation/checkDocsCurrent.sh
- uses: actions/setup-node@v1
with:
node-version: ${{ matrix.config.node }}
# https://github.com/actions/cache/blame/ccf96194800dbb7b7094edcd5a7cf3ec3c270f10/examples.md#L185-L200
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: yarn cache
uses: actions/cache@v2
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ matrix.config.os }}-${{ matrix.config.node }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ matrix.config.os }}-${{ matrix.config.node }}-yarn-
- name: Sync DESCRIPTION and package.json versions
run: |
Rscript -e 'pak::pkg_install("jsonlite")'
Rscript -e 'pkg <- jsonlite::read_json("package.json", simplifyVector = TRUE)' \
-e 'version <- as.list(read.dcf("DESCRIPTION")[1,])$Version' \
-e 'pkg$version <- gsub("^(\\d+).(\\d+).(\\d+).(.+)$", "\\1.\\2.\\3-alpha.\\4", version)' \
-e 'pkg$files <- as.list(pkg$files)' \
-e 'jsonlite::write_json(pkg, path = "package.json", pretty = TRUE, auto_unbox = TRUE)'
git add package.json && git commit -m 'sync package version (GitHub Actions)' || echo "No version changes to commit"
- name: Build JS
run: |
tree srcts
rm -r srcts/types
yarn install --immutable && yarn build
git add ./srcts/src && git commit -m 'yarn lint (GitHub Actions)' || echo "No yarn lint changes to commit"
git add ./srcts/types && git commit -m 'yarn tsc (GitHub Actions)' || echo "No type definition changes to commit"
git add ./inst && git commit -m 'yarn build (GitHub Actions)' || echo "No yarn build changes to commit"
if [ -n "$(git status --porcelain)" ]
then
git status --porcelain
>&2 echo "The above files changed when we built the JavaScript assets."
exit 1
else
echo "No difference detected; TypeScript build is current."
fi
- name: Git Push (PR)
uses: r-lib/actions/pr-push@master
if: github.event_name == 'pull_request'
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Verify no un-pushed commits (MASTER)
if: github.event_name == 'push'
run: |
# Can't push to a protected branch
if [ -n "`git cherry origin/master`" ]; then
echo "Un-pushed commits:"
git cherry -v origin/master
echo "\nCan not push to a protected branch. Exiting"
exit 1
fi
# Execute after pushing, as no updated files will be produced
- name: Test TypeScript code
run: |
yarn test

View File

@@ -1,3 +0,0 @@
trailingComma: "es5"
arrowParens: always
endOfLine: lf

16
.vscode/settings.json vendored
View File

@@ -1,10 +1,18 @@
{
"typescript.tsdk": ".yarn/sdks/typescript/lib",
"search.exclude": {
"**/.yarn": true,
"**/.pnp.*": true
},
"eslint.nodePath": ".yarn/sdks",
"prettier.prettierPath": ".yarn/sdks/prettier/index.js",
"typescript.enablePromptUseWorkspaceTsdk": true
"prettier.prettierPath": "./node_modules/prettier",
"typescript.enablePromptUseWorkspaceTsdk": true,
"[r]": {
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
},
}

View File

@@ -1,7 +1,7 @@
Package: shiny
Type: Package
Title: Web Application Framework for R
Version: 1.7.1
Version: 1.7.1.9002
Authors@R: c(
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@rstudio.com", comment = c(ORCID = "0000-0002-1576-2126")),
person("Joe", "Cheng", role = "aut", email = "joe@rstudio.com"),
@@ -189,6 +189,7 @@ Collate:
'shinywrappers.R'
'showcase.R'
'snapshot.R'
'staticimports.R'
'tar.R'
'test-export.R'
'test-server.R'

21
NEWS.md
View File

@@ -1,3 +1,22 @@
shiny 1.7.1.9001
================
## Full changelog
### Minor new features and improvements
* Shiny's internal HTML dependencies are now mounted dynamically instead of statically. (#3537)
* HTML dependencies that are sent to dynamic UI now have better type checking, and no longer require a `dep.src.href` field. (#3537)
### Bug fixes
* Closed tidyverse/dplyr#5552: Compatibility of dplyr 1.0 (and rlang chained errors in general) with `req()`, `validate()`, and friends.
* Closed #2955: Input and output bindings previously attempted to use `el['data-input-id']`, but that never worked. They now use `el.getAttribute('data-input-id')` instead. (#3538)
* Closed tidyverse/dplyr#6154: Values from an `actionButton()` had S3 classes in the incorrect order.
shiny 1.7.1
===========
@@ -40,7 +59,7 @@ shiny 1.7.0
### Other improvements
* Shiny's core JavaScript code was converted to TypeScript. For the latest development information, please see the [README.md in `./srcts`](https://github.com/rstudio/shiny/tree/master/srcts). (#3296)
* Shiny's core JavaScript code was converted to TypeScript. For the latest development information, please see the [README.md in `./srcts`](https://github.com/rstudio/shiny/tree/v1.7.0/srcts). (#3296)
* Switched from `digest::digest()` to `rlang::hash()` for hashing. (#3264)

View File

@@ -125,14 +125,14 @@ shinyAppTemplate <- function(path = NULL, examples = "default", dryrun = FALSE)
}
if ("shinytest" %in% examples) {
if (!is_available("shinytest", "1.4.0"))
if (!is_installed("shinytest", "1.4.0"))
{
message(
"The tests/shinytest directory needs shinytest 1.4.0 or later to work properly."
)
if (is_available("shinytest")) {
if (is_installed("shinytest")) {
message("You currently have shinytest ",
utils::packageVersion("shinytest"), " installed.")
get_package_version("shinytest"), " installed.")
}
}
@@ -152,7 +152,7 @@ shinyAppTemplate <- function(path = NULL, examples = "default", dryrun = FALSE)
# Helper to resolve paths relative to our template
template_path <- function(...) {
system.file("app_template", ..., package = "shiny")
system_file("app_template", ..., package = "shiny")
}
# Resolve path relative to destination

View File

@@ -1,87 +1,4 @@
#' Create a page with fluid layout
#'
#' Functions for creating fluid page layouts. A fluid page layout consists of
#' rows which in turn include columns. Rows exist for the purpose of making sure
#' their elements appear on the same line (if the browser has adequate width).
#' Columns exist for the purpose of defining how much horizontal space within a
#' 12-unit wide grid it's elements should occupy. Fluid pages scale their
#' components in realtime to fill all available browser width.
#'
#' @param ... Elements to include within the page
#' @param title The browser window title (defaults to the host URL of the page).
#' Can also be set as a side effect of the [titlePanel()] function.
#' @inheritParams bootstrapPage
#'
#' @return A UI defintion that can be passed to the [shinyUI] function.
#'
#' @details To create a fluid page use the `fluidPage` function and include
#' instances of `fluidRow` and [column()] within it. As an
#' alternative to low-level row and column functions you can also use
#' higher-level layout functions like [sidebarLayout()].
#'
#' @note See the [
#' Shiny-Application-Layout-Guide](https://shiny.rstudio.com/articles/layout-guide.html) for additional details on laying out fluid
#' pages.
#'
#' @family layout functions
#' @seealso [column()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' # Example of UI with fluidPage
#' ui <- fluidPage(
#'
#' # Application title
#' titlePanel("Hello Shiny!"),
#'
#' sidebarLayout(
#'
#' # Sidebar with a slider input
#' sidebarPanel(
#' sliderInput("obs",
#' "Number of observations:",
#' min = 0,
#' max = 1000,
#' value = 500)
#' ),
#'
#' # Show a plot of the generated distribution
#' mainPanel(
#' plotOutput("distPlot")
#' )
#' )
#' )
#'
#' # Server logic
#' server <- function(input, output) {
#' output$distPlot <- renderPlot({
#' hist(rnorm(input$obs))
#' })
#' }
#'
#' # Complete app with UI and server components
#' shinyApp(ui, server)
#'
#'
#' # UI demonstrating column layouts
#' ui <- fluidPage(
#' title = "Hello Shiny!",
#' fluidRow(
#' column(width = 4,
#' "4"
#' ),
#' column(width = 3, offset = 2,
#' "3 offset 2"
#' )
#' )
#' )
#'
#' shinyApp(ui, server = function(input, output) { })
#' }
#' @rdname fluidPage
#' @rdname bootstrapPage
#' @export
fluidPage <- function(..., title = NULL, theme = NULL, lang = NULL) {
bootstrapPage(div(class = "container-fluid", ...),
@@ -91,62 +8,13 @@ fluidPage <- function(..., title = NULL, theme = NULL, lang = NULL) {
}
#' @rdname fluidPage
#' @rdname column
#' @export
fluidRow <- function(...) {
div(class = "row", ...)
}
#' Create a page with a fixed layout
#'
#' Functions for creating fixed page layouts. A fixed page layout consists of
#' rows which in turn include columns. Rows exist for the purpose of making sure
#' their elements appear on the same line (if the browser has adequate width).
#' Columns exist for the purpose of defining how much horizontal space within a
#' 12-unit wide grid it's elements should occupy. Fixed pages limit their width
#' to 940 pixels on a typical display, and 724px or 1170px on smaller and larger
#' displays respectively.
#'
#' @param ... Elements to include within the container
#' @param title The browser window title (defaults to the host URL of the page)
#' @inheritParams bootstrapPage
#'
#' @return A UI defintion that can be passed to the [shinyUI] function.
#'
#' @details To create a fixed page use the `fixedPage` function and include
#' instances of `fixedRow` and [column()] within it. Note that
#' unlike [fluidPage()], fixed pages cannot make use of higher-level
#' layout functions like `sidebarLayout`, rather, all layout must be done
#' with `fixedRow` and `column`.
#'
#' @note See the [
#' Shiny Application Layout Guide](https://shiny.rstudio.com/articles/layout-guide.html) for additional details on laying out fixed
#' pages.
#'
#' @family layout functions
#'
#' @seealso [column()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fixedPage(
#' title = "Hello, Shiny!",
#' fixedRow(
#' column(width = 4,
#' "4"
#' ),
#' column(width = 3, offset = 2,
#' "3 offset 2"
#' )
#' )
#' )
#'
#' shinyApp(ui, server = function(input, output) { })
#' }
#'
#' @rdname fixedPage
#' @rdname bootstrapPage
#' @export
fixedPage <- function(..., title = NULL, theme = NULL, lang = NULL) {
bootstrapPage(div(class = "container", ...),
@@ -155,28 +23,39 @@ fixedPage <- function(..., title = NULL, theme = NULL, lang = NULL) {
lang = lang)
}
#' @rdname fixedPage
#' @rdname column
#' @export
fixedRow <- function(...) {
div(class = "row", ...)
}
#' Create a column within a UI definition
#' Responsive row-column based layout
#'
#' Create a column for use within a [fluidRow()] or
#' [fixedRow()]
#' Layout UI components using Bootstrap's grid layout system. Use
#' `fluidRow()` to group elements that should appear on the same line (if the
#' browser has adequate width) and `column()` to define how much horizontal
#' space within a 12-unit wide grid each on of these elements should occupy. See
#' the [layout guide](https://shiny.rstudio.com/articles/layout-guide.html) for
#' more context and examples.
#'
#' @param width The grid width of the column (must be between 1 and 12)
#' @param ... Elements to include within the column
#' To work properly, these functions need [Bootstrap](https://getbootstrap.com)
#' included on the page. Since most Shiny apps use [bootstrapPage()]
#' under-the-hood, this is usually the case, but custom page containers (i.e.,
#' [htmlTemplate()]) may need to explicitly include [bootstrapLib()]
#' dependencies.
#'
#' @param width The grid width of the column (must be between 1 and 12). When
#' the device width is small (e.g., the viewer is on a mobile phone), the
#' width is always 12. For more control over these responsive breakpoints, use
#' Bootstrap's grid system more directly (e.g., `fluidRow(div(class =
#' "col-lg-2", ...))`).
#' @param ... UI elements (i.e., [tags]). For `fluidRow()`, `...` should be a set of `column()`s.
#' @param offset The number of columns to offset this column from the end of the
#' previous column.
#'
#' @return A column that can be included within a
#' [fluidRow()] or [fixedRow()].
#' @return A UI element (i.e., [tags]).
#'
#'
#' @seealso [fluidRow()], [fixedRow()].
#' @seealso [fluidPage()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -202,16 +81,10 @@ fixedRow <- function(...) {
#'
#' shinyApp(ui, server)
#'
#'
#'
#' ui <- fluidPage(
#' fluidRow(
#' column(width = 4,
#' "4"
#' ),
#' column(width = 3, offset = 2,
#' "3 offset 2"
#' )
#' column(width = 4, "4"),
#' column(width = 3, offset = 2, "3 offset 2")
#' )
#' )
#' shinyApp(ui, server = function(input, output) { })

View File

@@ -1,36 +1,84 @@
#' @include utils.R
NULL
#' Create a Bootstrap page
#' Create a Bootstrap UI page container
#'
#' Create a Shiny UI page that loads the CSS and JavaScript for
#' [Bootstrap](https://getbootstrap.com/), and has no content in the page
#' body (other than what you provide).
#' @description
#' Create a user interface (UI) page container based on
#' [Bootstrap](https://getbootstrap.com/)'s CSS and JavaScript. Most Shiny apps
#' should use [fluidPage()] (or [navbarPage()]) to get a page container with a
#' responsive page width, but in some cases you may want a fixed width container
#' (`fixedPage()`) or just a bare `<body>` container (`bootstrapPage()`).
#'
#' This function is primarily intended for users who are proficient in HTML/CSS,
#' and know how to lay out pages in Bootstrap. Most applications should use
#' [fluidPage()] along with layout functions like
#' [fluidRow()] and [sidebarLayout()].
#' Most Shiny apps make use of other Shiny UI functions for [managing
#' layout](https://shiny.rstudio.com/articles/layout-guide.html) (e.g.,
#' [sidebarLayout()], [fluidRow()], etc), navigation (e.g., [tabPanel()]), and
#' other styling (e.g., [wellPanel()], [inputPanel()]). A good portion of these
#' Shiny UI functions require Bootstrap to work properly (so most Shiny apps
#' should use these functions to start their UI definitions), but more advanced
#' usage (i.e., custom HTML/CSS/JS) can avoid Bootstrap entirely by using
#' [htmlTemplate()] and/or HTML [tags].
#'
#' @param ... The contents of the document body.
#' @param title The browser window title (defaults to the host URL of the page)
#' @param ... UI elements (i.e., [tags]).
#' @param title The browser window title (defaults to the host URL of the page).
#' Can also be set as a side effect of the [titlePanel()] function.
#' @param theme One of the following:
#' * `NULL` (the default), which implies a "stock" build of Bootstrap 3.
#' * A [bslib::bs_theme()] object. This can be used to replace a stock
#' build of Bootstrap 3 with a customized version of Bootstrap 3 or higher.
#' * A character string pointing to an alternative Bootstrap stylesheet
#' (normally a css file within the www directory, e.g. `www/bootstrap.css`).
#' This option is here mainly for legacy reasons.
#' @param lang ISO 639-1 language code for the HTML page, such as "en" or "ko".
#' This will be used as the lang in the \code{<html>} tag, as in \code{<html lang="en">}.
#' This will be used as the lang in the `<html>` tag, as in `<html lang="en">`.
#' The default (NULL) results in an empty string.
#'
#' @return A UI defintion that can be passed to the [shinyUI] function.
#' @return A UI definition (i.e., a [tags] object) that can be passed to [shinyApp()].
#'
#' @note The `basicPage` function is deprecated, you should use the
#' [fluidPage()] function instead.
#'
#' @seealso [fluidPage()], [fixedPage()]
#' @seealso [navbarPage()], [fillPage()], [column()], [tabPanel()]
#' @export
#' @examples
#'
#' # First create some UI content.
#' # See the layout guide to learn more about creating different layouts
#' # https://shiny.rstudio.com/articles/layout-guide.html
#' ui <- sidebarLayout(
#' sidebarPanel(sliderInput("obs", "Number of observations:", 0, 1000, 500)),
#' mainPanel(plotOutput("distPlot"))
#' )
#' server <- function(input, output) {
#' output$distPlot <- renderPlot(hist(rnorm(input$obs)))
#' }
#'
#' # Demonstrating difference between fluidPage(), fixedPage(), bootstrapPage()
#' if (interactive()) {
#' # Container width scales _fluidly_ with window size
#' shinyApp(fluidPage(ui), server)
#' # Container width changes with window size at fixed breakpoints
#' shinyApp(fixedPage(ui), server)
#' # Container width is equal to the window's width
#' shinyApp(bootstrapPage(ui), server)
#' }
#'
#' # The default look is provided by Bootstrap 3, but {bslib} can be
#' # used to customize the Bootstrap version and its default styling
#' theme <- bslib::bs_theme(
#' version = 5,
#' bg = "#101010",
#' fg = "#FDF7F7",
#' primary = "#ED79F9",
#' base_font = bslib::font_google("Prompt"),
#' code_font = bslib::font_google("JetBrains Mono")
#' )
#' if (interactive()) {
#' # Call thematic::thematic_shiny(font = "auto") to automatically
#' # translate the theme/CSS to the R plot
#' shinyApp(
#' fluidPage(ui, theme = theme, title = "Hello Bootstrap 5"),
#' server
#' )
#' }
#'
bootstrapPage <- function(..., title = NULL, theme = NULL, lang = NULL) {
args <- list(
@@ -138,8 +186,7 @@ bs_theme_deps <- function(theme) {
}
is_bs_theme <- function(x) {
is_available("bslib", "0.2.0.9000") &&
bslib::is_bs_theme(x)
bslib::is_bs_theme(x)
}
#' Obtain Shiny's Bootstrap Sass theme
@@ -215,11 +262,10 @@ registerThemeDependency <- function(func) {
bootstrapDependency <- function(theme) {
htmlDependency(
"bootstrap", bootstrapVersion,
c(
href = "shared/bootstrap",
file = system.file("www/shared/bootstrap", package = "shiny")
),
"bootstrap",
bootstrapVersion,
src = "www/shared/bootstrap",
package = "shiny",
script = c(
"js/bootstrap.min.js",
# Safely adding accessibility plugin for screen readers and keyboard users; no break for sighted aspects (see https://github.com/paypal/bootstrap-accessibility-plugin)
@@ -236,14 +282,12 @@ bootstrapDependency <- function(theme) {
bootstrapVersion <- "3.4.1"
#' @rdname bootstrapPage
#' @export
basicPage <- function(...) {
bootstrapPage(div(class="container-fluid", list(...)))
}
#' Create a page that fills the window
#'
#' `fillPage` creates a page whose height and width always fill the
@@ -1109,11 +1153,17 @@ tableOutput <- function(outputId) {
dataTableDependency <- list(
htmlDependency(
"datatables", "1.10.5", c(href = "shared/datatables"),
"datatables",
"1.10.5",
src = "www/shared/datatables",
package = "shiny",
script = "js/jquery.dataTables.min.js"
),
htmlDependency(
"datatables-bootstrap", "1.10.5", c(href = "shared/datatables"),
"datatables-bootstrap",
"1.10.5",
src = "www/shared/datatables",
package = "shiny",
stylesheet = c("css/dataTables.bootstrap.css", "css/dataTables.extra.css"),
script = "js/dataTables.bootstrap.js"
)
@@ -1153,7 +1203,7 @@ dataTableOutput <- function(outputId) {
htmlOutput <- function(outputId, inline = FALSE,
container = if (inline) span else div, ...)
{
if (anyUnnamed(list(...))) {
if (any_unnamed(list(...))) {
warning("Unnamed elements in ... will be replaced with dynamic UI.")
}
container(id = outputId, class="shiny-html-output", ...)

View File

@@ -228,7 +228,9 @@ withLogErrors <- function(expr,
if (promises::is.promise(result)) {
result <- promises::catch(result, function(cond) {
# Don't print shiny.silent.error (i.e. validation errors)
if (inherits(cond, "shiny.silent.error")) return()
if (cnd_inherits(cond, "shiny.silent.error")) {
return()
}
if (isTRUE(getOption("show.error.messages"))) {
printError(cond, full = full, offset = offset)
}
@@ -239,7 +241,7 @@ withLogErrors <- function(expr,
},
error = function(cond) {
# Don't print shiny.silent.error (i.e. validation errors)
if (inherits(cond, "shiny.silent.error")) return()
if (cnd_inherits(cond, "shiny.silent.error")) return()
if (isTRUE(getOption("show.error.messages"))) {
printError(cond, full = full, offset = offset)
}

View File

@@ -1,55 +1,6 @@
# A scope where we can put mutable global state
.globals <- new.env(parent = emptyenv())
register_s3_method <- function(pkg, generic, class, fun = NULL) {
stopifnot(is.character(pkg), length(pkg) == 1)
stopifnot(is.character(generic), length(generic) == 1)
stopifnot(is.character(class), length(class) == 1)
if (is.null(fun)) {
fun <- get(paste0(generic, ".", class), envir = parent.frame())
} else {
stopifnot(is.function(fun))
}
if (pkg %in% loadedNamespaces()) {
registerS3method(generic, class, fun, envir = asNamespace(pkg))
}
# Always register hook in case pkg is loaded at some
# point the future (or, potentially, but less commonly,
# unloaded & reloaded)
setHook(
packageEvent(pkg, "onLoad"),
function(...) {
registerS3method(generic, class, fun, envir = asNamespace(pkg))
}
)
}
register_upgrade_message <- function(pkg, version) {
msg <- sprintf(
"This version of Shiny is designed to work with '%s' >= %s.
Please upgrade via install.packages('%s').",
pkg, version, pkg
)
if (pkg %in% loadedNamespaces() && !is_available(pkg, version)) {
packageStartupMessage(msg)
}
# Always register hook in case pkg is loaded at some
# point the future (or, potentially, but less commonly,
# unloaded & reloaded)
setHook(
packageEvent(pkg, "onLoad"),
function(...) {
if (!is_available(pkg, version)) packageStartupMessage(msg)
}
)
}
.onLoad <- function(libname, pkgname) {
# R's lazy-loading package scheme causes the private seed to be cached in the
# package itself, making our PRNG completely deterministic. This line resets
@@ -62,9 +13,9 @@ register_upgrade_message <- function(pkg, version) {
# Make sure these methods are available to knitr if shiny is loaded but not
# attached.
register_s3_method("knitr", "knit_print", "reactive")
register_s3_method("knitr", "knit_print", "shiny.appobj")
register_s3_method("knitr", "knit_print", "shiny.render.function")
s3_register("knitr::knit_print", "reactive")
s3_register("knitr::knit_print", "shiny.appobj")
s3_register("knitr::knit_print", "shiny.render.function")
# Shiny 1.4.0 bumps jQuery 1.x to 3.x, which caused a problem
# with static-rendering of htmlwidgets, and htmlwidgets 1.5

View File

@@ -4,7 +4,7 @@
# @param version The version of the package
check_suggested <- function(package, version = NULL) {
if (is_available(package, version)) {
if (is_installed(package, version)) {
return()
}
@@ -115,22 +115,28 @@ check_reactlog <- function() {
}
# read reactlog version from description file
# prevents version mismatch in code and description file
reactlog_version <- function() {
desc <- read.dcf(system.file("DESCRIPTION", package = "shiny", mustWork = TRUE))
suggests <- desc[1,"Suggests"][[1]]
suggests_pkgs <- strsplit(suggests, "\n")[[1]]
reactlog_version <- local({
version <- NULL
function() {
if (!is.null(version)) return(version)
reactlog_info <- suggests_pkgs[grepl("reactlog", suggests_pkgs)]
if (length(reactlog_info) == 0) {
stop("reactlog can not be found in shiny DESCRIPTION file")
desc <- read.dcf(system_file("DESCRIPTION", package = "shiny"))
suggests <- desc[1,"Suggests"][[1]]
suggests_pkgs <- strsplit(suggests, "\n")[[1]]
reactlog_info <- suggests_pkgs[grepl("reactlog", suggests_pkgs)]
if (length(reactlog_info) == 0) {
stop("reactlog can not be found in shiny DESCRIPTION file")
}
reactlog_info <- sub("^[^\\(]*\\(", "", reactlog_info)
reactlog_info <- sub("\\)[^\\)]*$", "", reactlog_info)
reactlog_info <- sub("^[>= ]*", "", reactlog_info)
version <<- package_version(reactlog_info)
version
}
reactlog_info <- sub("^[^\\(]*\\(", "", reactlog_info)
reactlog_info <- sub("\\)[^\\)]*$", "", reactlog_info)
reactlog_info <- sub("^[>= ]*", "", reactlog_info)
package_version(reactlog_info)
}
})
RLog <- R6Class(

View File

@@ -4,12 +4,12 @@ startPNG <- function(filename, width, height, res, ...) {
# to use ragg (say, instead of showtext, for custom font rendering).
# In the next shiny release, this option will likely be superseded in
# favor of a fully customizable graphics device option
if ((getOption('shiny.useragg') %||% FALSE) && is_available("ragg")) {
if ((getOption('shiny.useragg') %||% FALSE) && is_installed("ragg")) {
pngfun <- ragg::agg_png
} else if (capabilities("aqua")) {
# i.e., png(type = 'quartz')
pngfun <- grDevices::png
} else if ((getOption('shiny.usecairo') %||% TRUE) && is_available("Cairo")) {
} else if ((getOption('shiny.usecairo') %||% TRUE) && is_installed("Cairo")) {
pngfun <- Cairo::CairoPNG
} else {
# i.e., png(type = 'cairo')

View File

@@ -138,7 +138,8 @@ datePickerDependency <- function(theme) {
htmlDependency(
name = "bootstrap-datepicker-js",
version = version_bs_date_picker,
src = c(href = "shared/datepicker"),
src = "www/shared/datepicker",
package = "shiny",
script = if (getOption("shiny.minified", TRUE)) "js/bootstrap-datepicker.min.js"
else "js/bootstrap-datepicker.js",
# Need to enable noConflict mode. See #1346.
@@ -157,18 +158,19 @@ datePickerCSS <- function(theme) {
return(htmlDependency(
name = "bootstrap-datepicker-css",
version = version_bs_date_picker,
src = c(href = "shared/datepicker"),
src = "www/shared/datepicker",
package = "shiny",
stylesheet = "css/bootstrap-datepicker3.min.css"
))
}
scss_file <- system.file(package = "shiny", "www/shared/datepicker/scss/build3.scss")
scss_file <- system_file(package = "shiny", "www/shared/datepicker/scss/build3.scss")
bslib::bs_dependency(
input = sass::sass_file(scss_file),
theme = theme,
name = "bootstrap-datepicker",
version = version_bs_date_picker,
cache_key_extra = shinyPackageVersion()
cache_key_extra = get_package_version("shiny")
)
}

View File

@@ -213,14 +213,7 @@ selectizeIt <- function(inputId, select, options, nonempty = FALSE) {
deps <- list(selectizeDependency())
if ('drag_drop' %in% options$plugins) {
deps <- c(
deps,
list(htmlDependency(
'jqueryui', '1.12.1',
c(href = 'shared/jqueryui'),
script = 'jquery-ui.min.js'
))
)
deps[[length(deps) + 1]] <- jqueryuiDependency()
}
# Insert script on same level as <select> tag
@@ -247,7 +240,7 @@ selectizeDependencyFunc <- function(theme) {
return(selectizeStaticDependency(version_selectize))
}
selectizeDir <- system.file(package = "shiny", "www/shared/selectize/")
selectizeDir <- system_file(package = "shiny", "www/shared/selectize/")
bs_version <- bslib::theme_version(theme)
stylesheet <- file.path(
selectizeDir, "scss", paste0("selectize.bootstrap", bs_version, ".scss")
@@ -267,15 +260,17 @@ selectizeDependencyFunc <- function(theme) {
theme = theme,
name = "selectize",
version = version_selectize,
cache_key_extra = shinyPackageVersion(),
cache_key_extra = get_package_version("shiny"),
.dep_args = list(script = script)
)
}
selectizeStaticDependency <- function(version) {
htmlDependency(
"selectize", version,
src = c(href = "shared/selectize"),
"selectize",
version,
src = "www/shared/selectize",
package = "shiny",
stylesheet = "css/selectize.bootstrap3.css",
script = c(
"js/selectize.min.js",

View File

@@ -205,13 +205,17 @@ ionRangeSliderDependency <- function() {
list(
# ion.rangeSlider also needs normalize.css, which is already included in Bootstrap.
htmlDependency(
"ionrangeslider-javascript", version_ion_range_slider,
src = c(href = "shared/ionrangeslider"),
"ionrangeslider-javascript",
version_ion_range_slider,
src = "www/shared/ionrangeslider",
package = "shiny",
script = "js/ion.rangeSlider.min.js"
),
htmlDependency(
"strftime", version_strftime,
src = c(href = "shared/strftime"),
"strftime",
version_strftime,
src = "www/shared/strftime",
package = "shiny",
script = "strftime-min.js"
),
bslib::bs_dependency_defer(ionRangeSliderDependencyCSS)
@@ -223,7 +227,8 @@ ionRangeSliderDependencyCSS <- function(theme) {
return(htmlDependency(
"ionrangeslider-css",
version_ion_range_slider,
src = c(href = "shared/ionrangeslider"),
src = "www/shared/ionrangeslider",
package = "shiny",
stylesheet = "css/ion.rangeSlider.css"
))
}
@@ -232,13 +237,13 @@ ionRangeSliderDependencyCSS <- function(theme) {
input = list(
list(accent = "$component-active-bg"),
sass::sass_file(
system.file(package = "shiny", "www/shared/ionrangeslider/scss/shiny.scss")
system_file(package = "shiny", "www/shared/ionrangeslider/scss/shiny.scss")
)
),
theme = theme,
name = "ionRangeSlider",
version = version_ion_range_slider,
cache_key_extra = shinyPackageVersion()
cache_key_extra = get_package_version("shiny")
)
}

View File

@@ -41,7 +41,7 @@ normalizeChoicesArgs <- function(choices, choiceNames, choiceValues,
if (length(choiceNames) != length(choiceValues)) {
stop("`choiceNames` and `choiceValues` must have the same length.")
}
if (anyNamed(choiceNames) || anyNamed(choiceValues)) {
if (any_named(choiceNames) || any_named(choiceValues)) {
stop("`choiceNames` and `choiceValues` must not be named.")
}
} else {

View File

@@ -79,8 +79,8 @@ absolutePanel <- function(...,
if (isTRUE(draggable)) {
divTag <- tagAppendAttributes(divTag, class='draggable')
return(tagList(
singleton(tags$head(tags$script(src='shared/jqueryui/jquery-ui.min.js'))),
divTag,
jqueryuiDependency(),
tags$script('$(".draggable").draggable();')
))
} else {
@@ -99,3 +99,14 @@ fixedPanel <- function(...,
width=width, height=height, draggable=draggable, cursor=match.arg(cursor),
fixed=TRUE)
}
jqueryuiDependency <- function() {
htmlDependency(
'jqueryui',
'1.12.1',
src = 'www/shared/jqueryui',
package = 'shiny',
script = 'jquery-ui.min.js'
)
}

View File

@@ -63,7 +63,7 @@ knit_print.shiny.appobj <- function(x, ...) {
#' @param inline Whether the object is printed inline.
knit_print.shiny.render.function <- function(x, ..., inline = FALSE) {
x <- htmltools::as.tags(x, inline = inline)
output <- knitr::knit_print(tagList(x))
output <- knitr::knit_print(tagList(x), ..., inline = inline)
attr(output, "knit_cacheable") <- FALSE
attr(output, "knit_meta") <- append(attr(output, "knit_meta"),
shiny_rmd_warning())
@@ -77,5 +77,5 @@ knit_print.reactive <- function(x, ..., inline = FALSE) {
renderFunc <- if (inline) renderText else renderPrint
knitr::knit_print(renderFunc({
x()
}), inline = inline)
}), ..., inline = inline)
}

View File

@@ -348,7 +348,7 @@ HandlerManager <- R6Class("HandlerManager",
httpResponse(status = 500L,
content_type = "text/html; charset=UTF-8",
content = as.character(htmltools::htmlTemplate(
system.file("template", "error.html", package = "shiny"),
system_file("template", "error.html", package = "shiny"),
message = conditionMessage(err)
))
)

View File

@@ -1,5 +1,5 @@
# Promise helpers taken from:
# https://github.com/rstudio/promises/blob/master/tests/testthat/common.R
# https://github.com/rstudio/promises/blob/main/tests/testthat/common.R
# Block until all pending later tasks have executed
wait_for_it <- function() {
while (!later::loop_empty()) {

View File

@@ -1203,7 +1203,7 @@ Observer <- R6Class(
# validation = function(e) NULL,
# shiny.output.cancel = function(e) NULL
if (inherits(e, "shiny.silent.error")) {
if (cnd_inherits(e, "shiny.silent.error")) {
return()
}

View File

@@ -612,7 +612,7 @@ getGgplotCoordmap <- function(p, width, height, res) {
find_panel_info <- function(b) {
# Structure of ggplot objects changed after 2.1.0. After 2.2.1, there was a
# an API for extracting the necessary information.
ggplot_ver <- utils::packageVersion("ggplot2")
ggplot_ver <- get_package_version("ggplot2")
if (ggplot_ver > "2.2.1") {
find_panel_info_api(b)

View File

@@ -23,10 +23,10 @@
#' @examples
#' ## Only run this example in interactive R sessions
#' if (interactive()) {
#' runUrl('https://github.com/rstudio/shiny_example/archive/master.tar.gz')
#' runUrl('https://github.com/rstudio/shiny_example/archive/main.tar.gz')
#'
#' # Can run an app from a subdirectory in the archive
#' runUrl("https://github.com/rstudio/shiny_example/archive/master.zip",
#' runUrl("https://github.com/rstudio/shiny_example/archive/main.zip",
#' subdir = "inst/shinyapp/")
#' }
runUrl <- function(url, filetype = NULL, subdir = NULL, destdir = NULL, ...) {

View File

@@ -467,7 +467,7 @@ runExample <- function(example=NA,
launch.browser = getOption('shiny.launch.browser', interactive()),
host=getOption('shiny.host', '127.0.0.1'),
display.mode=c("auto", "normal", "showcase")) {
examplesDir <- system.file('examples', package='shiny')
examplesDir <- system_file('examples', package='shiny')
dir <- resolve(examplesDir, example)
if (is.null(dir)) {
if (is.na(example)) {

View File

@@ -181,7 +181,7 @@ registerInputHandler("shiny.datetime", function(val, ...){
registerInputHandler("shiny.action", function(val, shinysession, name) {
# mark up the action button value with a special class so we can recognize it later
class(val) <- c(class(val), "shinyActionButtonValue")
class(val) <- c("shinyActionButtonValue", class(val))
val
})

View File

@@ -128,7 +128,7 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
appvars <- new.env()
appvars$server <- NULL
sys.www.root <- system.file('www', package='shiny')
sys.www.root <- system_file('www', package='shiny')
# This value, if non-NULL, must be present on all HTTP and WebSocket
# requests as the Shiny-Shared-Secret header or else access will be
@@ -385,7 +385,7 @@ startApp <- function(appObj, port, host, quiet) {
list(
# Always handle /session URLs dynamically, even if / is a static path.
"session" = excludeStaticPath(),
"shared" = system.file(package = "shiny", "www", "shared")
"shared" = system_file(package = "shiny", "www", "shared")
),
.globals$resourcePaths
)

View File

@@ -403,7 +403,7 @@ ShinySession <- R6Class(
sendMessage = function(...) {
# This function is a wrapper for $write
msg <- list(...)
if (anyUnnamed(msg)) {
if (any_unnamed(msg)) {
stop("All arguments to sendMessage must be named.")
}
private$write(toJSON(msg))
@@ -825,7 +825,7 @@ ShinySession <- R6Class(
dots <- eval(substitute(alist(...)))
}
if (anyUnnamed(dots))
if (any_unnamed(dots))
stop("exportTestValues: all arguments must be named.")
names(dots) <- ns(names(dots))
@@ -913,7 +913,7 @@ ShinySession <- R6Class(
# Copy `values` from scopeState to state, adding namespace
if (length(scopeState$values) != 0) {
if (anyUnnamed(scopeState$values)) {
if (any_unnamed(scopeState$values)) {
stop("All scope values in must be named.")
}
@@ -1114,7 +1114,12 @@ ShinySession <- R6Class(
structure(list(), class = "try-error", condition = cond)
} else if (inherits(cond, "shiny.output.cancel")) {
structure(list(), class = "cancel-output")
} else if (inherits(cond, "shiny.silent.error")) {
} else if (cnd_inherits(cond, "shiny.silent.error")) {
# The error condition might have been chained by
# foreign code, e.g. dplyr. Find the original error.
while (!inherits(cond, "shiny.silent.error")) {
cond <- cond$parent
}
# Don't let shiny.silent.error go through the normal stop
# path of try, because we don't want it to print. But we
# do want to try to return the same looking result so that
@@ -1701,7 +1706,7 @@ ShinySession <- R6Class(
dots <- eval(substitute(alist(...)))
}
if (anyUnnamed(dots))
if (any_unnamed(dots))
stop("exportTestValues: all arguments must be named.")
# Create a named list where each item is a list with an expression and

View File

@@ -193,7 +193,7 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
staticPaths <- list()
}
fallbackWWWDir <- system.file("www-dir", package = "shiny")
fallbackWWWDir <- system_file("www-dir", package = "shiny")
serverSource <- cachedFuncWithFile(appDir, "server.R", case.sensitive = FALSE,
function(serverR) {
@@ -455,7 +455,7 @@ shinyAppDir_appR <- function(fileName, appDir, options=list())
staticPaths <- list()
}
fallbackWWWDir <- system.file("www-dir", package = "shiny")
fallbackWWWDir <- system_file("www-dir", package = "shiny")
oldwd <- NULL
monitorHandle <- NULL

View File

@@ -39,7 +39,7 @@ renderPage <- function(ui, showcase=0, testMode=FALSE) {
# Put the body into the default template
ui <- htmlTemplate(
system.file("template", "default.html", package = "shiny"),
system_file("template", "default.html", package = "shiny"),
lang = lang,
body = ui,
# this template is a complete HTML document
@@ -55,8 +55,14 @@ renderPage <- function(ui, showcase=0, testMode=FALSE) {
if (testMode) {
# Add code injection listener if in test mode
shiny_deps[[length(shiny_deps) + 1]] <-
htmlDependency("shiny-testmode", shinyPackageVersion(),
c(href="shared"), script = "shiny-testmode.js")
htmlDependency(
"shiny-testmode",
get_package_version("shiny"),
src = "www/shared",
package = "shiny",
script = "shiny-testmode.js",
all_files = FALSE
)
}
html <- renderDocument(ui, shiny_deps, processDep = createWebDependency)
@@ -68,23 +74,19 @@ jqueryDependency <- function() {
if (version == 3) {
return(htmlDependency(
"jquery", version_jquery,
src = c(
href = "shared",
file = "www/shared"
),
src = "www/shared",
package = "shiny",
script = "jquery.min.js"
script = "jquery.min.js",
all_files = FALSE
))
}
if (version == 1) {
return(htmlDependency(
"jquery", "1.12.4",
src = c(
href = "shared/legacy",
file = "www/shared/legacy"
),
src = "www/shared/legacy",
package = "shiny",
script = "jquery.min.js"
script = "jquery.min.js",
all_files = FALSE
))
}
stop("Unsupported version of jQuery: ", version)
@@ -95,8 +97,9 @@ shinyDependencies <- function() {
bslib::bs_dependency_defer(shinyDependencyCSS),
htmlDependency(
name = "shiny-javascript",
version = shinyPackageVersion(),
src = c(href = "shared"),
version = get_package_version("shiny"),
src = "www/shared",
package = "shiny",
script =
if (isTRUE(
get_devmode_option(
@@ -106,24 +109,27 @@ shinyDependencies <- function() {
))
"shiny.min.js"
else
"shiny.js"
"shiny.js",
all_files = FALSE
)
)
}
shinyDependencyCSS <- function(theme) {
version <- shinyPackageVersion()
version <- get_package_version("shiny")
if (!is_bs_theme(theme)) {
return(htmlDependency(
name = "shiny-css",
version = version,
src = c(href = "shared"),
stylesheet = "shiny.min.css"
src = "www/shared",
package = "shiny",
stylesheet = "shiny.min.css",
all_files = FALSE
))
}
scss_home <- system.file("www/shared/shiny_scss", package = "shiny")
scss_home <- system_file("www/shared/shiny_scss", package = "shiny")
scss_files <- file.path(scss_home, c("bootstrap.scss", "shiny.scss"))
scss_files <- lapply(scss_files, sass::sass_file)

View File

@@ -953,7 +953,7 @@ renderDataTable <- function(expr, options = NULL, searchDelay = 500,
DT10Names <- function() {
rbind(
utils::read.table(
system.file('www/shared/datatables/upgrade1.10.txt', package = 'shiny'),
system_file('www/shared/datatables/upgrade1.10.txt', package = 'shiny'),
stringsAsFactors = FALSE
),
c('aoColumns', 'Removed') # looks like an omission on the upgrade guide

View File

@@ -32,26 +32,40 @@ licenseLink <- function(licenseName) {
showcaseHead <- function() {
deps <- list(
htmlDependency("jqueryui", "1.12.1", c(href="shared/jqueryui"),
script = "jquery-ui.min.js"),
htmlDependency("showdown", "0.3.1", c(href="shared/showdown/compressed"),
script = "showdown.js"),
htmlDependency("highlight.js", "6.2", c(href="shared/highlight"),
script = "highlight.pack.js")
jqueryuiDependency(),
htmlDependency(
"showdown",
"0.3.1",
src = "www/shared/showdown/compressed",
package="shiny",
script = "showdown.js"
),
htmlDependency(
"highlight.js",
"6.2",
src = "www/shared/highlight",
package="shiny",
script = "highlight.pack.js",
stylesheet = "rstudio.css"
),
htmlDependency(
"showcase",
"0.1.0",
src = "www/shared",
package = "shiny",
script = "shiny-showcase.js",
stylesheet = "shiny-showcase.css",
all_files = FALSE
)
)
mdfile <- file.path.ci(getwd(), 'Readme.md')
html <- with(tags, tagList(
script(src="shared/shiny-showcase.js"),
link(rel="stylesheet", type="text/css",
href="shared/highlight/rstudio.css"),
link(rel="stylesheet", type="text/css",
href="shared/shiny-showcase.css"),
html <- tagList(
if (file.exists(mdfile))
script(type="text/markdown", id="showcase-markdown-content",
tags$script(type="text/markdown", id="showcase-markdown-content",
paste(readUTF8(mdfile), collapse="\n"))
else ""
))
)
return(attachDependencies(html, deps))
}

216
R/staticimports.R Normal file
View File

@@ -0,0 +1,216 @@
# Generated by staticimports; do not edit by hand.
# ======================================================================
# Imported from pkg:staticimports
# ======================================================================
# Given a vector, return TRUE if any elements are named, FALSE otherwise.
# For zero-length vectors, always return FALSE.
any_named <- function(x) {
if (length(x) == 0) return(FALSE)
nms <- names(x)
!is.null(nms) && any(nzchar(nms))
}
# Given a vector, return TRUE if any elements are unnamed, FALSE otherwise.
# For zero-length vectors, always return FALSE.
any_unnamed <- function(x) {
if (length(x) == 0) return(FALSE)
nms <- names(x)
is.null(nms) || !all(nzchar(nms))
}
# Borrowed from pkgload:::dev_meta, with some modifications.
# Returns TRUE if `pkg` was loaded with `devtools::load_all()`.
devtools_loaded <- function(pkg) {
ns <- .getNamespace(pkg)
if (is.null(ns) || is.null(ns$.__DEVTOOLS__)) {
return(FALSE)
}
TRUE
}
get_package_version <- function(pkg) {
# `utils::packageVersion()` can be slow, so first try the fast path of
# checking if the package is already loaded.
ns <- .getNamespace(pkg)
if (is.null(ns)) {
utils::packageVersion(pkg)
} else {
as.package_version(ns$.__NAMESPACE__.$spec[["version"]])
}
}
is_installed <- function(pkg, version = NULL) {
installed <- isNamespaceLoaded(pkg) || nzchar(system_file_cached(package = pkg))
if (is.null(version)) {
return(installed)
}
installed && isTRUE(get_package_version(pkg) >= version)
}
register_upgrade_message <- function(pkg, version, error = FALSE) {
msg <- sprintf(
"This version of '%s' is designed to work with '%s' >= %s.
Please upgrade via install.packages('%s').",
environmentName(environment(register_upgrade_message)),
pkg, version, pkg
)
cond <- if (error) stop else packageStartupMessage
if (pkg %in% loadedNamespaces() && !is_installed(pkg, version)) {
cond(msg)
}
# Always register hook in case pkg is loaded at some
# point the future (or, potentially, but less commonly,
# unloaded & reloaded)
setHook(
packageEvent(pkg, "onLoad"),
function(...) {
if (!is_installed(pkg, version)) cond(msg)
}
)
}
# Simplified version rlang:::s3_register() that just uses
# warning() instead of rlang::warn() when registration fails
# https://github.com/r-lib/rlang/blob/main/R/compat-s3-register.R
s3_register <- function(generic, class, method = NULL) {
stopifnot(is.character(generic), length(generic) == 1)
stopifnot(is.character(class), length(class) == 1)
pieces <- strsplit(generic, "::")[[1]]
stopifnot(length(pieces) == 2)
package <- pieces[[1]]
generic <- pieces[[2]]
caller <- parent.frame()
get_method_env <- function() {
top <- topenv(caller)
if (isNamespace(top)) {
asNamespace(environmentName(top))
} else {
caller
}
}
get_method <- function(method, env) {
if (is.null(method)) {
get(paste0(generic, ".", class), envir = get_method_env())
} else {
method
}
}
register <- function(...) {
envir <- asNamespace(package)
# Refresh the method each time, it might have been updated by
# `devtools::load_all()`
method_fn <- get_method(method)
stopifnot(is.function(method_fn))
# Only register if generic can be accessed
if (exists(generic, envir)) {
registerS3method(generic, class, method_fn, envir = envir)
} else {
warning(
"Can't find generic `", generic, "` in package ", package,
" register S3 method. Do you need to update ", package,
" to the latest version?", call. = FALSE
)
}
}
# Always register hook in case package is later unloaded & reloaded
setHook(packageEvent(package, "onLoad"), function(...) {
register()
})
# Avoid registration failures during loading (pkgload or regular).
# Check that environment is locked because the registering package
# might be a dependency of the package that exports the generic. In
# that case, the exports (and the generic) might not be populated
# yet (#1225).
if (isNamespaceLoaded(package) && environmentIsLocked(asNamespace(package))) {
register()
}
invisible()
}
# Borrowed from pkgload::shim_system.file, with some modifications. This behaves
# like `system.file()`, except that (1) for packages loaded with
# `devtools::load_all()`, it will return the path to files in the package's
# inst/ directory, and (2) for other packages, the directory lookup is cached.
# Also, to keep the implementation simple, it doesn't support specification of
# lib.loc or mustWork.
system_file <- function(..., package = "base") {
if (!devtools_loaded(package)) {
return(system_file_cached(..., package = package))
}
if (!is.null(names(list(...)))) {
stop("All arguments other than `package` must be unnamed.")
}
# If package was loaded with devtools (the package loaded with load_all),
# also search for files under inst/, and don't cache the results (it seems
# more likely that the package path will change during the development
# process)
pkg_path <- find.package(package)
# First look in inst/
files_inst <- file.path(pkg_path, "inst", ...)
present_inst <- file.exists(files_inst)
# For any files that weren't present in inst/, look in the base path
files_top <- file.path(pkg_path, ...)
present_top <- file.exists(files_top)
# Merge them together. Here are the different possible conditions, and the
# desired result. NULL means to drop that element from the result.
#
# files_inst: /inst/A /inst/B /inst/C /inst/D
# present_inst: T T F F
# files_top: /A /B /C /D
# present_top: T F T F
# result: /inst/A /inst/B /C NULL
#
files <- files_top
files[present_inst] <- files_inst[present_inst]
# Drop cases where not present in either location
files <- files[present_inst | present_top]
if (length(files) == 0) {
return("")
}
# Make sure backslashes are replaced with slashes on Windows
normalizePath(files, winslash = "/")
}
# A wrapper for `system.file()`, which caches the results, because
# `system.file()` can be slow. Note that because of caching, if
# `system_file_cached()` is called on a package that isn't installed, then the
# package is installed, and then `system_file_cached()` is called again, it will
# still return "".
system_file_cached <- local({
pkg_dir_cache <- character()
function(..., package = "base") {
if (!is.null(names(list(...)))) {
stop("All arguments other than `package` must be unnamed.")
}
not_cached <- is.na(match(package, names(pkg_dir_cache)))
if (not_cached) {
pkg_dir <- system.file(package = package)
pkg_dir_cache[[package]] <<- pkg_dir
} else {
pkg_dir <- pkg_dir_cache[[package]]
}
file.path(pkg_dir, ...)
}
})

View File

@@ -2,6 +2,11 @@
#' @include map.R
NULL
# @staticimports pkg:staticimports
# is_installed get_package_version system_file
# s3_register register_upgrade_message
# any_named any_unnamed
#' Make a random number generator repeatable
#'
#' Given a function that generates random data, returns a wrapped version of
@@ -126,34 +131,6 @@ dropNullsOrEmpty <- function(x) {
x[!vapply(x, nullOrEmpty, FUN.VALUE=logical(1))]
}
# Given a vector/list, return TRUE if any elements are named, FALSE otherwise.
anyNamed <- function(x) {
# Zero-length vector
if (length(x) == 0) return(FALSE)
nms <- names(x)
# List with no name attribute
if (is.null(nms)) return(FALSE)
# List with name attribute; check for any ""
any(nzchar(nms))
}
# Given a vector/list, return TRUE if any elements are unnamed, FALSE otherwise.
anyUnnamed <- function(x) {
# Zero-length vector
if (length(x) == 0) return(FALSE)
nms <- names(x)
# List with no name attribute
if (is.null(nms)) return(TRUE)
# List with name attribute; check for any ""
any(!nzchar(nms))
}
# Given a vector/list, returns a named vector/list (the labels will be blank).
asNamed <- function(x) {
@@ -173,7 +150,7 @@ empty_named_list <- function() {
# name as elements in a, the element in a is dropped. Also, if there are any
# duplicated names in a or b, only the last one with that name is kept.
mergeVectors <- function(a, b) {
if (anyUnnamed(a) || anyUnnamed(b)) {
if (any_unnamed(a) || any_unnamed(b)) {
stop("Vectors must be either NULL or have names for all elements")
}
@@ -186,7 +163,7 @@ mergeVectors <- function(a, b) {
# same name, preserve the original order of those items. For empty
# vectors/lists/NULL, return the original value.
sortByName <- function(x) {
if (anyUnnamed(x))
if (any_unnamed(x))
stop("All items must be named")
# Special case for empty vectors/lists, and NULL
@@ -495,7 +472,7 @@ shinyCallingHandlers <- function(expr) {
withCallingHandlers(captureStackTraces(expr),
error = function(e) {
# Don't intercept shiny.silent.error (i.e. validation errors)
if (inherits(e, "shiny.silent.error"))
if (cnd_inherits(e, "shiny.silent.error"))
return()
handle <- getOption('shiny.error')
@@ -1716,24 +1693,20 @@ findEnclosingApp <- function(path = ".") {
}
}
# Check if a package is installed, and if version is specified,
# that we have at least that version
is_available <- function(package, version = NULL) {
installed <- nzchar(system.file(package = package))
if (is.null(version)) {
return(installed)
}
installed && isTRUE(utils::packageVersion(package) >= version)
# Until `rlang::cnd_inherits()` is on CRAN
cnd_inherits <- function(cnd, class) {
cnd_some(cnd, ~ inherits(.x, class))
}
cnd_some <- function(.cnd, .p, ...) {
.p <- rlang::as_function(.p)
# cached version of utils::packageVersion("shiny")
shinyPackageVersion <- local({
version <- NULL
function() {
if (is.null(version)) {
version <<- utils::packageVersion("shiny")
while (rlang::is_condition(.cnd)) {
if (.p(.cnd, ...)) {
return(TRUE)
}
version
.cnd <- .cnd$parent
}
})
FALSE
}

View File

@@ -2,7 +2,7 @@
<!-- badges: start -->
[![CRAN](https://www.r-pkg.org/badges/version/shiny)](https://CRAN.R-project.org/package=shiny)
[![R build status](https://github.com/rstudio/shiny/workflows/R-CMD-check/badge.svg)](https://github.com/rstudio/shiny/actions)
[![R build status](https://github.com/rstudio/shiny/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/rstudio/shiny/actions)
[![RStudio community](https://img.shields.io/badge/community-shiny-blue?style=social&logo=rstudio&logoColor=75AADB)](https://community.rstudio.com/new-topic?category=shiny&tags=shiny)
<!-- badges: end -->
@@ -47,13 +47,13 @@ For help with learning fundamental Shiny programming concepts, check out the [Ma
## Getting Help
To ask a question about Shiny, please use the [RStudio Community website](https://community.rstudio.com/new-topic?category=shiny&tags=shiny).
To ask a question about Shiny, please use the [RStudio Community website](https://community.rstudio.com/new-topic?category=shiny&tags=shiny).
For bug reports, please use the [issue tracker](https://github.com/rstudio/shiny/issues) and also keep in mind that by [writing a good bug report](https://github.com/rstudio/shiny/wiki/Writing-Good-Bug-Reports), you're more likely to get help with your problem.
For bug reports, please use the [issue tracker](https://github.com/rstudio/shiny/issues) and also keep in mind that by [writing a good bug report](https://github.com/rstudio/shiny/wiki/Writing-Good-Bug-Reports), you're more likely to get help with your problem.
## Contributing
We welcome contributions to the **shiny** package. Please see our [CONTRIBUTING.md](https://github.com/rstudio/shiny/blob/master/.github/CONTRIBUTING.md) file for detailed guidelines of how to contribute.
We welcome contributions to the **shiny** package. Please see our [CONTRIBUTING.md](https://github.com/rstudio/shiny/blob/main/.github/CONTRIBUTING.md) file for detailed guidelines of how to contribute.
## License

View File

@@ -1,3 +1,3 @@
/*! shiny 1.7.0 | (c) 2012-2021 RStudio, PBC. | License: GPL-3 | file LICENSE */
/*! shiny 1.7.1.9002 | (c) 2012-2022 RStudio, PBC. | License: GPL-3 | file LICENSE */
(function(){var t="ws:";window.location.protocol==="https:"&&(t="wss:");var o=window.location.pathname;/\/$/.test(o)||(o+="/");o+="autoreload/";var n=new WebSocket(t+"//"+window.location.host+o);n.onmessage=function(a){a.data==="autoreload"&&window.location.reload()};})();
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjdHMvZXh0cmFzL3NoaW55LWF1dG9yZWxvYWQudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIi8qIGVzbGludC1kaXNhYmxlIHVuaWNvcm4vZmlsZW5hbWUtY2FzZSAqL1xudmFyIHByb3RvY29sID0gXCJ3czpcIjtcbmlmICh3aW5kb3cubG9jYXRpb24ucHJvdG9jb2wgPT09IFwiaHR0cHM6XCIpIHByb3RvY29sID0gXCJ3c3M6XCI7XG52YXIgZGVmYXVsdFBhdGggPSB3aW5kb3cubG9jYXRpb24ucGF0aG5hbWU7XG5pZiAoIS9cXC8kLy50ZXN0KGRlZmF1bHRQYXRoKSkgZGVmYXVsdFBhdGggKz0gXCIvXCI7XG5kZWZhdWx0UGF0aCArPSBcImF1dG9yZWxvYWQvXCI7XG52YXIgd3MgPSBuZXcgV2ViU29ja2V0KHByb3RvY29sICsgXCIvL1wiICsgd2luZG93LmxvY2F0aW9uLmhvc3QgKyBkZWZhdWx0UGF0aCk7XG5cbndzLm9ubWVzc2FnZSA9IGZ1bmN0aW9uIChldmVudCkge1xuICBpZiAoZXZlbnQuZGF0YSA9PT0gXCJhdXRvcmVsb2FkXCIpIHtcbiAgICB3aW5kb3cubG9jYXRpb24ucmVsb2FkKCk7XG4gIH1cbn07XG5cbmV4cG9ydCB7fTsiXSwKICAibWFwcGluZ3MiOiAiO1lBQ0EsR0FBSSxHQUFXLE1BQ2YsQUFBSSxPQUFPLFNBQVMsV0FBYSxVQUFVLEdBQVcsUUFDdEQsR0FBSSxHQUFjLE9BQU8sU0FBUyxTQUNsQyxBQUFLLE1BQU0sS0FBSyxJQUFjLElBQWUsS0FDN0MsR0FBZSxjQUNmLEdBQUksR0FBSyxHQUFJLFdBQVUsRUFBVyxLQUFPLE9BQU8sU0FBUyxLQUFPLEdBRWhFLEVBQUcsVUFBWSxTQUFVLEVBQU8sQ0FDOUIsQUFBSSxFQUFNLE9BQVMsY0FDakIsT0FBTyxTQUFTIiwKICAibmFtZXMiOiBbXQp9Cg==

View File

@@ -1,2 +1,2 @@
/*! shiny 1.7.0 | (c) 2012-2021 RStudio, PBC. | License: GPL-3 | file LICENSE */
/*! shiny 1.7.1.9002 | (c) 2012-2022 RStudio, PBC. | License: GPL-3 | file LICENSE */
#showcase-well{border-radius:0}.shiny-code{background-color:#fff;margin-bottom:0}.shiny-code code{font-family:Menlo,Consolas,"Courier New",monospace}.shiny-code-container{margin-top:20px;clear:both}.shiny-code-container h3{display:inline;margin-right:15px}.showcase-header{font-size:16px;font-weight:normal}.showcase-code-link{text-align:right;padding:15px}#showcase-app-container{vertical-align:top}#showcase-code-tabs{margin-right:15px}#showcase-code-tabs pre{border:none;line-height:1em}#showcase-code-tabs .nav{margin-bottom:0}#showcase-code-tabs ul{margin-bottom:0}#showcase-code-tabs .tab-content{border-style:solid;border-color:#e5e5e5;border-width:0px 1px 1px 1px;overflow:auto;border-bottom-right-radius:4px;border-bottom-left-radius:4px}#showcase-app-code{width:100%}#showcase-code-position-toggle{float:right}#showcase-sxs-code{padding-top:20px;vertical-align:top}.showcase-code-license{display:block;text-align:right}#showcase-code-content pre{background-color:#fff}

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,3 @@
/*! shiny 1.7.0 | (c) 2012-2021 RStudio, PBC. | License: GPL-3 | file LICENSE */
/*! shiny 1.7.1.9002 | (c) 2012-2022 RStudio, PBC. | License: GPL-3 | file LICENSE */
(function(){var a=eval;window.addEventListener("message",function(i){var e=i.data;e.code&&a(e.code)});})();
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjdHMvc3JjL3V0aWxzL2V2YWwudHMiLCAiLi4vLi4vLi4vc3JjdHMvZXh0cmFzL3NoaW55LXRlc3Rtb2RlLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyIvL2VzYnVpbGQuZ2l0aHViLmlvL2NvbnRlbnQtdHlwZXMvI2RpcmVjdC1ldmFsXG4vL3RsL2RyO1xuLy8gKiBEaXJlY3QgdXNhZ2Ugb2YgYGV2YWwoXCJ4XCIpYCBpcyBiYWQgd2l0aCBidW5kbGVkIGNvZGUuXG4vLyAqIEluc3RlYWQsIHVzZSBpbmRpcmVjdCBjYWxscyB0byBgZXZhbGAgc3VjaCBhcyBgaW5kaXJlY3RFdmFsKFwieFwiKWBcbi8vICAgKiBFdmVuIGp1c3QgcmVuYW1pbmcgdGhlIGZ1bmN0aW9uIHdvcmtzIHdlbGwgZW5vdWdoLlxuLy8gPiBUaGlzIGlzIGtub3duIGFzIFwiaW5kaXJlY3QgZXZhbFwiIGJlY2F1c2UgZXZhbCBpcyBub3QgYmVpbmcgY2FsbGVkIGRpcmVjdGx5LCBhbmQgc28gZG9lcyBub3QgdHJpZ2dlciB0aGUgZ3JhbW1hdGljYWwgc3BlY2lhbCBjYXNlIGZvciBkaXJlY3QgZXZhbCBpbiB0aGUgSmF2YVNjcmlwdCBWTS4gWW91IGNhbiBjYWxsIGluZGlyZWN0IGV2YWwgdXNpbmcgYW55IHN5bnRheCBhdCBhbGwgZXhjZXB0IGZvciBhbiBleHByZXNzaW9uIG9mIHRoZSBleGFjdCBmb3JtIGV2YWwoJ3gnKS4gRm9yIGV4YW1wbGUsIHZhciBldmFsMiA9IGV2YWw7IGV2YWwyKCd4JykgYW5kIFtldmFsXVswXSgneCcpIGFuZCB3aW5kb3cuZXZhbCgneCcpIGFyZSBhbGwgaW5kaXJlY3QgZXZhbCBjYWxscy5cbi8vID4gV2hlbiB5b3UgdXNlIGluZGlyZWN0IGV2YWwsIHRoZSBjb2RlIGlzIGV2YWx1YXRlZCBpbiB0aGUgZ2xvYmFsIHNjb3BlIGluc3RlYWQgb2YgaW4gdGhlIGlubGluZSBzY29wZSBvZiB0aGUgY2FsbGVyLlxudmFyIGluZGlyZWN0RXZhbCA9IGV2YWw7XG5leHBvcnQgeyBpbmRpcmVjdEV2YWwgfTsiLCAiLyogZXNsaW50LWRpc2FibGUgdW5pY29ybi9maWxlbmFtZS1jYXNlICovXG5pbXBvcnQgeyBpbmRpcmVjdEV2YWwgfSBmcm9tIFwiLi4vc3JjL3V0aWxzL2V2YWxcIjsgLy8gTGlzdGVuIGZvciBtZXNzYWdlcyBmcm9tIHBhcmVudCBmcmFtZS4gVGhpcyBmaWxlIGlzIG9ubHkgYWRkZWQgd2hlbiB0aGVcbi8vIHNoaW55LnRlc3Rtb2RlIG9wdGlvbiBpcyBUUlVFLlxuXG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcihcIm1lc3NhZ2VcIiwgZnVuY3Rpb24gKGUpIHtcbiAgdmFyIG1lc3NhZ2UgPSBlLmRhdGE7XG4gIGlmIChtZXNzYWdlLmNvZGUpIGluZGlyZWN0RXZhbChtZXNzYWdlLmNvZGUpO1xufSk7Il0sCiAgIm1hcHBpbmdzIjogIjtZQU9BLEdBQUksR0FBZSxLQ0huQixPQUFPLGlCQUFpQixVQUFXLFNBQVUsRUFBRyxDQUM5QyxHQUFJLEdBQVUsRUFBRSxLQUNoQixBQUFJLEVBQVEsTUFBTSxFQUFhLEVBQVEiLAogICJuYW1lcyI6IFtdCn0K

View File

@@ -1,4 +1,4 @@
/*! shiny 1.7.0 | (c) 2012-2021 RStudio, PBC. | License: GPL-3 | file LICENSE */
/*! shiny 1.7.1.9002 | (c) 2012-2022 RStudio, PBC. | License: GPL-3 | file LICENSE */
(function() {
var __create = Object.create;
var __defProp = Object.defineProperty;
@@ -2131,6 +2131,19 @@
}
});
// node_modules/core-js/internals/array-for-each.js
var require_array_for_each = __commonJS({
"node_modules/core-js/internals/array-for-each.js": function(exports, module) {
"use strict";
var $forEach2 = require_array_iteration().forEach;
var arrayMethodIsStrict4 = require_array_method_is_strict();
var STRICT_METHOD4 = arrayMethodIsStrict4("forEach");
module.exports = !STRICT_METHOD4 ? function forEach3(callbackfn) {
return $forEach2(this, callbackfn, arguments.length > 1 ? arguments[1] : void 0);
} : [].forEach;
}
});
// node_modules/core-js/internals/object-to-array.js
var require_object_to_array = __commonJS({
"node_modules/core-js/internals/object-to-array.js": function(exports, module) {
@@ -2769,19 +2782,6 @@
}
});
// node_modules/core-js/internals/array-for-each.js
var require_array_for_each = __commonJS({
"node_modules/core-js/internals/array-for-each.js": function(exports, module) {
"use strict";
var $forEach2 = require_array_iteration().forEach;
var arrayMethodIsStrict4 = require_array_method_is_strict();
var STRICT_METHOD4 = arrayMethodIsStrict4("forEach");
module.exports = !STRICT_METHOD4 ? function forEach3(callbackfn) {
return $forEach2(this, callbackfn, arguments.length > 1 ? arguments[1] : void 0);
} : [].forEach;
}
});
// srcts/src/initialize/disableForm.ts
var import_jquery = __toModule(require_jquery());
function disableFormSubmission() {
@@ -3762,7 +3762,7 @@
}, {
key: "getId",
value: function getId(el) {
return el["data-input-id"] || el.id;
return el.getAttribute("data-input-id") || el.id;
}
}, {
key: "getType",
@@ -8040,7 +8040,7 @@
}, {
key: "getId",
value: function getId(el) {
return el["data-input-id"] || el.id;
return el.getAttribute("data-input-id") || el.id;
}
}, {
key: "onValueChange",
@@ -8825,29 +8825,53 @@
var import_es_array_iterator20 = __toModule(require_es_array_iterator());
var import_jquery28 = __toModule(require_jquery());
// srcts/src/shiny/render.ts
var import_es_regexp_exec6 = __toModule(require_es_regexp_exec());
// node_modules/core-js/modules/es.array.for-each.js
"use strict";
var $45 = require_export();
var forEach = require_array_for_each();
$45({ target: "Array", proto: true, forced: [].forEach != forEach }, {
forEach: forEach
});
// node_modules/core-js/modules/web.dom-collections.for-each.js
var global6 = require_global();
var DOMIterables2 = require_dom_iterables();
var forEach2 = require_array_for_each();
var createNonEnumerableProperty3 = require_create_non_enumerable_property();
for (var COLLECTION_NAME in DOMIterables2) {
Collection = global6[COLLECTION_NAME];
CollectionPrototype = Collection && Collection.prototype;
if (CollectionPrototype && CollectionPrototype.forEach !== forEach2)
try {
createNonEnumerableProperty3(CollectionPrototype, "forEach", forEach2);
} catch (error) {
CollectionPrototype.forEach = forEach2;
}
}
var Collection;
var CollectionPrototype;
// node_modules/core-js/modules/es.object.entries.js
var $45 = require_export();
var $46 = require_export();
var $entries = require_object_to_array().entries;
$45({ target: "Object", stat: true }, {
$46({ target: "Object", stat: true }, {
entries: function entries(O) {
return $entries(O);
}
});
// srcts/src/shiny/render.ts
var import_es_regexp_exec6 = __toModule(require_es_regexp_exec());
var import_es_array_iterator19 = __toModule(require_es_array_iterator());
// node_modules/core-js/modules/es.array.from.js
var $46 = require_export();
var $47 = require_export();
var from = require_array_from();
var checkCorrectnessOfIteration = require_check_correctness_of_iteration();
var INCORRECT_ITERATION = !checkCorrectnessOfIteration(function(iterable) {
Array.from(iterable);
});
$46({ target: "Array", stat: true, forced: INCORRECT_ITERATION }, {
$47({ target: "Array", stat: true, forced: INCORRECT_ITERATION }, {
from: from
});
@@ -9064,9 +9088,7 @@
}
function renderDependencies(dependencies) {
if (dependencies) {
import_jquery27.default.each(dependencies, function(i, dep) {
renderDependency(dep);
});
dependencies.forEach(renderDependency);
}
}
function renderContent(el, content) {
@@ -9074,7 +9096,7 @@
if (where === "replace") {
shinyUnbindAll(el);
}
var html;
var html = "";
var dependencies = [];
if (content === null) {
html = "";
@@ -9123,128 +9145,208 @@
}
return htmlDependencies[names[idx]] === dep.version;
}
function renderDependency(dep) {
var restyle = needsRestyle(dep);
if (hasOwnProperty(htmlDependencies, dep.name) && !restyle)
function renderDependency(dep_) {
var dep = normalizeHtmlDependency(dep_);
var stylesheetLinks = dep.stylesheet.map(function(x) {
if (!hasOwnProperty(x, "rel"))
x.rel = "stylesheet";
if (!hasOwnProperty(x, "type"))
x.type = "text/css";
var link = document.createElement("link");
Object.entries(x).forEach(function(_ref) {
var _ref2 = _slicedToArray(_ref, 2), attr = _ref2[0], val = _ref2[1];
if (attr === "href") {
val = encodeURI(val);
}
link.setAttribute(attr, val ? val : "");
});
return link;
});
if (needsRestyle(dep)) {
addStylesheetsAndRestyle(stylesheetLinks);
return true;
}
if (hasOwnProperty(htmlDependencies, dep.name))
return false;
registerDependency(dep.name, dep.version);
var href = dep.src.href;
var $head = (0, import_jquery27.default)("head").first();
if (dep.meta && !restyle) {
var metas = import_jquery27.default.map(asArray(dep.meta), function(obj, idx) {
var name = Object.keys(obj)[0];
return (0, import_jquery27.default)("<meta>").attr("name", name).attr("content", obj[name]);
idx;
});
$head.append(metas);
}
if (dep.stylesheet) {
var links = import_jquery27.default.map(asArray(dep.stylesheet), function(stylesheet) {
return (0, import_jquery27.default)("<link rel='stylesheet' type='text/css'>").attr("href", href + "/" + encodeURI(stylesheet));
});
if (!restyle) {
$head.append(links);
} else {
var refreshStyle = function refreshStyle2(href2, oldSheet) {
var xhr = new XMLHttpRequest();
xhr.open("GET", href2);
xhr.onload = function() {
var id = "shiny_restyle_" + href2.split("?restyle")[0].replace(/\W/g, "_");
var oldStyle = $head.find("style#" + id);
var newStyle = (0, import_jquery27.default)("<style>").attr("id", id).html(xhr.responseText);
$head.append(newStyle);
oldStyle.remove();
removeSheet(oldSheet);
sendImageSizeFns.transitioned();
};
xhr.send();
};
var findSheet = function findSheet2(href2) {
for (var i = 0; i < document.styleSheets.length; i++) {
var sheet = document.styleSheets[i];
if (typeof sheet.href === "string" && sheet.href.indexOf(href2) > -1) {
return sheet;
}
}
return null;
};
var removeSheet = function removeSheet2(sheet) {
if (!sheet)
return;
sheet.disabled = true;
if (isIE())
sheet.cssText = "";
(0, import_jquery27.default)(sheet.ownerNode).remove();
};
import_jquery27.default.map(links, function(link) {
var oldSheet = findSheet(link.attr("href"));
var href2 = link.attr("href") + "?restyle=" + new Date().getTime();
if (isIE()) {
refreshStyle(href2, oldSheet);
} else {
link.attr("href", href2);
link.attr("onload", function() {
var $dummyEl = (0, import_jquery27.default)("<div>").css("transition", "0.1s all").css("position", "absolute").css("top", "-1000px").css("left", "0");
$dummyEl.one("transitionend", function() {
$dummyEl.remove();
removeSheet(oldSheet);
sendImageSizeFns.transitioned();
});
(0, import_jquery27.default)(document.body).append($dummyEl);
var color = "#" + Math.floor(Math.random() * 16777215).toString(16);
setTimeout(function() {
return $dummyEl.css("color", color);
}, 10);
});
$head.append(link);
}
});
dep.meta.forEach(function(x) {
var meta = document.createElement("meta");
for (var _i2 = 0, _Object$entries = Object.entries(x); _i2 < _Object$entries.length; _i2++) {
var _Object$entries$_i = _slicedToArray(_Object$entries[_i2], 2), attr = _Object$entries$_i[0], val = _Object$entries$_i[1];
meta.setAttribute(attr, val);
}
$head.append(meta);
});
if (stylesheetLinks.length !== 0) {
$head.append(stylesheetLinks);
}
if (dep.script && !restyle) {
var scriptsAttrs = asArray(dep.script);
var scripts = import_jquery27.default.map(scriptsAttrs, function(x) {
var script = document.createElement("script");
if (typeof x === "string") {
x = {
src: x
};
dep.script.forEach(function(x) {
var script = document.createElement("script");
Object.entries(x).forEach(function(_ref3) {
var _ref4 = _slicedToArray(_ref3, 2), attr = _ref4[0], val = _ref4[1];
if (attr === "src") {
val = encodeURI(val);
}
for (var _i = 0, _Object$entries = Object.entries(x); _i < _Object$entries.length; _i++) {
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), attr = _Object$entries$_i[0], val = _Object$entries$_i[1];
if (attr === "src") {
val = href + "/" + encodeURI(val);
}
script.setAttribute(attr, val ? val : "");
}
return script;
script.setAttribute(attr, val ? val : "");
});
$head.append(scripts);
}
if (dep.attachment && !restyle) {
var attachments = dep.attachment;
if (typeof attachments === "string")
attachments = [attachments];
if (Array.isArray(attachments)) {
var tmp = {};
import_jquery27.default.each(attachments, function(index, attachment) {
var key = index + 1 + "";
tmp[key] = attachment;
});
attachments = tmp;
}
var attach = import_jquery27.default.map(attachments, function(attachment, key) {
return (0, import_jquery27.default)("<link rel='attachment'>").attr("id", dep.name + "-" + key + "-attachment").attr("href", href + "/" + encodeURI(attachment));
});
$head.append(attach);
}
if (dep.head && !restyle) {
$head.append(script);
});
dep.attachment.forEach(function(x) {
var link = (0, import_jquery27.default)("<link rel='attachment'>").attr("id", dep.name + "-" + x.key + "-attachment").attr("href", encodeURI(x.href));
$head.append(link);
});
if (dep.head) {
var $newHead = (0, import_jquery27.default)("<head></head>");
$newHead.html(dep.head);
$head.append($newHead.children());
}
return true;
}
function addStylesheetsAndRestyle(links) {
var $head = (0, import_jquery27.default)("head").first();
var refreshStyle = function refreshStyle2(href, oldSheet) {
var xhr = new XMLHttpRequest();
xhr.open("GET", href);
xhr.onload = function() {
var id = "shiny_restyle_" + href.split("?restyle")[0].replace(/\W/g, "_");
var oldStyle = $head.find("style#" + id);
var newStyle = (0, import_jquery27.default)("<style>").attr("id", id).html(xhr.responseText);
$head.append(newStyle);
oldStyle.remove();
removeSheet(oldSheet);
sendImageSizeFns.transitioned();
};
xhr.send();
};
var findSheet = function findSheet2(href) {
if (!href)
return null;
for (var i = 0; i < document.styleSheets.length; i++) {
var sheet = document.styleSheets[i];
if (typeof sheet.href === "string" && sheet.href.indexOf(href) > -1) {
return sheet;
}
}
return null;
};
var removeSheet = function removeSheet2(sheet) {
if (!sheet)
return;
sheet.disabled = true;
if (isIE())
sheet.cssText = "";
if (sheet.ownerNode instanceof Element) {
(0, import_jquery27.default)(sheet.ownerNode).remove();
}
};
links.map(function(link) {
var $link = (0, import_jquery27.default)(link);
var oldSheet = findSheet($link.attr("href"));
var href = $link.attr("href") + "?restyle=" + new Date().getTime();
if (isIE()) {
refreshStyle(href, oldSheet);
} else {
$link.attr("href", href);
$link.attr("onload", function() {
var $dummyEl = (0, import_jquery27.default)("<div>").css("transition", "0.1s all").css("position", "absolute").css("top", "-1000px").css("left", "0");
$dummyEl.one("transitionend", function() {
$dummyEl.remove();
removeSheet(oldSheet);
sendImageSizeFns.transitioned();
});
(0, import_jquery27.default)(document.body).append($dummyEl);
var color = "#" + Math.floor(Math.random() * 16777215).toString(16);
setTimeout(function() {
return $dummyEl.css("color", color);
}, 10);
});
$head.append(link);
}
});
}
function normalizeHtmlDependency(dep) {
var _dep$src;
var hrefPrefix = (_dep$src = dep.src) === null || _dep$src === void 0 ? void 0 : _dep$src.href;
var result = {
name: dep.name,
version: dep.version,
restyle: dep.restyle,
meta: [],
stylesheet: [],
script: [],
attachment: [],
head: dep.head
};
if (dep.meta) {
if (Array.isArray(dep.meta)) {
result.meta = dep.meta;
} else {
result.meta = Object.entries(dep.meta).map(function(_ref5) {
var _ref6 = _slicedToArray(_ref5, 2), attr = _ref6[0], val = _ref6[1];
return {
name: attr,
content: val
};
});
}
}
result.stylesheet = asArray(dep.stylesheet).map(function(s) {
if (typeof s === "string") {
s = {
href: s
};
}
if (hrefPrefix) {
s.href = hrefPrefix + "/" + s.href;
}
return s;
});
result.script = asArray(dep.script).map(function(s) {
if (typeof s === "string") {
s = {
src: s
};
}
if (hrefPrefix) {
s.src = hrefPrefix + "/" + s.src;
}
return s;
});
var attachments = dep.attachment;
if (!attachments)
attachments = [];
if (typeof attachments === "string")
attachments = [attachments];
if (Array.isArray(attachments)) {
var tmp = attachments;
attachments = tmp.map(function(attachment, index) {
if (typeof attachment === "string") {
return {
key: (index + 1).toString(),
href: attachment
};
} else {
return attachment;
}
});
} else {
attachments = Object.entries(attachments).map(function(_ref7) {
var _ref8 = _slicedToArray(_ref7, 2), attr = _ref8[0], val = _ref8[1];
return {
key: attr,
href: val
};
});
}
result.attachment = attachments.map(function(s) {
if (hrefPrefix) {
s.href = hrefPrefix + "/" + s.href;
}
return s;
});
return result;
}
// srcts/src/bindings/output/html.ts
function _typeof20(obj) {
@@ -9372,11 +9474,11 @@
// node_modules/core-js/modules/es.array.filter.js
"use strict";
var $50 = require_export();
var $51 = require_export();
var $filter = require_array_iteration().filter;
var arrayMethodHasSpeciesSupport5 = require_array_method_has_species_support();
var HAS_SPECIES_SUPPORT4 = arrayMethodHasSpeciesSupport5("filter");
$50({ target: "Array", proto: true, forced: !HAS_SPECIES_SUPPORT4 }, {
$51({ target: "Array", proto: true, forced: !HAS_SPECIES_SUPPORT4 }, {
filter: function filter(callbackfn) {
return $filter(this, callbackfn, arguments.length > 1 ? arguments[1] : void 0);
}
@@ -11940,21 +12042,21 @@
// node_modules/core-js/modules/es.array-buffer.constructor.js
"use strict";
var $63 = require_export();
var global6 = require_global();
var $64 = require_export();
var global7 = require_global();
var arrayBufferModule = require_array_buffer();
var setSpecies = require_set_species();
var ARRAY_BUFFER = "ArrayBuffer";
var ArrayBuffer2 = arrayBufferModule[ARRAY_BUFFER];
var NativeArrayBuffer = global6[ARRAY_BUFFER];
$63({ global: true, forced: NativeArrayBuffer !== ArrayBuffer2 }, {
var NativeArrayBuffer = global7[ARRAY_BUFFER];
$64({ global: true, forced: NativeArrayBuffer !== ArrayBuffer2 }, {
ArrayBuffer: ArrayBuffer2
});
setSpecies(ARRAY_BUFFER);
// node_modules/core-js/modules/es.array-buffer.slice.js
"use strict";
var $64 = require_export();
var $65 = require_export();
var fails10 = require_fails();
var ArrayBufferModule = require_array_buffer();
var anObject9 = require_an_object();
@@ -11967,7 +12069,7 @@
var INCORRECT_SLICE = fails10(function() {
return !new ArrayBuffer3(2).slice(1, void 0).byteLength;
});
$64({ target: "ArrayBuffer", proto: true, unsafe: true, forced: INCORRECT_SLICE }, {
$65({ target: "ArrayBuffer", proto: true, unsafe: true, forced: INCORRECT_SLICE }, {
slice: function slice2(start, end) {
if (nativeArrayBufferSlice !== void 0 && end === void 0) {
return nativeArrayBufferSlice.call(anObject9(this), start);
@@ -11987,23 +12089,23 @@
});
// node_modules/core-js/modules/es.data-view.js
var $65 = require_export();
var $66 = require_export();
var ArrayBufferModule2 = require_array_buffer();
var NATIVE_ARRAY_BUFFER = require_array_buffer_native();
$65({ global: true, forced: !NATIVE_ARRAY_BUFFER }, {
$66({ global: true, forced: !NATIVE_ARRAY_BUFFER }, {
DataView: ArrayBufferModule2.DataView
});
// node_modules/core-js/modules/es.array.reduce.js
"use strict";
var $66 = require_export();
var $67 = require_export();
var $reduce = require_array_reduce().left;
var arrayMethodIsStrict3 = require_array_method_is_strict();
var CHROME_VERSION = require_engine_v8_version();
var IS_NODE = require_engine_is_node();
var STRICT_METHOD3 = arrayMethodIsStrict3("reduce");
var CHROME_BUG = !IS_NODE && CHROME_VERSION > 79 && CHROME_VERSION < 83;
$66({ target: "Array", proto: true, forced: !STRICT_METHOD3 || CHROME_BUG }, {
$67({ target: "Array", proto: true, forced: !STRICT_METHOD3 || CHROME_BUG }, {
reduce: function reduce(callbackfn) {
return $reduce(this, callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : void 0);
}
@@ -12011,34 +12113,6 @@
// srcts/src/shiny/shinyapp.ts
var import_es_regexp_exec9 = __toModule(require_es_regexp_exec());
// node_modules/core-js/modules/es.array.for-each.js
"use strict";
var $67 = require_export();
var forEach = require_array_for_each();
$67({ target: "Array", proto: true, forced: [].forEach != forEach }, {
forEach: forEach
});
// node_modules/core-js/modules/web.dom-collections.for-each.js
var global7 = require_global();
var DOMIterables2 = require_dom_iterables();
var forEach2 = require_array_for_each();
var createNonEnumerableProperty3 = require_create_non_enumerable_property();
for (var COLLECTION_NAME in DOMIterables2) {
Collection = global7[COLLECTION_NAME];
CollectionPrototype = Collection && Collection.prototype;
if (CollectionPrototype && CollectionPrototype.forEach !== forEach2)
try {
createNonEnumerableProperty3(CollectionPrototype, "forEach", forEach2);
} catch (error) {
CollectionPrototype.forEach = forEach2;
}
}
var Collection;
var CollectionPrototype;
// srcts/src/shiny/shinyapp.ts
var import_jquery41 = __toModule(require_jquery());
function _classCallCheck36(instance, Constructor) {
if (!(instance instanceof Constructor)) {
@@ -13265,7 +13339,7 @@
var windowShiny2;
function setShiny(windowShiny_) {
windowShiny2 = windowShiny_;
windowShiny2.version = "1.7.0";
windowShiny2.version = "1.7.1.9002";
var _initInputBindings = initInputBindings(), inputBindings = _initInputBindings.inputBindings, fileInputBinding2 = _initInputBindings.fileInputBinding;
var _initOutputBindings = initOutputBindings(), outputBindings = _initOutputBindings.outputBindings;
setFileInputBinding(fileInputBinding2);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -14,6 +14,7 @@ bootstrapLib(theme = NULL)
build of Bootstrap 3 with a customized version of Bootstrap 3 or higher.
\item A character string pointing to an alternative Bootstrap stylesheet
(normally a css file within the www directory, e.g. \code{www/bootstrap.css}).
This option is here mainly for legacy reasons.
}}
}
\description{

View File

@@ -1,18 +1,25 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/bootstrap.R
\name{bootstrapPage}
% Please edit documentation in R/bootstrap-layout.R, R/bootstrap.R
\name{fluidPage}
\alias{fluidPage}
\alias{fixedPage}
\alias{bootstrapPage}
\alias{basicPage}
\title{Create a Bootstrap page}
\title{Create a Bootstrap UI page container}
\usage{
fluidPage(..., title = NULL, theme = NULL, lang = NULL)
fixedPage(..., title = NULL, theme = NULL, lang = NULL)
bootstrapPage(..., title = NULL, theme = NULL, lang = NULL)
basicPage(...)
}
\arguments{
\item{...}{The contents of the document body.}
\item{...}{UI elements (i.e., \link{tags}).}
\item{title}{The browser window title (defaults to the host URL of the page)}
\item{title}{The browser window title (defaults to the host URL of the page).
Can also be set as a side effect of the \code{\link[=titlePanel]{titlePanel()}} function.}
\item{theme}{One of the following:
\itemize{
@@ -21,30 +28,74 @@ basicPage(...)
build of Bootstrap 3 with a customized version of Bootstrap 3 or higher.
\item A character string pointing to an alternative Bootstrap stylesheet
(normally a css file within the www directory, e.g. \code{www/bootstrap.css}).
This option is here mainly for legacy reasons.
}}
\item{lang}{ISO 639-1 language code for the HTML page, such as "en" or "ko".
This will be used as the lang in the \code{<html>} tag, as in \code{<html lang="en">}.
This will be used as the lang in the \verb{<html>} tag, as in \verb{<html lang="en">}.
The default (NULL) results in an empty string.}
}
\value{
A UI defintion that can be passed to the \link{shinyUI} function.
A UI definition (i.e., a \link{tags} object) that can be passed to \code{\link[=shinyApp]{shinyApp()}}.
}
\description{
Create a Shiny UI page that loads the CSS and JavaScript for
\href{https://getbootstrap.com/}{Bootstrap}, and has no content in the page
body (other than what you provide).
Create a user interface (UI) page container based on
\href{https://getbootstrap.com/}{Bootstrap}'s CSS and JavaScript. Most Shiny apps
should use \code{\link[=fluidPage]{fluidPage()}} (or \code{\link[=navbarPage]{navbarPage()}}) to get a page container with a
responsive page width, but in some cases you may want a fixed width container
(\code{fixedPage()}) or just a bare \verb{<body>} container (\code{bootstrapPage()}).
Most Shiny apps make use of other Shiny UI functions for \href{https://shiny.rstudio.com/articles/layout-guide.html}{managing layout} (e.g.,
\code{\link[=sidebarLayout]{sidebarLayout()}}, \code{\link[=fluidRow]{fluidRow()}}, etc), navigation (e.g., \code{\link[=tabPanel]{tabPanel()}}), and
other styling (e.g., \code{\link[=wellPanel]{wellPanel()}}, \code{\link[=inputPanel]{inputPanel()}}). A good portion of these
Shiny UI functions require Bootstrap to work properly (so most Shiny apps
should use these functions to start their UI definitions), but more advanced
usage (i.e., custom HTML/CSS/JS) can avoid Bootstrap entirely by using
\code{\link[=htmlTemplate]{htmlTemplate()}} and/or HTML \link{tags}.
}
\details{
This function is primarily intended for users who are proficient in HTML/CSS,
and know how to lay out pages in Bootstrap. Most applications should use
\code{\link[=fluidPage]{fluidPage()}} along with layout functions like
\code{\link[=fluidRow]{fluidRow()}} and \code{\link[=sidebarLayout]{sidebarLayout()}}.
\examples{
# First create some UI content.
# See the layout guide to learn more about creating different layouts
# https://shiny.rstudio.com/articles/layout-guide.html
ui <- sidebarLayout(
sidebarPanel(sliderInput("obs", "Number of observations:", 0, 1000, 500)),
mainPanel(plotOutput("distPlot"))
)
server <- function(input, output) {
output$distPlot <- renderPlot(hist(rnorm(input$obs)))
}
\note{
The \code{basicPage} function is deprecated, you should use the
\code{\link[=fluidPage]{fluidPage()}} function instead.
# Demonstrating difference between fluidPage(), fixedPage(), bootstrapPage()
if (interactive()) {
# Container width scales _fluidly_ with window size
shinyApp(fluidPage(ui), server)
# Container width changes with window size at fixed breakpoints
shinyApp(fixedPage(ui), server)
# Container width is equal to the window's width
shinyApp(bootstrapPage(ui), server)
}
# The default look is provided by Bootstrap 3, but {bslib} can be
# used to customize the Bootstrap version and its default styling
theme <- bslib::bs_theme(
version = 5,
bg = "#101010",
fg = "#FDF7F7",
primary = "#ED79F9",
base_font = bslib::font_google("Prompt"),
code_font = bslib::font_google("JetBrains Mono")
)
if (interactive()) {
# Call thematic::thematic_shiny(font = "auto") to automatically
# translate the theme/CSS to the R plot
shinyApp(
fluidPage(ui, theme = theme, title = "Hello Bootstrap 5"),
server
)
}
}
\seealso{
\code{\link[=fluidPage]{fluidPage()}}, \code{\link[=fixedPage]{fixedPage()}}
\code{\link[=navbarPage]{navbarPage()}}, \code{\link[=fillPage]{fillPage()}}, \code{\link[=column]{column()}}, \code{\link[=tabPanel]{tabPanel()}}
}

View File

@@ -1,26 +1,45 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/bootstrap-layout.R
\name{column}
\name{fluidRow}
\alias{fluidRow}
\alias{fixedRow}
\alias{column}
\title{Create a column within a UI definition}
\title{Responsive row-column based layout}
\usage{
fluidRow(...)
fixedRow(...)
column(width, ..., offset = 0)
}
\arguments{
\item{width}{The grid width of the column (must be between 1 and 12)}
\item{...}{UI elements (i.e., \link{tags}). For \code{fluidRow()}, \code{...} should be a set of \code{column()}s.}
\item{...}{Elements to include within the column}
\item{width}{The grid width of the column (must be between 1 and 12). When
the device width is small (e.g., the viewer is on a mobile phone), the
width is always 12. For more control over these responsive breakpoints, use
Bootstrap's grid system more directly (e.g., \code{fluidRow(div(class = "col-lg-2", ...))}).}
\item{offset}{The number of columns to offset this column from the end of the
previous column.}
}
\value{
A column that can be included within a
\code{\link[=fluidRow]{fluidRow()}} or \code{\link[=fixedRow]{fixedRow()}}.
A UI element (i.e., \link{tags}).
}
\description{
Create a column for use within a \code{\link[=fluidRow]{fluidRow()}} or
\code{\link[=fixedRow]{fixedRow()}}
Layout UI components using Bootstrap's grid layout system. Use
\code{fluidRow()} to group elements that should appear on the same line (if the
browser has adequate width) and \code{column()} to define how much horizontal
space within a 12-unit wide grid each on of these elements should occupy. See
the \href{https://shiny.rstudio.com/articles/layout-guide.html}{layout guide} for
more context and examples.
}
\details{
To work properly, these functions need \href{https://getbootstrap.com}{Bootstrap}
included on the page. Since most Shiny apps use \code{\link[=bootstrapPage]{bootstrapPage()}}
under-the-hood, this is usually the case, but custom page containers (i.e.,
\code{\link[=htmlTemplate]{htmlTemplate()}}) may need to explicitly include \code{\link[=bootstrapLib]{bootstrapLib()}}
dependencies.
}
\examples{
## Only run examples in interactive R sessions
@@ -46,21 +65,15 @@ server <- function(input, output) {
shinyApp(ui, server)
ui <- fluidPage(
fluidRow(
column(width = 4,
"4"
),
column(width = 3, offset = 2,
"3 offset 2"
)
column(width = 4, "4"),
column(width = 3, offset = 2, "3 offset 2")
)
)
shinyApp(ui, server = function(input, output) { })
}
}
\seealso{
\code{\link[=fluidRow]{fluidRow()}}, \code{\link[=fixedRow]{fixedRow()}}.
\code{\link[=fluidPage]{fluidPage()}}
}

View File

@@ -37,10 +37,11 @@ shown in the document).}
build of Bootstrap 3 with a customized version of Bootstrap 3 or higher.
\item A character string pointing to an alternative Bootstrap stylesheet
(normally a css file within the www directory, e.g. \code{www/bootstrap.css}).
This option is here mainly for legacy reasons.
}}
\item{lang}{ISO 639-1 language code for the HTML page, such as "en" or "ko".
This will be used as the lang in the \code{<html>} tag, as in \code{<html lang="en">}.
This will be used as the lang in the \verb{<html>} tag, as in \verb{<html lang="en">}.
The default (NULL) results in an empty string.}
}
\description{
@@ -101,9 +102,7 @@ fillPage(
}
\seealso{
Other layout functions:
\code{\link{fixedPage}()},
\code{\link{flowLayout}()},
\code{\link{fluidPage}()},
\code{\link{navbarPage}()},
\code{\link{sidebarLayout}()},
\code{\link{splitLayout}()},

View File

@@ -1,85 +0,0 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/bootstrap-layout.R
\name{fixedPage}
\alias{fixedPage}
\alias{fixedRow}
\title{Create a page with a fixed layout}
\usage{
fixedPage(..., title = NULL, theme = NULL, lang = NULL)
fixedRow(...)
}
\arguments{
\item{...}{Elements to include within the container}
\item{title}{The browser window title (defaults to the host URL of the page)}
\item{theme}{One of the following:
\itemize{
\item \code{NULL} (the default), which implies a "stock" build of Bootstrap 3.
\item A \code{\link[bslib:bs_theme]{bslib::bs_theme()}} object. This can be used to replace a stock
build of Bootstrap 3 with a customized version of Bootstrap 3 or higher.
\item A character string pointing to an alternative Bootstrap stylesheet
(normally a css file within the www directory, e.g. \code{www/bootstrap.css}).
}}
\item{lang}{ISO 639-1 language code for the HTML page, such as "en" or "ko".
This will be used as the lang in the \code{<html>} tag, as in \code{<html lang="en">}.
The default (NULL) results in an empty string.}
}
\value{
A UI defintion that can be passed to the \link{shinyUI} function.
}
\description{
Functions for creating fixed page layouts. A fixed page layout consists of
rows which in turn include columns. Rows exist for the purpose of making sure
their elements appear on the same line (if the browser has adequate width).
Columns exist for the purpose of defining how much horizontal space within a
12-unit wide grid it's elements should occupy. Fixed pages limit their width
to 940 pixels on a typical display, and 724px or 1170px on smaller and larger
displays respectively.
}
\details{
To create a fixed page use the \code{fixedPage} function and include
instances of \code{fixedRow} and \code{\link[=column]{column()}} within it. Note that
unlike \code{\link[=fluidPage]{fluidPage()}}, fixed pages cannot make use of higher-level
layout functions like \code{sidebarLayout}, rather, all layout must be done
with \code{fixedRow} and \code{column}.
}
\note{
See the \href{https://shiny.rstudio.com/articles/layout-guide.html}{ Shiny Application Layout Guide} for additional details on laying out fixed
pages.
}
\examples{
## Only run examples in interactive R sessions
if (interactive()) {
ui <- fixedPage(
title = "Hello, Shiny!",
fixedRow(
column(width = 4,
"4"
),
column(width = 3, offset = 2,
"3 offset 2"
)
)
)
shinyApp(ui, server = function(input, output) { })
}
}
\seealso{
\code{\link[=column]{column()}}
Other layout functions:
\code{\link{fillPage}()},
\code{\link{flowLayout}()},
\code{\link{fluidPage}()},
\code{\link{navbarPage}()},
\code{\link{sidebarLayout}()},
\code{\link{splitLayout}()},
\code{\link{verticalLayout}()}
}
\concept{layout functions}

View File

@@ -34,8 +34,6 @@ shinyApp(ui, server = function(input, output) { })
\seealso{
Other layout functions:
\code{\link{fillPage}()},
\code{\link{fixedPage}()},
\code{\link{fluidPage}()},
\code{\link{navbarPage}()},
\code{\link{sidebarLayout}()},
\code{\link{splitLayout}()},

View File

@@ -1,119 +0,0 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/bootstrap-layout.R
\name{fluidPage}
\alias{fluidPage}
\alias{fluidRow}
\title{Create a page with fluid layout}
\usage{
fluidPage(..., title = NULL, theme = NULL, lang = NULL)
fluidRow(...)
}
\arguments{
\item{...}{Elements to include within the page}
\item{title}{The browser window title (defaults to the host URL of the page).
Can also be set as a side effect of the \code{\link[=titlePanel]{titlePanel()}} function.}
\item{theme}{One of the following:
\itemize{
\item \code{NULL} (the default), which implies a "stock" build of Bootstrap 3.
\item A \code{\link[bslib:bs_theme]{bslib::bs_theme()}} object. This can be used to replace a stock
build of Bootstrap 3 with a customized version of Bootstrap 3 or higher.
\item A character string pointing to an alternative Bootstrap stylesheet
(normally a css file within the www directory, e.g. \code{www/bootstrap.css}).
}}
\item{lang}{ISO 639-1 language code for the HTML page, such as "en" or "ko".
This will be used as the lang in the \code{<html>} tag, as in \code{<html lang="en">}.
The default (NULL) results in an empty string.}
}
\value{
A UI defintion that can be passed to the \link{shinyUI} function.
}
\description{
Functions for creating fluid page layouts. A fluid page layout consists of
rows which in turn include columns. Rows exist for the purpose of making sure
their elements appear on the same line (if the browser has adequate width).
Columns exist for the purpose of defining how much horizontal space within a
12-unit wide grid it's elements should occupy. Fluid pages scale their
components in realtime to fill all available browser width.
}
\details{
To create a fluid page use the \code{fluidPage} function and include
instances of \code{fluidRow} and \code{\link[=column]{column()}} within it. As an
alternative to low-level row and column functions you can also use
higher-level layout functions like \code{\link[=sidebarLayout]{sidebarLayout()}}.
}
\note{
See the \href{https://shiny.rstudio.com/articles/layout-guide.html}{ Shiny-Application-Layout-Guide} for additional details on laying out fluid
pages.
}
\examples{
## Only run examples in interactive R sessions
if (interactive()) {
# Example of UI with fluidPage
ui <- fluidPage(
# Application title
titlePanel("Hello Shiny!"),
sidebarLayout(
# Sidebar with a slider input
sidebarPanel(
sliderInput("obs",
"Number of observations:",
min = 0,
max = 1000,
value = 500)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
)
)
# Server logic
server <- function(input, output) {
output$distPlot <- renderPlot({
hist(rnorm(input$obs))
})
}
# Complete app with UI and server components
shinyApp(ui, server)
# UI demonstrating column layouts
ui <- fluidPage(
title = "Hello Shiny!",
fluidRow(
column(width = 4,
"4"
),
column(width = 3, offset = 2,
"3 offset 2"
)
)
)
shinyApp(ui, server = function(input, output) { })
}
}
\seealso{
\code{\link[=column]{column()}}
Other layout functions:
\code{\link{fillPage}()},
\code{\link{fixedPage}()},
\code{\link{flowLayout}()},
\code{\link{navbarPage}()},
\code{\link{sidebarLayout}()},
\code{\link{splitLayout}()},
\code{\link{verticalLayout}()}
}
\concept{layout functions}

View File

@@ -71,6 +71,7 @@ layout.}
build of Bootstrap 3 with a customized version of Bootstrap 3 or higher.
\item A character string pointing to an alternative Bootstrap stylesheet
(normally a css file within the www directory, e.g. \code{www/bootstrap.css}).
This option is here mainly for legacy reasons.
}}
\item{windowTitle}{the browser window title (as a character string). The
@@ -79,7 +80,7 @@ default value, \code{NA}, means to use any character strings that appear in
default).}
\item{lang}{ISO 639-1 language code for the HTML page, such as "en" or "ko".
This will be used as the lang in the \code{<html>} tag, as in \code{<html lang="en">}.
This will be used as the lang in the \verb{<html>} tag, as in \verb{<html lang="en">}.
The default (NULL) results in an empty string.}
\item{menuName}{A name that identifies this \code{navbarMenu}. This
@@ -124,9 +125,7 @@ navbarPage("App Title",
Other layout functions:
\code{\link{fillPage}()},
\code{\link{fixedPage}()},
\code{\link{flowLayout}()},
\code{\link{fluidPage}()},
\code{\link{sidebarLayout}()},
\code{\link{splitLayout}()},
\code{\link{verticalLayout}()}

View File

@@ -62,10 +62,10 @@ respectively.
\examples{
## Only run this example in interactive R sessions
if (interactive()) {
runUrl('https://github.com/rstudio/shiny_example/archive/master.tar.gz')
runUrl('https://github.com/rstudio/shiny_example/archive/main.tar.gz')
# Can run an app from a subdirectory in the archive
runUrl("https://github.com/rstudio/shiny_example/archive/master.zip",
runUrl("https://github.com/rstudio/shiny_example/archive/main.zip",
subdir = "inst/shinyapp/")
}
## Only run this example in interactive R sessions

View File

@@ -83,9 +83,7 @@ shinyApp(ui, server)
\seealso{
Other layout functions:
\code{\link{fillPage}()},
\code{\link{fixedPage}()},
\code{\link{flowLayout}()},
\code{\link{fluidPage}()},
\code{\link{navbarPage}()},
\code{\link{splitLayout}()},
\code{\link{verticalLayout}()}

View File

@@ -64,9 +64,7 @@ shinyApp(ui, server)
\seealso{
Other layout functions:
\code{\link{fillPage}()},
\code{\link{fixedPage}()},
\code{\link{flowLayout}()},
\code{\link{fluidPage}()},
\code{\link{navbarPage}()},
\code{\link{sidebarLayout}()},
\code{\link{verticalLayout}()}

View File

@@ -33,9 +33,7 @@ shinyApp(ui, server = function(input, output) { })
\seealso{
Other layout functions:
\code{\link{fillPage}()},
\code{\link{fixedPage}()},
\code{\link{flowLayout}()},
\code{\link{fluidPage}()},
\code{\link{navbarPage}()},
\code{\link{sidebarLayout}()},
\code{\link{splitLayout}()}

View File

@@ -3,7 +3,7 @@
"homepage": "https://shiny.rstudio.com",
"repository": "github:rstudio/shiny",
"name": "@types/rstudio-shiny",
"version": "1.7.0",
"version": "1.7.1-alpha.9002",
"license": "GPL-3.0-only",
"main": "",
"browser": "",

View File

@@ -196,7 +196,7 @@ Both JavaScript files will produce a sourcemap (`**.js.map`) that the browser wi
### GitHub Actions
On push to the `master` branch or push to a Pull Request to the `master` branch, a GitHub Action will be run to make sure the bundled JavaScript code is up to date. If the source code does not compile to the exact same file, it will be committed an pushed back to the outdated branch. (This makes it so the full build tools are not necessary for small tweaks and comments. 🎉)
On push to the `main` branch or push to a Pull Request to the `main` branch, a GitHub Action will be run to make sure the bundled JavaScript code is up to date. If the source code does not compile to the exact same file, it will be committed an pushed back to the outdated branch. (This makes it so the full build tools are not necessary for small tweaks and comments. 🎉)
<!-- #### Auto build and browser refresh

View File

@@ -1,283 +0,0 @@
module.exports = function(grunt) {
var instdir = '../inst/';
var js_srcdir = '../srcjs/'
// Make sure all plattforms to use `\n` as eol char: https://stackoverflow.com/questions/29817511/grunt-issue-with-line-endings
grunt.util.linefeed = "\n";
gruntConfig = {
pkg: pkgInfo(),
clean: {
options: { force: true },
src: [
instdir + "www/shared/shiny.js",
instdir + "www/shared/shiny.js.map",
instdir + "www/shared/shiny.min.js",
instdir + "www/shared/shiny.min.js.map",
"./temp_concat/shiny.js",
"./temp_concat/shiny.js.map",
instdir + "www/shared/datepicker/js/bootstrap-datepicker.min.js",
instdir + "www/shared/ionrangeslider/js/ion.rangeSlider.min.js"
]
},
concat: {
options: {
process: function(src, filepath) {
return (
"//---------------------------------------------------------------------\n" +
"// Source file: " +
filepath +
"\n\n" +
src
);
},
sourceMap: true
},
shiny: {
src: [
js_srcdir + "_start.js",
js_srcdir + "utils.js",
js_srcdir + "browser.js",
js_srcdir + "input_rate.js",
js_srcdir + "shinyapp.js",
js_srcdir + "notifications.js",
js_srcdir + "modal.js",
js_srcdir + "file_processor.js",
js_srcdir + "binding_registry.js",
js_srcdir + "output_binding.js",
js_srcdir + "output_binding_text.js",
js_srcdir + "output_binding_image.js",
js_srcdir + "output_binding_html.js",
js_srcdir + "output_binding_downloadlink.js",
js_srcdir + "output_binding_datatable.js",
js_srcdir + "output_binding_adapter.js",
js_srcdir + "input_binding.js",
js_srcdir + "input_binding_text.js",
js_srcdir + "input_binding_textarea.js",
js_srcdir + "input_binding_password.js",
js_srcdir + "input_binding_number.js",
js_srcdir + "input_binding_checkbox.js",
js_srcdir + "input_binding_slider.js",
js_srcdir + "input_binding_date.js",
js_srcdir + "input_binding_daterange.js",
js_srcdir + "input_binding_select.js",
js_srcdir + "input_binding_radio.js",
js_srcdir + "input_binding_checkboxgroup.js",
js_srcdir + "input_binding_actionbutton.js",
js_srcdir + "input_binding_tabinput.js",
js_srcdir + "input_binding_fileinput.js",
js_srcdir + "init_shiny.js",
js_srcdir + "reactlog.js",
js_srcdir + "_end.js"
],
// The temp_concat/ directory would have gone under /srcjs/, but the
// Babel Grunt plugin has trouble finding presets if it operates on a
// file that's not under the current directory. So we'll put it under
// ./
dest: "./temp_concat/shiny.js",
nonull: true
}
},
"string-replace": {
version: {
files: {
"./temp_concat/shiny.js": "./temp_concat/shiny.js"
},
options: {
replacements: [
{
pattern: /{{\s*VERSION\s*}}/g,
replacement: pkgInfo().version
}
]
}
}
},
babel: {
options: {
sourceMap: true,
compact: false,
presets: ["@babel/preset-env"]
},
shiny: {
src: "./temp_concat/shiny.js",
dest: instdir + "/www/shared/shiny.js"
}
},
eslint: {
options: {
parser: "babel-eslint",
format: require("eslint-stylish-mapped"),
extends: "eslint:recommended",
rules: {
"consistent-return": 1,
"dot-location": [1, "property"],
eqeqeq: 1,
// "no-shadow": 1,
"no-implicit-globals": 1,
"no-restricted-globals": [
"error",
"name",
"length",
"top",
"location",
"parent",
"status"
],
"no-global-assign": 1,
"no-undef": 1,
"no-unused-vars": [1, { args: "none" }],
"guard-for-in": 1,
// "no-use-before-define": [1, {"functions": false}],
semi: [1, "always"]
},
envs: ["es6", "browser", "jquery"],
globals: ["strftime"]
},
shiny: ["./temp_concat/shiny.js"]
},
uglify: {
shiny: {
options: {
banner:
"/*! <%= pkg.name %> <%= pkg.version %> | " +
'(c) 2012-<%= grunt.template.today("yyyy") %> RStudio, Inc. | ' +
"License: <%= pkg.license %> */\n",
sourceMap: {
includeSources: true
},
// Base the .min.js sourcemap off of the .js sourcemap created by concat
sourceMapIn: instdir + "www/shared/shiny.js.map"
},
src: instdir + "www/shared/shiny.js",
dest: instdir + "www/shared/shiny.min.js"
},
datepicker: {
src: [
instdir + "www/shared/datepicker/js/bootstrap-datepicker.js",
instdir + "www/shared/datepicker/js/locales/bootstrap-datepicker.*.js"
],
dest: instdir + "www/shared/datepicker/js/bootstrap-datepicker.min.js"
},
ionrangeslider: {
src: instdir + "www/shared/ionrangeslider/js/ion.rangeSlider.js",
dest: instdir + "www/shared/ionrangeslider/js/ion.rangeSlider.min.js"
},
selectize: {
src: instdir + "www/shared/selectize/accessibility/js/selectize-plugin-a11y.js",
dest: instdir + "www/shared/selectize/accessibility/js/selectize-plugin-a11y.min.js"
}
},
watch: {
shiny: {
files: ["<%= concat.shiny.src %>", "../DESCRIPTION"],
tasks: ["default"]
},
datepicker: {
files: "<%= uglify.datepicker.src %>",
tasks: ["uglify:datepicker"]
}
},
newer: {
options: {
override: function(detail, include) {
// If DESCRIPTION is updated, we'll also need to re-minify shiny.js
// because the min.js file embeds the version number.
if (detail.task === "uglify" && detail.target === "shiny") {
include(isNewer("../DESCRIPTION", detail.time));
} else {
include(false);
}
}
}
}
};
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-string-replace');
grunt.loadNpmTasks('grunt-babel');
grunt.loadNpmTasks('grunt-eslint');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-newer');
// Need this here so that babel reads in the source map file after it's
// generated. Without this task, it would read in the source map when Grunt
// runs, which is wrong, if the source map doesn't exist, or is change later.
grunt.task.registerTask("configureBabel", "configures babel options", function() {
gruntConfig.babel.options.inputSourceMap = grunt.file.readJSON('./temp_concat/shiny.js.map');
});
grunt.task.registerTask(
"validateStringReplace",
"tests to make sure the version value was replaced",
function() {
var shinyContent = require('fs').readFileSync('./temp_concat/shiny.js', 'utf8');
if (/{{\s*VERSION\s*}}/.test(shinyContent)) {
grunt.fail.fatal("{{ VERSION }} was not replaced in compiled shiny.js file!")
}
}
);
grunt.initConfig(gruntConfig);
grunt.registerTask('default', [
'concat',
'string-replace',
'validateStringReplace',
'eslint',
'configureBabel',
'babel',
'uglify'
]);
// ---------------------------------------------------------------------------
// Utility functions
// ---------------------------------------------------------------------------
// Return an object which merges information from package.json and the
// DESCRIPTION file.
function pkgInfo() {
var pkg = grunt.file.readJSON('package.json');
pkg.name = descKeyValue('Package');
pkg.version = descKeyValue('Version');
pkg.license = descKeyValue('License');
return pkg;
}
// From the DESCRIPTION file, get the value of a key. This presently only
// works if the value is on one line, the same line as the key.
function descKeyValue(key) {
var lines = require('fs').readFileSync('../DESCRIPTION', 'utf8').split(/\r?\n/);
var pattern = new RegExp('^' + key + ':');
var txt = lines.filter(function(line) {
return pattern.test(line);
});
txt = txt[0];
pattern = new RegExp(key + ': *');
txt = txt.replace(pattern, '');
return txt;
}
// Return true if file's mtime is newer than mtime; false otherwise.
function isNewer(file, mtime) {
return require('fs').statSync(file).mtime > mtime;
}
};

View File

@@ -1,17 +0,0 @@
(function() {
var protocol = 'ws:';
if (window.location.protocol === 'https:')
protocol = 'wss:';
var defaultPath = window.location.pathname;
if (!/\/$/.test(defaultPath))
defaultPath += '/';
defaultPath += 'autoreload/';
var ws = new WebSocket(protocol + '//' + window.location.host + defaultPath);
ws.onmessage = function(event) {
if (event.data === "autoreload") {
window.location.reload()
}
}
})();

View File

@@ -1,87 +0,0 @@
#showcase-well {
border-radius: 0;
-webkit-border-radius: 0;
-moz-border-radius: 0;
}
.shiny-code {
background-color: white;
margin-bottom: 0;
}
.shiny-code code {
font-family: Menlo, Consolas, "Courier New", monospace;
}
.shiny-code-container {
margin-top: 20px;
clear: both;
}
.shiny-code-container h3 {
display: inline;
margin-right: 15px;
}
.showcase-header {
font-size: 16px;
font-weight: normal;
}
.showcase-code-link {
text-align: right;
padding: 15px;
}
#showcase-app-container {
vertical-align: top;
}
#showcase-code-tabs pre {
border: none;
line-height: 1em;
}
#showcase-code-tabs .nav,
#showcase-code-tabs ul {
margin-bottom: 0px;
}
#showcase-app-code {
width: 100%;
}
#showcase-code-tabs {
margin-right: 15px;
}
#showcase-code-tabs .tab-content {
border-style: solid;
border-color: #e5e5e5;
border-width: 0px 1px 1px 1px;
overflow:auto;
-webkit-border-bottom-right-radius: 4px;
-webkit-border-bottom-left-radius: 4px;
-moz-border-radius-bottomright: 4px;
-moz-border-radius-bottomleft: 4px;
border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px;
}
#showcase-code-position-toggle {
float: right;
}
#showcase-sxs-code {
padding-top: 20px;
vertical-align: top;
}
.showcase-code-license {
display: block;
text-align: right;
}
#showcase-code-content pre {
background-color: white;
}

View File

@@ -1,272 +0,0 @@
/*jshint browser:true, jquery:true, strict:false, curly:false, indent:2*/
(function() {
var animateMs = 400;
// Given a DOM node and a column (count of characters), walk recursively
// through the node's siblings counting characters until the given number
// of characters have been found.
//
// If the given count is bigger than the number of characters contained by
// the node and its siblings, returns a null node and the number of
// characters found.
function findTextColPoint(node, col) {
var cols = 0;
if (node.nodeType === 3) {
var nchar = node.nodeValue.replace(/\n/g, "").length;
if (nchar >= col) {
return { element: node, offset: col };
} else {
cols += nchar;
}
} else if (node.nodeType === 1 && node.firstChild) {
var ret = findTextColPoint(node.firstChild, col);
if (ret.element !== null) {
return ret;
} else {
cols += ret.offset;
}
}
if (node.nextSibling)
return findTextColPoint(node.nextSibling, col - cols);
else
return { element: null, offset: cols };
}
// Returns an object indicating the element containing the given line and
// column of text, and the offset into that element where the text was found.
//
// If the given line and column are not found, returns a null element and
// the number of lines found.
function findTextPoint(el, line, col) {
var newlines = 0;
for (var childId = 0; childId < el.childNodes.length; childId++) {
var child = el.childNodes[childId];
// If this is a text node, count the number of newlines it contains.
if (child.nodeType === 3) { // TEXT_NODE
var newlinere = /\n/g;
var match;
while ((match = newlinere.exec(child.nodeValue)) !== null) {
newlines++;
// Found the desired line, now find the column.
if (newlines === line) {
return findTextColPoint(child, match.index + col + 1);
}
}
}
// If this is not a text node, descend recursively to see how many
// lines it contains.
else if (child.nodeType === 1) { // ELEMENT_NODE
var ret = findTextPoint(child, line - newlines, col);
if (ret.element !== null)
return ret;
else
newlines += ret.offset;
}
}
return { element: null, offset: newlines };
}
// Draw a highlight effect for the given source ref. srcref is assumed to be
// an integer array of length 6, following the standard R format for source
// refs.
function highlightSrcref (srcref, srcfile) {
// Check to see if the browser supports text ranges (IE8 doesn't)
if (!document.createRange)
return;
// Check to see if we already have a marker for this source ref
var el = document.getElementById("srcref_" + srcref);
if (!el) {
// We don't have a marker, create one
el = document.createElement("span");
el.id = "srcref_" + srcref;
var ref = srcref;
var code = document.getElementById(srcfile.replace(/\./g, "_") + "_code");
// if there is no code file (might be a shiny file), quit early
if (!code) return;
var start = findTextPoint(code, ref[0], ref[4]);
var end = findTextPoint(code, ref[2], ref[5]);
// If the insertion point can't be found, bail out now
if (start.element === null || end.element === null)
return;
var range = document.createRange();
// If the text points are inside different <SPAN>s, we may not be able to
// surround them without breaking apart the elements to keep the DOM tree
// intact. Just move the selection points to encompass the contents of
// the SPANs.
if (start.element.parentNode.nodeName === "SPAN" &&
start.element !== end.element) {
range.setStartBefore(start.element.parentNode);
} else {
range.setStart(start.element, start.offset);
}
if (end.element.parentNode.nodeName === "SPAN" &&
start.element !== end.element) {
range.setEndAfter(end.element.parentNode);
} else {
range.setEnd(end.element, end.offset);
}
range.surroundContents(el);
}
// End any previous highlight before starting this one
jQuery(el)
.stop(true, true)
.effect("highlight", null, 1600);
}
// If this is the main Shiny window, wire up our custom message handler.
if (window.Shiny) {
Shiny.addCustomMessageHandler('showcase-src', function(message) {
if (message.srcref && message.srcfile) {
highlightSrcref(message.srcref, message.srcfile);
}
});
}
var isCodeAbove = false;
var setCodePosition = function(above, animate) {
var animateCodeMs = animate ? animateMs : 1;
// set the source and targets for the tab move
var newHostElement = above ?
document.getElementById("showcase-sxs-code") :
document.getElementById("showcase-code-inline");
var currentHostElement = above ?
document.getElementById("showcase-code-inline") :
document.getElementById("showcase-sxs-code");
var metadataElement = document.getElementById("showcase-app-metadata");
if (metadataElement === null) {
// if there's no app metadata, show and hide the entire well container
// when the code changes position
var wellElement = $("#showcase-well");
if (above) {
wellElement.fadeOut(animateCodeMs);
} else {
wellElement.fadeIn(animateCodeMs);
}
}
// hide the new element before doing anything to it
$(newHostElement).hide();
$(currentHostElement).fadeOut(animateCodeMs, function() {
var tabs = document.getElementById("showcase-code-tabs");
currentHostElement.removeChild(tabs);
newHostElement.appendChild(tabs);
// remove or set the height of the code
if (above) {
setCodeHeightFromDocHeight();
} else {
document.getElementById("showcase-code-content").removeAttribute("style");
}
$(newHostElement).fadeIn(animateCodeMs);
if (!above) {
// remove the applied width and zoom on the app container, and
// scroll smoothly down to the code's new home
document.getElementById("showcase-app-container").removeAttribute("style");
if (animate)
$(document.body).animate({ scrollTop: $(newHostElement).offset().top });
}
// if there's a readme, move it either alongside the code or beneath
// the app
var readme = document.getElementById("readme-md");
if (readme !== null) {
readme.parentElement.removeChild(readme);
if (above) {
currentHostElement.appendChild(readme);
$(currentHostElement).fadeIn(animateCodeMs);
}
else
document.getElementById("showcase-app-metadata").appendChild(readme);
}
// change the text on the toggle button to reflect the new state
document.getElementById("showcase-code-position-toggle").innerHTML = above ?
'<i class="fa fa-level-down"></i> show below' :
'<i class="fa fa-level-up"></i> show with app';
});
if (above) {
$(document.body).animate({ scrollTop: 0 }, animateCodeMs);
}
isCodeAbove = above;
setAppCodeSxsWidths(above && animate);
$(window).trigger("resize");
};
var setAppCodeSxsWidths = function(animate) {
var appTargetWidth = 960;
var appWidth = appTargetWidth;
var zoom = 1.0;
var totalWidth = document.getElementById("showcase-app-code").offsetWidth;
if (totalWidth / 2 > appTargetWidth) {
// If the app can use only half the available space and still meet its
// target, take half the available space.
appWidth = totalWidth / 2;
} else if (totalWidth * 0.66 > appTargetWidth) {
// If the app can meet its target by taking up more space (up to 66%
// of its container), take up more space.
appWidth = 960;
} else {
// The space is too narrow for the app and code to live side-by-side
// in a friendly way. Keep the app at 2/3 of the space but scale it.
appWidth = totalWidth * 0.66;
zoom = appWidth/appTargetWidth;
}
var app = document.getElementById("showcase-app-container");
$(app).animate({
width: appWidth + "px",
zoom: (zoom*100) + "%"
}, animate ? animateMs : 0);
};
var toggleCodePosition = function() {
setCodePosition(!isCodeAbove, true);
};
// if the browser is sized to wider than 1350px, show the code next to the
// app by default
var setInitialCodePosition = function() {
if (document.body.offsetWidth > 1350) {
setCodePosition(true, false);
}
};
// make the code scrollable to about the height of the browser, less space
// for the tabs
var setCodeHeightFromDocHeight = function() {
document.getElementById("showcase-code-content").style.height =
$(window).height() + "px";
};
// if there's a block of markdown content, render it to HTML
var renderMarkdown = function() {
var mdContent = document.getElementById("showcase-markdown-content");
if (mdContent !== null) {
// IE8 puts the content of <script> tags into innerHTML but
// not innerText
var content = mdContent.innerText || mdContent.innerHTML;
document.getElementById("readme-md").innerHTML =
(new Showdown.converter()).makeHtml(content)
}
}
$(window).resize(function() {
if (isCodeAbove) {
setAppCodeSxsWidths(false);
setCodeHeightFromDocHeight();
}
});
window.toggleCodePosition = toggleCodePosition;
$(window).on("load", setInitialCodePosition);
$(window).on("load", renderMarkdown);
if (window.hljs)
hljs.initHighlightingOnLoad();
})();

View File

@@ -1,8 +0,0 @@
// Listen for messages from parent frame. This file is only added when the
// shiny.testmode option is TRUE.
window.addEventListener("message", function(e) {
var message = e.data;
if (message.code)
eval(message.code);
});

View File

@@ -1 +0,0 @@
})();

View File

@@ -1,17 +0,0 @@
(function() {
var $ = jQuery;
var exports = window.Shiny = window.Shiny || {};
exports.version = "{{ VERSION }}"; // Version number inserted by Grunt
var origPushState = window.history.pushState;
window.history.pushState = function() {
var result = origPushState.apply(this, arguments);
$(document).trigger("pushstate");
return result;
};
$(document).on('submit', 'form:not([action])', function(e) {
e.preventDefault();
});

View File

@@ -1,37 +0,0 @@
var BindingRegistry = function() {
this.bindings = [];
this.bindingNames = {};
};
(function() {
this.register = function(binding, bindingName, priority) {
var bindingObj = {binding: binding, priority: priority || 0};
this.bindings.unshift(bindingObj);
if (bindingName) {
this.bindingNames[bindingName] = bindingObj;
binding.name = bindingName;
}
};
this.setPriority = function(bindingName, priority) {
var bindingObj = this.bindingNames[bindingName];
if (!bindingObj)
throw "Tried to set priority on unknown binding " + bindingName;
bindingObj.priority = priority || 0;
};
this.getPriority = function(bindingName) {
var bindingObj = this.bindingNames[bindingName];
if (!bindingObj)
return false;
return bindingObj.priority;
};
this.getBindings = function() {
// Sort the bindings. The ones with higher priority are consulted
// first; ties are broken by most-recently-registered.
return mergeSort(this.bindings, function(a, b) {
return b.priority - a.priority;
});
};
}).call(BindingRegistry.prototype);
var inputBindings = exports.inputBindings = new BindingRegistry();
var outputBindings = exports.outputBindings = new BindingRegistry();

View File

@@ -1,41 +0,0 @@
var browser = (function() {
var isQt = false;
// For easy handling of Qt quirks using CSS
if (/\bQt\//.test(window.navigator.userAgent)) {
$(document.documentElement).addClass('qt');
isQt = true;
}
// Enable special treatment for Qt 5 quirks on Linux
if (/\bQt\/5/.test(window.navigator.userAgent) &&
/Linux/.test(window.navigator.userAgent)) {
$(document.documentElement).addClass('qt5');
}
// Detect IE and older (pre-Chromium) Edge
var ua = window.navigator.userAgent;
var isIE = /MSIE|Trident|Edge/.test(ua);
function getIEVersion() {
var msie = ua.indexOf('MSIE ');
if (isIE && msie > 0) {
// IE 10 or older => return version number
return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
}
var trident = ua.indexOf('Trident/');
if (trident > 0) {
// IE 11 => return version number
var rv = ua.indexOf('rv:');
return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
}
return -1;
}
return {
isQt: isQt,
isIE: isIE,
IEVersion: getIEVersion()
};
})();

View File

@@ -1,79 +0,0 @@
// Generic driver class for doing chunk-wise asynchronous processing of a
// FileList object. Subclass/clone it and override the `on*` functions to
// make it do something useful.
var FileProcessor = function(files) {
this.files = files;
this.fileIndex = -1;
// Currently need to use small chunk size because R-Websockets can't
// handle continuation frames
this.aborted = false;
this.completed = false;
// TODO: Register error/abort callbacks
this.$run();
};
(function() {
// Begin callbacks. Subclassers/cloners may override any or all of these.
this.onBegin = function(files, cont) {
setTimeout(cont, 0);
};
this.onFile = function(file, cont) {
setTimeout(cont, 0);
};
this.onComplete = function() {
};
this.onAbort = function() {
};
// End callbacks
// Aborts processing, unless it's already completed
this.abort = function() {
if (this.completed || this.aborted)
return;
this.aborted = true;
this.onAbort();
};
// Returns a bound function that will call this.$run one time.
this.$getRun = function() {
var self = this;
var called = false;
return function() {
if (called)
return;
called = true;
self.$run();
};
};
// This function will be called multiple times to advance the process.
// It relies on the state of the object's fields to know what to do next.
this.$run = function() {
if (this.aborted || this.completed)
return;
if (this.fileIndex < 0) {
// Haven't started yet--begin
this.fileIndex = 0;
this.onBegin(this.files, this.$getRun());
return;
}
if (this.fileIndex === this.files.length) {
// Just ended
this.completed = true;
this.onComplete();
return;
}
// If we got here, then we have a file to process, or we are
// in the middle of processing a file, or have just finished
// processing a file.
var file = this.files[this.fileIndex++];
this.onFile(file, this.$getRun());
};
}).call(FileProcessor.prototype);

View File

@@ -1,618 +0,0 @@
function initShiny() {
var shinyapp = exports.shinyapp = new ShinyApp();
function bindOutputs(scope = document) {
scope = $(scope);
var bindings = outputBindings.getBindings();
for (var i = 0; i < bindings.length; i++) {
var binding = bindings[i].binding;
var matches = binding.find(scope) || [];
for (var j = 0; j < matches.length; j++) {
var el = matches[j];
var id = binding.getId(el);
// 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
// gets called on two ancestors)
continue;
}
// If this element reports its CSS styles to getCurrentOutputInfo()
// then it should have a MutationObserver() to resend CSS if its
// style/class attributes change. This observer should already exist
// for _static_ UI, but not yet for _dynamic_ UI
maybeAddThemeObserver(el);
var bindingAdapter = new OutputBindingAdapter(el, binding);
shinyapp.bindOutput(id, bindingAdapter);
$el.data('shiny-output-binding', bindingAdapter);
$el.addClass('shiny-bound-output');
if (!$el.attr("aria-live")) $el.attr("aria-live", "polite");
$el.trigger({
type: 'shiny:bound',
binding: binding,
bindingType: 'output'
});
}
}
// Send later in case DOM layout isn't final yet.
setTimeout(sendImageSize, 0);
setTimeout(sendOutputHiddenState, 0);
}
function unbindOutputs(scope = document, includeSelf = false) {
var outputs = $(scope).find('.shiny-bound-output');
if (includeSelf && $(scope).hasClass('shiny-bound-output')) {
outputs.push(scope);
}
for (var i = 0; i < outputs.length; i++) {
var $el = $(outputs[i]);
var bindingAdapter = $el.data('shiny-output-binding');
if (!bindingAdapter)
continue;
var id = bindingAdapter.binding.getId(outputs[i]);
shinyapp.unbindOutput(id, bindingAdapter);
$el.removeClass('shiny-bound-output');
$el.removeData('shiny-output-binding');
$el.trigger({
type: 'shiny:unbound',
binding: bindingAdapter.binding,
bindingType: 'output'
});
}
// Send later in case DOM layout isn't final yet.
setTimeout(sendImageSize, 0);
setTimeout(sendOutputHiddenState, 0);
}
var inputBatchSender = new InputBatchSender(shinyapp);
var inputsNoResend = new InputNoResendDecorator(inputBatchSender);
var inputsEvent = new InputEventDecorator(inputsNoResend);
var inputsRate = new InputRateDecorator(inputsEvent);
var inputsDefer = new InputDeferDecorator(inputsEvent);
var inputs;
if ($('input[type="submit"], button[type="submit"]').length > 0) {
// If there is a submit button on the page, use defer decorator
inputs = inputsDefer;
$('input[type="submit"], button[type="submit"]').each(function() {
$(this).click(function(event) {
event.preventDefault();
inputsDefer.submit();
});
});
} else {
// By default, use rate decorator
inputs = inputsRate;
}
inputs = new InputValidateDecorator(inputs);
exports.setInputValue = exports.onInputChange = function(name, value, opts) {
opts = addDefaultInputOpts(opts);
inputs.setInput(name, value, opts);
};
// By default, Shiny deduplicates input value changes; that is, if
// `setInputValue` is called with the same value as the input already
// has, the call is ignored (unless opts.priority = "event"). Calling
// `forgetLastInputValue` tells Shiny that the very next call to
// `setInputValue` for this input id shouldn't be ignored, even if it
// is a dupe of the existing value.
exports.forgetLastInputValue = function(name) {
inputsNoResend.forget(name);
};
var boundInputs = {};
function valueChangeCallback(binding, el, allowDeferred) {
var id = binding.getId(el);
if (id) {
var value = binding.getValue(el);
var type = binding.getType(el);
if (type)
id = id + ":" + type;
let opts = {
priority: allowDeferred ? "deferred" : "immediate",
binding: binding,
el: el
};
inputs.setInput(id, value, opts);
}
}
function bindInputs(scope = document) {
var bindings = inputBindings.getBindings();
var inputItems = {};
for (var i = 0; i < bindings.length; i++) {
var binding = bindings[i].binding;
var matches = binding.find(scope) || [];
for (var j = 0; j < matches.length; j++) {
var el = matches[j];
var id = binding.getId(el);
// Check if ID is falsy, or if already bound
if (!id || boundInputs[id])
continue;
var type = binding.getType(el);
var effectiveId = type ? id + ":" + type : id;
inputItems[effectiveId] = {
value: binding.getValue(el),
opts: {
immediate: true,
binding: binding,
el: el
}
};
/*jshint loopfunc:true*/
var thisCallback = (function() {
var thisBinding = binding;
var thisEl = el;
return function(allowDeferred) {
valueChangeCallback(thisBinding, thisEl, allowDeferred);
};
})();
binding.subscribe(el, thisCallback);
$(el).data('shiny-input-binding', binding);
$(el).addClass('shiny-bound-input');
var ratePolicy = binding.getRatePolicy(el);
if (ratePolicy !== null) {
inputsRate.setRatePolicy(
effectiveId,
ratePolicy.policy,
ratePolicy.delay);
}
boundInputs[id] = {
binding: binding,
node: el
};
$(el).trigger({
type: 'shiny:bound',
binding: binding,
bindingType: 'input'
});
}
}
return inputItems;
}
function unbindInputs(scope = document, includeSelf = false) {
var inputs = $(scope).find('.shiny-bound-input');
if (includeSelf && $(scope).hasClass('shiny-bound-input')) {
inputs.push(scope);
}
for (var i = 0; i < inputs.length; i++) {
var el = inputs[i];
var binding = $(el).data('shiny-input-binding');
if (!binding)
continue;
var id = binding.getId(el);
$(el).removeClass('shiny-bound-input');
delete boundInputs[id];
binding.unsubscribe(el);
$(el).trigger({
type: 'shiny:unbound',
binding: binding,
bindingType: 'input'
});
}
}
function _bindAll(scope) {
bindOutputs(scope);
return bindInputs(scope);
}
function unbindAll(scope, includeSelf = false) {
unbindInputs(scope, includeSelf);
unbindOutputs(scope, includeSelf);
}
exports.bindAll = function(scope) {
// _bindAll returns input values; it doesn't send them to the server.
// export.bindAll needs to send the values to the server.
var currentInputItems = _bindAll(scope);
$.each(currentInputItems, function(name, item) {
inputs.setInput(name, item.value, item.opts);
});
// Not sure if the iframe stuff is an intrinsic part of bindAll, but bindAll
// is a convenient place to hang it. bindAll will be called anytime new HTML
// appears that might contain inputs/outputs; it's reasonable to assume that
// any such HTML may contain iframes as well.
initDeferredIframes();
};
exports.unbindAll = unbindAll;
// Calls .initialize() for all of the input objects in all input bindings,
// in the given scope.
function initializeInputs(scope = document) {
var bindings = inputBindings.getBindings();
// Iterate over all bindings
for (var i = 0; i < bindings.length; i++) {
var binding = bindings[i].binding;
var inputObjects = binding.find(scope) || [];
// Iterate over all input objects for this binding
for (var j = 0; j < inputObjects.length; j++) {
if (!inputObjects[j]._shiny_initialized) {
inputObjects[j]._shiny_initialized = true;
binding.initialize(inputObjects[j]);
}
}
}
}
exports.initializeInputs = initializeInputs;
function getIdFromEl(el) {
var $el = $(el);
var bindingAdapter = $el.data("shiny-output-binding");
if (!bindingAdapter)
return null;
else
return bindingAdapter.getId();
}
// Initialize all input objects in the document, before binding
initializeInputs(document);
// The input values returned by _bindAll() each have a structure like this:
// { value: 123, opts: { ... } }
// We want to only keep the value. This is because when the initialValues is
// passed to ShinyApp.connect(), the ShinyApp object stores the
// initialValues object for the duration of the session, and the opts may
// have a reference to the DOM element, which would prevent it from being
// GC'd.
var initialValues = mapValues(_bindAll(document), x => x.value);
// The server needs to know the size of each image and plot output element,
// in case it is auto-sizing
$('.shiny-image-output, .shiny-plot-output, .shiny-report-size').each(function() {
var id = getIdFromEl(this);
if (this.offsetWidth !== 0 || this.offsetHeight !== 0) {
initialValues['.clientdata_output_' + id + '_width'] = this.offsetWidth;
initialValues['.clientdata_output_' + id + '_height'] = this.offsetHeight;
}
});
function getComputedBgColor(el) {
if (!el) {
// Top of document, can't recurse further
return null;
}
let bgColor = getStyle(el, "background-color");
let m = bgColor.match(/^rgba\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*\)$/);
if (bgColor === "transparent" || (m && parseFloat(m[4]) === 0)) {
// No background color on this element. See if it has a background image.
let bgImage = getStyle(el, "background-image");
if (bgImage && bgImage !== "none") {
// Failed to detect background color, since it has a background image
return null;
} else {
// Recurse
return getComputedBgColor(el.parentElement);
}
}
return bgColor;
}
function getComputedFont(el) {
let fontFamily = getStyle(el, "font-family");
let fontSize = getStyle(el, "font-size");
return {
families: fontFamily.replace(/"/g, '').split(", "),
size: fontSize
};
}
$('.shiny-image-output, .shiny-plot-output, .shiny-report-theme').each(function() {
var el = this, id = getIdFromEl(el);
initialValues['.clientdata_output_' + id + '_bg'] = getComputedBgColor(el);
initialValues['.clientdata_output_' + id + '_fg'] = getStyle(el, "color");
initialValues['.clientdata_output_' + id + '_accent'] = getComputedLinkColor(el);
initialValues['.clientdata_output_' + id + '_font'] = getComputedFont(el);
maybeAddThemeObserver(el);
});
// Resend computed styles if *an output element's* class or style attribute changes.
// This gives us some level of confidence that getCurrentOutputInfo() will be
// properly invalidated if output container is mutated; but unfortunately,
// we don't have a reasonable way to detect change in *inherited* styles
// (other than session$setCurrentTheme())
// https://github.com/rstudio/shiny/issues/3196
// https://github.com/rstudio/shiny/issues/2998
function maybeAddThemeObserver(el) {
if (!window.MutationObserver) {
return; // IE10 and lower
}
var cl = el.classList;
var reportTheme = cl.contains('shiny-image-output') || cl.contains('shiny-plot-output') || cl.contains('shiny-report-theme');
if (!reportTheme) {
return;
}
var $el = $(el);
if ($el.data("shiny-theme-observer")) {
return; // i.e., observer is already observing
}
var observerCallback = new Debouncer(null, () => doSendTheme(el), 100);
var observer = new MutationObserver(() => observerCallback.normalCall());
var config = {attributes: true, attributeFilter: ['style', 'class']};
observer.observe(el, config);
$el.data("shiny-theme-observer", observer);
}
function doSendTheme(el) {
// Sending theme info on error isn't necessary (it'd add an unnecessary additional round-trip)
if (el.classList.contains("shiny-output-error")) {
return;
}
var id = getIdFromEl(el);
inputs.setInput('.clientdata_output_' + id + '_bg', getComputedBgColor(el));
inputs.setInput('.clientdata_output_' + id + '_fg', getStyle(el, "color"));
inputs.setInput('.clientdata_output_' + id + '_accent', getComputedLinkColor(el));
inputs.setInput('.clientdata_output_' + id + '_font', getComputedFont(el));
}
function doSendImageSize() {
$('.shiny-image-output, .shiny-plot-output, .shiny-report-size').each(function() {
var id = getIdFromEl(this);
if (this.offsetWidth !== 0 || this.offsetHeight !== 0) {
inputs.setInput('.clientdata_output_' + id + '_width', this.offsetWidth);
inputs.setInput('.clientdata_output_' + id + '_height', this.offsetHeight);
}
});
$('.shiny-image-output, .shiny-plot-output, .shiny-report-theme').each(function() {
doSendTheme(this);
});
$('.shiny-bound-output').each(function() {
var $this = $(this), binding = $this.data('shiny-output-binding');
$this.trigger({
type: 'shiny:visualchange',
visible: !isHidden(this),
binding: binding
});
binding.onResize();
});
}
var sendImageSizeDebouncer = new Debouncer(null, doSendImageSize, 0);
function sendImageSize() {
sendImageSizeDebouncer.normalCall();
}
// Make sure sendImageSize actually gets called before the inputBatchSender
// sends data to the server.
inputBatchSender.lastChanceCallback.push(function() {
if (sendImageSizeDebouncer.isPending())
sendImageSizeDebouncer.immediateCall();
});
// Return true if the object or one of its ancestors in the DOM tree has
// style='display:none'; otherwise return false.
function isHidden(obj) {
// null means we've hit the top of the tree. If width or height is
// non-zero, then we know that no ancestor has display:none.
if (obj === null || obj.offsetWidth !== 0 || obj.offsetHeight !== 0) {
return false;
} else if (getStyle(obj, 'display') === 'none') {
return true;
} else {
return(isHidden(obj.parentNode));
}
}
var lastKnownVisibleOutputs = {};
// Set initial state of outputs to hidden, if needed
$('.shiny-bound-output').each(function() {
var id = getIdFromEl(this);
if (isHidden(this)) {
initialValues['.clientdata_output_' + id + '_hidden'] = true;
} else {
lastKnownVisibleOutputs[id] = true;
initialValues['.clientdata_output_' + id + '_hidden'] = false;
}
});
// Send update when hidden state changes
function doSendOutputHiddenState() {
var visibleOutputs = {};
$('.shiny-bound-output').each(function() {
var id = getIdFromEl(this);
delete lastKnownVisibleOutputs[id];
// Assume that the object is hidden when width and height are 0
var hidden = isHidden(this), evt = {
type: 'shiny:visualchange',
visible: !hidden
};
if (hidden) {
inputs.setInput('.clientdata_output_' + id + '_hidden', true);
} else {
visibleOutputs[id] = true;
inputs.setInput('.clientdata_output_' + id + '_hidden', false);
}
var $this = $(this);
evt.binding = $this.data('shiny-output-binding');
$this.trigger(evt);
});
// Anything left in lastKnownVisibleOutputs is orphaned
for (var name in lastKnownVisibleOutputs) {
if (lastKnownVisibleOutputs.hasOwnProperty(name))
inputs.setInput('.clientdata_output_' + name + '_hidden', true);
}
// Update the visible outputs for next time
lastKnownVisibleOutputs = visibleOutputs;
}
// sendOutputHiddenState gets called each time DOM elements are shown or
// hidden. This can be in the hundreds or thousands of times at startup.
// We'll debounce it, so that we do the actual work once per tick.
var sendOutputHiddenStateDebouncer = new Debouncer(null, doSendOutputHiddenState, 0);
function sendOutputHiddenState() {
sendOutputHiddenStateDebouncer.normalCall();
}
// We need to make sure doSendOutputHiddenState actually gets called before
// the inputBatchSender sends data to the server. The lastChanceCallback
// here does that - if the debouncer has a pending call, flush it.
inputBatchSender.lastChanceCallback.push(function() {
if (sendOutputHiddenStateDebouncer.isPending())
sendOutputHiddenStateDebouncer.immediateCall();
});
// Given a namespace and a handler function, return a function that invokes
// the handler only when e's namespace matches. For example, if the
// namespace is "bs", it would match when e.namespace is "bs" or "bs.tab".
// If the namespace is "bs.tab", it would match for "bs.tab", but not "bs".
function filterEventsByNamespace(namespace, handler) {
namespace = namespace.split(".");
return function(e) {
var eventNamespace = e.namespace.split(".");
// If any of the namespace strings aren't present in this event, quit.
for (var i=0; i<namespace.length; i++) {
if (eventNamespace.indexOf(namespace[i]) === -1)
return;
}
handler.apply(this, arguments);
};
}
// The size of each image may change either because the browser window was
// resized, or because a tab was shown/hidden (hidden elements report size
// of 0x0). It's OK to over-report sizes because the input pipeline will
// filter out values that haven't changed.
$(window).resize(debounce(500, sendImageSize));
// Need to register callbacks for each Bootstrap 3 class.
var bs3classes = ['modal', 'dropdown', 'tab', 'tooltip', 'popover', 'collapse'];
$.each(bs3classes, function(idx, classname) {
$(document.body).on('shown.bs.' + classname + '.sendImageSize', '*',
filterEventsByNamespace('bs', sendImageSize));
$(document.body).on('shown.bs.' + classname + '.sendOutputHiddenState ' +
'hidden.bs.' + classname + '.sendOutputHiddenState',
'*', filterEventsByNamespace('bs', sendOutputHiddenState));
});
// This is needed for Bootstrap 2 compatibility and for non-Bootstrap
// related shown/hidden events (like conditionalPanel)
$(document.body).on('shown.sendImageSize', '*', sendImageSize);
$(document.body).on('shown.sendOutputHiddenState hidden.sendOutputHiddenState', '*',
sendOutputHiddenState);
// Send initial pixel ratio, and update it if it changes
initialValues['.clientdata_pixelratio'] = pixelRatio();
$(window).resize(function() {
inputs.setInput('.clientdata_pixelratio', pixelRatio());
});
// Send initial URL
initialValues['.clientdata_url_protocol'] = window.location.protocol;
initialValues['.clientdata_url_hostname'] = window.location.hostname;
initialValues['.clientdata_url_port'] = window.location.port;
initialValues['.clientdata_url_pathname'] = window.location.pathname;
// Send initial URL search (query string) and update it if it changes
initialValues['.clientdata_url_search'] = window.location.search;
$(window).on('pushstate', function(e) {
inputs.setInput('.clientdata_url_search', window.location.search);
});
$(window).on('popstate', function(e) {
inputs.setInput('.clientdata_url_search', window.location.search);
});
// This is only the initial value of the hash. The hash can change, but
// a reactive version of this isn't sent because watching for changes can
// require polling on some browsers. The JQuery hashchange plugin can be
// used if this capability is important.
initialValues['.clientdata_url_hash_initial'] = window.location.hash;
initialValues['.clientdata_url_hash'] = window.location.hash;
$(window).on('hashchange', function(e) {
inputs.setInput('.clientdata_url_hash', window.location.hash);
});
// The server needs to know what singletons were rendered as part of
// the page loading
var singletonText = initialValues['.clientdata_singletons'] =
$('script[type="application/shiny-singletons"]').text();
singletons.registerNames(singletonText.split(/,/));
var dependencyText = $('script[type="application/html-dependencies"]').text();
$.each(dependencyText.split(/;/), function(i, depStr) {
var match = /\s*^(.+)\[(.+)\]\s*$/.exec(depStr);
if (match) {
registerDependency(match[1], match[2]);
}
});
// We've collected all the initial values--start the server process!
inputsNoResend.reset(initialValues);
shinyapp.connect(initialValues);
$(document).one("shiny:connected", function() {
initDeferredIframes();
});
} // function initShiny()
// Give any deferred iframes a chance to load.
function initDeferredIframes() {
if (!window.Shiny || !window.Shiny.shinyapp || !window.Shiny.shinyapp.isConnected()) {
// If somehow we accidentally call this before the server connection is
// established, just ignore the call. At the time of this writing it
// doesn't happen, but it's easy to imagine a later refactoring putting
// us in this situation and it'd be hard to notice with either manual
// testing or automated tests, because the only effect is on HTTP request
// timing. (Update: Actually Aron saw this being called without even
// window.Shiny being defined, but it was hard to repro.)
return;
}
$(".shiny-frame-deferred").each(function (i, el) {
var $el = $(el);
$el.removeClass("shiny-frame-deferred");
$el.attr("src", $el.attr("data-deferred-src"));
$el.attr("data-deferred-src", null);
});
}
$(function() {
// Init Shiny a little later than document ready, so user code can
// run first (i.e. to register bindings)
setTimeout(initShiny, 1);
});

View File

@@ -1,44 +0,0 @@
var InputBinding = exports.InputBinding = function() {};
(function() {
// Returns a jQuery object or element array that contains the
// descendants of scope that match this binding
this.find = function(scope) { throw "Not implemented"; };
this.getId = function(el) {
return el['data-input-id'] || el.id;
};
// Gives the input a type in case the server needs to know it
// 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) { };
// This is used for receiving messages that tell the input object to do
// things, such as setting values (including min, max, and others).
// 'data' should be an object with elements corresponding to value, min,
// max, etc., as appropriate for the type of input object. It also should
// trigger a change event.
this.receiveMessage = function(el, data) { throw "Not implemented"; };
this.getState = function(el, data) { throw "Not implemented"; };
this.getRatePolicy = function() { return null; };
// Some input objects need initialization before being bound. This is
// called when the document is ready (for statically-added input objects),
// and when new input objects are added to the document with
// htmlOutputBinding.renderValue() (for dynamically-added input objects).
// This is called before the input is bound.
this.initialize = function(el) { };
// This is called after unbinding the output.
this.dispose = function(el) { };
}).call(InputBinding.prototype);

View File

@@ -1,65 +0,0 @@
var actionButtonInputBinding = new InputBinding();
$.extend(actionButtonInputBinding, {
find: function(scope) {
return $(scope).find(".action-button");
},
getValue: function(el) {
return $(el).data('val') || 0;
},
setValue: function(el, value) {
$(el).data('val', value);
},
getType: function(el) {
return 'shiny.action';
},
subscribe: function(el, callback) {
$(el).on("click.actionButtonInputBinding", function(e) {
var $el = $(this);
var val = $el.data('val') || 0;
$el.data('val', val + 1);
callback();
});
},
getState: function(el) {
return { value: this.getValue(el) };
},
receiveMessage: function(el, data) {
var $el = $(el);
// retrieve current label and icon
var label = $el.text();
var icon = '';
// to check (and store) the previous icon, we look for a $el child
// object that has an i tag, and some (any) class (this prevents
// italicized text - which has an i tag but, usually, no class -
// from being mistakenly selected)
if ($el.find('i[class]').length > 0) {
var icon_html = $el.find('i[class]')[0];
if (icon_html === $el.children()[0]) { // another check for robustness
icon = $(icon_html).prop('outerHTML');
}
}
// update the requested properties
if (data.hasOwnProperty('label')) label = data.label;
if (data.hasOwnProperty('icon')) {
icon = data.icon;
// if the user entered icon=character(0), remove the icon
if (icon.length === 0) icon = '';
}
// produce new html
$el.html(icon + ' ' + label);
},
unsubscribe: function(el) {
$(el).off(".actionButtonInputBinding");
}
});
inputBindings.register(actionButtonInputBinding, 'shiny.actionButtonInput');
$(document).on('click', 'a.action-button', function(e) {
e.preventDefault();
});

View File

@@ -1,38 +0,0 @@
var checkboxInputBinding = new InputBinding();
$.extend(checkboxInputBinding, {
find: function(scope) {
return $(scope).find('input[type="checkbox"]');
},
getValue: function(el) {
return el.checked;
},
setValue: function(el, value) {
el.checked = value;
},
subscribe: function(el, callback) {
$(el).on('change.checkboxInputBinding', function(event) {
callback(true);
});
},
unsubscribe: function(el) {
$(el).off('.checkboxInputBinding');
},
getState: function(el) {
return {
label: $(el).parent().find('span').text(),
value: el.checked
};
},
receiveMessage: function(el, data) {
if (data.hasOwnProperty('value'))
el.checked = data.value;
// checkboxInput()'s label works different from other
// input labels...the label container should always exist
if (data.hasOwnProperty('label'))
$(el).parent().find('span').text(data.label);
$(el).trigger('change');
}
});
inputBindings.register(checkboxInputBinding, 'shiny.checkboxInput');

View File

@@ -1,100 +0,0 @@
var checkboxGroupInputBinding = new InputBinding();
$.extend(checkboxGroupInputBinding, {
find: function(scope) {
return $(scope).find('.shiny-input-checkboxgroup');
},
getValue: function(el) {
// Select the checkbox objects that have name equal to the grouping div's id
var $objs = $('input:checkbox[name="' + $escape(el.id) + '"]:checked');
var values = new Array($objs.length);
for (var i = 0; i < $objs.length; i ++) {
values[i] = $objs[i].value;
}
return values;
},
setValue: function(el, value) {
// Clear all checkboxes
$('input:checkbox[name="' + $escape(el.id) + '"]').prop('checked', false);
// Accept array
if (value instanceof Array) {
for (var i = 0; i < value.length; i++) {
$('input:checkbox[name="' + $escape(el.id) + '"][value="' + $escape(value[i]) + '"]')
.prop('checked', true);
}
// Else assume it's a single value
} else {
$('input:checkbox[name="' + $escape(el.id) + '"][value="' + $escape(value) + '"]')
.prop('checked', true);
}
},
getState: function(el) {
var $objs = $('input:checkbox[name="' + $escape(el.id) + '"]');
// Store options in an array of objects, each with with value and label
var options = new Array($objs.length);
for (var i = 0; i < options.length; i++) {
options[i] = { value: $objs[i].value,
label: this._getLabel($objs[i]) };
}
return { label: this._getLabelNode(el).text(),
value: this.getValue(el),
options: options
};
},
receiveMessage: function(el, data) {
var $el = $(el);
// This will replace all the options
if (data.hasOwnProperty('options')) {
// Clear existing options and add each new one
$el.find('div.shiny-options-group').remove();
// Backward compatibility: for HTML generated by shinybootstrap2 package
$el.find('label.checkbox').remove();
$el.append(data.options);
}
if (data.hasOwnProperty('value'))
this.setValue(el, data.value);
updateLabel(data.label, this._getLabelNode(el));
$(el).trigger('change');
},
subscribe: function(el, callback) {
$(el).on('change.checkboxGroupInputBinding', function(event) {
callback();
});
},
unsubscribe: function(el) {
$(el).off('.checkboxGroupInputBinding');
},
// Get the DOM element that contains the top-level label
_getLabelNode: function(el) {
return $(el).find('label[for="' + $escape(el.id) + '"]');
},
// Given an input DOM object, get the associated label. Handles labels
// that wrap the input as well as labels associated with 'for' attribute.
_getLabel: function(obj) {
// If <label><input /><span>label text</span></label>
if (obj.parentNode.tagName === "LABEL") {
return $(obj.parentNode).find('span').text().trim();
}
return null;
},
// Given an input DOM object, set the associated label. Handles labels
// that wrap the input as well as labels associated with 'for' attribute.
_setLabel: function(obj, value) {
// If <label><input /><span>label text</span></label>
if (obj.parentNode.tagName === "LABEL") {
$(obj.parentNode).find('span').text(value);
}
return null;
}
});
inputBindings.register(checkboxGroupInputBinding, 'shiny.checkboxGroupInput');

View File

@@ -1,249 +0,0 @@
var dateInputBinding = new InputBinding();
$.extend(dateInputBinding, {
find: function(scope) {
return $(scope).find('.shiny-date-input');
},
getType: function(el) {
return "shiny.date";
},
// Return the date in an unambiguous format, yyyy-mm-dd (as opposed to a
// format like mm/dd/yyyy)
getValue: function(el) {
var date = $(el).find('input').bsDatepicker('getUTCDate');
return formatDateUTC(date);
},
// value must be an unambiguous string like '2001-01-01', or a Date object.
setValue: function(el, value) {
// R's NA, which is null here will remove current value
if (value === null) {
$(el).find('input').val('').bsDatepicker('update');
return;
}
var date = this._newDate(value);
// If date is invalid, do nothing
if (isNaN(date))
return;
$(el).find('input').bsDatepicker('setUTCDate', date);
},
getState: function(el) {
var $el = $(el);
var $input = $el.find('input');
var min = $input.data('datepicker').startDate;
var max = $input.data('datepicker').endDate;
// Stringify min and max. If min and max aren't set, they will be
// -Infinity and Infinity; replace these with null.
min = (min === -Infinity) ? null : formatDateUTC(min);
max = (max === Infinity) ? null : formatDateUTC(max);
// startViewMode is stored as a number; convert to string
var startview = $input.data('datepicker').startViewMode;
if (startview === 2) startview = 'decade';
else if (startview === 1) startview = 'year';
else if (startview === 0) startview = 'month';
return {
label: this._getLabelNode(el).text(),
value: this.getValue(el),
valueString: $input.val(),
min: min,
max: max,
language: $input.data('datepicker').language,
weekstart: $input.data('datepicker').weekStart,
format: this._formatToString($input.data('datepicker').format),
startview: startview
};
},
receiveMessage: function(el, data) {
var $input = $(el).find('input');
updateLabel(data.label, this._getLabelNode(el));
if (data.hasOwnProperty('min'))
this._setMin($input[0], data.min);
if (data.hasOwnProperty('max'))
this._setMax($input[0], data.max);
// Must set value only after min and max have been set. If new value is
// outside the bounds of the previous min/max, then the result will be a
// blank input.
if (data.hasOwnProperty('value'))
this.setValue(el, data.value);
$(el).trigger('change');
},
subscribe: function(el, callback) {
$(el).on('keyup.dateInputBinding input.dateInputBinding', function(event) {
// Use normal debouncing policy when typing
callback(true);
});
$(el).on('changeDate.dateInputBinding change.dateInputBinding', function(event) {
// Send immediately when clicked
callback(false);
});
},
unsubscribe: function(el) {
$(el).off('.dateInputBinding');
},
getRatePolicy: function() {
return {
policy: 'debounce',
delay: 250
};
},
initialize: function(el) {
var $input = $(el).find('input');
// The challenge with dates is that we want them to be at 00:00 in UTC so
// that we can do comparisons with them. However, the Date object itself
// does not carry timezone information, so we should call _floorDateTime()
// on Dates as soon as possible so that we know we're always working with
// consistent objects.
var date = $input.data('initial-date');
// If initial_date is null, set to current date
if (date === undefined || date === null) {
// Get local date, but normalized to beginning of day in UTC.
date = this._floorDateTime(this._dateAsUTC(new Date()));
}
this.setValue(el, date);
// Set the start and end dates, from min-date and max-date. These always
// use yyyy-mm-dd format, instead of bootstrap-datepicker's built-in
// support for date-startdate and data-enddate, which use the current
// date format.
if ($input.data('min-date') !== undefined) {
this._setMin($input[0], $input.data('min-date'));
}
if ($input.data('max-date') !== undefined) {
this._setMax($input[0], $input.data('max-date'));
}
},
_getLabelNode: function(el) {
return $(el).find('label[for="' + $escape(el.id) + '"]');
},
// Given a format object from a date picker, return a string
_formatToString: function(format) {
// Format object has structure like:
// { parts: ['mm', 'dd', 'yy'], separators: ['', '/', '/' ,''] }
var str = '';
for (var i = 0; i < format.parts.length; i++) {
str += format.separators[i] + format.parts[i];
}
str += format.separators[i];
return str;
},
// Given an unambiguous date string or a Date object, set the min (start) date.
// null will unset. undefined will result in no change,
_setMin: function(el, date) {
if (date === undefined)
return;
if (date === null) {
$(el).bsDatepicker('setStartDate', null);
return;
}
date = this._newDate(date);
// If date parsing fails, do nothing
if (date === null)
return;
if (isNaN(date))
return;
// Workarounds for
// https://github.com/rstudio/shiny/issues/2335
var curValue = $(el).bsDatepicker('getUTCDate');
// Note that there's no `setUTCStartDate`, so we need to convert this Date.
// It starts at 00:00 UTC, and we convert it to 00:00 in local time, which
// is what's needed for `setStartDate`.
$(el).bsDatepicker('setStartDate', this._UTCDateAsLocal(date));
// If the new min is greater than the current date, unset the current date.
if (date && curValue && date.getTime() > curValue.getTime()) {
$(el).bsDatepicker('clearDates');
} else {
// Setting the date needs to be done AFTER `setStartDate`, because the
// datepicker has a bug where calling `setStartDate` will clear the date
// internally (even though it will still be visible in the UI) when a
// 2-digit year format is used.
// https://github.com/eternicode/bootstrap-datepicker/issues/2010
$(el).bsDatepicker('setUTCDate', curValue);
}
},
// Given an unambiguous date string or a Date object, set the max (end) date
// null will unset.
_setMax: function(el, date) {
if (date === undefined)
return;
if (date === null) {
$(el).bsDatepicker('setEndDate', null);
return;
}
date = this._newDate(date);
// If date parsing fails, do nothing
if (date === null)
return;
if (isNaN(date))
return;
// Workaround for same issue as in _setMin.
var curValue = $(el).bsDatepicker('getUTCDate');
$(el).bsDatepicker('setEndDate', this._UTCDateAsLocal(date));
// If the new min is greater than the current date, unset the current date.
if (date && curValue && date.getTime() < curValue.getTime()) {
$(el).bsDatepicker('clearDates');
} else {
$(el).bsDatepicker('setUTCDate', curValue);
}
},
// Given a date string of format yyyy-mm-dd, return a Date object with
// that date at 12AM UTC.
// If date is a Date object, return it unchanged.
_newDate: function(date) {
if (date instanceof Date)
return date;
if (!date)
return null;
// Get Date object - this will be at 12AM in UTC, but may print
// differently at the Javascript console.
var d = parseDate(date);
// If invalid date, return null
if (isNaN(d))
return null;
return d;
},
// A Date can have any time during a day; this will return a new Date object
// set to 00:00 in UTC.
_floorDateTime: function(date) {
date = new Date(date.getTime());
date.setUTCHours(0, 0, 0, 0);
return date;
},
// Given a Date object, return a Date object which has the same "clock time"
// in UTC. For example, if input date is 2013-02-01 23:00:00 GMT-0600 (CST),
// output will be 2013-02-01 23:00:00 UTC. Note that the JS console may
// print this in local time, as "Sat Feb 02 2013 05:00:00 GMT-0600 (CST)".
_dateAsUTC: function(date) {
return new Date(date.getTime() - date.getTimezoneOffset() * 60000);
},
// The inverse of _dateAsUTC. This is needed to adjust time zones because
// some bootstrap-datepicker methods only take local dates as input, and not
// UTC.
_UTCDateAsLocal: function(date) {
return new Date(date.getTime() + date.getTimezoneOffset() * 60000);
}
});
inputBindings.register(dateInputBinding, 'shiny.dateInput');

View File

@@ -1,147 +0,0 @@
var dateRangeInputBinding = {};
$.extend(dateRangeInputBinding, dateInputBinding, {
find: function(scope) {
return $(scope).find('.shiny-date-range-input');
},
// Return the date in an unambiguous format, yyyy-mm-dd (as opposed to a
// format like mm/dd/yyyy)
getValue: function(el) {
var $inputs = $(el).find('input');
var start = $inputs.eq(0).bsDatepicker('getUTCDate');
var end = $inputs.eq(1).bsDatepicker('getUTCDate');
return [formatDateUTC(start), formatDateUTC(end)];
},
// 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;
}
// Get the start and end input objects
var $inputs = $(el).find('input');
// If value is undefined, don't try to set
// null will remove the current value
if (value.start !== undefined) {
if (value.start === null) {
$inputs.eq(0).val('').bsDatepicker('update');
} else {
var start = this._newDate(value.start);
$inputs.eq(0).bsDatepicker('setUTCDate', start);
}
}
if (value.end !== undefined) {
if (value.end === null) {
$inputs.eq(1).val('').bsDatepicker('update');
} else {
var end = this._newDate(value.end);
$inputs.eq(1).bsDatepicker('setUTCDate', end);
}
}
},
getState: function(el) {
var $el = $(el);
var $inputs = $el.find('input');
var $startinput = $inputs.eq(0);
var $endinput = $inputs.eq(1);
// For many of the properties, assume start and end have the same values
var min = $startinput.bsDatepicker('getStartDate');
var max = $startinput.bsDatepicker('getEndDate');
// Stringify min and max. If min and max aren't set, they will be
// -Infinity and Infinity; replace these with null.
min = (min === -Infinity) ? null : formatDateUTC(min);
max = (max === Infinity) ? null : formatDateUTC(max);
// startViewMode is stored as a number; convert to string
var startview = $startinput.data('datepicker').startView;
if (startview === 2) startview = 'decade';
else if (startview === 1) startview = 'year';
else if (startview === 0) startview = 'month';
return {
label: this._getLabelNode(el).text(),
value: this.getValue(el),
valueString: [ $startinput.val(), $endinput.val() ],
min: min,
max: max,
weekstart: $startinput.data('datepicker').weekStart,
format: this._formatToString($startinput.data('datepicker').format),
language: $startinput.data('datepicker').language,
startview: startview
};
},
receiveMessage: function(el, data) {
var $el = $(el);
var $inputs = $el.find('input');
var $startinput = $inputs.eq(0);
var $endinput = $inputs.eq(1);
updateLabel(data.label, this._getLabelNode(el));
if (data.hasOwnProperty('min')) {
this._setMin($startinput[0], data.min);
this._setMin($endinput[0], data.min);
}
if (data.hasOwnProperty('max')) {
this._setMax($startinput[0], data.max);
this._setMax($endinput[0], data.max);
}
// Must set value only after min and max have been set. If new value is
// outside the bounds of the previous min/max, then the result will be a
// blank input.
if (data.hasOwnProperty('value'))
this.setValue(el, data.value);
$el.trigger('change');
},
initialize: function(el) {
var $el = $(el);
var $inputs = $el.find('input');
var $startinput = $inputs.eq(0);
var $endinput = $inputs.eq(1);
var start = $startinput.data('initial-date');
var end = $endinput.data('initial-date');
// If empty/null, use local date, but as UTC
if (start === undefined || start === null)
start = this._dateAsUTC(new Date());
if (end === undefined || end === null)
end = this._dateAsUTC(new Date());
this.setValue(el, { "start": start, "end": end });
// // Set the start and end dates, from min-date and max-date. These always
// // use yyyy-mm-dd format, instead of bootstrap-datepicker's built-in
// // support for date-startdate and data-enddate, which use the current
// // date format.
this._setMin($startinput[0], $startinput.data('min-date'));
this._setMin($endinput[0], $startinput.data('min-date'));
this._setMax($startinput[0], $endinput.data('max-date'));
this._setMax($endinput[0], $endinput.data('max-date'));
},
subscribe: function(el, callback) {
$(el).on('keyup.dateRangeInputBinding input.dateRangeInputBinding', function(event) {
// Use normal debouncing policy when typing
callback(true);
});
$(el).on('changeDate.dateRangeInputBinding change.dateRangeInputBinding', function(event) {
// Send immediately when clicked
callback(false);
});
},
unsubscribe: function(el) {
$(el).off('.dateRangeInputBinding');
},
_getLabelNode: function(el) {
return $(el).find('label[for="' + $escape(el.id) + '"]');
},
});
inputBindings.register(dateRangeInputBinding, 'shiny.dateRangeInput');

View File

@@ -1,401 +0,0 @@
var FileUploader = function(shinyapp, id, files, el) {
this.shinyapp = shinyapp;
this.id = id;
this.el = el;
FileProcessor.call(this, files);
};
$.extend(FileUploader.prototype, FileProcessor.prototype);
(function() {
this.makeRequest = function(method, args, onSuccess, onFailure, blobs) {
this.shinyapp.makeRequest(method, args, onSuccess, onFailure, blobs);
};
this.onBegin = function(files, cont) {
var self = this;
// Reset progress bar
this.$setError(null);
this.$setActive(true);
this.$setVisible(true);
this.onProgress(null, 0);
this.totalBytes = 0;
this.progressBytes = 0;
$.each(files, function(i, file) {
self.totalBytes += file.size;
});
var fileInfo = $.map(files, function(file, i) {
return {
name: file.name,
size: file.size,
type: file.type
};
});
this.makeRequest(
'uploadInit', [fileInfo],
function(response) {
self.jobId = response.jobId;
self.uploadUrl = response.uploadUrl;
cont();
},
function(error) {
self.onError(error);
});
};
this.onFile = function(file, cont) {
var self = this;
this.onProgress(file, 0);
$.ajax(this.uploadUrl, {
type: 'POST',
cache: false,
xhr: function() {
var xhrVal = $.ajaxSettings.xhr();
if (xhrVal.upload) {
xhrVal.upload.onprogress = function(e) {
if (e.lengthComputable) {
self.onProgress(
file,
(self.progressBytes + e.loaded) / self.totalBytes);
}
};
}
return xhrVal;
},
data: file,
contentType: 'application/octet-stream',
processData: false,
success: function() {
self.progressBytes += file.size;
cont();
},
error: function(jqXHR, textStatus, errorThrown) {
self.onError(jqXHR.responseText || textStatus);
}
});
};
this.onComplete = function() {
var self = this;
var fileInfo = $.map(this.files, function(file, i) {
return {
name: file.name,
size: file.size,
type: file.type
};
});
// Trigger shiny:inputchanged. Unlike a normal shiny:inputchanged event,
// it's not possible to modify the information before the values get
// sent to the server.
var evt = jQuery.Event("shiny:inputchanged");
evt.name = this.id;
evt.value = fileInfo;
evt.binding = fileInputBinding;
evt.el = this.el;
evt.inputType = 'shiny.fileupload';
$(document).trigger(evt);
this.makeRequest(
'uploadEnd', [this.jobId, this.id],
function(response) {
self.$setActive(false);
self.onProgress(null, 1);
self.$bar().text('Upload complete');
// Reset the file input's value to "". This allows the same file to be
// uploaded again. https://stackoverflow.com/a/22521275
$(evt.el).val("");
},
function(error) {
self.onError(error);
});
this.$bar().text('Finishing upload');
};
this.onError = function(message) {
this.$setError(message || '');
this.$setActive(false);
};
this.onAbort = function() {
this.$setVisible(false);
};
this.onProgress = function(file, completed) {
this.$bar().width(Math.round(completed*100) + '%');
this.$bar().text(file ? file.name : '');
};
this.$container = function() {
return $('#' + $escape(this.id) + '_progress.shiny-file-input-progress');
};
this.$bar = function() {
return $('#' + $escape(this.id) + '_progress.shiny-file-input-progress .progress-bar');
};
this.$setVisible = function(visible) {
this.$container().css('visibility', visible ? 'visible' : 'hidden');
};
this.$setError = function(error) {
this.$bar().toggleClass('progress-bar-danger', (error !== null));
if (error !== null) {
this.onProgress(null, 1);
this.$bar().text(error);
}
};
this.$setActive = function(active) {
this.$container().toggleClass('active', !!active);
};
}).call(FileUploader.prototype);
// NOTE On Safari, at least version 10.1.2, *if the developer console is open*,
// setting the input's value will behave strangely because of a Safari bug. The
// uploaded file's name will appear over the placeholder value, instead of
// replacing it. The workaround is to restart Safari. When I (Alan Dipert) ran
// into this bug Winston Chang helped me diagnose the exact problem, and Winston
// then submitted a bug report to Apple.
function setFileText($el, files) {
var $fileText = $el.closest('div.input-group').find('input[type=text]');
if (files.length === 1) {
$fileText.val(files[0].name);
} else {
$fileText.val(files.length + " files");
}
}
// If previously selected files are uploading, abort that.
function abortCurrentUpload($el) {
var uploader = $el.data('currentUploader');
if (uploader) uploader.abort();
// Clear data-restore attribute if present.
$el.removeAttr('data-restore');
}
function uploadDroppedFilesIE10Plus(el, files) {
var $el = $(el);
abortCurrentUpload($el);
// Set the label in the text box
setFileText($el, files);
// Start the new upload and put the uploader in 'currentUploader'.
$el.data('currentUploader',
new FileUploader(exports.shinyapp,
fileInputBinding.getId(el),
files,
el));
}
function uploadFiles(evt) {
var $el = $(evt.target);
abortCurrentUpload($el);
var files = evt.target.files;
var id = fileInputBinding.getId(evt.target);
if (files.length === 0)
return;
// Set the label in the text box
setFileText($el, files);
// Start the new upload and put the uploader in 'currentUploader'.
$el.data('currentUploader',
new FileUploader(exports.shinyapp, id, files, evt.target));
}
// Here we maintain a list of all the current file inputs. This is necessary
// because we need to trigger events on them in order to respond to file drag
// events. For example, they should all light up when a file is dragged on to
// the page.
var $fileInputs = $();
var fileInputBinding = new InputBinding();
$.extend(fileInputBinding, {
find: function(scope) {
return $(scope).find('input[type="file"]');
},
getId: function(el) {
return InputBinding.prototype.getId.call(this, el) || el.name;
},
getValue: function(el) {
// This returns a non-undefined value only when there's a 'data-restore'
// attribute, which is set only when restoring Shiny state. If a file is
// uploaded through the browser, 'data-restore' gets cleared.
var data = $(el).attr('data-restore');
if (data) {
data = JSON.parse(data);
// Set the label in the text box
var $fileText = $(el).closest('div.input-group').find('input[type=text]');
if (data.name.length === 1) {
$fileText.val(data.name[0]);
} else {
$fileText.val(data.name.length + " files");
}
// Manually set up progress bar. A bit inelegant because it duplicates
// code from FileUploader, but duplication is less bad than alternatives.
var $progress = $(el).closest('div.form-group').find('.progress');
var $bar = $progress.find('.progress-bar');
$progress.removeClass('active');
$bar.width('100%');
$bar.css('visibility', 'visible');
return data;
} else {
return null;
}
},
setValue: function(el, value) {
// Not implemented
},
getType: function(el) {
// This will be used only when restoring a file from a saved state.
return 'shiny.file';
},
_zoneOf: function(el) {
return $(el).closest("div.input-group");
},
// This function makes it possible to attach listeners to the dragenter,
// dragleave, and drop events of a single element with children. It's not
// intuitive to do directly because outer elements fire "dragleave" events
// both when the drag leaves the element and when the drag enters a child. To
// make it easier, we maintain a count of the elements being dragged across
// and trigger 3 new types of event:
//
// 1. draghover:enter - When a drag enters el and any of its children.
// 2. draghover:leave - When the drag leaves el and all of its children.
// 3. draghover:drop - When an item is dropped on el or any of its children.
_enableDraghover: function(el) {
let $el = $(el),
childCounter = 0;
$el.on({
"dragenter.draghover": e => {
if (childCounter++ === 0) {
$el.trigger("draghover:enter", e);
}
},
"dragleave.draghover": e => {
if (--childCounter === 0) {
$el.trigger("draghover:leave", e);
}
if (childCounter < 0) {
console.error("draghover childCounter is negative somehow");
}
},
"dragover.draghover": e => {
e.preventDefault();
},
"drop.draghover": e => {
childCounter = 0;
$el.trigger("draghover:drop", e);
e.preventDefault();
}
});
return $el;
},
_disableDraghover: function(el) {
return $(el).off(".draghover");
},
_ZoneClass: {
ACTIVE: "shiny-file-input-active",
OVER: "shiny-file-input-over"
},
_enableDocumentEvents: function() {
let $doc = $("html"),
{ACTIVE, OVER} = this._ZoneClass;
this._enableDraghover($doc)
.on({
"draghover:enter.draghover": e => {
this._zoneOf($fileInputs).addClass(ACTIVE);
},
"draghover:leave.draghover": e => {
this._zoneOf($fileInputs).removeClass(ACTIVE);
},
"draghover:drop.draghover": e => {
this._zoneOf($fileInputs)
.removeClass(OVER)
.removeClass(ACTIVE);
}
});
},
_disableDocumentEvents: function() {
let $doc = $("html");
$doc.off(".draghover");
this._disableDraghover($doc);
},
_canSetFiles: function(fileList) {
var testEl = document.createElement("input");
testEl.type = "file";
try {
testEl.files = fileList;
} catch (e) {
return false;
}
return true;
},
_handleDrop: function(e, el) {
const files = e.originalEvent.dataTransfer.files,
$el = $(el);
if (files === undefined || files === null) {
// 1. The FileList object isn't supported by this browser, and
// there's nothing else we can try. (< IE 10)
console.log("Dropping files is not supported on this browser. (no FileList)");
} else if (!this._canSetFiles(files)) {
// 2. The browser doesn't support assigning a type=file input's .files
// property, but we do have a FileList to work with. (IE10+/Edge)
$el.val("");
uploadDroppedFilesIE10Plus(el, files);
} else {
// 3. The browser supports FileList and input.files assignment.
// (Chrome, Safari)
$el.val("");
el.files = e.originalEvent.dataTransfer.files;
// Recent versions of Firefox (57+, or "Quantum" and beyond) don't seem to
// automatically trigger a change event, so we trigger one manually here.
// On browsers that do trigger change, this operation appears to be
// idempotent, as el.files doesn't change between events.
$el.trigger("change");
}
},
subscribe: function(el, callback) {
$(el).on("change.fileInputBinding", uploadFiles);
// Here we try to set up the necessary events for Drag and Drop ("DnD").
if ($fileInputs.length === 0) this._enableDocumentEvents();
$fileInputs = $fileInputs.add(el);
let $zone = this._zoneOf(el),
{OVER} = this._ZoneClass;
this._enableDraghover($zone)
.on({
"draghover:enter.draghover": e => {
$zone.addClass(OVER);
},
"draghover:leave.draghover": e => {
$zone.removeClass(OVER);
// Prevent this event from bubbling to the document handler,
// which would deactivate all zones.
e.stopPropagation();
},
"draghover:drop.draghover": (e, dropEvent) => {
this._handleDrop(dropEvent, el);
}
});
},
unsubscribe: function(el) {
let $el = $(el),
$zone = this._zoneOf(el);
$zone
.removeClass(this._ZoneClass.OVER)
.removeClass(this._ZoneClass.ACTIVE);
this._disableDraghover($zone);
$el.off(".fileInputBinding");
$zone.off(".draghover");
// Remove el from list of inputs and (maybe) clean up global event handlers.
$fileInputs = $fileInputs.not(el);
if ($fileInputs.length === 0) this._disableDocumentEvents();
}
});
inputBindings.register(fileInputBinding, 'shiny.fileInputBinding');

View File

@@ -1,42 +0,0 @@
var numberInputBinding = {};
$.extend(numberInputBinding, textInputBinding, {
find: function(scope) {
return $(scope).find('input[type="number"]');
},
getValue: function(el) {
var numberVal = $(el).val();
if (/^\s*$/.test(numberVal)) // Return null if all whitespace
return null;
else if (!isNaN(numberVal)) // If valid Javascript number string, coerce to number
return +numberVal;
else
return numberVal; // If other string like "1e6", send it unchanged
},
setValue: function(el, value) {
el.value = value;
},
getType: function(el) {
return "shiny.number";
},
receiveMessage: function(el, data) {
if (data.hasOwnProperty('value')) el.value = data.value;
if (data.hasOwnProperty('min')) el.min = data.min;
if (data.hasOwnProperty('max')) el.max = data.max;
if (data.hasOwnProperty('step')) el.step = data.step;
updateLabel(data.label, this._getLabelNode(el));
$(el).trigger('change');
},
getState: function(el) {
return { label: this._getLabelNode(el).text(),
value: this.getValue(el),
min: Number(el.min),
max: Number(el.max),
step: Number(el.step) };
},
_getLabelNode: function(el) {
return $(el).parent().find('label[for="' + $escape(el.id) + '"]');
}
});
inputBindings.register(numberInputBinding, 'shiny.numberInput');

View File

@@ -1,10 +0,0 @@
var passwordInputBinding = {};
$.extend(passwordInputBinding, textInputBinding, {
find: function(scope) {
return $(scope).find('input[type="password"]');
},
getType: function(el) {
return "shiny.password";
}
});
inputBindings.register(passwordInputBinding, 'shiny.passwordInput');

View File

@@ -1,95 +0,0 @@
var radioInputBinding = new InputBinding();
$.extend(radioInputBinding, {
find: function(scope) {
return $(scope).find('.shiny-input-radiogroup');
},
getValue: function(el) {
// Select the radio objects that have name equal to the grouping div's id
var checked_items = $('input:radio[name="' + $escape(el.id) + '"]:checked');
if (checked_items.length === 0) {
// If none are checked, the input will return null (it's the default on load,
// but it wasn't emptied when calling updateRadioButtons with character(0)
return null;
}
return checked_items.val();
},
setValue: function(el, value) {
if ($.isArray(value) && value.length === 0) {
// Removing all checked item if the sent data is empty
$('input:radio[name="' + $escape(el.id) + '"]').prop('checked', false);
} else {
$('input:radio[name="' + $escape(el.id) + '"][value="' + $escape(value) + '"]').prop('checked', true);
}
},
getState: function(el) {
var $objs = $('input:radio[name="' + $escape(el.id) + '"]');
// Store options in an array of objects, each with with value and label
var options = new Array($objs.length);
for (var i = 0; i < options.length; i++) {
options[i] = { value: $objs[i].value,
label: this._getLabel($objs[i]) };
}
return {
label: this._getLabelNode(el).text(),
value: this.getValue(el),
options: options
};
},
receiveMessage: function(el, data) {
var $el = $(el);
// This will replace all the options
if (data.hasOwnProperty('options')) {
// Clear existing options and add each new one
$el.find('div.shiny-options-group').remove();
// Backward compatibility: for HTML generated by shinybootstrap2 package
$el.find('label.radio').remove();
$el.append(data.options);
}
if (data.hasOwnProperty('value'))
this.setValue(el, data.value);
updateLabel(data.label, this._getLabelNode(el));
$(el).trigger('change');
},
subscribe: function(el, callback) {
$(el).on('change.radioInputBinding', function(event) {
callback();
});
},
unsubscribe: function(el) {
$(el).off('.radioInputBinding');
},
// Get the DOM element that contains the top-level label
_getLabelNode: function(el) {
return $(el).parent().find('label[for="' + $escape(el.id) + '"]');
},
// Given an input DOM object, get the associated label. Handles labels
// that wrap the input as well as labels associated with 'for' attribute.
_getLabel: function(obj) {
// If <label><input /><span>label text</span></label>
if (obj.parentNode.tagName === "LABEL") {
return $(obj.parentNode).find('span').text().trim();
}
return null;
},
// Given an input DOM object, set the associated label. Handles labels
// that wrap the input as well as labels associated with 'for' attribute.
_setLabel: function(obj, value) {
// If <label><input /><span>label text</span></label>
if (obj.parentNode.tagName === "LABEL") {
$(obj.parentNode).find('span').text(value);
}
return null;
}
});
inputBindings.register(radioInputBinding, 'shiny.radioInput');

View File

@@ -1,206 +0,0 @@
var selectInputBinding = new InputBinding();
$.extend(selectInputBinding, {
find: function(scope) {
return $(scope).find('select');
},
getType: function(el) {
var $el = $(el);
if (!$el.hasClass("symbol")) {
// default character type
return null;
}
if ($el.attr("multiple") === "multiple") {
return 'shiny.symbolList';
} else {
return 'shiny.symbol';
}
},
getId: function(el) {
return InputBinding.prototype.getId.call(this, el) || el.name;
},
getValue: function(el) {
return $(el).val();
},
setValue: function(el, value) {
if (!this._is_selectize(el)) {
$(el).val(value);
} else {
let selectize = this._selectize(el);
if (selectize) {
selectize.setValue(value);
}
}
},
getState: function(el) {
// Store options in an array of objects, each with with value and label
var options = new Array(el.length);
for (var i = 0; i < el.length; i++) {
options[i] = { value: el[i].value,
label: el[i].label };
}
return {
label: this._getLabelNode(el),
value: this.getValue(el),
options: options
};
},
receiveMessage: function(el, data) {
var $el = $(el), selectize;
// This will replace all the options
if (data.hasOwnProperty('options')) {
selectize = this._selectize(el);
// Must destroy selectize before appending new options, otherwise
// selectize will restore the original select
if (selectize) selectize.destroy();
// Clear existing options and add each new one
$el.empty().append(data.options);
this._selectize(el);
}
// re-initialize selectize
if (data.hasOwnProperty('config')) {
$el.parent()
.find('script[data-for="' + $escape(el.id) + '"]')
.replaceWith(data.config);
this._selectize(el, true);
}
// use server-side processing for selectize
if (data.hasOwnProperty('url')) {
selectize = this._selectize(el);
selectize.clearOptions();
var loaded = false;
selectize.settings.load = function(query, callback) {
var settings = selectize.settings;
$.ajax({
url: data.url,
data: {
query: query,
field: JSON.stringify([settings.searchField]),
value: settings.valueField,
conju: settings.searchConjunction,
maxop: settings.maxOptions
},
type: 'GET',
error: function() {
callback();
},
success: function(res) {
// res = [{label: '1', value: '1', group: '1'}, ...]
// success is called after options are added, but
// groups need to be added manually below
$.each(res, function(index, elem) {
// Call selectize.addOptionGroup once for each optgroup; the
// first argument is the group ID, the second is an object with
// the group's label and value. We use the current settings of
// the selectize object to decide the fieldnames of that obj.
let optgroupId = elem[settings.optgroupField || "optgroup"];
let optgroup = {};
optgroup[settings.optgroupLabelField || "label"] = optgroupId;
optgroup[settings.optgroupValueField || "value"] = optgroupId;
selectize.addOptionGroup(optgroupId, optgroup);
});
callback(res);
if (!loaded) {
if (data.hasOwnProperty('value')) {
selectize.setValue(data.value);
} else if (settings.maxItems === 1) {
// first item selected by default only for single-select
selectize.setValue(res[0].value);
}
}
loaded = true;
}
});
};
// perform an empty search after changing the `load` function
selectize.load(function(callback) {
selectize.settings.load.apply(selectize, ['', callback]);
});
} else if (data.hasOwnProperty('value')) {
this.setValue(el, data.value);
}
updateLabel(data.label, this._getLabelNode(el));
$(el).trigger('change');
},
subscribe: function(el, callback) {
$(el).on('change.selectInputBinding', event => {
// https://github.com/rstudio/shiny/issues/2162
// Prevent spurious events that are gonna be squelched in
// a second anyway by the onItemRemove down below
if (el.nonempty && this.getValue(el) === "") {
return;
}
callback();
});
},
unsubscribe: function(el) {
$(el).off('.selectInputBinding');
},
initialize: function(el) {
this._selectize(el);
},
_getLabelNode: function(el) {
let escaped_id = $escape(el.id);
if (this._is_selectize(el)) {
escaped_id += "-selectized";
}
return $(el).parent().parent().find('label[for="' + escaped_id + '"]');
},
// Return true if it's a selectize input, false if it's a regular select input.
_is_selectize: function(el) {
var config = $(el).parent().find('script[data-for="' + $escape(el.id) + '"]');
return (config.length > 0);
},
_selectize: function(el, update) {
if (!$.fn.selectize) return undefined;
var $el = $(el);
var config = $el.parent().find('script[data-for="' + $escape(el.id) + '"]');
if (config.length === 0) return undefined;
var options = $.extend({
labelField: 'label',
valueField: 'value',
searchField: ['label']
}, JSON.parse(config.html()));
// selectize created from selectInput()
if (typeof(config.data('nonempty')) !== 'undefined') {
el.nonempty = true;
options = $.extend(options, {
onItemRemove: function(value) {
if (this.getValue() === "")
$("select#" + $escape(el.id)).empty().append($("<option/>", {
"value": value,
"selected": true
})).trigger("change");
},
onDropdownClose: function($dropdown) {
if (this.getValue() === "")
this.setValue($("select#" + $escape(el.id)).val());
}
});
} else {
el.nonempty = false;
}
// options that should be eval()ed
if (config.data('eval') instanceof Array)
$.each(config.data('eval'), function(i, x) {
/*jshint evil: true*/
options[x] = eval('(' + options[x] + ')');
});
var control = $el.selectize(options)[0].selectize;
// .selectize() does not really update settings; must destroy and rebuild
if (update) {
var settings = $.extend(control.settings, options);
control.destroy();
control = $el.selectize(settings)[0].selectize;
}
return control;
}
});
inputBindings.register(selectInputBinding, 'shiny.selectInput');

View File

@@ -1,311 +0,0 @@
// Necessary to get hidden sliders to send their updated values
function forceIonSliderUpdate(slider) {
if (slider.$cache && slider.$cache.input)
slider.$cache.input.trigger('change');
else
console.log("Couldn't force ion slider to update");
}
function getTypePrettifyer(dataType, timeFormat, timezone) {
var timeFormatter;
var prettify;
if (dataType === 'date') {
timeFormatter = strftime.utc();
prettify = function(num) {
return timeFormatter(timeFormat, new Date(num));
};
} else if (dataType === 'datetime') {
if (timezone)
timeFormatter = strftime.timezone(timezone);
else
timeFormatter = strftime;
prettify = function(num) {
return timeFormatter(timeFormat, new Date(num));
};
} else {
// The default prettify function for ion.rangeSlider adds thousands
// separators after the decimal mark, so we have our own version here.
// (#1958)
prettify = function(num) {
// When executed, `this` will refer to the `IonRangeSlider.options`
// object.
return formatNumber(num, this.prettify_separator);
};
}
return prettify;
}
var sliderInputBinding = {};
$.extend(sliderInputBinding, textInputBinding, {
find: function(scope) {
// Check if ionRangeSlider plugin is loaded
if (!$.fn.ionRangeSlider)
return [];
return $(scope).find('input.js-range-slider');
},
getType: function(el) {
var dataType = $(el).data('data-type');
if (dataType === 'date')
return 'shiny.date';
else if (dataType === 'datetime')
return 'shiny.datetime';
else
return false;
},
getValue: function(el) {
var $el = $(el);
var result = $(el).data('ionRangeSlider').result;
// Function for converting numeric value from slider to appropriate type.
var convert;
var dataType = $el.data('data-type');
if (dataType === 'date') {
convert = function(val) {
return formatDateUTC(new Date(+val));
};
} else if (dataType === 'datetime') {
convert = function(val) {
// Convert ms to s
return +val / 1000;
};
} else {
convert = function(val) { return +val; };
}
if (this._numValues(el) === 2) {
return [convert(result.from), convert(result.to)];
}
else {
return convert(result.from);
}
},
setValue: function(el, value) {
var $el = $(el);
var slider = $el.data('ionRangeSlider');
$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);
}
},
subscribe: function(el, callback) {
$(el).on('change.sliderInputBinding', function(event) {
callback(!$(el).data('immediate') && !$(el).data('animating'));
});
},
unsubscribe: function(el) {
$(el).off('.sliderInputBinding');
},
receiveMessage: function(el, data) {
var $el = $(el);
var slider = $el.data('ionRangeSlider');
var msg = {};
if (data.hasOwnProperty('value')) {
if (this._numValues(el) === 2 && data.value instanceof Array) {
msg.from = data.value[0];
msg.to = data.value[1];
} else {
msg.from = data.value;
}
}
var sliderFeatures = ['min', 'max', 'step'];
for (var i = 0; i < sliderFeatures.length; i++) {
var feats = sliderFeatures[i];
if (data.hasOwnProperty(feats)) {
msg[feats] = data[feats];
}
}
updateLabel(data.label, this._getLabelNode(el));
var domElements = ['data-type', 'time-format', 'timezone'];
for (var i = 0; i < domElements.length; i++) {
var elem = domElements[i];
if (data.hasOwnProperty(elem)) {
$el.data(elem, data[elem]);
}
}
var dataType = $el.data('data-type');
var timeFormat = $el.data('time-format');
var timezone = $el.data('timezone');
msg.prettify = getTypePrettifyer(dataType, timeFormat, timezone);
$el.data('immediate', true);
try {
slider.update(msg);
forceIonSliderUpdate(slider);
} finally {
$el.data('immediate', false);
}
},
getRatePolicy: function() {
return {
policy: 'debounce',
delay: 250
};
},
getState: function(el) {
},
initialize: function(el) {
var opts = {};
var $el = $(el);
var dataType = $el.data('data-type');
var timeFormat = $el.data('time-format');
var timezone = $el.data('timezone');
opts.prettify = getTypePrettifyer(dataType, timeFormat, timezone);
$el.ionRangeSlider(opts);
},
_getLabelNode: function(el) {
return $(el).parent().find('label[for="' + $escape(el.id) + '"]');
},
// Number of values; 1 for single slider, 2 for range slider
_numValues: function(el) {
if ($(el).data('ionRangeSlider').options.type === 'double')
return 2;
else
return 1;
}
});
inputBindings.register(sliderInputBinding, 'shiny.sliderInput');
// Format numbers for nicer output.
// formatNumber(1234567.12345) === "1,234,567.12345"
// formatNumber(1234567.12345, ".", ",") === "1.234.567,12345"
// formatNumber(1000, " ") === "1 000"
// formatNumber(20) === "20"
// formatNumber(1.2345e24) === "1.2345e+24"
function formatNumber(num, thousand_sep = ",", decimal_sep = ".") {
let parts = num.toString().split(".");
// Add separators to portion before decimal mark.
parts[0] = parts[0].replace(/(\d{1,3}(?=(?:\d\d\d)+(?!\d)))/g, "$1" + thousand_sep);
if (parts.length === 1)
return parts[0];
else if (parts.length === 2)
return parts[0] + decimal_sep + parts[1];
else
return "";
};
$(document).on('click', '.slider-animate-button', function(evt) {
evt.preventDefault();
var self = $(this);
var target = $('#' + $escape(self.attr('data-target-id')));
var startLabel = 'Play';
var stopLabel = 'Pause';
var loop = self.attr('data-loop') !== undefined &&
!/^\s*false\s*$/i.test(self.attr('data-loop'));
var animInterval = self.attr('data-interval');
if (isNaN(animInterval))
animInterval = 1500;
else
animInterval = +animInterval;
if (!target.data('animTimer')) {
var slider;
var timer;
// Separate code paths:
// Backward compatible code for old-style jsliders (Shiny <= 0.10.2.2),
// and new-style ionsliders.
if (target.hasClass('jslider')) {
slider = target.slider();
// If we're currently at the end, restart
if (!slider.canStepNext())
slider.resetToStart();
timer = setInterval(function() {
if (loop && !slider.canStepNext()) {
slider.resetToStart();
}
else {
slider.stepNext();
if (!loop && !slider.canStepNext()) {
self.click(); // stop the animation
}
}
}, animInterval);
} else {
slider = target.data('ionRangeSlider');
// Single sliders have slider.options.type == "single", and only the
// `from` value is used. Double sliders have type == "double", and also
// use the `to` value for the right handle.
var sliderCanStep = function() {
if (slider.options.type === "double")
return slider.result.to < slider.result.max;
else
return slider.result.from < slider.result.max;
};
var sliderReset = function() {
var val = { from: slider.result.min };
// Preserve the current spacing for double sliders
if (slider.options.type === "double")
val.to = val.from + (slider.result.to - slider.result.from);
slider.update(val);
forceIonSliderUpdate(slider);
};
var sliderStep = function() {
// Don't overshoot the end
var val = {
from: Math.min(slider.result.max, slider.result.from + slider.options.step)
};
if (slider.options.type === "double")
val.to = Math.min(slider.result.max, slider.result.to + slider.options.step);
slider.update(val);
forceIonSliderUpdate(slider);
};
// If we're currently at the end, restart
if (!sliderCanStep())
sliderReset();
timer = setInterval(function() {
if (loop && !sliderCanStep()) {
sliderReset();
}
else {
sliderStep();
if (!loop && !sliderCanStep()) {
self.click(); // stop the animation
}
}
}, animInterval);
}
target.data('animTimer', timer);
self.attr('title', stopLabel);
self.addClass('playing');
target.data('animating', true);
}
else {
clearTimeout(target.data('animTimer'));
target.removeData('animTimer');
self.attr('title', startLabel);
self.removeClass('playing');
target.removeData('animating');
}
});

View File

@@ -1,53 +0,0 @@
var bootstrapTabInputBinding = new InputBinding();
$.extend(bootstrapTabInputBinding, {
find: function(scope) {
return $(scope).find('ul.nav.shiny-tab-input');
},
getValue: function(el) {
var anchor = $(el).find('li:not(.dropdown).active').children('a');
if (anchor.length === 1)
return this._getTabName(anchor);
return null;
},
setValue: function(el, value) {
let self = this;
let success = false;
if (value) {
let anchors = $(el).find('li:not(.dropdown)').children('a');
anchors.each(function() {
if (self._getTabName($(this)) === value) {
$(this).tab('show');
success = true;
return false; // Break out of each()
}
return true;
});
}
if (!success) {
// This is to handle the case where nothing is selected, e.g. the last tab
// was removed using removeTab.
$(el).trigger("change");
}
},
getState: function(el) {
return { value: this.getValue(el) };
},
receiveMessage: function(el, data) {
if (data.hasOwnProperty('value'))
this.setValue(el, data.value);
$(el).trigger("change");
},
subscribe: function(el, callback) {
$(el).on('change shown.bootstrapTabInputBinding shown.bs.tab.bootstrapTabInputBinding', function(event) {
callback();
});
},
unsubscribe: function(el) {
$(el).off('.bootstrapTabInputBinding');
},
_getTabName: function(anchor) {
return anchor.attr('data-value') || anchor.text();
}
});
inputBindings.register(bootstrapTabInputBinding, 'shiny.bootstrapTabInput');

View File

@@ -1,59 +0,0 @@
var textInputBinding = new InputBinding();
$.extend(textInputBinding, {
find: function(scope) {
var $inputs = $(scope).find('input[type="text"], input[type="search"], input[type="url"], input[type="email"]');
// selectize.js 0.12.4 inserts a hidden text input with an
// id that ends in '-selectized'. The .not() selector below
// is to prevent textInputBinding from accidentally picking up
// this hidden element as a shiny input (#2396)
return $inputs.not('input[type="text"][id$="-selectized"]');
},
getId: function(el) {
return InputBinding.prototype.getId.call(this, el) || el.name;
},
getValue: function(el) {
return el.value;
},
setValue: function(el, value) {
el.value = value;
},
subscribe: function(el, callback) {
$(el).on('keyup.textInputBinding input.textInputBinding', function(event) {
callback(true);
});
$(el).on('change.textInputBinding', function(event) {
callback(false);
});
},
unsubscribe: function(el) {
$(el).off('.textInputBinding');
},
receiveMessage: function(el, data) {
if (data.hasOwnProperty('value'))
this.setValue(el, data.value);
updateLabel(data.label, this._getLabelNode(el));
if (data.hasOwnProperty('placeholder'))
el.placeholder = data.placeholder;
$(el).trigger('change');
},
getState: function(el) {
return {
label: this._getLabelNode(el).text(),
value: el.value,
placeholder: el.placeholder
};
},
getRatePolicy: function() {
return {
policy: 'debounce',
delay: 250
};
},
_getLabelNode: function(el) {
return $(el).parent().find('label[for="' + $escape(el.id) + '"]');
}
});
inputBindings.register(textInputBinding, 'shiny.textInput');

View File

@@ -1,7 +0,0 @@
var textareaInputBinding = {};
$.extend(textareaInputBinding, textInputBinding, {
find: function(scope) {
return $(scope).find('textarea');
}
});
inputBindings.register(textareaInputBinding, 'shiny.textareaInput');

View File

@@ -1,409 +0,0 @@
var Invoker = function(target, func) {
this.target = target;
this.func = func;
};
(function() {
this.normalCall =
this.immediateCall = function() {
this.func.apply(this.target, arguments);
};
}).call(Invoker.prototype);
var Debouncer = function(target, func, delayMs) {
this.target = target;
this.func = func;
this.delayMs = delayMs;
this.timerId = null;
this.args = null;
};
(function() {
this.normalCall = function() {
var self = this;
this.$clearTimer();
this.args = arguments;
this.timerId = setTimeout(function() {
// IE8 doesn't reliably clear timeout, so this additional
// check is needed
if (self.timerId === null)
return;
self.$clearTimer();
self.$invoke();
}, this.delayMs);
};
this.immediateCall = function() {
this.$clearTimer();
this.args = arguments;
this.$invoke();
};
this.isPending = function() {
return this.timerId !== null;
};
this.$clearTimer = function() {
if (this.timerId !== null) {
clearTimeout(this.timerId);
this.timerId = null;
}
};
this.$invoke = function() {
this.func.apply(this.target, this.args);
this.args = null;
};
}).call(Debouncer.prototype);
var Throttler = function(target, func, delayMs) {
this.target = target;
this.func = func;
this.delayMs = delayMs;
this.timerId = null;
this.args = null;
};
(function() {
this.normalCall = function() {
var self = this;
this.args = arguments;
if (this.timerId === null) {
this.$invoke();
this.timerId = setTimeout(function() {
// IE8 doesn't reliably clear timeout, so this additional
// check is needed
if (self.timerId === null)
return;
self.$clearTimer();
if (self.args)
self.normalCall.apply(self, self.args);
}, this.delayMs);
}
};
this.immediateCall = function() {
this.$clearTimer();
this.args = arguments;
this.$invoke();
};
this.isPending = function() {
return this.timerId !== null;
};
this.$clearTimer = function() {
if (this.timerId !== null) {
clearTimeout(this.timerId);
this.timerId = null;
}
};
this.$invoke = function() {
this.func.apply(this.target, this.args);
this.args = null;
};
}).call(Throttler.prototype);
// Returns a debounced version of the given function.
// Debouncing means that when the function is invoked,
// there is a delay of `threshold` milliseconds before
// it is actually executed, and if the function is
// invoked again before that threshold has elapsed then
// the clock starts over.
//
// For example, if a function is debounced with a
// threshold of 1000ms, then calling it 17 times at
// 900ms intervals will result in a single execution
// of the underlying function, 1000ms after the 17th
// call.
function debounce(threshold, func) {
var timerId = null;
var self, args;
return function() {
self = this;
args = arguments;
if (timerId !== null) {
clearTimeout(timerId);
timerId = null;
}
timerId = setTimeout(function() {
// IE8 doesn't reliably clear timeout, so this additional
// check is needed
if (timerId === null)
return;
timerId = null;
func.apply(self, args);
}, threshold);
};
}
// Returns a throttled version of the given function.
// Throttling means that the underlying function will
// be executed no more than once every `threshold`
// milliseconds.
//
// For example, if a function is throttled with a
// threshold of 1000ms, then calling it 17 times at
// 900ms intervals will result in something like 15
// or 16 executions of the underlying function.
// eslint-disable-next-line no-unused-vars
function throttle(threshold, func) {
var executionPending = false;
var timerId = null;
var self, args;
function throttled() {
self = null;
args = null;
if (timerId === null) {
// Haven't seen a call recently. Execute now and
// start a timer to buffer any subsequent calls.
timerId = setTimeout(function() {
// When time expires, clear the timer; and if
// there has been a call in the meantime, repeat.
timerId = null;
if (executionPending) {
executionPending = false;
throttled.apply(self, args);
}
}, threshold);
func.apply(this, arguments);
}
else {
// Something executed recently. Don't do anything
// except set up target/arguments to be called later
executionPending = true;
self = this;
args = arguments;
}
}
return throttled;
}
// Schedules data to be sent to shinyapp at the next setTimeout(0).
// Batches multiple input calls into one websocket message.
var InputBatchSender = function(shinyapp) {
this.shinyapp = shinyapp;
this.timerId = null;
this.pendingData = {};
this.reentrant = false;
this.lastChanceCallback = [];
};
(function() {
this.setInput = function(nameType, value, opts) {
this.pendingData[nameType] = value;
if (!this.reentrant) {
if (opts.priority === "event") {
this.$sendNow();
} else if (!this.timerId) {
this.timerId = setTimeout(this.$sendNow.bind(this), 0);
}
}
};
this.$sendNow = function() {
if (this.reentrant) {
console.trace("Unexpected reentrancy in InputBatchSender!");
}
this.reentrant = true;
try {
this.timerId = null;
$.each(this.lastChanceCallback, (i, callback) => {
callback();
});
var currentData = this.pendingData;
this.pendingData = {};
this.shinyapp.sendInput(currentData);
} finally {
this.reentrant = false;
}
};
}).call(InputBatchSender.prototype);
var InputNoResendDecorator = function(target, initialValues) {
this.target = target;
this.lastSentValues = this.reset(initialValues);
};
(function() {
this.setInput = function(nameType, value, opts) {
const { name: inputName, inputType: inputType } = splitInputNameType(nameType);
const jsonValue = JSON.stringify(value);
if (opts.priority !== "event" &&
this.lastSentValues[inputName] &&
this.lastSentValues[inputName].jsonValue === jsonValue &&
this.lastSentValues[inputName].inputType === inputType) {
return;
}
this.lastSentValues[inputName] = { jsonValue, inputType };
this.target.setInput(nameType, value, opts);
};
this.reset = function(values = {}) {
// Given an object with flat name-value format:
// { x: "abc", "y.shiny.number": 123 }
// Create an object in cache format and save it:
// { x: { jsonValue: '"abc"', inputType: "" },
// y: { jsonValue: "123", inputType: "shiny.number" } }
const cacheValues = {};
for (let inputName in values) {
if (values.hasOwnProperty(inputName)) {
let { name, inputType } = splitInputNameType(inputName);
cacheValues[name] = {
jsonValue: JSON.stringify(values[inputName]),
inputType: inputType
};
}
}
this.lastSentValues = cacheValues;
};
this.forget = function(name) {
delete this.lastSentValues[name];
};
}).call(InputNoResendDecorator.prototype);
var InputEventDecorator = function(target) {
this.target = target;
};
(function() {
this.setInput = function(nameType, value, opts) {
var evt = jQuery.Event("shiny:inputchanged");
const input = splitInputNameType(nameType);
evt.name = input.name;
evt.inputType = input.inputType;
evt.value = value;
evt.binding = opts.binding;
evt.el = opts.el;
evt.priority = opts.priority;
$(opts.el).trigger(evt);
if (!evt.isDefaultPrevented()) {
let name = evt.name;
if (evt.inputType !== '') name += ':' + evt.inputType;
// Most opts aren't passed along to lower levels in the input decorator
// stack.
this.target.setInput(name, evt.value, { priority: opts.priority });
}
};
}).call(InputEventDecorator.prototype);
var InputRateDecorator = function(target) {
this.target = target;
this.inputRatePolicies = {};
};
(function() {
// Note that the first argument of setInput() and setRatePolicy()
// are passed both the input name (i.e., inputId) and type.
// https://github.com/rstudio/shiny/blob/67d3a/srcjs/init_shiny.js#L111-L126
// However, $ensureInit() and $doSetInput() are meant to be passed just
// the input name (i.e., inputId), which is why we distinguish between
// nameType and name.
this.setInput = function(nameType, value, opts) {
const {name: inputName} = splitInputNameType(nameType);
this.$ensureInit(inputName);
if (opts.priority !== "deferred")
this.inputRatePolicies[inputName].immediateCall(nameType, value, opts);
else
this.inputRatePolicies[inputName].normalCall(nameType, value, opts);
};
this.setRatePolicy = function(nameType, mode, millis) {
const {name: inputName} = splitInputNameType(nameType);
if (mode === 'direct') {
this.inputRatePolicies[inputName] = new Invoker(this, this.$doSetInput);
}
else if (mode === 'debounce') {
this.inputRatePolicies[inputName] = new Debouncer(this, this.$doSetInput, millis);
}
else if (mode === 'throttle') {
this.inputRatePolicies[inputName] = new Throttler(this, this.$doSetInput, millis);
}
};
this.$ensureInit = function(name) {
if (!(name in this.inputRatePolicies))
this.setRatePolicy(name, 'direct');
};
this.$doSetInput = function(nameType, value, opts) {
this.target.setInput(nameType, value, opts);
};
}).call(InputRateDecorator.prototype);
var InputDeferDecorator = function(target) {
this.target = target;
this.pendingInput = {};
};
(function() {
this.setInput = function(nameType, value, opts) {
if (/^\./.test(nameType))
this.target.setInput(nameType, value, opts);
else
this.pendingInput[nameType] = { value, opts };
};
this.submit = function() {
for (var nameType in this.pendingInput) {
if (this.pendingInput.hasOwnProperty(nameType)) {
let { value, opts } = this.pendingInput[nameType];
this.target.setInput(nameType, value, opts);
}
}
};
}).call(InputDeferDecorator.prototype);
const InputValidateDecorator = function(target) {
this.target = target;
};
(function() {
this.setInput = function(nameType, value, opts) {
if (!nameType)
throw "Can't set input with empty name.";
opts = addDefaultInputOpts(opts);
this.target.setInput(nameType, value, opts);
};
}).call(InputValidateDecorator.prototype);
// Merge opts with defaults, and return a new object.
function addDefaultInputOpts(opts) {
opts = $.extend({
priority: "immediate",
binding: null,
el: null
}, opts);
if (opts && typeof(opts.priority) !== "undefined") {
switch (opts.priority) {
case "deferred":
case "immediate":
case "event":
break;
default:
throw new Error("Unexpected input value mode: '" + opts.priority + "'");
}
}
return opts;
}
function splitInputNameType(nameType) {
const name2 = nameType.split(':');
return {
name: name2[0],
inputType: name2.length > 1 ? name2[1] : ''
};
}

View File

@@ -1,66 +0,0 @@
exports.modal = {
// Show a modal dialog. This is meant to handle two types of cases: one is
// that the content is a Bootstrap modal dialog, and the other is that the
// content is non-Bootstrap. Bootstrap modals require some special handling,
// which is coded in here.
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
// removed; otherwise there can be multiple of these divs.
$('.modal-backdrop').remove();
// Get existing wrapper DOM element, or create if needed.
let $modal = $('#shiny-modal-wrapper');
if ($modal.length === 0) {
$modal = $('<div id="shiny-modal-wrapper"></div>');
$(document.body).append($modal);
// If the wrapper's content is a Bootstrap modal, then when the inner
// modal is hidden, remove the entire thing, including wrapper.
$modal.on('hidden.bs.modal', function(e) {
if (e.target === $("#shiny-modal")[0]) {
exports.unbindAll($modal);
$modal.remove();
}
});
}
$modal.on('keydown.shinymodal', function(e) {
// If we're listening for Esc, don't let the event propagate. See
// https://github.com/rstudio/shiny/issues/1453. The value of
// data("keyboard") needs to be checked inside the handler, because at
// the time that $modal.on() is called, the $("#shiny-modal") div doesn't
// yet exist.
if ($("#shiny-modal").data("keyboard") === false)
return;
if (e.keyCode === 27) {
e.stopPropagation();
e.preventDefault();
}
});
// Set/replace contents of wrapper with html.
exports.renderContent($modal, { html: html, deps: deps });
},
remove: function() {
const $modal = $('#shiny-modal-wrapper');
$modal.off('keydown.shinymodal');
// Look for a Bootstrap modal and if present, trigger hide event. This will
// trigger the hidden.bs.modal callback that we set in show(), which unbinds
// and removes the element.
if ($modal.find('.modal').length > 0) {
$modal.find('.modal').modal('hide');
} else {
// If not a Bootstrap modal dialog, simply unbind and remove it.
exports.unbindAll($modal);
$modal.remove();
}
}
};

View File

@@ -1,154 +0,0 @@
exports.notifications = (function() {
// Milliseconds to fade in or out
const fadeDuration = 250;
function show({ html='', action='', deps=[], duration=5000,
id=null, closeButton=true, type=null } = {})
{
if (!id)
id = randomId();
// Create panel if necessary
_createPanel();
// Get existing DOM element for this ID, or create if needed.
let $notification = _get(id);
if ($notification.length === 0)
$notification = _create(id);
// Render html and dependencies
const newHtml = `<div class="shiny-notification-content-text">${html}</div>` +
`<div class="shiny-notification-content-action">${action}</div>`;
const $content = $notification.find('.shiny-notification-content');
exports.renderContent($content, { html: newHtml, deps: deps });
// Remove any existing classes of the form 'shiny-notification-xxxx'.
// The xxxx would be strings like 'warning'.
const classes = $notification.attr('class')
.split(/\s+/)
.filter(cls => cls.match(/^shiny-notification-/))
.join(' ');
$notification.removeClass(classes);
// Add class. 'default' means no additional CSS class.
if (type && type !== 'default')
$notification.addClass('shiny-notification-' + type);
// Make sure that the presence/absence of close button matches with value
// of `closeButton`.
const $close = $notification.find('.shiny-notification-close');
if (closeButton && $close.length === 0) {
$notification.append('<div class="shiny-notification-close">&times;</div>');
} else if (!closeButton && $close.length !== 0) {
$close.remove();
}
// If duration was provided, schedule removal. If not, clear existing
// removal callback (this happens if a message was first added with
// a duration, and then updated with no duration).
if (duration)
_addRemovalCallback(id, duration);
else
_clearRemovalCallback(id);
return id;
}
function remove(id) {
_get(id).fadeOut(fadeDuration, function() {
exports.unbindAll(this);
$(this).remove();
// If no more notifications, remove the panel from the DOM.
if (_ids().length === 0) {
_getPanel().remove();
}
});
}
// Returns an individual notification DOM object (wrapped in jQuery).
function _get(id) {
if (!id)
return null;
return _getPanel().find('#shiny-notification-' + $escape(id));
}
// Return array of all notification IDs
function _ids() {
return _getPanel()
.find('.shiny-notification')
.map(function() { return this.id.replace(/shiny-notification-/, ''); })
.get();
}
// Returns the notification panel DOM object (wrapped in jQuery).
function _getPanel() {
return $('#shiny-notification-panel');
}
// Create notifications panel and return the jQuery object. If the DOM
// element already exists, just return it.
function _createPanel() {
let $panel = _getPanel();
if ($panel.length > 0)
return $panel;
$(document.body).append('<div id="shiny-notification-panel">');
return $panel;
}
// Create a notification DOM element and return the jQuery object. If the
// DOM element already exists for the ID, just return it without creating.
function _create(id) {
let $notification = _get(id);
if ($notification.length === 0) {
$notification = $(
`<div id="shiny-notification-${id}" class="shiny-notification">` +
'<div class="shiny-notification-close">&times;</div>' +
'<div class="shiny-notification-content"></div>' +
'</div>'
);
$notification.find('.shiny-notification-close').on('click', e => {
e.preventDefault();
e.stopPropagation();
remove(id);
});
_getPanel().append($notification);
}
return $notification;
}
// Add a callback to remove a notification after a delay in ms.
function _addRemovalCallback(id, delay) {
// If there's an existing removalCallback, clear it before adding the new
// one.
_clearRemovalCallback(id);
// Attach new removal callback
const removalCallback = setTimeout(function() { remove(id); }, delay);
_get(id).data('removalCallback', removalCallback);
}
// Clear a removal callback from a notification, if present.
function _clearRemovalCallback(id) {
const $notification = _get(id);
const oldRemovalCallback = $notification.data('removalCallback');
if (oldRemovalCallback) {
clearTimeout(oldRemovalCallback);
}
}
return {
show,
remove
};
})();

View File

@@ -1,46 +0,0 @@
var OutputBinding = exports.OutputBinding = function() {};
(function() {
// Returns a jQuery object or element array that contains the
// descendants of scope that match this binding
this.find = function(scope) { throw "Not implemented"; };
this.getId = function(el) {
return el['data-input-id'] || el.id;
};
this.onValueChange = function(el, data) {
this.clearError(el);
this.renderValue(el, data);
};
this.onValueError = function(el, err) {
this.renderError(el, err);
};
this.renderError = function(el, err) {
this.clearError(el);
if (err.message === '') {
// not really error, but we just need to wait (e.g. action buttons)
$(el).empty();
return;
}
var errClass = 'shiny-output-error';
if (err.type !== null) {
// use the classes of the error condition as CSS class names
errClass = errClass + ' ' + $.map(asArray(err.type), function(type) {
return errClass + '-' + type;
}).join(' ');
}
$(el).addClass(errClass).text(err.message);
};
this.clearError = function(el) {
$(el).attr('class', function(i, c) {
return c.replace(/(^|\s)shiny-output-error\S*/g, '');
});
};
this.showProgress = function(el, show) {
var RECALC_CLASS = 'recalculating';
if (show)
$(el).addClass(RECALC_CLASS);
else
$(el).removeClass(RECALC_CLASS);
};
}).call(OutputBinding.prototype);

View File

@@ -1,29 +0,0 @@
var OutputBindingAdapter = function(el, binding) {
this.el = el;
this.binding = binding;
// If the binding actually has a resize method, override the prototype of
// onResize with a version that does a makeResizeFilter on the element.
if (binding.resize) {
this.onResize = makeResizeFilter(el, function(width, height) {
binding.resize(el, width, height);
});
}
};
(function() {
this.getId = function() {
return this.binding.getId(this.el);
};
this.onValueChange = function(data) {
this.binding.onValueChange(this.el, data);
};
this.onValueError = function(err) {
this.binding.onValueError(this.el, err);
};
this.showProgress = function(show) {
this.binding.showProgress(this.el, show);
};
this.onResize = function() {
// Intentionally left blank; see constructor
};
}).call(OutputBindingAdapter.prototype);

View File

@@ -1,86 +0,0 @@
var datatableOutputBinding = new OutputBinding();
$.extend(datatableOutputBinding, {
find: function(scope) {
return $(scope).find('.shiny-datatable-output');
},
onValueError: function(el, err) {
exports.unbindAll(el);
this.renderError(el, err);
},
renderValue: function(el, data) {
var $el = $(el).empty();
if (!data || !data.colnames) return;
var colnames = $.makeArray(data.colnames);
var header = $.map(colnames, function(x) {
return '<th>' + x + '</th>';
}).join('');
header = '<thead><tr>' + header + '</tr></thead>';
var footer = '';
if (data.options === null || data.options.searching !== false) {
footer = $.map(colnames, function(x) {
// placeholder needs to be escaped (and HTML tags are stripped off)
return '<th><input type="text" placeholder="' +
escapeHTML(x.replace(/(<([^>]+)>)/ig, '')) +
'" /></th>';
}).join('');
footer = '<tfoot>' + footer + '</tfoot>';
}
var content = '<table class="table table-striped table-hover">' +
header + footer + '</table>';
$el.append(content);
// options that should be eval()ed
if (data.evalOptions)
$.each(data.evalOptions, function(i, x) {
/*jshint evil: true */
data.options[x] = eval('(' + data.options[x] + ')');
});
// caseInsensitive searching? default true
var searchCI = data.options === null || typeof(data.options.search) === 'undefined' ||
data.options.search.caseInsensitive !== false;
var oTable = $(el).children("table").DataTable($.extend({
"processing": true,
"serverSide": true,
"order": [],
"orderClasses": false,
"pageLength": 25,
"ajax": {
"url": data.action,
"type": "POST",
"data": function(d) {
d.search.caseInsensitive = searchCI;
d.escape = data.escape;
}
}
}, data.options));
// the table object may need post-processing
if (typeof data.callback === 'string') {
/*jshint evil: true */
var callback = eval('(' + data.callback + ')');
if (typeof callback === 'function') callback(oTable);
}
// use debouncing for searching boxes
$el.find('label input').first().unbind('keyup')
.keyup(debounce(data.searchDelay, function() {
oTable.search(this.value).draw();
}));
var searchInputs = $el.find("tfoot input");
if (searchInputs.length > 0) {
// this is a little weird: aoColumns/bSearchable are still in DT 1.10
// https://github.com/DataTables/DataTables/issues/388
$.each(oTable.settings()[0].aoColumns, function(i, x) {
// hide the text box if not searchable
if (!x.bSearchable) searchInputs.eq(i).hide();
});
searchInputs.keyup(debounce(data.searchDelay, function() {
oTable.column(searchInputs.index(this)).search(this.value).draw();
}));
}
// FIXME: ugly scrollbars in tab panels b/c Bootstrap uses 'visible: auto'
$el.parents('.tab-content').css('overflow', 'visible');
}
});
outputBindings.register(datatableOutputBinding, 'shiny.datatableOutput');

View File

@@ -1,19 +0,0 @@
var downloadLinkOutputBinding = new OutputBinding();
$.extend(downloadLinkOutputBinding, {
find: function(scope) {
return $(scope).find('a.shiny-download-link');
},
renderValue: function(el, data) {
$(el).attr('href', data);
}
});
outputBindings.register(downloadLinkOutputBinding, 'shiny.downloadLink');
// Trigger shiny:filedownload event whenever a downloadButton/Link is clicked
$(document).on('click.shinyDownloadLink', 'a.shiny-download-link', function(e) {
var evt = jQuery.Event('shiny:filedownload');
evt.name = this.id;
evt.href = this.href;
$(document).trigger(evt);
});

View File

@@ -1,297 +0,0 @@
var htmlOutputBinding = new OutputBinding();
$.extend(htmlOutputBinding, {
find: function(scope) {
return $(scope).find('.shiny-html-output');
},
onValueError: function(el, err) {
exports.unbindAll(el);
this.renderError(el, err);
},
renderValue: function(el, data) {
exports.renderContent(el, data);
}
});
outputBindings.register(htmlOutputBinding, 'shiny.htmlOutput');
var renderDependencies = exports.renderDependencies = function(dependencies) {
if (dependencies) {
$.each(dependencies, function(i, dep) {
renderDependency(dep);
});
}
};
// Render HTML in a DOM element, add dependencies, and bind Shiny
// inputs/outputs. `content` can be null, a string, or an object with
// properties 'html' and 'deps'.
exports.renderContent = function(el, content, where="replace") {
if (where === "replace") {
exports.unbindAll(el);
}
var html;
var dependencies = [];
if (content === null) {
html = '';
} else if (typeof(content) === 'string') {
html = content;
} else if (typeof(content) === 'object') {
html = content.html;
dependencies = content.deps || [];
}
exports.renderHtml(html, el, dependencies, where);
var scope = el;
if (where === "replace") {
exports.initializeInputs(el);
exports.bindAll(el);
} else {
var $parent = $(el).parent();
if ($parent.length > 0) {
scope = $parent;
if (where === "beforeBegin" || where === "afterEnd") {
var $grandparent = $parent.parent();
if ($grandparent.length > 0) scope = $grandparent;
}
}
exports.initializeInputs(scope);
exports.bindAll(scope);
}
};
// Render HTML in a DOM element, inserting singletons into head as needed
exports.renderHtml = function(html, el, dependencies, where = 'replace') {
renderDependencies(dependencies);
return singletons.renderHtml(html, el, where);
};
var htmlDependencies = {};
function registerDependency(name, version) {
htmlDependencies[name] = version;
}
// Re-render stylesheet(s) if the dependency has specificially requested it
// and it matches an existing dependency (name and version)
function needsRestyle(dep) {
if (!dep.restyle) {
return false;
}
var names = Object.keys(htmlDependencies);
var idx = names.indexOf(dep.name);
if (idx === -1) {
return false;
}
return htmlDependencies[names[idx]] === dep.version;
}
// Client-side dependency resolution and rendering
function renderDependency(dep) {
var restyle = needsRestyle(dep);
if (htmlDependencies.hasOwnProperty(dep.name) && !restyle)
return false;
registerDependency(dep.name, dep.version);
var href = dep.src.href;
var $head = $("head").first();
if (dep.meta && !restyle) {
var metas = $.map(asArray(dep.meta), function(obj, idx) {
// only one named pair is expected in obj as it's already been decomposed
var name = Object.keys(obj)[0];
return $("<meta>").attr("name", name).attr("content", obj[name]);
});
$head.append(metas);
}
if (dep.stylesheet) {
var links = $.map(asArray(dep.stylesheet), function(stylesheet) {
return $("<link rel='stylesheet' type='text/css'>").attr("href", href + "/" + encodeURI(stylesheet));
});
if (!restyle) {
$head.append(links);
} else {
$.map(links, function(link) {
// Find any document.styleSheets that match this link's href
// so we can remove it after bringing in the new stylesheet
var oldSheet = findSheet(link.attr("href"));
// Add a timestamp to the href to prevent caching
var href = link.attr("href") + "?restyle=" + new Date().getTime();
// Use inline <style> approach for IE, otherwise use the more elegant
// <link> -based approach
if (browser.isIE) {
refreshStyle(href, oldSheet);
} else {
link.attr("href", href);
// Once the new <link> is loaded, schedule the old <link> to be removed
// on the next tick which is needed to avoid FOUC
link.attr("onload", () => {
setTimeout(() => removeSheet(oldSheet), 500);
});
$head.append(link);
}
});
// Once the new styles are applied, CSS values that are accessible server-side
// (e.g., getCurrentOutputInfo(), output visibility, etc) may become outdated.
// At the time of writing, that means we need to do sendImageSize() &
// sendOutputHiddenState() again, which can be done by re-binding.
/* global Shiny */
var bindDebouncer = new Debouncer(null, Shiny.bindAll, 100);
setTimeout(() => bindDebouncer.normalCall(), 100);
// This inline <style> based approach works for IE11
function refreshStyle(href, oldSheet) {
var xhr = new XMLHttpRequest();
xhr.open('GET', href);
xhr.onload = function() {
var id = "shiny_restyle_" + href.split("?restyle")[0].replace(/\W/g, '_');
var oldStyle = $head.find("style#" + id);
var newStyle = $("<style>").attr("id", id).html(xhr.responseText);
$head.append(newStyle);
setTimeout(() => oldStyle.remove(), 500);
setTimeout(() => removeSheet(oldSheet), 500);
};
xhr.send();
}
function findSheet(href) {
for (var i = 0; i < document.styleSheets.length; i++) {
var sheet = document.styleSheets[i];
// The sheet's href is a full URL
if (typeof sheet.href === "string" && sheet.href.indexOf(href) > -1) {
return sheet;
}
}
return null;
}
function removeSheet(sheet) {
if (!sheet) return;
sheet.disabled = true;
if (browser.isIE) sheet.cssText = "";
$(sheet.ownerNode).remove();
}
}
}
if (dep.script && !restyle) {
var scripts = $.map(asArray(dep.script), function(scriptName) {
return $("<script>").attr("src", href + "/" + encodeURI(scriptName));
});
$head.append(scripts);
}
if (dep.attachment && !restyle) {
// dep.attachment might be a single string, an array, or an object.
var attachments = dep.attachment;
if (typeof(attachments) === "string")
attachments = [attachments];
if ($.isArray(attachments)) {
// The contract for attachments is that arrays of attachments are
// addressed using 1-based indexes. Convert this array to an object.
var tmp = {};
$.each(attachments, function(index, attachment) {
tmp[(index + 1) + ""] = attachment;
});
attachments = tmp;
}
var attach = $.map(attachments, function(attachment, key) {
return $("<link rel='attachment'>")
.attr("id", dep.name + "-" + key + "-attachment")
.attr("href", href + "/" + encodeURI(attachment));
});
$head.append(attach);
}
if (dep.head && !restyle) {
var $newHead = $("<head></head>");
$newHead.html(dep.head);
$head.append($newHead.children());
}
return true;
}
var singletons = {
knownSingletons: {},
renderHtml: function(html, el, where) {
var processed = this._processHtml(html);
this._addToHead(processed.head);
this.register(processed.singletons);
if (where === "replace") {
$(el).html(processed.html);
} else {
el.insertAdjacentHTML(where, processed.html);
}
return processed;
},
// Take an object where keys are names of singletons, and merges it into
// knownSingletons
register: function(s) {
$.extend(this.knownSingletons, s);
},
// Takes a string or array of strings and adds them to knownSingletons
registerNames: function(s) {
if (typeof s === 'string') {
this.knownSingletons[s] = true;
} else if (s instanceof Array) {
for (var i = 0; i < s.length; i++) {
this.knownSingletons[s[i]] = true;
}
}
},
// Inserts new content into document head
_addToHead: function(head) {
if (head.length > 0) {
var tempDiv = $("<div>" + head + "</div>")[0];
var $head = $('head');
while (tempDiv.hasChildNodes()) {
$head.append(tempDiv.firstChild);
}
}
},
// Reads HTML and returns an object with info about singletons
_processHtml: function(val) {
var self = this;
var newSingletons = {};
var newVal;
var findNewPayload = function(match, p1, sig, payload) {
if (self.knownSingletons[sig] || newSingletons[sig])
return "";
newSingletons[sig] = true;
return payload;
};
while (true) {
newVal = val.replace(self._reSingleton, findNewPayload);
if (val.length === newVal.length)
break;
val = newVal;
}
var heads = [];
var headAddPayload = function(match, payload) {
heads.push(payload);
return "";
};
while (true) {
newVal = val.replace(self._reHead, headAddPayload);
if (val.length === newVal.length)
break;
val = newVal;
}
return {
html: val,
head: heads.join("\n"),
singletons: newSingletons
};
},
_reSingleton: /<!--(SHINY.SINGLETON\[([\w]+)\])-->([\s\S]*?)<!--\/\1-->/,
_reHead: /<head(?:\s[^>]*)?>([\s\S]*?)<\/head>/
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +0,0 @@
var textOutputBinding = new OutputBinding();
$.extend(textOutputBinding, {
find: function(scope) {
return $(scope).find('.shiny-text-output');
},
renderValue: function(el, data) {
$(el).text(data);
}
});
outputBindings.register(textOutputBinding, 'shiny.textOutput');

View File

@@ -1,43 +0,0 @@
$(document).on('keydown', function(e) {
if (e.which !== 114 || (!e.ctrlKey && !e.metaKey) || (e.shiftKey || e.altKey))
return;
var url = 'reactlog?w=' + window.escape(exports.shinyapp.config.workerId) +
"&s=" + window.escape(exports.shinyapp.config.sessionId);
window.open(url);
e.preventDefault();
});
$(document).on('keydown', function(e) {
if (
// if not one of the key combos below
!(
// cmd/ctrl + fn + f4
(e.which === 115 && (e.ctrlKey || e.metaKey) && !e.shiftKey && !e.altKey) ||
// cmd/ctrl + shift + fn + f3
(e.which === 114 && (e.ctrlKey || e.metaKey) && e.shiftKey && !e.altKey)
)
) {
return;
}
var url = 'reactlog/mark?w=' + window.escape(exports.shinyapp.config.workerId) +
"&s=" + window.escape(exports.shinyapp.config.sessionId);
// send notification
$.get(url, function(result) {
if (result !== "marked") return;
var html = '<span id="shiny-reactlog-mark-text">Marked time point in reactlog</span>';
exports.notifications.show({
html: html,
closeButton: true,
});
}).fail(function() {
// found returned error while marking, should open webpage
window.open(url);
});
e.preventDefault();
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,362 +0,0 @@
function escapeHTML(str) {
var escaped = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
"\"": "&quot;",
"'": "&#039;",
"/": "&#x2F;",
};
return str.replace(/[&<>'"\/]/g, function (m) {
return escaped[m];
});
}
function randomId() {
return Math.floor(0x100000000 + Math.random() * 0xf00000000).toString(16);
}
function strToBool(str) {
if (!str || !str.toLowerCase) return undefined;
switch (str.toLowerCase()) {
case "true":
return true;
case "false":
return false;
default:
return undefined;
}
}
// A wrapper for getComputedStyle that is compatible with older browsers.
// This is significantly faster than jQuery's .css() function.
function getStyle(el, styleProp) {
var x;
if (el.currentStyle) x = el.currentStyle[styleProp];
else if (window.getComputedStyle) {
// getComputedStyle can return null when we're inside a hidden iframe on
// Firefox; don't attempt to retrieve style props in this case.
// https://bugzilla.mozilla.org/show_bug.cgi?id=548397
var style = document.defaultView.getComputedStyle(el, null);
if (style) x = style.getPropertyValue(styleProp);
}
return x;
}
// Convert a number to a string with leading zeros
function padZeros(n, digits) {
var str = n.toString();
while (str.length < digits) str = "0" + str;
return str;
}
// Round to a specified number of significant digits.
function roundSignif(x, digits = 1) {
if (digits < 1) throw "Significant digits must be at least 1.";
// This converts to a string and back to a number, which is inelegant, but
// is less prone to FP rounding error than an alternate method which used
// Math.round().
return parseFloat(x.toPrecision(digits));
}
// Take a string with format "YYYY-MM-DD" and return a Date object.
// IE8 and QTWebKit don't support YYYY-MM-DD, but they support YYYY/MM/DD
function parseDate(dateString) {
var date = new Date(dateString);
if (isNaN(date)) date = new Date(dateString.replace(/-/g, "/"));
return date;
}
// Given a Date object, return a string in yyyy-mm-dd format, using the
// UTC date. This may be a day off from the date in the local time zone.
function formatDateUTC(date) {
if (date instanceof Date) {
return (
date.getUTCFullYear() +
"-" +
padZeros(date.getUTCMonth() + 1, 2) +
"-" +
padZeros(date.getUTCDate(), 2)
);
} else {
return null;
}
}
// Given an element and a function(width, height), returns a function(). When
// the output function is called, it calls the input function with the offset
// width and height of the input element--but only if the size of the element
// is non-zero and the size is different than the last time the output
// function was called.
//
// Basically we are trying to filter out extraneous calls to func, so that
// when the window size changes or whatever, we don't run resize logic for
// elements that haven't actually changed size or aren't visible anyway.
function makeResizeFilter(el, func) {
var lastSize = {};
return function () {
var size = { w: el.offsetWidth, h: el.offsetHeight };
if (size.w === 0 && size.h === 0) return;
if (size.w === lastSize.w && size.h === lastSize.h) return;
lastSize = size;
func(size.w, size.h);
};
}
var _BlobBuilder =
window.BlobBuilder ||
window.WebKitBlobBuilder ||
window.MozBlobBuilder ||
window.MSBlobBuilder;
function makeBlob(parts) {
// Browser compatibility is a mess right now. The code as written works in
// a variety of modern browsers, but sadly gives a deprecation warning
// message on the console in current versions (as of this writing) of
// Chrome.
// Safari 6.0 (8536.25) on Mac OS X 10.8.1:
// Has Blob constructor but it doesn't work with ArrayBufferView args
// Google Chrome 21.0.1180.81 on Xubuntu 12.04:
// Has Blob constructor, accepts ArrayBufferView args, accepts ArrayBuffer
// but with a deprecation warning message
// Firefox 15.0 on Xubuntu 12.04:
// Has Blob constructor, accepts both ArrayBuffer and ArrayBufferView args
// Chromium 18.0.1025.168 (Developer Build 134367 Linux) on Xubuntu 12.04:
// No Blob constructor. Has WebKitBlobBuilder.
try {
return new Blob(parts);
} catch (e) {
var blobBuilder = new _BlobBuilder();
$.each(parts, function (i, part) {
blobBuilder.append(part);
});
return blobBuilder.getBlob();
}
}
function pixelRatio() {
if (window.devicePixelRatio) {
return Math.round(window.devicePixelRatio * 100) / 100;
} else {
return 1;
}
}
// Takes a string expression and returns a function that takes an argument.
//
// When the function is executed, it will evaluate that expression using
// "with" on the argument value, and return the result.
function scopeExprToFunc(expr) {
/*jshint evil: true */
var expr_escaped = expr
.replace(/[\\"']/g, "\\$&")
.replace(/\u0000/g, "\\0")
.replace(/\n/g, "\\n")
.replace(/\r/g, "\\r")
// \b has a special meaning; need [\b] to match backspace char.
.replace(/[\b]/g, "\\b");
try {
var func = new Function(
`with (this) {
try {
return (${expr});
} catch (e) {
console.error('Error evaluating expression: ${expr_escaped}');
throw e;
}
}`
);
} catch (e) {
console.error("Error parsing expression: " + expr);
throw e;
}
return function (scope) {
return func.call(scope);
};
}
function asArray(value) {
if (value === null || value === undefined) return [];
if ($.isArray(value)) return value;
return [value];
}
// We need a stable sorting algorithm for ordering
// bindings by priority and insertion order.
function mergeSort(list, sortfunc) {
function merge(sortfunc, a, b) {
var ia = 0;
var ib = 0;
var sorted = [];
while (ia < a.length && ib < b.length) {
if (sortfunc(a[ia], b[ib]) <= 0) {
sorted.push(a[ia++]);
} else {
sorted.push(b[ib++]);
}
}
while (ia < a.length) sorted.push(a[ia++]);
while (ib < b.length) sorted.push(b[ib++]);
return sorted;
}
// Don't mutate list argument
list = list.slice(0);
for (var chunkSize = 1; chunkSize < list.length; chunkSize *= 2) {
for (var i = 0; i < list.length; i += chunkSize * 2) {
var listA = list.slice(i, i + chunkSize);
var listB = list.slice(i + chunkSize, i + chunkSize * 2);
var merged = merge(sortfunc, listA, listB);
var args = [i, merged.length];
Array.prototype.push.apply(args, merged);
Array.prototype.splice.apply(list, args);
}
}
return list;
}
// Escape jQuery selector metacharacters: !"#$%&'()*+,./:;<=>?@[\]^`{|}~
var $escape = (exports.$escape = function (val) {
return val.replace(/([!"#$%&'()*+,.\/:;<=>?@\[\\\]^`{|}~])/g, "\\$1");
});
// Maps a function over an object, preserving keys. Like the mapValues
// function from lodash.
function mapValues(obj, f) {
const newObj = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) newObj[key] = f(obj[key], key, obj);
}
return newObj;
}
// This is does the same as Number.isNaN, but that function unfortunately does
// not exist in any version of IE.
function isnan(x) {
return typeof x === "number" && isNaN(x);
}
// Binary equality function used by the equal function.
function _equal(x, y) {
if ($.type(x) === "object" && $.type(y) === "object") {
if (Object.keys(x).length !== Object.keys(y).length) return false;
for (let prop in x)
if (!y.hasOwnProperty(prop) || !_equal(x[prop], y[prop])) return false;
return true;
} else if ($.type(x) === "array" && $.type(y) === "array") {
if (x.length !== y.length) return false;
for (let i = 0; i < x.length; i++) if (!_equal(x[i], y[i])) return false;
return true;
} else {
return x === y;
}
}
// Structural or "deep" equality predicate. Tests two or more arguments for
// equality, traversing arrays and objects (as determined by $.type) as
// necessary.
//
// Objects other than objects and arrays are tested for equality using ===.
function equal(...args) {
if (args.length < 2)
throw new Error("equal requires at least two arguments.");
for (let i = 0; i < args.length - 1; i++) {
if (!_equal(args[i], args[i + 1])) return false;
}
return true;
}
// Compare version strings like "1.0.1", "1.4-2". `op` must be a string like
// "==" or "<".
exports.compareVersion = function (a, op, b) {
function versionParts(ver) {
return (ver + "")
.replace(/-/, ".")
.replace(/(\.0)+[^\.]*$/, "")
.split(".");
}
function cmpVersion(a, b) {
a = versionParts(a);
b = versionParts(b);
var len = Math.min(a.length, b.length);
var cmp;
for (var i = 0; i < len; i++) {
cmp = parseInt(a[i], 10) - parseInt(b[i], 10);
if (cmp !== 0) {
return cmp;
}
}
return a.length - b.length;
}
var diff = cmpVersion(a, b);
if (op === "==") return diff === 0;
else if (op === ">=") return diff >= 0;
else if (op === ">") return diff > 0;
else if (op === "<=") return diff <= 0;
else if (op === "<") return diff < 0;
else throw `Unknown operator: ${op}`;
};
function updateLabel(labelTxt, labelNode) {
// Only update if label was specified in the update method
if (typeof labelTxt === "undefined") return;
if (labelNode.length !== 1) {
throw new Error("labelNode must be of length 1");
}
// Should the label be empty?
var emptyLabel = $.isArray(labelTxt) && labelTxt.length === 0;
if (emptyLabel) {
labelNode.addClass("shiny-label-null");
} else {
labelNode.text(labelTxt);
labelNode.removeClass("shiny-label-null");
}
}
// Compute the color property of an a tag, scoped within the element
function getComputedLinkColor(el) {
let a = document.createElement("a");
a.href = "/";
let div = document.createElement("div");
div.style.setProperty("position", "absolute", "important");
div.style.setProperty("top", "-1000px", "important");
div.style.setProperty("left", "0", "important");
div.style.setProperty("width", "30px", "important");
div.style.setProperty("height", "10px", "important");
div.appendChild(a);
el.appendChild(div);
let linkColor = window.getComputedStyle(a).getPropertyValue("color");
el.removeChild(div);
return linkColor;
}

View File

@@ -1,95 +0,0 @@
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function main(): void {
// "_start.js"
// √
// "utils.js"
// √
// "browser.js"
// √
// "input_rate.js"
// √; ./time/invoke.ts
// √; ./time/debounce.ts
// √; ./time/throttle.ts
// √; ./inputPolicies/inputBatchSender.ts
// √; ./inputPolicies/inputNoResendDecorator.ts
// √; ./inputPolicies/inputEventDecorator.ts
// √; ./inputPolicies/inputRateDecorator.ts
// √; ./inputPolicies/inputDeferDecorator.ts
// √; ./inputPolicies/inputValidateDecorator.ts
// "shinyapp.js"
// √; ./shiny/shinyapp.ts
// √; ./shiny/reconnectDialog.ts
// "notifications.js"
// √; shiny/notifications.ts
// "modal.js"
// √; shiny/modal.ts
// "file_processor.js"
// √
// "binding_registry.js"
// √; ./bindings/registry.ts
// √; ./bindings/input/index.ts
// const inputBindings = (Shiny.inputBindings = new BindingRegistry());
// √; ./bindings/output/index.ts
// const outputBindings = (Shiny.outputBindings = new BindingRegistry());
// "output_binding.js"
// √; ./bindings/output/index.ts
// "output_binding_text.js"
// √; ./bindings/output/text.ts
// "output_binding_image.js"
// √; ./bindings/output/image.ts
// √; ./imageutils/index.ts
// √; ./imageutils/disableDrag.ts
// √; ./imageutils/initPanelScales.ts
// √; ./imageutils/initCoordmap.ts
// √; ./imageutils/findbox.ts
// √; ./imageutils/shiftToRange.ts
// √; ./imageutils/createClickInfo.ts
// √; ./imageutils/createHandlers.ts
// √; ./imageutils/createBrush.ts
// √; ./imageutils/resetBrush.ts
// "output_binding_html.js"
// √; ./bindings/output/html.ts
// √; ./shiny/render.ts
// "output_binding_downloadlink.js"
// √; ./bindings/output/downloadlink.ts
// "output_binding_datatable.js"
// √; ./bindings/output/datatable.ts
// "output_binding_adapter.js"
// √; ./bindings/output_adapter.ts
// "input_binding.js"
// √; ./bindings/input/index.ts
// "input_binding_text.js"
// √; ./bindings/input/text.ts
// "input_binding_textarea.js"
// √; ./bindings/input/textarea.ts
// "input_binding_password.js"
// √; ./bindings/input/password.ts
// "input_binding_number.js"
// √; ./bindings/input/number.ts
// "input_binding_checkbox.js"
// √; ./bindings/input/checkbox.ts
// "input_binding_slider.js"
// √; ./bindings/input/slider.ts
// "input_binding_date.js"
// √; ./bindings/input/date.ts
// "input_binding_daterange.js"
// √; ./bindings/input/daterange.ts
// "input_binding_select.js"
// "input_binding_radio.js"
// √; ./bindings/input/radio.ts
// "input_binding_checkboxgroup.js"
// √; ./bindings/input/checkboxgroup.ts
// "input_binding_actionbutton.js"
// √; ./bindings/input/actionbutton.ts
// "input_binding_tabinput.js"
// √; ./bindings/input/tabinput.ts
// "input_binding_fileinput.js"
// √; ./bindings/input/fileinput.ts
// √; ./shiny/init.ts
// "reactlog.js"
// √; ./shiny/reactlog.ts
// "_end.js"
// √
}
export {};

View File

@@ -13,7 +13,7 @@ class InputBinding {
}
getId(el: HTMLElement): string {
return el["data-input-id"] || el.id;
return el.getAttribute("data-input-id") || el.id;
}
// Gives the input a type in case the server needs to know it

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