mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-11 16:08:19 -05:00
Compare commits
12 Commits
joe/bugfix
...
jeff-skele
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
337f4c9c40 | ||
|
|
e86c0c4be4 | ||
|
|
44a485b07a | ||
|
|
9138adf8a1 | ||
|
|
e588fc5a4a | ||
|
|
19965c9eb7 | ||
|
|
98e390bc1b | ||
|
|
3994055056 | ||
|
|
374f7c2aa2 | ||
|
|
27ad5d6110 | ||
|
|
d374f1dc88 | ||
|
|
38349f354d |
198
.github/workflows/R-CMD-check.yaml
vendored
198
.github/workflows/R-CMD-check.yaml
vendored
@@ -1,198 +0,0 @@
|
||||
name: R-CMD-check
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
|
||||
jobs:
|
||||
|
||||
R-CMD-check:
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
|
||||
name: ${{ matrix.config.os }} (${{ matrix.config.r }})
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- {os: macOS-latest, r: 'devel'}
|
||||
- {os: macOS-latest, r: '4.0'}
|
||||
- {os: windows-latest, r: '4.0'}
|
||||
- {os: ubuntu-16.04, r: '4.0', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"}
|
||||
- {os: ubuntu-16.04, r: '3.6', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"}
|
||||
- {os: ubuntu-16.04, r: '3.5', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"}
|
||||
- {os: ubuntu-16.04, r: '3.4', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"}
|
||||
- {os: ubuntu-16.04, r: '3.3', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"}
|
||||
|
||||
env:
|
||||
_R_CHECK_FORCE_SUGGESTS_: false
|
||||
R_REMOTES_NO_ERRORS_FROM_WARNINGS: true
|
||||
RSPM: ${{ matrix.config.rspm }}
|
||||
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@master
|
||||
with:
|
||||
r-version: ${{ matrix.config.r }}
|
||||
|
||||
- uses: r-lib/actions/setup-pandoc@master
|
||||
|
||||
- name: Query dependencies
|
||||
run: |
|
||||
install.packages('remotes')
|
||||
saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2)
|
||||
shell: Rscript {0}
|
||||
|
||||
- name: Cache R packages
|
||||
if: runner.os != 'Windows'
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ env.R_LIBS_USER }}
|
||||
key: ${{ matrix.config.os }}-r-${{ matrix.config.r }}-1-${{ hashFiles('.github/depends.Rds') }}
|
||||
restore-keys: ${{ matrix.config.os }}-r-${{ matrix.config.r }}-1-
|
||||
|
||||
- name: Install system dependencies
|
||||
if: runner.os == 'Linux'
|
||||
env:
|
||||
RHUB_PLATFORM: linux-x86_64-ubuntu-gcc
|
||||
run: |
|
||||
Rscript -e "remotes::install_github('r-hub/sysreqs')"
|
||||
sysreqs=$(Rscript -e "cat(sysreqs::sysreq_commands('DESCRIPTION'))")
|
||||
sudo -s eval "$sysreqs"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
remotes::install_deps(dependencies = TRUE)
|
||||
remotes::install_cran("rcmdcheck")
|
||||
shell: Rscript {0}
|
||||
|
||||
- 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@v1
|
||||
with:
|
||||
path: ${{ steps.phantomjs.outputs.path }}
|
||||
key: ${{ runner.os }}-phantomjs
|
||||
restore-keys: ${{ runner.os }}-phantomjs
|
||||
- name: Install PhantomJS
|
||||
run: >
|
||||
Rscript
|
||||
-e "if (!shinytest::dependenciesInstalled()) shinytest::installDependencies()"
|
||||
|
||||
- name: Session info
|
||||
run: |
|
||||
options(width = 100)
|
||||
pkgs <- installed.packages()[, "Package"]
|
||||
sessioninfo::session_info(pkgs, include_base = TRUE)
|
||||
shell: Rscript {0}
|
||||
|
||||
- name: Check
|
||||
env:
|
||||
_R_CHECK_CRAN_INCOMING_: false
|
||||
run: 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@v2
|
||||
with:
|
||||
name: ${{ runner.os }}-r${{ matrix.config.r }}-results
|
||||
path: check
|
||||
|
||||
|
||||
documentation:
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
name: documentation
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- {os: macOS-latest, r: '4.0'}
|
||||
|
||||
env:
|
||||
R_REMOTES_NO_ERRORS_FROM_WARNINGS: true
|
||||
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: r-lib/actions/setup-r@master
|
||||
with:
|
||||
r-version: ${{ matrix.config.r }}
|
||||
|
||||
- name: Query dependencies
|
||||
run: |
|
||||
install.packages('remotes')
|
||||
saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2)
|
||||
shell: Rscript {0}
|
||||
- name: Cache R packages
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ env.R_LIBS_USER }}
|
||||
key: ${{ matrix.config.os }}-r-${{ matrix.config.r }}-2-${{ hashFiles('.github/depends.Rds') }}
|
||||
restore-keys: ${{ matrix.config.os }}-r-${{ matrix.config.r }}-2-
|
||||
- name: Remove dependencies file
|
||||
run: |
|
||||
rm .github/depends.Rds
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
install.packages(c("remotes"))
|
||||
remotes::install_deps(dependencies = TRUE)
|
||||
remotes::install_cran("devtools")
|
||||
remotes::install_cran("rprojroot")
|
||||
shell: Rscript {0}
|
||||
|
||||
- name: Check documentation
|
||||
run: |
|
||||
./tools/documentation/checkDocsCurrent.sh
|
||||
|
||||
|
||||
node_js:
|
||||
runs-on: macOS-latest
|
||||
name: node_js
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '12.x'
|
||||
|
||||
# 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@v1
|
||||
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: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Check node build
|
||||
run: |
|
||||
./tools/checkJSCurrent.sh
|
||||
35
.github/workflows/pr-commands.yaml
vendored
35
.github/workflows/pr-commands.yaml
vendored
@@ -1,35 +0,0 @@
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
name: Commands
|
||||
jobs:
|
||||
document:
|
||||
if: startsWith(github.event.comment.body, '/document')
|
||||
name: document
|
||||
runs-on: macOS-latest
|
||||
env:
|
||||
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: r-lib/actions/pr-fetch@master
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: r-lib/actions/setup-r@master
|
||||
- name: Install dependencies
|
||||
run: Rscript -e 'install.packages(c("remotes", "roxygen2"))' -e 'remotes::install_deps(dependencies = TRUE)'
|
||||
- name: Document
|
||||
run: Rscript -e 'roxygen2::roxygenise()'
|
||||
- name: commit
|
||||
run: |
|
||||
git add man/\* NAMESPACE
|
||||
git commit -m 'Document'
|
||||
- uses: r-lib/actions/pr-push@master
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# added so that the workflow doesn't fail.
|
||||
always_runner:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Always run
|
||||
run: echo "This job is used to prevent the workflow status from showing as failed when all other jobs are skipped"
|
||||
32
.travis.yml
Normal file
32
.travis.yml
Normal file
@@ -0,0 +1,32 @@
|
||||
language: r
|
||||
matrix:
|
||||
include:
|
||||
- name: "Roxygen check"
|
||||
r: release
|
||||
r_packages:
|
||||
- devtools
|
||||
- rprojroot
|
||||
script: ./tools/documentation/checkDocsCurrent.sh
|
||||
env:
|
||||
# GITHUB_PAT for gh::gh calls
|
||||
- secure: "Hk4piVNtDobLT1dQPnCOcM7sOlwNGJOU5cpvbRvOxYSgxP+Bj2MyRZMe825rdHkHbFez0h8w3tJOBf9DDBH7PC1BhhNll2+WM/WxGlkNleg8vsoH/Xopffl+2YgtWbAYZjQ2j0QYdgNn0e/TY86/ggk9qit6+gpsZ7z/HmWQuVY="
|
||||
|
||||
- name: "Javascript check"
|
||||
language: node_js
|
||||
cache: yarn
|
||||
script: ./tools/checkJSCurrent.sh
|
||||
node_js:
|
||||
- "12"
|
||||
- r: 3.2
|
||||
- r: 3.3
|
||||
- r: 3.4
|
||||
- r: 3.5
|
||||
- r: release
|
||||
- r: devel
|
||||
|
||||
sudo: false
|
||||
cache: packages
|
||||
notifications:
|
||||
email:
|
||||
on_success: change
|
||||
on_failure: change
|
||||
37
DESCRIPTION
37
DESCRIPTION
@@ -1,7 +1,7 @@
|
||||
Package: shiny
|
||||
Type: Package
|
||||
Title: Web Application Framework for R
|
||||
Version: 1.5.0.9001
|
||||
Version: 1.4.0.9002
|
||||
Authors@R: c(
|
||||
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@rstudio.com"),
|
||||
person("Joe", "Cheng", role = "aut", email = "joe@rstudio.com"),
|
||||
@@ -23,18 +23,10 @@ Authors@R: c(
|
||||
comment = "Bootstrap library"),
|
||||
person(family = "Twitter, Inc", role = "cph",
|
||||
comment = "Bootstrap library"),
|
||||
person("Prem Nawaz", "Khan", role = "ctb",
|
||||
comment = "Bootstrap accessibility plugin"),
|
||||
person("Victor", "Tsaran", role = "ctb",
|
||||
comment = "Bootstrap accessibility plugin"),
|
||||
person("Dennis", "Lembree", role = "ctb",
|
||||
comment = "Bootstrap accessibility plugin"),
|
||||
person("Srinivasu", "Chakravarthula", role = "ctb",
|
||||
comment = "Bootstrap accessibility plugin"),
|
||||
person("Cathy", "O'Connor", role = "ctb",
|
||||
comment = "Bootstrap accessibility plugin"),
|
||||
person(family = "PayPal, Inc", role = "cph",
|
||||
comment = "Bootstrap accessibility plugin"),
|
||||
person("Alexander", "Farkas", role = c("ctb", "cph"),
|
||||
comment = "html5shiv library"),
|
||||
person("Scott", "Jehl", role = c("ctb", "cph"),
|
||||
comment = "Respond.js library"),
|
||||
person("Stefan", "Petre", role = c("ctb", "cph"),
|
||||
comment = "Bootstrap-datepicker library"),
|
||||
person("Andrew", "Rowls", role = c("ctb", "cph"),
|
||||
@@ -43,8 +35,10 @@ Authors@R: c(
|
||||
comment = "Font-Awesome font"),
|
||||
person("Brian", "Reavis", role = c("ctb", "cph"),
|
||||
comment = "selectize.js library"),
|
||||
person("Salmen", "Bejaoui", role = c("ctb", "cph"),
|
||||
comment = "selectize-plugin-a11y library"),
|
||||
person("Kristopher Michael", "Kowal", role = c("ctb", "cph"),
|
||||
comment = "es5-shim library"),
|
||||
person(family = "es5-shim contributors", role = c("ctb", "cph"),
|
||||
comment = "es5-shim library"),
|
||||
person("Denis", "Ineshin", role = c("ctb", "cph"),
|
||||
comment = "ion.rangeSlider library"),
|
||||
person("Sami", "Samhuri", role = c("ctb", "cph"),
|
||||
@@ -76,7 +70,7 @@ Imports:
|
||||
jsonlite (>= 0.9.16),
|
||||
xtable,
|
||||
digest,
|
||||
htmltools (>= 0.4.0.9003),
|
||||
htmltools (>= 0.4.0.9001),
|
||||
R6 (>= 2.0),
|
||||
sourcetools,
|
||||
later (>= 1.0.0),
|
||||
@@ -101,9 +95,9 @@ Suggests:
|
||||
shinytest,
|
||||
yaml,
|
||||
future,
|
||||
dygraphs,
|
||||
ragg,
|
||||
showtext
|
||||
dygraphs
|
||||
Remotes:
|
||||
rstudio/htmltools
|
||||
URL: http://shiny.rstudio.com
|
||||
BugReports: https://github.com/rstudio/shiny/issues
|
||||
Collate:
|
||||
@@ -119,6 +113,7 @@ Collate:
|
||||
'map.R'
|
||||
'utils.R'
|
||||
'bootstrap.R'
|
||||
'cache-context.R'
|
||||
'cache-disk.R'
|
||||
'cache-memory.R'
|
||||
'cache-utils.R'
|
||||
@@ -156,7 +151,6 @@ Collate:
|
||||
'middleware-shiny.R'
|
||||
'middleware.R'
|
||||
'timer.R'
|
||||
'shiny.R'
|
||||
'mock-session.R'
|
||||
'modal.R'
|
||||
'modules.R'
|
||||
@@ -173,6 +167,7 @@ Collate:
|
||||
'server-input-handlers.R'
|
||||
'server.R'
|
||||
'shiny-options.R'
|
||||
'shiny.R'
|
||||
'shinyui.R'
|
||||
'shinywrappers.R'
|
||||
'showcase.R'
|
||||
@@ -182,6 +177,6 @@ Collate:
|
||||
'test-server.R'
|
||||
'test.R'
|
||||
'update-input.R'
|
||||
RoxygenNote: 7.1.1
|
||||
RoxygenNote: 7.1.0
|
||||
Encoding: UTF-8
|
||||
Roxygen: list(markdown = TRUE)
|
||||
|
||||
437
LICENSE
437
LICENSE
@@ -8,11 +8,12 @@ these components are included below):
|
||||
- jQuery, https://github.com/jquery/jquery
|
||||
- jQuery UI (some components), https://github.com/jquery/jquery-ui
|
||||
- Bootstrap, https://github.com/twbs/bootstrap
|
||||
- bootstrap-accessibility-plugin, https://github.com/paypal/bootstrap-accessibility-plugin
|
||||
- html5shiv, https://github.com/aFarkas/html5shiv
|
||||
- Respond.js, https://github.com/scottjehl/Respond
|
||||
- bootstrap-datepicker, https://github.com/eternicode/bootstrap-datepicker
|
||||
- Font Awesome, https://github.com/FortAwesome/Font-Awesome
|
||||
- selectize.js, https://github.com/selectize/selectize.js
|
||||
- selectize-plugin-a11y, https://github.com/SLMNBJ/selectize-plugin-a11y
|
||||
- es5-shim, https://github.com/es-shims/es5-shim
|
||||
- ion.rangeSlider, https://github.com/IonDen/ion.rangeSlider
|
||||
- strftime for Javascript, https://github.com/samsonjs/strftime
|
||||
- DataTables, https://github.com/DataTables/DataTables
|
||||
@@ -71,35 +72,399 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
bootstrap-accessibility-plugin (BSD-3-Clause License)
|
||||
html5shiv License (MIT and GPL-2)
|
||||
----------------------------------------------------------------------
|
||||
Copyright (c) 2014, PayPal
|
||||
All rights reserved.
|
||||
Copyright (c) 2014 Alexander Farkas (aFarkas).
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
Licensed under MIT
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
* Neither the name of the PayPal nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<Html5shiv, The HTML5 Shiv enables use of HTML5 sectioning elements in
|
||||
legacy Internet Explorer and provides basic HTML5 styling for Internet Explorer 6-9,
|
||||
Safari 4.x (and iPhone 3.x), and Firefox 3.x.>
|
||||
Copyright (C) 2014 Alexander Farkas (aFarkas)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 2014 Alexander Farkas (aFarkas)
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
|
||||
|
||||
Respond.js License
|
||||
----------------------------------------------------------------------
|
||||
Copyright (c) 2012 Scott Jehl
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
bootstrap-datepicker
|
||||
@@ -957,18 +1322,30 @@ selectize.js
|
||||
limitations under the License.
|
||||
|
||||
|
||||
selectize-plugin-a11y License
|
||||
es5-shim License
|
||||
----------------------------------------------------------------------
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright 2018-present Salmen Bejaoui
|
||||
Copyright (C) 2009-2014 Kristopher Michael Kowal and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
ion.rangeSlider License
|
||||
|
||||
@@ -32,7 +32,6 @@ S3method(print,reactive)
|
||||
S3method(print,reactivevalues)
|
||||
S3method(print,shiny.appobj)
|
||||
S3method(print,shiny.render.function)
|
||||
S3method(print,shiny_runtests)
|
||||
S3method(str,reactivevalues)
|
||||
export("conditionStackTrace<-")
|
||||
export(..stacktraceoff..)
|
||||
@@ -116,7 +115,6 @@ export(hoverOpts)
|
||||
export(hr)
|
||||
export(htmlOutput)
|
||||
export(htmlTemplate)
|
||||
export(httpResponse)
|
||||
export(icon)
|
||||
export(imageOutput)
|
||||
export(img)
|
||||
@@ -256,7 +254,6 @@ export(strong)
|
||||
export(submitButton)
|
||||
export(suppressDependencies)
|
||||
export(tabPanel)
|
||||
export(tabPanelBody)
|
||||
export(tableOutput)
|
||||
export(tabsetPanel)
|
||||
export(tag)
|
||||
@@ -276,7 +273,6 @@ export(throttle)
|
||||
export(titlePanel)
|
||||
export(uiOutput)
|
||||
export(updateActionButton)
|
||||
export(updateActionLink)
|
||||
export(updateCheckboxGroupInput)
|
||||
export(updateCheckboxInput)
|
||||
export(updateDateInput)
|
||||
|
||||
535
NEWS.md
535
NEWS.md
@@ -1,107 +1,40 @@
|
||||
|
||||
shiny 1.5.0.9000
|
||||
================
|
||||
|
||||
## Full changelog
|
||||
|
||||
### Accessibility
|
||||
|
||||
* Added [bootstrap accessibility plugin](https://github.com/paypal/bootstrap-accessibility-plugin) under the hood to improve accessibility of shiny apps for screen-reader and keyboard users: the enhancements include better navigations for alert, tooltip, popover, modal dialog, dropdown, tab Panel, collapse, and carousel elements. (#2911)
|
||||
|
||||
* Added appropriate labels to `icon()` element to provide screen-reader users with alternative descriptions for the `fontawesome` and `glyphicon`: `aria-label` is automatically applied based on the fontawesome name. For example, `icon("calendar")` will be announced as "calendar icon" to screen readers. "presentation" aria role has also been attached to `icon()` to remove redundant semantic info for screen readers. (#2917)
|
||||
|
||||
* Closed #2929: Fixed keyboard accessibility for file picker button: keyboard users can now tab to focus on `fileInput()` widget. (#2937)
|
||||
|
||||
* Fixed #2951: screen readers correctly announce labels and date formats for `dateInput()` and `dateRangeInput()` widgets. (#2978)
|
||||
|
||||
* Closed #2847: `selectInput()` is reasonably accessible for screen readers even when `selectize` option is set to TRUE. To improve `selectize.js` accessibility, we have added [selectize-plugin-a11y](https://github.com/SLMNBJ/selectize-plugin-a11y) by default. (#2993)
|
||||
|
||||
* Closed #612: Added `alt` argument to `renderPlot()` and `renderCachedPlot()` to specify descriptive texts for `plotOutput()` objects, which is essential for screen readers. By default, alt text is set to the static text, "Plot object," but even dynamic text can be made with reactive function. (#3006, thanks @trafficonese and @leonawicz for the original PR and discussion via #2494)
|
||||
|
||||
### Minor new features and improvements
|
||||
|
||||
* When UI is specified as a function (e.g. `ui <- function(req) { ... }`), the response can now be an HTTP response as returned from the (newly exported) `httpResponse()` function. (#2970)
|
||||
|
||||
* Closed #2980: `addResourcePath()` now allows paths with a leading `.` (thanks to @ColinFay). (#2981)
|
||||
|
||||
* Closed #2972: `runExample()` now supports the `shiny.port` option (thanks to @ColinFay). (#2982)
|
||||
|
||||
* Closed #2692: `downloadButton()` icon can now be changed via the `icon` parameter (thanks to @ColinFay). (#3010)
|
||||
|
||||
* Closed #2984: improved documentation for `renderCachedPlot()` (thanks to @aalucaci). (#3016)
|
||||
|
||||
* `reactiveValuesToList()` will save its `reactlog` label as `reactiveValuesToList(<ID>)` vs `as.list(<ID>)` (#3017)
|
||||
|
||||
* Removed unused (and non-exported) `cacheContext` class.
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed #2859: `renderPlot()` wasn't correctly setting `showtext::showtext_opts()`'s `dpi` setting with the correct resolution on high resolution displays; which means, if the font was rendered by showtext, font sizes would look smaller than they should on such displays. (#2941)
|
||||
|
||||
* Fixed #1942: Calling `runApp("app.R")` no longer ignores options passed into `shinyApp()`. This makes it possible for Shiny apps to specify what port/host should be used by default. (#2969)
|
||||
|
||||
* Fixed #3033: When a `DiskCache` was created with both `max_n` and `max_size`, too many items could get pruned when `prune()` was called. (#3034)
|
||||
|
||||
### Library updates
|
||||
|
||||
* Removed html5shiv and respond.js, which were used for IE 8 and IE 9 compatibility. (#2973)
|
||||
|
||||
* Removed es5-shim library, which was internally used within `selectInput()` for ECMAScript 5 compatibility. (#2993)
|
||||
|
||||
|
||||
shiny 1.5.0
|
||||
shiny 1.4.0.9001
|
||||
===========
|
||||
|
||||
## Full changelog
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Fixed #2869: Until this release, `renderImage()` had a dangerous default of `deleteFile = TRUE`. (Sorry!) Going forward, calls to `renderImage()` will need an explicit `deleteFile` argument; for now, failing to provide one will result in a warning message, and the file will be deleted if it appears to be within the `tempdir()`. (#2881)
|
||||
|
||||
### New features
|
||||
|
||||
* The new `shinyAppTemplate()` function creates a new template Shiny application, where components are optional, such as helper files in an R/ subdirectory, a module, and various kinds of tests. (#2704)
|
||||
* `runTests()` is a new function that behaves much like R CMD check. `runTests()` invokes all of the top-level R files in the tests/ directory inside an application, in that application's environment. ([#2585](https://github.com/rstudio/shiny/pull/2585))
|
||||
|
||||
* `runTests()` is a new function that behaves much like R CMD check. `runTests()` invokes all of the top-level R files in the tests/ directory inside an application, in that application's environment. (#2585)
|
||||
* `testServer()` and `testModule()` are two new functions for testing reactive behavior inside server functions and modules, respectively. ([#2682](https://github.com/rstudio/shiny/pull/2682), [#2764](https://github.com/rstudio/shiny/pull/2764))
|
||||
|
||||
* `testServer()` is a new function for testing reactive behavior inside server functions and modules. ([#2682](https://github.com/rstudio/shiny/pull/2682), [#2764](https://github.com/rstudio/shiny/pull/2764), [#2807](https://github.com/rstudio/shiny/pull/2807))
|
||||
* The new `moduleServer` function provides a simpler interface for creating and using modules. ([#2773](https://github.com/rstudio/shiny/pull/2773))
|
||||
|
||||
* The new `moduleServer` function provides a simpler interface for creating and using modules. (#2773)
|
||||
|
||||
* Resolved #2732: `markdown()` is a new function for writing Markdown with Github extensions directly in Shiny UIs. Markdown rendering is performed by the [commonmark](https://github.com/jeroen/commonmark) package. (#2737)
|
||||
|
||||
* The `getCurrentOutputInfo()` function can now return the background color (`bg`), foreground color (`fg`), `accent` (i.e., hyperlink) color, and `font` information of the output's HTML container. This information is reported by `plotOutput()`, `imageOutput()`, and any other output bindings containing a class of `.shiny-report-theme`. This feature allows developers to style an output's contents based on the container's CSS styling. (#2740)
|
||||
* Resolved [#2732](https://github.com/rstudio/shiny/issues/2732): `markdown()` is a new function for writing Markdown with Github extensions directly in Shiny UIs. Markdown rendering is performed by the [commonmark](https://github.com/jeroen/commonmark) package. ([#2737](https://github.com/rstudio/shiny/pull/2737))
|
||||
|
||||
### Minor new features and improvements
|
||||
|
||||
* Fixed #2042, #2628: In a `dateInput` and `dateRangeInput`, disabled months and years are now a lighter gray, to make it easier to see that they are disabled. (#2690)
|
||||
* Fixed [#2042](https://github.com/rstudio/shiny/issues/2042), [#2628](https://github.com/rstudio/shiny/issues/2628): In a `dateInput` and `dateRangeInput`, disabled months and years are now a lighter gray, to make it easier to see that they are disabled. ([#2690](https://github.com/rstudio/shiny/pull/2690))
|
||||
|
||||
* `getCurrentOutputInfo()` previously threw an error when called from outside of an output; now it returns `NULL`. (#2707 and #2858)
|
||||
* `getCurrentOutputInfo()` previously threw an error when called from outside of an output; now it returns `NULL`. ([#2707](https://github.com/rstudio/shiny/pull/2707))
|
||||
|
||||
* Added a label to observer that auto-reloads `R/` directory to avoid confusion when using `reactlog`. (#58)
|
||||
* Added a label to observer that auto-reloads `R/` directory to avoid confusion when using `reactlog`. ([#58](https://github.com/rstudio/reactlog/issues/58))
|
||||
|
||||
* `getDefaultReactiveDomain()` can now be called inside a `session$onSessionEnded` callback and will return the calling `session` information. (#2757)
|
||||
* `getDefaultReactiveDomain()` can now be called inside a `session$onSessionEnded` callback and will return the calling `session` information. ([#2757](https://github.com/rstudio/shiny/pull/2757))
|
||||
|
||||
* Added a `'function'` class to `reactive()` and `reactiveVal()` objects. (#2793)
|
||||
|
||||
* Added a new option (`type = "hidden"`) to `tabsetPanel()`, making it easier to set the active tab via other input controls (e.g., `radioButtons()`) rather than tabs or pills. Use this option in conjunction with `updateTabsetPanel()` and the new `tabsetPanelBody()` function (see `help(tabsetPanel)` for an example and more details). (#2814)
|
||||
|
||||
* Added function `updateActionLink()` to update an `actionLink()` label and/or icon value. (#2811)
|
||||
|
||||
* Fixed #2856: Bumped jQuery 3 from 3.4.1 to 3.5.1. (#2857)
|
||||
* Added a `'function'` class to `reactive()` and `reactiveVal()` objects. ([#2793](https://github.com/rstudio/shiny/pull/2793))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed #2606: `debounce()` would not work properly if the code in the reactive expression threw an error on the first run. (#2652)
|
||||
* Fixed [#2606](https://github.com/rstudio/shiny/issues/2606): `debounce()` would not work properly if the code in the reactive expression threw an error on the first run. ([#2652](https://github.com/rstudio/shiny/pull/2652))
|
||||
|
||||
* Fixed #2653: The `dataTableOutput()` could have incorrect output if certain characters were in the column names. (#2658)
|
||||
* Fixed [#2653](https://github.com/rstudio/shiny/issues/2653): The `dataTableOutput()` could have incorrect output if certain characters were in the column names. ([#2658](https://github.com/rstudio/shiny/pull/2658))
|
||||
|
||||
### Documentation Updates
|
||||
|
||||
### Library updates
|
||||
|
||||
* Updated from Font-Awesome 5.3.1 to 5.13.0, which includes icons related to COVID-19. For upgrade notes, see https://github.com/FortAwesome/Font-Awesome/blob/master/UPGRADING.md. (#2891)
|
||||
|
||||
|
||||
shiny 1.4.0.2
|
||||
===========
|
||||
@@ -112,7 +45,7 @@ Minor patch release: fixed some timing-dependent tests failed intermittently on
|
||||
shiny 1.4.0.1
|
||||
===========
|
||||
|
||||
Minor patch release to account for changes to the grid package that will be upcoming in the R 4.0 release (#2776).
|
||||
Minor patch release to account for changes to the grid package that will be upcoming in the R 4.0 release ([#2776](https://github.com/rstudio/shiny/pull/2776)).
|
||||
|
||||
|
||||
shiny 1.4.0
|
||||
@@ -122,61 +55,61 @@ shiny 1.4.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Resolved #2554: Upgraded jQuery from v.1.12.4 to v3.4.1 and bootstrap from v3.3.7 to v3.4.1. (#2557). Since the jQuery upgrade may introduce breaking changes to user code, there is an option to switch back to the old version by setting `options(shiny.jquery.version = 1)`. If you've hard-coded `shared/jquery[.min].js` in the HTML of your Shiny app, in order to downgrade, you'll have to change that filepath to `shared/legacy/jquery[.min].js`.
|
||||
* Resolved [#2554](https://github.com/rstudio/shiny/issues/2554): Upgraded jQuery from v.1.12.4 to v3.4.1 and bootstrap from v3.3.7 to v3.4.1. ([#2557](https://github.com/rstudio/shiny/pull/2557)). Since the jQuery upgrade may introduce breaking changes to user code, there is an option to switch back to the old version by setting `options(shiny.jquery.version = 1)`. If you've hard-coded `shared/jquery[.min].js` in the HTML of your Shiny app, in order to downgrade, you'll have to change that filepath to `shared/legacy/jquery[.min].js`.
|
||||
|
||||
### Improvements
|
||||
|
||||
* Resolved #1433: `plotOutput()`'s coordmap info now includes discrete axis limits for **ggplot2** plots. As a result, any **shinytest** tests that contain **ggplot2** plots with discrete axes (that were recorded before this change) will now report differences that can safely be updated. This new coordmap info was added to correctly infer what data points are within an input brush and/or near input click/hover in scenarios where a non-trivial discrete axis scale is involved (e.g., whenever `scale_[x/y]_discrete(limits = ...)` and/or free scales across multiple discrete axes are used). (#2410)
|
||||
* Resolved [#1433](https://github.com/rstudio/shiny/issues/1433): `plotOutput()`'s coordmap info now includes discrete axis limits for **ggplot2** plots. As a result, any **shinytest** tests that contain **ggplot2** plots with discrete axes (that were recorded before this change) will now report differences that can safely be updated. This new coordmap info was added to correctly infer what data points are within an input brush and/or near input click/hover in scenarios where a non-trivial discrete axis scale is involved (e.g., whenever `scale_[x/y]_discrete(limits = ...)` and/or free scales across multiple discrete axes are used). ([#2410](https://github.com/rstudio/shiny/pull/2410))
|
||||
|
||||
* Resolved #2402: An informative warning is now thrown for mis-specified (date) strings in `dateInput()`, `updateDateInput()`, `dateRangeInput()`, and `updateDateRangeInput()`. (#2403)
|
||||
* Resolved [#2402](https://github.com/rstudio/shiny/issues/2402): An informative warning is now thrown for mis-specified (date) strings in `dateInput()`, `updateDateInput()`, `dateRangeInput()`, and `updateDateRangeInput()`. ([#2403](https://github.com/rstudio/shiny/pull/2403))
|
||||
|
||||
* If the `shiny.autoload.r` option is set to `TRUE`, all files ending in `.r` or `.R` contained in a directory named `R/` adjacent to your application are sourced when your app is started. This will become the default Shiny behavior in a future release (#2547)
|
||||
* If the `shiny.autoload.r` option is set to `TRUE`, all files ending in `.r` or `.R` contained in a directory named `R/` adjacent to your application are sourced when your app is started. This will become the default Shiny behavior in a future release ([#2547](https://github.com/rstudio/shiny/pull/2547))
|
||||
|
||||
* Resolved #2442: The `shiny:inputchanged` JavaScript event now triggers on the related input element instead of `document`. Existing event listeners bound to `document` will still detect the event due to event bubbling. (#2446)
|
||||
* Resolved [#2442](https://github.com/rstudio/shiny/issues/2442): The `shiny:inputchanged` JavaScript event now triggers on the related input element instead of `document`. Existing event listeners bound to `document` will still detect the event due to event bubbling. ([#2446](https://github.com/rstudio/shiny/pull/2446))
|
||||
|
||||
* Fixed #1393, #2223: For plots with any interactions enabled, the image is no longer draggable. (#2460)
|
||||
* Fixed [#1393](https://github.com/rstudio/shiny/issues/1393), [#2223](https://github.com/rstudio/shiny/issues/2223): For plots with any interactions enabled, the image is no longer draggable. ([#2460](https://github.com/rstudio/shiny/pull/2460))
|
||||
|
||||
* Resolved #2469: `renderText` now takes a `sep` argument that is passed to `cat`. (#2497)
|
||||
* Resolved [#2469](https://github.com/rstudio/shiny/issues/2469): `renderText` now takes a `sep` argument that is passed to `cat`. ([#2497](https://github.com/rstudio/shiny/pull/2497))
|
||||
|
||||
* Added `resourcePaths()` and `removeResourcePaths()` functions. (#2459)
|
||||
* Added `resourcePaths()` and `removeResourcePaths()` functions. ([#2459](https://github.com/rstudio/shiny/pull/2459))
|
||||
|
||||
* Resolved #2433: An informative warning is now thrown if subdirectories of the app's `www/` directory are masked by other resource prefixes and/or the same resource prefix is mapped to different local file paths. (#2434)
|
||||
* Resolved [#2433](https://github.com/rstudio/shiny/issues/2433): An informative warning is now thrown if subdirectories of the app's `www/` directory are masked by other resource prefixes and/or the same resource prefix is mapped to different local file paths. ([#2434](https://github.com/rstudio/shiny/pull/2434))
|
||||
|
||||
* Resolved #2478: `cmd + shift + f3` and `ctrl + shift + f3` can now be used to add a reactlog mark. If reactlog keybindings are used and the reactlog is not enabled, an error page is displayed showing how to enable reactlog recordings. (#2560)
|
||||
* Resolved [#2478](https://github.com/rstudio/shiny/issues/2478): `cmd + shift + f3` and `ctrl + shift + f3` can now be used to add a reactlog mark. If reactlog keybindings are used and the reactlog is not enabled, an error page is displayed showing how to enable reactlog recordings. ([#2560](https://github.com/rstudio/shiny/pull/2560))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Partially resolved #2423: Reactivity in Shiny leaked some memory, because R can leak memory whenever a new symbols is interned, which happens whenever a new name/key is used in an environment. R now uses the fastmap package, which avoids this problem. (#2429)
|
||||
* Partially resolved [#2423](https://github.com/rstudio/shiny/issues/2423): Reactivity in Shiny leaked some memory, because R can leak memory whenever a new symbols is interned, which happens whenever a new name/key is used in an environment. R now uses the fastmap package, which avoids this problem. ([#2429](https://github.com/rstudio/shiny/pull/2429))
|
||||
|
||||
* Fixed #2267: Fixed a memory leak with `invalidateLater`. (#2555)
|
||||
* Fixed [#2267](https://github.com/rstudio/shiny/issues/2267): Fixed a memory leak with `invalidateLater`. ([#2555](https://github.com/rstudio/shiny/pull/2555))
|
||||
|
||||
* Fixed #1548: The `reactivePoll` function leaked an observer; that is the observer would continue to exist even if the `reactivePoll` object was no longer accessible. #2522
|
||||
* Fixed [#1548](https://github.com/rstudio/shiny/issues/1548): The `reactivePoll` function leaked an observer; that is the observer would continue to exist even if the `reactivePoll` object was no longer accessible. [#2522](https://github.com/rstudio/shiny/pull/2522)
|
||||
|
||||
* Fixed #2116: Fixed an issue where dynamic tabs could not be added when on a hosted platform. (#2545)
|
||||
* Fixed [#2116](https://github.com/rstudio/shiny/issues/2116): Fixed an issue where dynamic tabs could not be added when on a hosted platform. ([#2545](https://github.com/rstudio/shiny/pull/2545))
|
||||
|
||||
* Resolved #2515: `selectInput()` and `selectizeInput()` now deal appropriately with named factors. Note that `updateSelectInput()` and `updateSelectizeInput()` **do not** yet handle factors; their behavior is unchanged. (#2524, #2540, #2625)
|
||||
* Resolved [#2515](https://github.com/rstudio/shiny/issues/2515): `selectInput()` and `selectizeInput()` now deal appropriately with named factors. Note that `updateSelectInput()` and `updateSelectizeInput()` **do not** yet handle factors; their behavior is unchanged. ([#2524](https://github.com/rstudio/shiny/pull/2524), [#2540](https://github.com/rstudio/shiny/pull/2540), [#2625](https://github.com/rstudio/shiny/pull/2625))
|
||||
|
||||
* Resolved #2471: Large file uploads to a Windows computer were slow. (#2579)
|
||||
* Resolved [#2471](https://github.com/rstudio/shiny/issues/2471): Large file uploads to a Windows computer were slow. ([#2579](https://github.com/rstudio/shiny/pull/2579))
|
||||
|
||||
* Fixed #2387: Updating a `sliderInput()`'s type from numeric to date no longer changes the rate policy from debounced to immediate. More generally, updating an input binding with a new type should (no longer) incorrectly alter the input rate policy. (#2404)
|
||||
* Fixed [#2387](https://github.com/rstudio/shiny/issues/2387): Updating a `sliderInput()`'s type from numeric to date no longer changes the rate policy from debounced to immediate. More generally, updating an input binding with a new type should (no longer) incorrectly alter the input rate policy. ([#2404](https://github.com/rstudio/shiny/pull/2404))
|
||||
|
||||
* Fixed #868: If an input is initialized with a `NULL` label, it can now be updated with a string. Moreover, if an input label is initialized with a string, it can now be removed by updating with `label=character(0)` (similar to how `choices` and `selected` can be cleared in `updateSelectInput()`). (#2406)
|
||||
* Fixed [#868](https://github.com/rstudio/shiny/issues/868): If an input is initialized with a `NULL` label, it can now be updated with a string. Moreover, if an input label is initialized with a string, it can now be removed by updating with `label=character(0)` (similar to how `choices` and `selected` can be cleared in `updateSelectInput()`). ([#2406](https://github.com/rstudio/shiny/pull/2406))
|
||||
|
||||
* Fixed #2250: `updateSliderInput()` now works with un-specified (or zero-length) `min`, `max`, and `value`. (#2416)
|
||||
* Fixed [#2250](https://github.com/rstudio/shiny/issues/2250): `updateSliderInput()` now works with un-specified (or zero-length) `min`, `max`, and `value`. ([#2416](https://github.com/rstudio/shiny/pull/2416))
|
||||
|
||||
* Fixed #2396: `selectInput("myID", ...)` resulting in an extra `myID-selectized` input (introduced in v1.2.0). (#2418)
|
||||
* Fixed [#2396](https://github.com/rstudio/shiny/issues/2396): `selectInput("myID", ...)` resulting in an extra `myID-selectized` input (introduced in v1.2.0). ([#2418](https://github.com/rstudio/shiny/pull/2418))
|
||||
|
||||
* Fixed #2233: `verbatimTextOutput()` produced wrapped text on Safari, but the text should not be wrapped. (#2353)
|
||||
* Fixed [#2233](https://github.com/rstudio/shiny/issues/2233): `verbatimTextOutput()` produced wrapped text on Safari, but the text should not be wrapped. ([#2353](https://github.com/rstudio/shiny/pull/2353))
|
||||
|
||||
* Fixed #2335: When `dateInput()`'s `value` was unspecified, and `max` and/or `min` was set to `Sys.Date()`, the value was not being set properly. (#2526)
|
||||
* Fixed [#2335](https://github.com/rstudio/shiny/issues/2335): When `dateInput()`'s `value` was unspecified, and `max` and/or `min` was set to `Sys.Date()`, the value was not being set properly. ([#2526](https://github.com/rstudio/shiny/pull/2526))
|
||||
|
||||
* Fixed #2591: Providing malformed date-strings to `min` or `max` no longer results in JS errors for `dateInput()` and `dateRangeInput()`. (#2592)
|
||||
* Fixed [#2591](https://github.com/rstudio/shiny/issues/2591): Providing malformed date-strings to `min` or `max` no longer results in JS errors for `dateInput()` and `dateRangeInput()`. ([#2592](https://github.com/rstudio/shiny/pull/2592))
|
||||
|
||||
* Fixed [rstudio/reactlog#36](https://github.com/rstudio/reactlog/issues/36): Changes to reactive values not displaying accurately in reactlog. (#2424)
|
||||
* Fixed [rstudio/reactlog#36](https://github.com/rstudio/reactlog/issues/36): Changes to reactive values not displaying accurately in reactlog. ([#2424](https://github.com/rstudio/shiny/pull/2424))
|
||||
|
||||
* Fixed #2598: Showcase files don't appear with a wide window. (#2582)
|
||||
* Fixed [#2598](https://github.com/rstudio/shiny/issues/2598): Showcase files don't appear with a wide window. ([#2582](https://github.com/rstudio/shiny/pull/2582))
|
||||
|
||||
* Fixed #2329, #1817: These bugs were reported as fixed in Shiny 1.3.0 but were not actually fixed because some JavaScript changes were accidentally not included in the release. The fix resolves issues that occur when `withProgressBar()` or bookmarking are combined with the [networkD3](https://christophergandrud.github.io/networkD3/) package's Sankey plot.
|
||||
* Fixed [#2329](https://github.com/rstudio/shiny/issues/2329), [#1817](https://github.com/rstudio/shiny/issues/1817): These bugs were reported as fixed in Shiny 1.3.0 but were not actually fixed because some JavaScript changes were accidentally not included in the release. The fix resolves issues that occur when `withProgressBar()` or bookmarking are combined with the [networkD3](https://christophergandrud.github.io/networkD3/) package's Sankey plot.
|
||||
|
||||
|
||||
shiny 1.3.2
|
||||
@@ -184,9 +117,9 @@ shiny 1.3.2
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed #2385: Static CSS/JS resources in subapps in R Markdown documents did not render properly. (#2386)
|
||||
* Fixed [#2385](https://github.com/rstudio/shiny/issues/2385): Static CSS/JS resources in subapps in R Markdown documents did not render properly. ([#2386](https://github.com/rstudio/shiny/pull/2386))
|
||||
|
||||
* Fixed #2280: Shiny applications that used a www/index.html file did not serve up the index file. (#2382)
|
||||
* Fixed [#2280](https://github.com/rstudio/shiny/issues/2280): Shiny applications that used a www/index.html file did not serve up the index file. ([#2382](https://github.com/rstudio/shiny/pull/2382))
|
||||
|
||||
|
||||
shiny 1.3.1
|
||||
@@ -196,7 +129,7 @@ shiny 1.3.1
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed a performance issue introduced in v1.3.0 when using large nested lists within Shiny. (#2377)
|
||||
* Fixed a performance issue introduced in v1.3.0 when using large nested lists within Shiny. ([#2377](https://github.com/rstudio/shiny/pull/2377))
|
||||
|
||||
|
||||
shiny 1.3.0
|
||||
@@ -208,27 +141,27 @@ shiny 1.3.0
|
||||
|
||||
### New features
|
||||
|
||||
* Revamped Shiny's [reactlog](https://github.com/rstudio/reactlog) viewer which debugs reactivity within a shiny application. This allows users to traverse the reactivity history of a shiny application, filter to the dependency tree of a selected reactive object, and search for matching reactive objects. See `?reactlogShow` for more details and how to enable this feature. (#2107)
|
||||
* Revamped Shiny's [reactlog](https://github.com/rstudio/reactlog) viewer which debugs reactivity within a shiny application. This allows users to traverse the reactivity history of a shiny application, filter to the dependency tree of a selected reactive object, and search for matching reactive objects. See `?reactlogShow` for more details and how to enable this feature. ([#2107](https://github.com/rstudio/shiny/pull/2107))
|
||||
|
||||
* Shiny now serves static files on a background thread. This means that things like JavaScript and CSS assets can be served without blocking or being blocked by the main R thread, and should result in significantly better performance for heavily loaded servers. (#2280)
|
||||
* Shiny now serves static files on a background thread. This means that things like JavaScript and CSS assets can be served without blocking or being blocked by the main R thread, and should result in significantly better performance for heavily loaded servers. ([#2280](https://github.com/rstudio/shiny/pull/2280))
|
||||
|
||||
### Minor new features and improvements
|
||||
|
||||
* The `Shiny-Shared-Secret` security header is now checked using constant-time comparison to prevent timing attacks (thanks @dirkschumacher!). (#2319)
|
||||
* The `Shiny-Shared-Secret` security header is now checked using constant-time comparison to prevent timing attacks (thanks @dirkschumacher!). ([#2319](https://github.com/rstudio/shiny/pull/2319))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed #2245: `updateSelectizeInput()` did not update labels. (#2248)
|
||||
* Fixed [#2245](https://github.com/rstudio/shiny/issues/2245): `updateSelectizeInput()` did not update labels. ([#2248](https://github.com/rstudio/shiny/pull/2248))
|
||||
|
||||
* Fixed #2308: When restoring a bookmarked application, inputs with a leading `.` would not be restored. (#2311)
|
||||
* Fixed [#2308](https://github.com/rstudio/shiny/issues/2308): When restoring a bookmarked application, inputs with a leading `.` would not be restored. ([#2311](https://github.com/rstudio/shiny/pull/2311))
|
||||
|
||||
* Fixed #2305, #2322, #2351: When an input in dynamic UI is restored from bookmarks, it would keep getting set to the same value. (#2360)
|
||||
* Fixed [#2305](https://github.com/rstudio/shiny/issues/2305), [#2322](https://github.com/rstudio/shiny/issues/2322), [#2351](https://github.com/rstudio/shiny/issues/2351): When an input in dynamic UI is restored from bookmarks, it would keep getting set to the same value. ([#2360](https://github.com/rstudio/shiny/pull/2360))
|
||||
|
||||
* Fixed #2349, #2329, #1817: These were various bugs triggered by the presence of the [networkD3](https://christophergandrud.github.io/networkD3/) package's Sankey plot in an app. Impacted features included `dateRangeInput`, `withProgressBar`, and bookmarking (#2359)
|
||||
* Fixed [#2349](https://github.com/rstudio/shiny/issues/2349), [#2329](https://github.com/rstudio/shiny/issues/2329), [#1817](https://github.com/rstudio/shiny/issues/1817): These were various bugs triggered by the presence of the [networkD3](https://christophergandrud.github.io/networkD3/) package's Sankey plot in an app. Impacted features included `dateRangeInput`, `withProgressBar`, and bookmarking ([#2359](https://github.com/rstudio/shiny/pull/2359))
|
||||
|
||||
### Documentation Updates
|
||||
|
||||
* Fixed #2247: `renderCachedPlot` now supports using promises for either `expr` or `cacheKeyExpr`. (Shiny v1.2.0 supported async `expr`, but only if `cacheKeyExpr` was async as well; now you can use any combination of sync/async for `expr` and `cacheKeyExpr`.) #2261
|
||||
* Fixed [#2247](https://github.com/rstudio/shiny/issues/2247): `renderCachedPlot` now supports using promises for either `expr` or `cacheKeyExpr`. (Shiny v1.2.0 supported async `expr`, but only if `cacheKeyExpr` was async as well; now you can use any combination of sync/async for `expr` and `cacheKeyExpr`.) [#2261](https://github.com/rstudio/shiny/pull/2261)
|
||||
|
||||
|
||||
shiny 1.2.0
|
||||
@@ -244,57 +177,57 @@ This release features plot caching, an important new tool for improving performa
|
||||
|
||||
### New features
|
||||
|
||||
* Added `renderCachedPlot()`, which stores plots in a cache so that they can be served up almost instantly. (#1997)
|
||||
* Added `renderCachedPlot()`, which stores plots in a cache so that they can be served up almost instantly. ([#1997](https://github.com/rstudio/shiny/pull/1997))
|
||||
|
||||
### Minor new features and improvements
|
||||
|
||||
* Upgrade FontAwesome from 4.7.0 to 5.3.1 and made `icon` tags browsable, which means they will display in a web browser or RStudio viewer by default (#2186). Note that if your application or library depends on FontAwesome directly using custom CSS, you may need to make some or all of the changes recommended in [Upgrade from Version 4](https://fontawesome.com/how-to-use/on-the-web/setup/upgrading-from-version-4). Font Awesome icons can also now be used in static R Markdown documents.
|
||||
* Upgrade FontAwesome from 4.7.0 to 5.3.1 and made `icon` tags browsable, which means they will display in a web browser or RStudio viewer by default ([#2186](https://github.com/rstudio/shiny/issues/2186)). Note that if your application or library depends on FontAwesome directly using custom CSS, you may need to make some or all of the changes recommended in [Upgrade from Version 4](https://fontawesome.com/how-to-use/on-the-web/setup/upgrading-from-version-4). Font Awesome icons can also now be used in static R Markdown documents.
|
||||
|
||||
* Address #174: Added `datesdisabled` and `daysofweekdisabled` as new parameters to `dateInput()`. This resolves #174 and exposes the underlying arguments of [Bootstrap Datepicker](http://bootstrap-datepicker.readthedocs.io/en/latest/options.html#datesdisabled). `datesdisabled` expects a character vector with values in `yyyy/mm/dd` format and `daysofweekdisabled` expects an integer vector with day interger ids (Sunday=0, Saturday=6). The default value for both is `NULL`, which leaves all days selectable. Thanks, @nathancday! (#2147)
|
||||
* Address [#174](https://github.com/rstudio/shiny/issues/174): Added `datesdisabled` and `daysofweekdisabled` as new parameters to `dateInput()`. This resolves [#174](https://github.com/rstudio/shiny/issues/174) and exposes the underlying arguments of [Bootstrap Datepicker](http://bootstrap-datepicker.readthedocs.io/en/latest/options.html#datesdisabled). `datesdisabled` expects a character vector with values in `yyyy/mm/dd` format and `daysofweekdisabled` expects an integer vector with day interger ids (Sunday=0, Saturday=6). The default value for both is `NULL`, which leaves all days selectable. Thanks, @nathancday! ([#2147](https://github.com/rstudio/shiny/pull/2147))
|
||||
|
||||
* Support for selecting variables of a data frame with the output values to be used within tidy evaluation. Added functions: `varSelectInput`, `varSelectizeInput`, `updateVarSelectInput`, `updateVarSelectizeInput`. (#2091)
|
||||
* Support for selecting variables of a data frame with the output values to be used within tidy evaluation. Added functions: `varSelectInput`, `varSelectizeInput`, `updateVarSelectInput`, `updateVarSelectizeInput`. ([#2091](https://github.com/rstudio/shiny/pull/2091))
|
||||
|
||||
* Addressed #2042: dates outside of `min`/`max` date range are now a lighter shade of grey to highlight the allowed range. (#2087)
|
||||
* Addressed [#2042](https://github.com/rstudio/shiny/issues/2042): dates outside of `min`/`max` date range are now a lighter shade of grey to highlight the allowed range. ([#2087](https://github.com/rstudio/shiny/pull/2087))
|
||||
|
||||
* Added support for plot interaction when the plot is scaled. (#2125)
|
||||
* Added support for plot interaction when the plot is scaled. ([#2125](https://github.com/rstudio/shiny/pull/2125))
|
||||
|
||||
* Fixed #1933: extended server-side selectize to lists and optgroups. (#2102)
|
||||
* Fixed [#1933](https://github.com/rstudio/shiny/issues/1933): extended server-side selectize to lists and optgroups. ([#2102](https://github.com/rstudio/shiny/pull/2102))
|
||||
|
||||
* Added namespace support when freezing reactiveValue keys. #2080
|
||||
* Added namespace support when freezing reactiveValue keys. [#2080](https://github.com/rstudio/shiny/pull/2080)
|
||||
|
||||
* Upgrade selectize.js from 0.12.1 to 0.12.4 #2028
|
||||
* Upgrade selectize.js from 0.12.1 to 0.12.4 [#2028](https://github.com/rstudio/shiny/issues/2028)
|
||||
|
||||
* Addressed #2079: Added `coords_img`, `coords_css`, and `img_css_ratio` fields containing x and y location information for plot brush, hover, and click events. #2183
|
||||
* Addressed [#2079](https://github.com/rstudio/shiny/issues/2079): Added `coords_img`, `coords_css`, and `img_css_ratio` fields containing x and y location information for plot brush, hover, and click events. [#2183](https://github.com/rstudio/shiny/pull/2183)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed #2033: RStudio Viewer window not closed on `shiny::stopApp()`. Thanks, @vnijs! #2047
|
||||
* Fixed [#2033](https://github.com/rstudio/shiny/issues/2033): RStudio Viewer window not closed on `shiny::stopApp()`. Thanks, @vnijs! [#2047](https://github.com/rstudio/shiny/pull/2047)
|
||||
|
||||
* Fixed #1935: correctly returns plot coordinates when using outer margins. (#2108)
|
||||
* Fixed [#1935](https://github.com/rstudio/shiny/issues/1935): correctly returns plot coordinates when using outer margins. ([#2108](https://github.com/rstudio/shiny/pull/2108))
|
||||
|
||||
* Resolved #2019: `updateSliderInput` now changes the slider formatting if the input type changes. (#2099)
|
||||
* Resolved [#2019](https://github.com/rstudio/shiny/issues/2019): `updateSliderInput` now changes the slider formatting if the input type changes. ([#2099](https://github.com/rstudio/shiny/pull/2099))
|
||||
|
||||
* Fixed #2138: Inputs that are part of a `renderUI` were no longer restoring correctly from bookmarked state. #2139
|
||||
* Fixed [#2138](https://github.com/rstudio/shiny/issues/2138): Inputs that are part of a `renderUI` were no longer restoring correctly from bookmarked state. [#2139](https://github.com/rstudio/shiny/pull/2139)
|
||||
|
||||
* The `knit_print` methods from htmltools are no longer imported into shiny and then exported. Also, shiny's `knit_print` methods are no longer exported. #2166
|
||||
* The `knit_print` methods from htmltools are no longer imported into shiny and then exported. Also, shiny's `knit_print` methods are no longer exported. [#2166](https://github.com/rstudio/shiny/pull/2166)
|
||||
|
||||
* Fixed #2093: Make sure bookmark scope directory does not exist before trying to create it. #2168
|
||||
* Fixed [#2093](https://github.com/rstudio/shiny/issues/2093): Make sure bookmark scope directory does not exist before trying to create it. [#2168](https://github.com/rstudio/shiny/pull/2168)
|
||||
|
||||
* Fixed #2177: The session name is now being recorded when exiting a context. Multiple sessions can now view their respective reactlogs. #2180
|
||||
* Fixed [#2177](https://github.com/rstudio/shiny/issues/2177): The session name is now being recorded when exiting a context. Multiple sessions can now view their respective reactlogs. [#2180](https://github.com/rstudio/shiny/pull/2180)
|
||||
|
||||
* Fixed #2162: `selectInput` was sending spurious duplicate values to the server when using backspace. Thanks, @sada1993! #2187
|
||||
* Fixed [#2162](https://github.com/rstudio/shiny/issues/2162): `selectInput` was sending spurious duplicate values to the server when using backspace. Thanks, @sada1993! [#2187](https://github.com/rstudio/shiny/pull/2187)
|
||||
|
||||
* Fixed #2142: Dropping files on `fileInput`s stopped working on recent releases of Firefox. Thanks @dmenne for reporting! #2203
|
||||
* Fixed [#2142](https://github.com/rstudio/shiny/issues/2142): Dropping files on `fileInput`s stopped working on recent releases of Firefox. Thanks @dmenne for reporting! [#2203](https://github.com/rstudio/shiny/pull/2203)
|
||||
|
||||
* Fixed #2204: `updateDateInput` could set the wrong date on days where DST begins. (Thanks @GaGaMan1101!) #2212
|
||||
* Fixed [#2204](https://github.com/rstudio/shiny/issues/2204): `updateDateInput` could set the wrong date on days where DST begins. (Thanks @GaGaMan1101!) [#2212](https://github.com/rstudio/shiny/pull/2212)
|
||||
|
||||
* Fixed #2225: Input event queue can stall in apps that use async. #2226
|
||||
* Fixed [#2225](https://github.com/rstudio/shiny/issues/2225): Input event queue can stall in apps that use async. [#2226](https://github.com/rstudio/shiny/pull/2226)
|
||||
|
||||
* Fixed #2228: `reactiveTimer` fails when not owned by a session. Thanks, @P-Bettega! #2229
|
||||
* Fixed [#2228](https://github.com/rstudio/shiny/issues/2228): `reactiveTimer` fails when not owned by a session. Thanks, @P-Bettega! [#2229](https://github.com/rstudio/shiny/pull/2229)
|
||||
|
||||
### Documentation Updates
|
||||
|
||||
* Addressed #1864 by changing `optgroup` documentation to use `list` instead of `c`. (#2084)
|
||||
* Addressed [#1864](https://github.com/rstudio/shiny/issues/1864) by changing `optgroup` documentation to use `list` instead of `c`. ([#2084](https://github.com/rstudio/shiny/pull/2084))
|
||||
|
||||
|
||||
shiny 1.1.0
|
||||
@@ -310,59 +243,59 @@ This is a significant release for Shiny, with a major new feature that was nearl
|
||||
|
||||
### New features
|
||||
|
||||
* Support for asynchronous operations! Built-in render functions that expected a certain kind of object to be yielded from their `expr`, now generally can handle a promise for that kind of object. Reactive expressions and observers are now promise-aware as well. (#1932)
|
||||
* Support for asynchronous operations! Built-in render functions that expected a certain kind of object to be yielded from their `expr`, now generally can handle a promise for that kind of object. Reactive expressions and observers are now promise-aware as well. ([#1932](https://github.com/rstudio/shiny/pull/1932))
|
||||
|
||||
* Introduced two changes to the (undocumented but widely used) JavaScript function `Shiny.onInputChange(name, value)`. First, we changed the function name to `Shiny.setInputValue` (but don't worry--the old function name will continue to work). Second, until now, all calls to `Shiny.onInputChange(inputId, value)` have been "deduplicated"; that is, anytime an input is set to the same value it already has, the set is ignored. With Shiny v1.1, you can now add an options object as the third parameter: `Shiny.setInputValue("name", value, {priority: "event"})`. When the priority option is set to `"event"`, Shiny will always send the value and trigger reactivity, whether it is a duplicate or not. This closes #928, which was the most upvoted open issue by far! Thanks, @daattali. (#2018)
|
||||
* Introduced two changes to the (undocumented but widely used) JavaScript function `Shiny.onInputChange(name, value)`. First, we changed the function name to `Shiny.setInputValue` (but don't worry--the old function name will continue to work). Second, until now, all calls to `Shiny.onInputChange(inputId, value)` have been "deduplicated"; that is, anytime an input is set to the same value it already has, the set is ignored. With Shiny v1.1, you can now add an options object as the third parameter: `Shiny.setInputValue("name", value, {priority: "event"})`. When the priority option is set to `"event"`, Shiny will always send the value and trigger reactivity, whether it is a duplicate or not. This closes [#928](https://github.com/rstudio/shiny/issues/928), which was the most upvoted open issue by far! Thanks, @daattali. ([#2018](https://github.com/rstudio/shiny/pull/2018))
|
||||
|
||||
### Minor new features and improvements
|
||||
|
||||
* Addressed #1978: `shiny:value` is now triggered when duplicate output data is received from the server. (Thanks, @andrewsali! #1999)
|
||||
* Addressed [#1978](https://github.com/rstudio/shiny/issues/1978): `shiny:value` is now triggered when duplicate output data is received from the server. (Thanks, @andrewsali! [#1999](https://github.com/rstudio/shiny/pull/1999))
|
||||
|
||||
* If a shiny output contains a css class of `shiny-report-size`, its container height and width are now reported in `session$clientData`. So, for an output with an id with `"myID"`, the height/width can be accessed via `session$clientData[['output_myID_height']]`/`session$clientData[['output_myID_width']]`. Addresses #1980. (Thanks, @cpsievert! #1981)
|
||||
* If a shiny output contains a css class of `shiny-report-size`, its container height and width are now reported in `session$clientData`. So, for an output with an id with `"myID"`, the height/width can be accessed via `session$clientData[['output_myID_height']]`/`session$clientData[['output_myID_width']]`. Addresses [#1980](https://github.com/rstudio/shiny/issues/1980). (Thanks, @cpsievert! [#1981](https://github.com/rstudio/shiny/pull/1981))
|
||||
|
||||
* Added a new `autoclose = TRUE` parameter to `dateInput()` and `dateRangeInput()`. This closed #1969 which was a duplicate of much older issue, #173. The default value is `TRUE` since that seems to be the common use case. However, this will cause existing apps with date inputs (that update to this version of Shiny) to have the datepicker be immediately closed once a date is selected. For most apps, this is actually desired behavior; if you wish to keep the datepicker open until the user clicks out of it use `autoclose = FALSE`. (#1987)
|
||||
* Added a new `autoclose = TRUE` parameter to `dateInput()` and `dateRangeInput()`. This closed [#1969](https://github.com/rstudio/shiny/issues/1969) which was a duplicate of much older issue, [#173](https://github.com/rstudio/shiny/issues/173). The default value is `TRUE` since that seems to be the common use case. However, this will cause existing apps with date inputs (that update to this version of Shiny) to have the datepicker be immediately closed once a date is selected. For most apps, this is actually desired behavior; if you wish to keep the datepicker open until the user clicks out of it use `autoclose = FALSE`. ([#1987](https://github.com/rstudio/shiny/pull/1987))
|
||||
|
||||
* The version of Shiny is now accessible from Javascript, with `Shiny.version`. There is also a new function for comparing version strings, `Shiny.compareVersion()`. (#1826, #1830)
|
||||
* The version of Shiny is now accessible from Javascript, with `Shiny.version`. There is also a new function for comparing version strings, `Shiny.compareVersion()`. ([#1826](https://github.com/rstudio/shiny/pull/1826), [#1830](https://github.com/rstudio/shiny/pull/1830))
|
||||
|
||||
* Addressed #1851: Stack traces are now smaller in some places `do.call()` is used. (#1856)
|
||||
* Addressed [#1851](https://github.com/rstudio/shiny/issues/1851): Stack traces are now smaller in some places `do.call()` is used. ([#1856](https://github.com/rstudio/shiny/pull/1856))
|
||||
|
||||
* Stack traces have been improved, with more aggressive de-noising and support for deep stack traces (stitching together multiple stack traces that are conceptually part of the same async operation).
|
||||
|
||||
* Addressed #1859: Server-side selectize is now significantly faster. (Thanks to @dselivanov #1861)
|
||||
* Addressed [#1859](https://github.com/rstudio/shiny/issues/1859): Server-side selectize is now significantly faster. (Thanks to @dselivanov [#1861](https://github.com/rstudio/shiny/pull/1861))
|
||||
|
||||
* #1989: The server side of outputs can now be removed (e.g. `output$plot <- NULL`). This is not usually necessary but it does allow some objects to be garbage collected, which might matter if you are dynamically creating and destroying many outputs. (Thanks, @mmuurr! #2011)
|
||||
* [#1989](https://github.com/rstudio/shiny/issues/1989): The server side of outputs can now be removed (e.g. `output$plot <- NULL`). This is not usually necessary but it does allow some objects to be garbage collected, which might matter if you are dynamically creating and destroying many outputs. (Thanks, @mmuurr! [#2011](https://github.com/rstudio/shiny/pull/2011))
|
||||
|
||||
* Removed the (ridiculously outdated) "experimental feature" tag from the reference documentation for `renderUI`. (#2036)
|
||||
* Removed the (ridiculously outdated) "experimental feature" tag from the reference documentation for `renderUI`. ([#2036](https://github.com/rstudio/shiny/pull/2036))
|
||||
|
||||
* Addressed #1907: the `ignoreInit` argument was first added only to `observeEvent`. Later, we also added it to `eventReactive`, but forgot to update the documentation. Now done, thanks [@flo12392](https://github.com/flo12392)! (#2036)
|
||||
* Addressed [#1907](https://github.com/rstudio/shiny/issues/1907): the `ignoreInit` argument was first added only to `observeEvent`. Later, we also added it to `eventReactive`, but forgot to update the documentation. Now done, thanks [@flo12392](https://github.com/flo12392)! ([#2036](https://github.com/rstudio/shiny/pull/2036))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed #1006: Slider inputs sometimes showed too many digits. (#1956)
|
||||
* Fixed [#1006](https://github.com/rstudio/shiny/issues/1006): Slider inputs sometimes showed too many digits. ([#1956](https://github.com/rstudio/shiny/pull/1956))
|
||||
|
||||
* Fixed #1958: Slider inputs previously displayed commas after a decimal point. (#1960)
|
||||
* Fixed [#1958](https://github.com/rstudio/shiny/issues/1958): Slider inputs previously displayed commas after a decimal point. ([#1960](https://github.com/rstudio/shiny/pull/1960))
|
||||
|
||||
* The internal `URLdecode()` function previously was a copy of `httpuv::decodeURIComponent()`, assigned at build time; now it invokes the httpuv function at run time.
|
||||
|
||||
* Fixed #1840: with the release of Shiny 1.0.5, we accidently changed the relative positioning of the icon and the title text in `navbarMenu`s and `tabPanel`s. This fix reverts this behavior back (i.e. the icon should be to the left of the text and/or the downward arrow in case of `navbarMenu`s). (#1848)
|
||||
* Fixed [#1840](https://github.com/rstudio/shiny/issues/1840): with the release of Shiny 1.0.5, we accidently changed the relative positioning of the icon and the title text in `navbarMenu`s and `tabPanel`s. This fix reverts this behavior back (i.e. the icon should be to the left of the text and/or the downward arrow in case of `navbarMenu`s). ([#1848](https://github.com/rstudio/shiny/pull/1848))
|
||||
|
||||
* Fixed #1600: URL-encoded bookmarking did not work with sliders that had dates or date-times. (#1961)
|
||||
* Fixed [#1600](https://github.com/rstudio/shiny/issues/1600): URL-encoded bookmarking did not work with sliders that had dates or date-times. ([#1961](https://github.com/rstudio/shiny/pull/1961))
|
||||
|
||||
* Fixed #1962: [File dragging and dropping](https://blog.rstudio.com/2017/08/15/shiny-1-0-4/) broke in the presence of jQuery version 3.0 as introduced by the [rhandsontable](https://jrowen.github.io/rhandsontable/) [htmlwidget](https://www.htmlwidgets.org/). (#2005)
|
||||
* Fixed [#1962](https://github.com/rstudio/shiny/issues/1962): [File dragging and dropping](https://blog.rstudio.com/2017/08/15/shiny-1-0-4/) broke in the presence of jQuery version 3.0 as introduced by the [rhandsontable](https://jrowen.github.io/rhandsontable/) [htmlwidget](https://www.htmlwidgets.org/). ([#2005](https://github.com/rstudio/shiny/pull/2005))
|
||||
|
||||
* Improved the error handling inside the `addResourcePath()` function, to give end users more informative error messages when the `directoryPath` argument cannot be normalized. This is especially useful for `runtime: shiny_prerendered` Rmd documents, like `learnr` tutorials. (#1968)
|
||||
* Improved the error handling inside the `addResourcePath()` function, to give end users more informative error messages when the `directoryPath` argument cannot be normalized. This is especially useful for `runtime: shiny_prerendered` Rmd documents, like `learnr` tutorials. ([#1968](https://github.com/rstudio/shiny/pull/1968))
|
||||
|
||||
* Changed script tags in reactlog ([inst/www/reactive-graph.html](https://github.com/rstudio/shiny/blob/v1.1.0/inst/www/reactive-graph.html)) from HTTP to HTTPS in order to avoid mixed content blocking by most browsers. (Thanks, @jekriske-lilly! #1844)
|
||||
* Changed script tags in reactlog ([inst/www/reactive-graph.html](https://github.com/rstudio/shiny/blob/v1.1.0/inst/www/reactive-graph.html)) from HTTP to HTTPS in order to avoid mixed content blocking by most browsers. (Thanks, @jekriske-lilly! [#1844](https://github.com/rstudio/shiny/pull/1844))
|
||||
|
||||
* Addressed #1784: `runApp()` will avoid port 6697, which is considered unsafe by Chrome.
|
||||
* Addressed [#1784](https://github.com/rstudio/shiny/issues/1784): `runApp()` will avoid port 6697, which is considered unsafe by Chrome.
|
||||
|
||||
* Fixed #2000: Implicit calls to `xxxOutput` not working inside modules. (Thanks, @GregorDeCillia! #2010)
|
||||
* Fixed [#2000](https://github.com/rstudio/shiny/issues/2000): Implicit calls to `xxxOutput` not working inside modules. (Thanks, @GregorDeCillia! [#2010](https://github.com/rstudio/shiny/pull/2010))
|
||||
|
||||
* Fixed #2021: Memory leak with `reactiveTimer` and `invalidateLater`. (#2022)
|
||||
* Fixed [#2021](https://github.com/rstudio/shiny/issues/2021): Memory leak with `reactiveTimer` and `invalidateLater`. ([#2022](https://github.com/rstudio/shiny/pull/2022))
|
||||
|
||||
### Library updates
|
||||
|
||||
* Updated to ion.rangeSlider 2.2.0. (#1955)
|
||||
* Updated to ion.rangeSlider 2.2.0. ([#1955](https://github.com/rstudio/shiny/pull/1955))
|
||||
|
||||
|
||||
## Known issues
|
||||
@@ -377,11 +310,11 @@ shiny 1.0.5
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed #1818: `conditionalPanel()` expressions that have a newline character in them caused the application to not work. (#1820)
|
||||
* Fixed [#1818](https://github.com/rstudio/shiny/issues/1818): `conditionalPanel()` expressions that have a newline character in them caused the application to not work. ([#1820](https://github.com/rstudio/shiny/pull/1820))
|
||||
|
||||
* Added a safe wrapper function for internal calls to `jsonlite::fromJSON()`. (#1822)
|
||||
* Added a safe wrapper function for internal calls to `jsonlite::fromJSON()`. ([#1822](https://github.com/rstudio/shiny/pull/1822))
|
||||
|
||||
* Fixed #1824: HTTP HEAD requests on static files caused the application to stop. (#1825)
|
||||
* Fixed [#1824](https://github.com/rstudio/shiny/issues/1824): HTTP HEAD requests on static files caused the application to stop. ([#1825](https://github.com/rstudio/shiny/pull/1825))
|
||||
|
||||
|
||||
shiny 1.0.4
|
||||
@@ -393,57 +326,57 @@ There are three headlining features in this release of Shiny. It is now possible
|
||||
|
||||
### New features
|
||||
|
||||
* Implemented #1668: dynamic tabs: added functions (`insertTab`, `appendTab`, `prependTab`, `removeTab`, `showTab` and `hideTab`) that allow you to do those actions for an existing `tabsetPanel`. (#1794)
|
||||
* Implemented [#1668](https://github.com/rstudio/shiny/issues/1668): dynamic tabs: added functions (`insertTab`, `appendTab`, `prependTab`, `removeTab`, `showTab` and `hideTab`) that allow you to do those actions for an existing `tabsetPanel`. ([#1794](https://github.com/rstudio/shiny/pull/1794))
|
||||
|
||||
* Implemented #1213: Added a new function, `onStop()`, which can be used to register callback functions that are invoked when an application exits, or when a user session ends. (Multiple sessions can be connected to a single running Shiny application.) This is useful if you have finalization/clean-up code that should be run after the application exits. (#1770
|
||||
* Implemented [#1213](https://github.com/rstudio/shiny/issues/1213): Added a new function, `onStop()`, which can be used to register callback functions that are invoked when an application exits, or when a user session ends. (Multiple sessions can be connected to a single running Shiny application.) This is useful if you have finalization/clean-up code that should be run after the application exits. ([#1770](https://github.com/rstudio/shiny/pull/1770)
|
||||
|
||||
* Implemented #1155: Files can now be drag-and-dropped on `fileInput` controls. The appearance of `fileInput` controls while files are being dragged can be modified by overriding the `shiny-file-input-active` and `shiny-file-input-over` classes. (#1782)
|
||||
* Implemented [#1155](https://github.com/rstudio/shiny/issues/1155): Files can now be drag-and-dropped on `fileInput` controls. The appearance of `fileInput` controls while files are being dragged can be modified by overriding the `shiny-file-input-active` and `shiny-file-input-over` classes. ([#1782](https://github.com/rstudio/shiny/pull/1782))
|
||||
|
||||
### Minor new features and improvements
|
||||
|
||||
* Addressed #1688: trigger a new `shiny:outputinvalidated` event when an output gets invalidated, at the same time that the `recalculating` CSS class is added. (#1758, thanks [@andrewsali](https://github.com/andrewsali)!)
|
||||
* Addressed [#1688](https://github.com/rstudio/shiny/issues/1688): trigger a new `shiny:outputinvalidated` event when an output gets invalidated, at the same time that the `recalculating` CSS class is added. ([#1758](https://github.com/rstudio/shiny/pull/1758), thanks [@andrewsali](https://github.com/andrewsali)!)
|
||||
|
||||
* Addressed #1508: `fileInput` now permits the same file to be uploaded multiple times. (#1719)
|
||||
* Addressed [#1508](https://github.com/rstudio/shiny/issues/1508): `fileInput` now permits the same file to be uploaded multiple times. ([#1719](https://github.com/rstudio/shiny/pull/1719))
|
||||
|
||||
* Addressed #1501: The `fileInput` control now retains uploaded file extensions on the server. This fixes [readxl](https://github.com/tidyverse/readxl)'s `readxl::read_excel` and other functions that must recognize a file's extension in order to work. (#1706)
|
||||
* Addressed [#1501](https://github.com/rstudio/shiny/issues/1501): The `fileInput` control now retains uploaded file extensions on the server. This fixes [readxl](https://github.com/tidyverse/readxl)'s `readxl::read_excel` and other functions that must recognize a file's extension in order to work. ([#1706](https://github.com/rstudio/shiny/pull/1706))
|
||||
|
||||
* For `conditionalPanel`s, Shiny now gives more informative messages if there are errors evaluating or parsing the JavaScript conditional expression. (#1727)
|
||||
* For `conditionalPanel`s, Shiny now gives more informative messages if there are errors evaluating or parsing the JavaScript conditional expression. ([#1727](https://github.com/rstudio/shiny/pull/1727))
|
||||
|
||||
* Addressed #1586: The `conditionalPanel` function now accepts an `ns` argument. The `ns` argument can be used in a [module](https://shiny.rstudio.com/articles/modules.html) UI function to scope the `condition` expression to the module's own input and output IDs. (#1735)
|
||||
* Addressed [#1586](https://github.com/rstudio/shiny/issues/1586): The `conditionalPanel` function now accepts an `ns` argument. The `ns` argument can be used in a [module](https://shiny.rstudio.com/articles/modules.html) UI function to scope the `condition` expression to the module's own input and output IDs. ([#1735](https://github.com/rstudio/shiny/pull/1735))
|
||||
|
||||
* With `options(shiny.testmode=TRUE)`, the Shiny process will send a message to the client in response to a changed input, even if no outputs have changed. This helps to streamline testing using the shinytest package. (#1747)
|
||||
* With `options(shiny.testmode=TRUE)`, the Shiny process will send a message to the client in response to a changed input, even if no outputs have changed. This helps to streamline testing using the shinytest package. ([#1747](https://github.com/rstudio/shiny/pull/1747))
|
||||
|
||||
* Addressed #1738: The `updateTextInput` and `updateTextAreaInput` functions can now update the placeholder. (#1742)
|
||||
* Addressed [#1738](https://github.com/rstudio/shiny/issues/1738): The `updateTextInput` and `updateTextAreaInput` functions can now update the placeholder. ([#1742](https://github.com/rstudio/shiny/pull/1742))
|
||||
|
||||
* Converted examples to single file apps, and made updates and enhancements to comments in the example app scripts. (#1685)
|
||||
* Converted examples to single file apps, and made updates and enhancements to comments in the example app scripts. ([#1685](https://github.com/rstudio/shiny/pull/1685))
|
||||
|
||||
* Added new `snapshotPreprocessInput()` and `snapshotPreprocessOutput()` functions, which is used for preprocessing and input and output values before taking a test snapshot. (#1760, #1789)
|
||||
* Added new `snapshotPreprocessInput()` and `snapshotPreprocessOutput()` functions, which is used for preprocessing and input and output values before taking a test snapshot. ([#1760](https://github.com/rstudio/shiny/pull/1760), [#1789](https://github.com/rstudio/shiny/pull/1789))
|
||||
|
||||
* The HTML generated by `renderTable()` no longer includes comments with the R version, xtable version, and timestamp. (#1771)
|
||||
* The HTML generated by `renderTable()` no longer includes comments with the R version, xtable version, and timestamp. ([#1771](https://github.com/rstudio/shiny/pull/1771))
|
||||
|
||||
* Added a function `isRunning` to test whether a Shiny app is currently running. (#1785)
|
||||
* Added a function `isRunning` to test whether a Shiny app is currently running. ([#1785](https://github.com/rstudio/shiny/pull/1785))
|
||||
|
||||
* Added a function `setSerializer`, which allows authors to specify a function for serializing the value of a custom input. (#1791)
|
||||
* Added a function `setSerializer`, which allows authors to specify a function for serializing the value of a custom input. ([#1791](https://github.com/rstudio/shiny/pull/1791))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed #1546: make it possible (without any hacks) to write arbitrary data into a module's `session$userData` (which is exactly the same environment as the parent's `session$userData`). To be clear, it allows something like `session$userData$x <- TRUE`, but not something like `session$userData <- TRUE` (that is not allowed in any context, whether you're in the main app, or in a module) (#1732).
|
||||
* Fixed [#1546](https://github.com/rstudio/shiny/issues/1546): make it possible (without any hacks) to write arbitrary data into a module's `session$userData` (which is exactly the same environment as the parent's `session$userData`). To be clear, it allows something like `session$userData$x <- TRUE`, but not something like `session$userData <- TRUE` (that is not allowed in any context, whether you're in the main app, or in a module) ([#1732](https://github.com/rstudio/shiny/pull/1732)).
|
||||
|
||||
* Fixed #1701: There was a partial argument match in the `generateOptions` function. (#1702)
|
||||
* Fixed [#1701](https://github.com/rstudio/shiny/issues/1701): There was a partial argument match in the `generateOptions` function. ([#1702](https://github.com/rstudio/shiny/pull/1702))
|
||||
|
||||
* Fixed #1710: `ReactiveVal` objects did not have separate dependents. (#1712)
|
||||
* Fixed [#1710](https://github.com/rstudio/shiny/issues/1710): `ReactiveVal` objects did not have separate dependents. ([#1712](https://github.com/rstudio/shiny/pull/1712))
|
||||
|
||||
* Fixed #1438: `unbindAll()` should not be called when inserting content with `insertUI()`. A previous fix (#1449) did not work correctly. (#1736)
|
||||
* Fixed [#1438](https://github.com/rstudio/shiny/issues/1438): `unbindAll()` should not be called when inserting content with `insertUI()`. A previous fix ([#1449](https://github.com/rstudio/shiny/pull/1449)) did not work correctly. ([#1736](https://github.com/rstudio/shiny/pull/1736))
|
||||
|
||||
* Fixed #1755: dynamic htmlwidgets sent the path of the package on the server to the client. (#1756)
|
||||
* Fixed [#1755](https://github.com/rstudio/shiny/issues/1755): dynamic htmlwidgets sent the path of the package on the server to the client. ([#1756](https://github.com/rstudio/shiny/pull/1756))
|
||||
|
||||
* Fixed #1763: Shiny's private random stream leaked out into the main random stream. (#1768)
|
||||
* Fixed [#1763](https://github.com/rstudio/shiny/issues/1763): Shiny's private random stream leaked out into the main random stream. ([#1768](https://github.com/rstudio/shiny/pull/1768))
|
||||
|
||||
* Fixed #1680: `options(warn=2)` was not respected when running an app. (#1790)
|
||||
* Fixed [#1680](https://github.com/rstudio/shiny/issues/1680): `options(warn=2)` was not respected when running an app. ([#1790](https://github.com/rstudio/shiny/pull/1790))
|
||||
|
||||
* Fixed #1772: ensure that `runApp()` respects the `shinyApp(onStart = function())` argument. (#1770)
|
||||
* Fixed [#1772](https://github.com/rstudio/shiny/issues/1772): ensure that `runApp()` respects the `shinyApp(onStart = function())` argument. ([#1770](https://github.com/rstudio/shiny/pull/1770))
|
||||
|
||||
* Fixed #1474: A `browser()` call in an observer could cause an error in the RStudio IDE on Windows. (#1802)
|
||||
* Fixed [#1474](https://github.com/rstudio/shiny/issues/1474): A `browser()` call in an observer could cause an error in the RStudio IDE on Windows. ([#1802](https://github.com/rstudio/shiny/pull/1802))
|
||||
|
||||
|
||||
shiny 1.0.3
|
||||
@@ -455,9 +388,9 @@ This is a hotfix release of Shiny. With previous versions of Shiny, when running
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed #1672: When an error occurred while uploading a file, the progress bar did not change colors. (#1673)
|
||||
* Fixed [#1672](https://github.com/rstudio/shiny/issues/1672): When an error occurred while uploading a file, the progress bar did not change colors. ([#1673](https://github.com/rstudio/shiny/pull/1673))
|
||||
|
||||
* Fixed #1676: On R 3.4.0, running a Shiny application gave a warning: `Warning in body(fun) : argument is not a function`. (#1677)
|
||||
* Fixed [#1676](https://github.com/rstudio/shiny/issues/1676): On R 3.4.0, running a Shiny application gave a warning: `Warning in body(fun) : argument is not a function`. ([#1677](https://github.com/rstudio/shiny/pull/1677))
|
||||
|
||||
|
||||
shiny 1.0.2
|
||||
@@ -469,15 +402,15 @@ This is a hotfix release of Shiny. The primary reason for this release is becaus
|
||||
|
||||
### Minor new features and improvements
|
||||
|
||||
* Added a `shiny:sessioninitialized` Javascript event, which is fired at the end of the initialize method of the Session object. This allows us to listen for this event when we want to get the value of things like `Shiny.user`. (#1568)
|
||||
* Added a `shiny:sessioninitialized` Javascript event, which is fired at the end of the initialize method of the Session object. This allows us to listen for this event when we want to get the value of things like `Shiny.user`. ([#1568](https://github.com/rstudio/shiny/pull/1568))
|
||||
|
||||
* Fixed #1649: allow the `choices` argument in `checkboxGroupInput()` to be `NULL` (or `c()`) to keep backward compatibility with Shiny < 1.0.1. This will result in the same thing as providing `choices = character(0)`. (#1652)
|
||||
* Fixed [#1649](https://github.com/rstudio/shiny/issues/1649): allow the `choices` argument in `checkboxGroupInput()` to be `NULL` (or `c()`) to keep backward compatibility with Shiny < 1.0.1. This will result in the same thing as providing `choices = character(0)`. ([#1652](https://github.com/rstudio/shiny/pull/1652))
|
||||
|
||||
* The official URL for accessing MathJax libraries over CDN has been deprecated and will be removed soon. We have switched to a new rstudio.com URL that we will support going forward. (#1664)
|
||||
* The official URL for accessing MathJax libraries over CDN has been deprecated and will be removed soon. We have switched to a new rstudio.com URL that we will support going forward. ([#1664](https://github.com/rstudio/shiny/pull/1664))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed #1653: wrong code example in documentation. (#1658)
|
||||
* Fixed [#1653](https://github.com/rstudio/shiny/issues/1653): wrong code example in documentation. ([#1658](https://github.com/rstudio/shiny/pull/1658))
|
||||
|
||||
|
||||
shiny 1.0.1
|
||||
@@ -493,62 +426,62 @@ This is a maintenance release of Shiny, mostly aimed at fixing bugs and introduc
|
||||
|
||||
### New features
|
||||
|
||||
* Added `reactiveVal` function, for storing a single value which can be (reactively) read and written. Similar to `reactiveValues`, except that `reactiveVal` just lets you store a single value instead of storing multiple values by name. (#1614)
|
||||
* Added `reactiveVal` function, for storing a single value which can be (reactively) read and written. Similar to `reactiveValues`, except that `reactiveVal` just lets you store a single value instead of storing multiple values by name. ([#1614](https://github.com/rstudio/shiny/pull/1614))
|
||||
|
||||
### Minor new features and improvements
|
||||
|
||||
* Fixed #1637: Outputs stay faded on MS Edge. (#1640)
|
||||
* Fixed [#1637](https://github.com/rstudio/shiny/issues/1637): Outputs stay faded on MS Edge. ([#1640](https://github.com/rstudio/shiny/pull/1640))
|
||||
|
||||
* Addressed #1348 and #1437 by adding two new arguments to `radioButtons()` and `checkboxGroupInput()`: `choiceNames` (list or vector) and `choiceValues` (list or vector). These can be passed in as an alternative to `choices`, with the added benefit that the elements in `choiceNames` can be arbitrary UI (i.e. anything created by `HTML()` and the `tags()` functions, like icons and images). While the underlying values for each choice (passed in through `choiceValues`) must still be simple text, their visual representation on the app (what the user actually clicks to select a different option) can be any valid HTML element. See `?radioButtons` for a small example. (#1521)
|
||||
* Addressed [#1348](https://github.com/rstudio/shiny/issues/1348) and [#1437](https://github.com/rstudio/shiny/issues/1437) by adding two new arguments to `radioButtons()` and `checkboxGroupInput()`: `choiceNames` (list or vector) and `choiceValues` (list or vector). These can be passed in as an alternative to `choices`, with the added benefit that the elements in `choiceNames` can be arbitrary UI (i.e. anything created by `HTML()` and the `tags()` functions, like icons and images). While the underlying values for each choice (passed in through `choiceValues`) must still be simple text, their visual representation on the app (what the user actually clicks to select a different option) can be any valid HTML element. See `?radioButtons` for a small example. ([#1521](https://github.com/rstudio/shiny/pull/1521))
|
||||
|
||||
* Updated `tools/README.md` with more detailed instructions. (#1616)
|
||||
* Updated `tools/README.md` with more detailed instructions. ([#1616](https://github.com/rstudio/shiny/pull/1616))
|
||||
|
||||
* Fixed #1565, which meant that resources with spaces in their names return HTTP 404. (#1566)
|
||||
* Fixed [#1565](https://github.com/rstudio/shiny/issues/1565), which meant that resources with spaces in their names return HTTP 404. ([#1566](https://github.com/rstudio/shiny/pull/1566))
|
||||
|
||||
* Exported `session$user` (if it exists) to the client-side; it's accessible in the Shiny object: `Shiny.user`. (#1563)
|
||||
* Exported `session$user` (if it exists) to the client-side; it's accessible in the Shiny object: `Shiny.user`. ([#1563](https://github.com/rstudio/shiny/pull/1563))
|
||||
|
||||
* Added support for HTML5's `pushState` which allows for pseudo-navigation
|
||||
in shiny apps. For more info, see the documentation (`?updateQueryString` and `?getQueryString`). (#1447)
|
||||
in shiny apps. For more info, see the documentation (`?updateQueryString` and `?getQueryString`). ([#1447](https://github.com/rstudio/shiny/pull/1447))
|
||||
|
||||
* Fixed #1121: plot interactions with ggplot2 now support `coord_fixed()`. (#1525)
|
||||
* Fixed [#1121](https://github.com/rstudio/shiny/issues/1121): plot interactions with ggplot2 now support `coord_fixed()`. ([#1525](https://github.com/rstudio/shiny/pull/1525))
|
||||
|
||||
* Added `snapshotExclude` function, which marks an output so that it is not recorded in a test snapshot. (#1559)
|
||||
* Added `snapshotExclude` function, which marks an output so that it is not recorded in a test snapshot. ([#1559](https://github.com/rstudio/shiny/pull/1559))
|
||||
|
||||
* Added `shiny:filedownload` JavaScript event, which is triggered when a `downloadButton` or `downloadLink` is clicked. Also, the values of `downloadHandler`s are not recorded in test snapshots, because the values change every time the application is run. (#1559)
|
||||
* Added `shiny:filedownload` JavaScript event, which is triggered when a `downloadButton` or `downloadLink` is clicked. Also, the values of `downloadHandler`s are not recorded in test snapshots, because the values change every time the application is run. ([#1559](https://github.com/rstudio/shiny/pull/1559))
|
||||
|
||||
* Added support for plot interactions with ggplot2 > 2.2.1. (#1578)
|
||||
* Added support for plot interactions with ggplot2 > 2.2.1. ([#1578](https://github.com/rstudio/shiny/pull/1578))
|
||||
|
||||
* Fixed #1577: Improved `escapeHTML` (util.js) in terms of the order dependency of replacing, XSS risk attack and performance. (#1579)
|
||||
* Fixed [#1577](https://github.com/rstudio/shiny/issues/1577): Improved `escapeHTML` (util.js) in terms of the order dependency of replacing, XSS risk attack and performance. ([#1579](https://github.com/rstudio/shiny/pull/1579))
|
||||
|
||||
* The `shiny:inputchanged` JavaScript event now includes two new fields, `binding` and `el`, which contain the input binding and DOM element, respectively. Additionally, `Shiny.onInputChange()` now accepts an optional argument, `opts`, which can contain the same fields. (#1596)
|
||||
* The `shiny:inputchanged` JavaScript event now includes two new fields, `binding` and `el`, which contain the input binding and DOM element, respectively. Additionally, `Shiny.onInputChange()` now accepts an optional argument, `opts`, which can contain the same fields. ([#1596](https://github.com/rstudio/shiny/pull/1596))
|
||||
|
||||
* The `NS()` function now returns a vectorized function. (#1613)
|
||||
* The `NS()` function now returns a vectorized function. ([#1613](https://github.com/rstudio/shiny/pull/1613))
|
||||
|
||||
* Fixed #1617: `fileInput` can have customized text for the button and the placeholder. (#1619)
|
||||
* Fixed [#1617](https://github.com/rstudio/shiny/issues/1617): `fileInput` can have customized text for the button and the placeholder. ([#1619](https://github.com/rstudio/shiny/pull/1619))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed #1511: `fileInput`s did not trigger the `shiny:inputchanged` event on the client. Also removed `shiny:fileuploaded` JavaScript event, because it is no longer needed after this fix. (#1541, #1570)
|
||||
* Fixed [#1511](https://github.com/rstudio/shiny/issues/1511): `fileInput`s did not trigger the `shiny:inputchanged` event on the client. Also removed `shiny:fileuploaded` JavaScript event, because it is no longer needed after this fix. ([#1541](https://github.com/rstudio/shiny/pull/1541), [#1570](https://github.com/rstudio/shiny/pull/1570))
|
||||
|
||||
* Fixed #1472: With a Progress object, calling `set(value=NULL)` made the progress bar go to 100%. Now it does not change the value of the progress bar. The documentation also incorrectly said that setting the `value` to `NULL` would hide the progress bar. (#1547)
|
||||
* Fixed [#1472](https://github.com/rstudio/shiny/issues/1472): With a Progress object, calling `set(value=NULL)` made the progress bar go to 100%. Now it does not change the value of the progress bar. The documentation also incorrectly said that setting the `value` to `NULL` would hide the progress bar. ([#1547](https://github.com/rstudio/shiny/pull/1547))
|
||||
|
||||
* Fixed #162: When a dynamically-generated input changed to a different `inputType`, it might be incorrectly deduplicated. (#1594)
|
||||
* Fixed [#162](https://github.com/rstudio/shiny/issues/162): When a dynamically-generated input changed to a different `inputType`, it might be incorrectly deduplicated. ([#1594](https://github.com/rstudio/shiny/pull/1594))
|
||||
|
||||
* Removed redundant call to `inputs.setInput`. (#1595)
|
||||
* Removed redundant call to `inputs.setInput`. ([#1595](https://github.com/rstudio/shiny/pull/1595))
|
||||
|
||||
* Fixed bug where `dateRangeInput` did not respect `weekstart` argument. (#1592)
|
||||
* Fixed bug where `dateRangeInput` did not respect `weekstart` argument. ([#1592](https://github.com/rstudio/shiny/pull/1592))
|
||||
|
||||
* Fixed #1598: `setBookmarkExclude()` did not work properly inside of modules. (#1599)
|
||||
* Fixed [#1598](https://github.com/rstudio/shiny/issues/1598): `setBookmarkExclude()` did not work properly inside of modules. ([#1599](https://github.com/rstudio/shiny/pull/1599))
|
||||
|
||||
* Fixed #1605: sliders did not move when clicked on the bar area. (#1610)
|
||||
* Fixed [#1605](https://github.com/rstudio/shiny/issues/1605): sliders did not move when clicked on the bar area. ([#1610](https://github.com/rstudio/shiny/pull/1610))
|
||||
|
||||
* Fixed #1621: if a `reactiveTimer`'s session was closed before the first time that the `reactiveTimer` fired, then the `reactiveTimer` would not get cleared and would keep firing indefinitely. (#1623)
|
||||
* Fixed [#1621](https://github.com/rstudio/shiny/issues/1621): if a `reactiveTimer`'s session was closed before the first time that the `reactiveTimer` fired, then the `reactiveTimer` would not get cleared and would keep firing indefinitely. ([#1623](https://github.com/rstudio/shiny/pull/1623))
|
||||
|
||||
* Fixed #1634: If brushing on a plot causes the plot to redraw, then the redraw could in turn trigger the plot to redraw again and again. This was due to spurious changes in values of floating point numbers. (#1641)
|
||||
* Fixed [#1634](https://github.com/rstudio/shiny/issues/1634): If brushing on a plot causes the plot to redraw, then the redraw could in turn trigger the plot to redraw again and again. This was due to spurious changes in values of floating point numbers. ([#1641](https://github.com/rstudio/shiny/pull/1641))
|
||||
|
||||
### Library updates
|
||||
|
||||
* Closed #1500: Updated ion.rangeSlider to 2.1.6. (#1540)
|
||||
* Closed [#1500](https://github.com/rstudio/shiny/issues/1500): Updated ion.rangeSlider to 2.1.6. ([#1540](https://github.com/rstudio/shiny/pull/1540))
|
||||
|
||||
|
||||
shiny 1.0.0
|
||||
@@ -560,57 +493,57 @@ Here are some highlights from this release. For more details, see the full chang
|
||||
|
||||
## Support for testing Shiny applications
|
||||
|
||||
Shiny now supports automated testing of applications, with the [shinytest](https://github.com/rstudio/shinytest) package. Shinytest has not yet been released on CRAN, but will be soon. (#18, #1464)
|
||||
Shiny now supports automated testing of applications, with the [shinytest](https://github.com/rstudio/shinytest) package. Shinytest has not yet been released on CRAN, but will be soon. ([#18](https://github.com/rstudio/shiny/issues/18), [#1464](https://github.com/rstudio/shiny/pull/1464))
|
||||
|
||||
## Debounce/throttle reactives
|
||||
|
||||
Now there's an official way to slow down reactive values and expressions that invalidate too quickly. Pass a reactive expression to the new `debounce` or `throttle` function, and get back a modified reactive expression that doesn't invalidate as often. (#1510)
|
||||
Now there's an official way to slow down reactive values and expressions that invalidate too quickly. Pass a reactive expression to the new `debounce` or `throttle` function, and get back a modified reactive expression that doesn't invalidate as often. ([#1510](https://github.com/rstudio/shiny/pull/1510))
|
||||
|
||||
## Full changelog
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Added a new `placeholder` argument to `verbatimTextOutput()`. The default is `FALSE`, which means that, if there is no content for this output, no representation of this slot will be made in the UI. Previsouly, even if there was no content, you'd see an empty rectangle in the UI that served as a placeholder. You can set `placeholder = TRUE` to revert back to that look. (#1480)
|
||||
* Added a new `placeholder` argument to `verbatimTextOutput()`. The default is `FALSE`, which means that, if there is no content for this output, no representation of this slot will be made in the UI. Previsouly, even if there was no content, you'd see an empty rectangle in the UI that served as a placeholder. You can set `placeholder = TRUE` to revert back to that look. ([#1480](https://github.com/rstudio/shiny/pull/1480))
|
||||
|
||||
### New features
|
||||
|
||||
* Added support for testing Shiny applications with the shinytest package. (#18, #1464)
|
||||
* Added support for testing Shiny applications with the shinytest package. ([#18](https://github.com/rstudio/shiny/issues/18), [#1464](https://github.com/rstudio/shiny/pull/1464))
|
||||
|
||||
* Added `debounce` and `throttle` functions, to control the rate at which reactive values and expressions invalidate. (#1510)
|
||||
* Added `debounce` and `throttle` functions, to control the rate at which reactive values and expressions invalidate. ([#1510](https://github.com/rstudio/shiny/pull/1510))
|
||||
|
||||
### Minor new features and improvements
|
||||
|
||||
* Addressed #1486 by adding a new argument to `observeEvent` and `eventReactive`, called `ignoreInit` (defaults to `FALSE` for backwards compatibility). When set to `TRUE`, the action (i.e. the second argument: `handlerExpr` and `valueExpr`, respectively) will not be triggered when the observer/reactive is first created/initialized. In other words, `ignoreInit = TRUE` ensures that the `observeEvent` (or `eventReactive`) is *never* run right away. For more info, see the documentation (`?observeEvent`). (#1494)
|
||||
* Addressed [#1486](https://github.com/rstudio/shiny/issues/1486) by adding a new argument to `observeEvent` and `eventReactive`, called `ignoreInit` (defaults to `FALSE` for backwards compatibility). When set to `TRUE`, the action (i.e. the second argument: `handlerExpr` and `valueExpr`, respectively) will not be triggered when the observer/reactive is first created/initialized. In other words, `ignoreInit = TRUE` ensures that the `observeEvent` (or `eventReactive`) is *never* run right away. For more info, see the documentation (`?observeEvent`). ([#1494](https://github.com/rstudio/shiny/pull/1494))
|
||||
|
||||
* Added a new argument to `observeEvent` called `once`. When set to `TRUE`, it results in the observer being destroyed (stop observing) after the first time that `handlerExpr` is run (i.e. `once = TRUE` guarantees that the observer only runs, at most, once). For more info, see the documentation (`?observeEvent`). (#1494)
|
||||
* Added a new argument to `observeEvent` called `once`. When set to `TRUE`, it results in the observer being destroyed (stop observing) after the first time that `handlerExpr` is run (i.e. `once = TRUE` guarantees that the observer only runs, at most, once). For more info, see the documentation (`?observeEvent`). ([#1494](https://github.com/rstudio/shiny/pull/1494))
|
||||
|
||||
* Addressed #1358: more informative error message when calling `runApp()` inside of an app's app.R (or inside ui.R or server.R). (#1482)
|
||||
* Addressed [#1358](https://github.com/rstudio/shiny/issues/1358): more informative error message when calling `runApp()` inside of an app's app.R (or inside ui.R or server.R). ([#1482](https://github.com/rstudio/shiny/pull/1482))
|
||||
|
||||
* Added a more descriptive JS warning for `insertUI()` when the selector argument does not match anything in DOM. (#1488)
|
||||
* Added a more descriptive JS warning for `insertUI()` when the selector argument does not match anything in DOM. ([#1488](https://github.com/rstudio/shiny/pull/1488))
|
||||
|
||||
* Added support for injecting JavaScript code when the `shiny.testmode` option is set to `TRUE`. This makes it possible to record test events interactively. (#1464)
|
||||
* Added support for injecting JavaScript code when the `shiny.testmode` option is set to `TRUE`. This makes it possible to record test events interactively. ([#1464](https://github.com/rstudio/shiny/pull/1464))
|
||||
|
||||
* Added ability through arguments to the `a` tag function called inside `downloadButton()` and `downloadLink()`. Closes #986. (#1492)
|
||||
* Added ability through arguments to the `a` tag function called inside `downloadButton()` and `downloadLink()`. Closes [#986](https://github.com/rstudio/shiny/issues/986). ([#1492](https://github.com/rstudio/shiny/pulls/1492))
|
||||
|
||||
* Implemented #1512: added a `userData` environment to `session`, for storing arbitrary session-related variables. Generally, session-scoped variables are created just by declaring normal variables that are local to the Shiny server function, but `session$userData` may be more convenient for some advanced scenarios. (#1513)
|
||||
* Implemented [#1512](https://github.com/rstudio/shiny/issues/1512): added a `userData` environment to `session`, for storing arbitrary session-related variables. Generally, session-scoped variables are created just by declaring normal variables that are local to the Shiny server function, but `session$userData` may be more convenient for some advanced scenarios. ([#1513](https://github.com/rstudio/shiny/pull/1513))
|
||||
|
||||
* Relaxed naming requirements for `addResourcePath()` (the first character no longer needs to be a letter). (#1529)
|
||||
* Relaxed naming requirements for `addResourcePath()` (the first character no longer needs to be a letter). ([#1529](https://github.com/rstudio/shiny/pull/1529))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed #969: allow navbarPage's `fluid` param to control both containers. (#1481)
|
||||
* Fixed [#969](https://github.com/rstudio/shiny/issues/969): allow navbarPage's `fluid` param to control both containers. ([#1481](https://github.com/rstudio/shiny/pull/1481))
|
||||
|
||||
* Fixed #1438: `unbindAll()` should not be called when inserting content with `insertUI()` (#1449)
|
||||
* Fixed [#1438](https://github.com/rstudio/shiny/issues/1438): `unbindAll()` should not be called when inserting content with `insertUI()` ([#1449](https://github.com/rstudio/shiny/pull/1449))
|
||||
|
||||
* Fixed bug causing `<meta>` tags associated with HTML dependencies of Shiny R Markdown files to be rendered incorrectly. (#1463)
|
||||
* Fixed bug causing `<meta>` tags associated with HTML dependencies of Shiny R Markdown files to be rendered incorrectly. ([#1463](https://github.com/rstudio/shiny/pull/1463))
|
||||
|
||||
* Fixed #1359: `shinyApp()` options argument ignored when passed to `runApp()`. (#1483)
|
||||
* Fixed [#1359](https://github.com/rstudio/shiny/issues/1359): `shinyApp()` options argument ignored when passed to `runApp()`. ([#1483](https://github.com/rstudio/shiny/pull/1483))
|
||||
|
||||
* Fixed #117: Reactive expressions now release references to cached values as soon as they are invalidated, potentially making those cached values eligible for garbage collection sooner. Previously, this would not occur until the next cached value was calculated and stored. (#1504)
|
||||
* Fixed [#117](https://github.com/rstudio/shiny/issues/117): Reactive expressions now release references to cached values as soon as they are invalidated, potentially making those cached values eligible for garbage collection sooner. Previously, this would not occur until the next cached value was calculated and stored. ([#1504](https://github.com/rstudio/shiny/pull/1504/files))
|
||||
|
||||
* Fixed #1013: `flushReact` should be called after app loads. Observers set up outside of server functions were not running until after the first user connects. (#1503)
|
||||
* Fixed [#1013](https://github.com/rstudio/shiny/issues/1013): `flushReact` should be called after app loads. Observers set up outside of server functions were not running until after the first user connects. ([#1503](https://github.com/rstudio/shiny/pull/1503))
|
||||
|
||||
* Fixed #1453: When using a modal dialog with `easyClose=TRUE` in a Shiny gadget, pressing Esc would close both the modal and the gadget. Now pressing Esc only closes the modal. (#1523)
|
||||
* Fixed [#1453](https://github.com/rstudio/shiny/issues/1453): When using a modal dialog with `easyClose=TRUE` in a Shiny gadget, pressing Esc would close both the modal and the gadget. Now pressing Esc only closes the modal. ([#1523](https://github.com/rstudio/shiny/pull/1523))
|
||||
|
||||
### Library updates
|
||||
|
||||
@@ -626,25 +559,25 @@ This is a maintenance release of Shiny, with some bug fixes and minor new featur
|
||||
|
||||
### Minor new features and improvements
|
||||
|
||||
* Added a `fade` argument to `modalDialog()` -- setting it to `FALSE` will remove the usual fade-in animation for that modal window. (#1414)
|
||||
* Added a `fade` argument to `modalDialog()` -- setting it to `FALSE` will remove the usual fade-in animation for that modal window. ([#1414](https://github.com/rstudio/shiny/pull/1414))
|
||||
|
||||
* Fixed a "duplicate binding" error that occurred in some edge cases involving `insertUI` and nested `uiOutput`. (#1402)
|
||||
* Fixed a "duplicate binding" error that occurred in some edge cases involving `insertUI` and nested `uiOutput`. ([#1402](https://github.com/rstudio/shiny/pull/1402))
|
||||
|
||||
* Fixed #1422: When using the `shiny.trace` option, allow specifying to only log SEND or RECV messages, or both. (PR #1428)
|
||||
* Fixed [#1422](https://github.com/rstudio/shiny/issues/1422): When using the `shiny.trace` option, allow specifying to only log SEND or RECV messages, or both. (PR [#1428](https://github.com/rstudio/shiny/pull/1428))
|
||||
|
||||
* Fixed #1419: Allow overriding a JS custom message handler. (PR #1445)
|
||||
* Fixed [#1419](https://github.com/rstudio/shiny/issues/1419): Allow overriding a JS custom message handler. (PR [#1445](https://github.com/rstudio/shiny/pull/1445))
|
||||
|
||||
* Added `exportTestValues()` function, which allows a test driver to query the session for values internal to an application's server function. This only has an effect if the `shiny.testmode` option is set to `TRUE`. (#1436)
|
||||
* Added `exportTestValues()` function, which allows a test driver to query the session for values internal to an application's server function. This only has an effect if the `shiny.testmode` option is set to `TRUE`. ([#1436](https://github.com/rstudio/shiny/pull/1436))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed #1427: make sure that modals do not close incorrectly when an element inside them is triggered as hidden. (#1430)
|
||||
* Fixed [#1427](https://github.com/rstudio/shiny/issues/1427): make sure that modals do not close incorrectly when an element inside them is triggered as hidden. ([#1430](https://github.com/rstudio/shiny/pull/1430))
|
||||
|
||||
* Fixed #1404: stack trace tests were not compatible with the byte-code compiler in R-devel, which now tracks source references.
|
||||
* Fixed [#1404](https://github.com/rstudio/shiny/issues/1404): stack trace tests were not compatible with the byte-code compiler in R-devel, which now tracks source references.
|
||||
|
||||
* `sliderInputBinding.setValue()` now sends a slider's value immediately, instead of waiting for the usual 250ms debounce delay. (#1429)
|
||||
* `sliderInputBinding.setValue()` now sends a slider's value immediately, instead of waiting for the usual 250ms debounce delay. ([#1429](https://github.com/rstudio/shiny/pull/1429))
|
||||
|
||||
* Fixed a bug where, in versions of R before 3.2, Shiny applications could crash due to a bug in R's implementation of `list2env()`. (#1446)
|
||||
* Fixed a bug where, in versions of R before 3.2, Shiny applications could crash due to a bug in R's implementation of `list2env()`. ([#1446](https://github.com/rstudio/shiny/pull/1446))
|
||||
|
||||
shiny 0.14.1
|
||||
============
|
||||
@@ -655,26 +588,26 @@ This is a maintenance release of Shiny, with some bug fixes and minor new featur
|
||||
|
||||
### Minor new features and improvements
|
||||
|
||||
* Restored file inputs are now copied on restore, so that the restored application can't modify the bookmarked file. (#1370)
|
||||
* Restored file inputs are now copied on restore, so that the restored application can't modify the bookmarked file. ([#1370](https://github.com/rstudio/shiny/issues/1370))
|
||||
|
||||
* Added support for plot interaction in the development version of ggplot2, 2.1.0.9000. Also added support for ggplot2 plots with `coord_flip()` (in the development version of ggplot2). ([hadley/ggplot2#1781](https://github.com/hadley/ggplot2/issues/1781), #1392)
|
||||
* Added support for plot interaction in the development version of ggplot2, 2.1.0.9000. Also added support for ggplot2 plots with `coord_flip()` (in the development version of ggplot2). ([hadley/ggplot2#1781](https://github.com/hadley/ggplot2/issues/1781), [#1392](https://github.com/rstudio/shiny/pull/1392))
|
||||
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed #1093 better: `updateRadioButtons()` and `updateCheckboxGroupInput()` were not working correctly if the choices were given as a numeric vector. This had been solved in #1291, but that introduced a different bug #1396 that this better fix avoids. (#1370)
|
||||
* Fixed [#1093](https://github.com/rstudio/shiny/issues/1093) better: `updateRadioButtons()` and `updateCheckboxGroupInput()` were not working correctly if the choices were given as a numeric vector. This had been solved in [#1291](https://github.com/rstudio/shiny/pull/1291), but that introduced a different bug [#1396](https://github.com/rstudio/shiny/issues/1396) that this better fix avoids. ([#1370](https://github.com/rstudio/shiny/pull/1397))
|
||||
|
||||
* Fixed #1368: If an app with a file input was bookmarked and restored, and then the restored app was bookmarked and restored (without uploading a new file), then it would fail to restore the file the second time. (#1370)
|
||||
* Fixed [#1368](https://github.com/rstudio/shiny/issues/1368): If an app with a file input was bookmarked and restored, and then the restored app was bookmarked and restored (without uploading a new file), then it would fail to restore the file the second time. ([#1370](https://github.com/rstudio/shiny/pull/1370))
|
||||
|
||||
* Fixed #1369: `sliderInput()` did not allow showing numbers without a thousands separator.
|
||||
* Fixed [#1369](https://github.com/rstudio/shiny/issues/1369): `sliderInput()` did not allow showing numbers without a thousands separator.
|
||||
|
||||
* Fixed #1346 and #1107 : jQuery UI's datepicker conflicted with the bootstrap-datepicker used by Shiny's `dateInput()` and `dateRangeInput()`. (#1374)
|
||||
* Fixed [#1346](https://github.com/rstudio/shiny/issues/1346) and [#1107](https://github.com/rstudio/shiny/issues/1107) : jQuery UI's datepicker conflicted with the bootstrap-datepicker used by Shiny's `dateInput()` and `dateRangeInput()`. ([#1374](https://github.com/rstudio/shiny/pull/1374))
|
||||
|
||||
### Library updates
|
||||
|
||||
* Updated to bootstrap-datepicker 1.6.4. (#1218, #1374)
|
||||
* Updated to bootstrap-datepicker 1.6.4. ([#1218](https://github.com/rstudio/shiny/issues/1218), [#1374](https://github.com/rstudio/shiny/pull/1374))
|
||||
|
||||
* Updated to jQuery UI 1.12.1. Previously, Shiny included a build of 1.11.4 which was missing the datepicker component due to a conflict with the bootstrap-datepicker used by Shiny's `dateInput()` and `dateRangeInput()`. (#1374)
|
||||
* Updated to jQuery UI 1.12.1. Previously, Shiny included a build of 1.11.4 which was missing the datepicker component due to a conflict with the bootstrap-datepicker used by Shiny's `dateInput()` and `dateRangeInput()`. ([#1374](https://github.com/rstudio/shiny/pull/1374))
|
||||
|
||||
|
||||
shiny 0.14
|
||||
@@ -746,7 +679,7 @@ There are many more minor features, small improvements, and bug fixes than we ca
|
||||
|
||||
* **Error Sanitization**: you now have the option to sanitize error messages; in other words, the content of the original error message can be suppressed so that it doesn't leak any sensitive information. To sanitize errors everywhere in your app, just add `options(shiny.sanitize.errors = TRUE)` somewhere in your app. Read [this article](http://shiny.rstudio.com/articles/sanitize-errors.html) for more, or play with the [demo app](https://gallery.shinyapps.io/110-error-sanitization/).
|
||||
|
||||
* **Code Diagnostics**: if there is an error parsing `ui.R`, `server.R`, `app.R`, or `global.R`, Shiny will search the code for missing commas, extra commas, and unmatched braces, parens, and brackets, and will print out messages pointing out those problems. (#1126)
|
||||
* **Code Diagnostics**: if there is an error parsing `ui.R`, `server.R`, `app.R`, or `global.R`, Shiny will search the code for missing commas, extra commas, and unmatched braces, parens, and brackets, and will print out messages pointing out those problems. ([#1126](https://github.com/rstudio/shiny/pull/1126))
|
||||
|
||||
* **Reactlog visualization**: by default, the [`showReactLog()` function](http://shiny.rstudio.com/reference/shiny/latest/reactlog.html) (which brings up the reactive graph) also displays the time that each reactive and observer were active for:
|
||||
|
||||
@@ -770,99 +703,99 @@ There are many more minor features, small improvements, and bug fixes than we ca
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Progress indicators can now either use the new notification API, using `style = "notification"` (default), or be displayed with the previous styling, using `style = "old"`. You can also call `shinyOptions(progress.style = "old")` in the server function to make all progress indicators use the old styling. Note that if you had customized your progress indicators with additional CSS, you'll need to use the old style if you want your UI to look the same (#1160 and #1329).
|
||||
* Progress indicators can now either use the new notification API, using `style = "notification"` (default), or be displayed with the previous styling, using `style = "old"`. You can also call `shinyOptions(progress.style = "old")` in the server function to make all progress indicators use the old styling. Note that if you had customized your progress indicators with additional CSS, you'll need to use the old style if you want your UI to look the same ([#1160](https://github.com/rstudio/shiny/pull/1160) and [#1329](https://github.com/rstudio/shiny/pull/1329)).
|
||||
|
||||
* Closed #1161: Deprecated the `position` argument to `tabsetPanel()` since Bootstrap 3 stopped supporting this feature.
|
||||
* Closed [#1161](https://github.com/rstudio/shiny/issues/1161): Deprecated the `position` argument to `tabsetPanel()` since Bootstrap 3 stopped supporting this feature.
|
||||
|
||||
* The long-deprecated ability to pass a `func` argument to many of the `render` functions has been removed.
|
||||
|
||||
### New features
|
||||
|
||||
* Added the ability to bookmark and restore application state. (main PR: #1209)
|
||||
* Added the ability to bookmark and restore application state. (main PR: [#1209](https://github.com/rstudio/shiny/pull/1209))
|
||||
|
||||
* Added a new notification API. From R, there are new functions `showNotification` and `hideNotification`. From JavaScript, there is a new `Shiny.notification` object that controls notifications. (#1141)
|
||||
* Added a new notification API. From R, there are new functions `showNotification` and `hideNotification`. From JavaScript, there is a new `Shiny.notification` object that controls notifications. ([#1141](https://github.com/rstudio/shiny/pull/1141))
|
||||
|
||||
* Progress indicators now use the notification API. (#1160)
|
||||
* Progress indicators now use the notification API. ([#1160](https://github.com/rstudio/shiny/pull/1160))
|
||||
|
||||
* Added the ability for the client browser to reconnect to a new session on the server, by setting `session$allowReconnect(TRUE)`. This requires a version of Shiny Server that supports reconnections. (#1074)
|
||||
* Added the ability for the client browser to reconnect to a new session on the server, by setting `session$allowReconnect(TRUE)`. This requires a version of Shiny Server that supports reconnections. ([#1074](https://github.com/rstudio/shiny/pull/1074))
|
||||
|
||||
* Added modal dialogs. (#1157)
|
||||
* Added modal dialogs. ([#1157](https://github.com/rstudio/shiny/pull/1157))
|
||||
|
||||
* Added insertUI and removeUI functions to be able to add and remove chunks of UI, standalone, and all independent of one another. (#1174 and #1189)
|
||||
* Added insertUI and removeUI functions to be able to add and remove chunks of UI, standalone, and all independent of one another. ([#1174](https://github.com/rstudio/shiny/pull/1174) and [#1189](https://github.com/rstudio/shiny/pull/1189))
|
||||
|
||||
* Improved `renderTable()` function to make the tables look prettier and also provide the user with a lot more parameters to customize their tables with. (#1129)
|
||||
* Improved `renderTable()` function to make the tables look prettier and also provide the user with a lot more parameters to customize their tables with. ([#1129](https://github.com/rstudio/shiny/pull/1129))
|
||||
|
||||
* Added support for the `pool` package (use Shiny's timer/scheduler). (#1226)
|
||||
* Added support for the `pool` package (use Shiny's timer/scheduler). ([#1226](https://github.com/rstudio/shiny/pull/1226))
|
||||
|
||||
### Minor new features and improvements
|
||||
|
||||
* Added `cancelOutput` argument to `req()`. This causes the currently executing reactive to cancel its execution, and leave its previous state alone (as opposed to clearing the output). (#1272)
|
||||
* Added `cancelOutput` argument to `req()`. This causes the currently executing reactive to cancel its execution, and leave its previous state alone (as opposed to clearing the output). ([#1272](https://github.com/rstudio/shiny/pull/1272))
|
||||
|
||||
* `Display: Showcase` now displays the .js, .html and .css files in the `www` directory by default. In order to use showcase mode and not display these, include a new line in your Description file: `IncludeWWW: False`. (#1185)
|
||||
* `Display: Showcase` now displays the .js, .html and .css files in the `www` directory by default. In order to use showcase mode and not display these, include a new line in your Description file: `IncludeWWW: False`. ([#1185](https://github.com/rstudio/shiny/pull/1185))
|
||||
|
||||
* Added an error sanitization option: `options(shiny.sanitize.errors = TRUE)`. By default, this option is `FALSE`. When `TRUE`, normal errors will be sanitized, displaying only a generic error message. This changes the look of an app when errors are printed (but the console log remains the same). (#1156)
|
||||
* Added an error sanitization option: `options(shiny.sanitize.errors = TRUE)`. By default, this option is `FALSE`. When `TRUE`, normal errors will be sanitized, displaying only a generic error message. This changes the look of an app when errors are printed (but the console log remains the same). ([#1156](https://github.com/rstudio/shiny/pull/1156))
|
||||
|
||||
* Added the option of passing arguments to an `xxxOutput()` function through the corresponding `renderXXX()` function via an `outputArgs` parameter to the latter. This is only valid for snippets of Shiny code in an interactive `runtime: shiny` Rmd document (never for full apps, even if embedded in an Rmd). (#1443)
|
||||
* Added the option of passing arguments to an `xxxOutput()` function through the corresponding `renderXXX()` function via an `outputArgs` parameter to the latter. This is only valid for snippets of Shiny code in an interactive `runtime: shiny` Rmd document (never for full apps, even if embedded in an Rmd). ([#1443](https://github.com/rstudio/shiny/pull/1143))
|
||||
|
||||
* Added `updateActionButton()` function, so the user can change an `actionButton`'s (or `actionLink`'s) label and/or icon. It also checks that the icon argument (for both creation and updating of a button) is valid and throws a warning otherwise. (#1134)
|
||||
* Added `updateActionButton()` function, so the user can change an `actionButton`'s (or `actionLink`'s) label and/or icon. It also checks that the icon argument (for both creation and updating of a button) is valid and throws a warning otherwise. ([#1134](https://github.com/rstudio/shiny/pull/1134))
|
||||
|
||||
* Added code diagnostics: if there is an error parsing ui.R, server.R, app.R, or global.R, Shiny will search the code for missing commas, extra commas, and unmatched braces, parens, and brackets, and will print out messages pointing out those problems. (#1126)
|
||||
* Added code diagnostics: if there is an error parsing ui.R, server.R, app.R, or global.R, Shiny will search the code for missing commas, extra commas, and unmatched braces, parens, and brackets, and will print out messages pointing out those problems. ([#1126](https://github.com/rstudio/shiny/pull/1126))
|
||||
|
||||
* Added support for horizontal dividers in `navbarMenu`. (#1147)
|
||||
* Added support for horizontal dividers in `navbarMenu`. ([#1147](https://github.com/rstudio/shiny/pull/1147))
|
||||
|
||||
* Added `placeholder` option to `passwordInput`. (#1152)
|
||||
* Added `placeholder` option to `passwordInput`. ([#1152](https://github.com/rstudio/shiny/pull/1152))
|
||||
|
||||
* Added `session$resetBrush(brushId)` (R) and `Shiny.resetBrush(brushId)` (JS) to programatically clear brushes from `imageOutput`/`plotOutput`. (#1197)
|
||||
* Added `session$resetBrush(brushId)` (R) and `Shiny.resetBrush(brushId)` (JS) to programatically clear brushes from `imageOutput`/`plotOutput`. ([#1197](https://github.com/rstudio/shiny/pull/1197))
|
||||
|
||||
* Added textAreaInput. (thanks, [@nuno-agostinho](https://github.com/nuno-agostinho)! #1300)
|
||||
* Added textAreaInput. (thanks, [@nuno-agostinho](https://github.com/nuno-agostinho)! [#1300](https://github.com/rstudio/shiny/pull/1300))
|
||||
|
||||
* Added `session$sendBinaryMessage(type, message)` method for sending custom binary data to the client. See `?session`. (thanks, [@daef](https://github.com/daef)! #1316 and #1320)
|
||||
* Added `session$sendBinaryMessage(type, message)` method for sending custom binary data to the client. See `?session`. (thanks, [@daef](https://github.com/daef)! [#1316](https://github.com/rstudio/shiny/pull/1316) and [#1320](https://github.com/rstudio/shiny/pull/1320))
|
||||
|
||||
* Almost all code examples now have a runnable example with `shinyApp()`, so that users can run the examples and see them in action. (#1158)
|
||||
* Almost all code examples now have a runnable example with `shinyApp()`, so that users can run the examples and see them in action. ([#1158](https://github.com/rstudio/shiny/pull/1158))
|
||||
|
||||
* When resized, plots are drawn with `replayPlot()`, instead of re-executing all plotting code. This results in faster plot rendering. (#1112)
|
||||
* When resized, plots are drawn with `replayPlot()`, instead of re-executing all plotting code. This results in faster plot rendering. ([#1112](https://github.com/rstudio/shiny/pull/1112))
|
||||
|
||||
* Exported the `isTruthy()` function. (part of PR #1272)
|
||||
* Exported the `isTruthy()` function. (part of PR [#1272](https://github.com/rstudio/shiny/pull/1272))
|
||||
|
||||
* Reactive log now shows elapsed time for reactives and observers. (#1132)
|
||||
* Reactive log now shows elapsed time for reactives and observers. ([#1132](https://github.com/rstudio/shiny/pull/1132))
|
||||
|
||||
* Nodes in the reactlog visualization are now sticky if the user drags them. (#1283)
|
||||
* Nodes in the reactlog visualization are now sticky if the user drags them. ([#1283](https://github.com/rstudio/shiny/pull/1283))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed #1350: Highlighting of reactives didn't work in showcase mode.
|
||||
* Fixed [#1350](https://github.com/rstudio/shiny/issues/1350): Highlighting of reactives didn't work in showcase mode.
|
||||
|
||||
* Fixed #1331: `renderPlot()` now correctly records and replays plots when `execOnResize = FALSE`.
|
||||
* Fixed [#1331](https://github.com/rstudio/shiny/issues/1331): `renderPlot()` now correctly records and replays plots when `execOnResize = FALSE`.
|
||||
|
||||
* `updateDateInput()` and `updateDateRangeInput()` can now clear the date input fields. (thanks, [@gaborcsardi](https://github.com/gaborcsardi)! #1299, #1315 and #1317)
|
||||
* `updateDateInput()` and `updateDateRangeInput()` can now clear the date input fields. (thanks, [@gaborcsardi](https://github.com/gaborcsardi)! [#1299](https://github.com/rstudio/shiny/pull/1299), [#1315](https://github.com/rstudio/shiny/pull/1315) and [#1317](https://github.com/rstudio/shiny/pull/1317))
|
||||
|
||||
* Fixed #561: DataTables previously might pop up a warning when the data was updated extremely frequently.
|
||||
* Fixed [#561](https://github.com/rstudio/shiny/issues/561): DataTables previously might pop up a warning when the data was updated extremely frequently.
|
||||
|
||||
* Fixed #776: In some browsers, plots sometimes flickered when updated.
|
||||
* Fixed [#776](https://github.com/rstudio/shiny/issues/776): In some browsers, plots sometimes flickered when updated.
|
||||
|
||||
* Fixed #543 and #855: When `navbarPage()` had a `navbarMenu()` as the first item, it did not automatically select an item.
|
||||
* Fixed [#543](https://github.com/rstudio/shiny/issues/543) and [#855](https://github.com/rstudio/shiny/issues/855): When `navbarPage()` had a `navbarMenu()` as the first item, it did not automatically select an item.
|
||||
|
||||
* Fixed #970: `navbarPage()` previously did not have an option to set the selected tab.
|
||||
* Fixed [#970](https://github.com/rstudio/shiny/issues/970): `navbarPage()` previously did not have an option to set the selected tab.
|
||||
|
||||
* Fixed #1253: Memory could leak when an observer was destroyed without first being invalidated.
|
||||
* Fixed [#1253](https://github.com/rstudio/shiny/issues/1253): Memory could leak when an observer was destroyed without first being invalidated.
|
||||
|
||||
* Fixed #931: Nested observers could leak memory.
|
||||
* Fixed [#931](https://github.com/rstudio/shiny/issues/931): Nested observers could leak memory.
|
||||
|
||||
* Fixed #1144: `updateRadioButton()` and `updateCheckboxGroupInput()` broke controls when used in modules (thanks, [@sipemu](https://github.com/sipemu)!).
|
||||
* Fixed [#1144](https://github.com/rstudio/shiny/issues/1144): `updateRadioButton()` and `updateCheckboxGroupInput()` broke controls when used in modules (thanks, [@sipemu](https://github.com/sipemu)!).
|
||||
|
||||
* Fixed #1093: `updateRadioButtons()` and `updateCheckboxGroupInput()` didn't work if `choices` was numeric vector.
|
||||
* Fixed [#1093](https://github.com/rstudio/shiny/issues/1093): `updateRadioButtons()` and `updateCheckboxGroupInput()` didn't work if `choices` was numeric vector.
|
||||
|
||||
* Fixed #1122: `downloadHandler()` popped up empty browser window if the file wasn't present. It now gives a 404 error code.
|
||||
* Fixed [#1122](https://github.com/rstudio/shiny/issues/1122): `downloadHandler()` popped up empty browser window if the file wasn't present. It now gives a 404 error code.
|
||||
|
||||
* Fixed #1278: Reactive system was being flushed too often (usually this just means a more-expensive no-op than necessary).
|
||||
* Fixed [#1278](https://github.com/rstudio/shiny/issues/1278): Reactive system was being flushed too often (usually this just means a more-expensive no-op than necessary).
|
||||
|
||||
* Fixed #803 and #1179: handling malformed dates in `dateInput` and `updateDateInput()`.
|
||||
* Fixed [#803](https://github.com/rstudio/shiny/issues/803) and [#1179](https://github.com/rstudio/shiny/issues/1179): handling malformed dates in `dateInput` and `updateDateInput()`.
|
||||
|
||||
* Fixed #1257: `updateSelectInput()` didn't work correctly in IE 11 and Edge.
|
||||
* Fixed [#1257](https://github.com/rstudio/shiny/issues/1257): `updateSelectInput()` didn't work correctly in IE 11 and Edge.
|
||||
|
||||
* Fixed #971: `runApp()` would give confusing error if `port` was not numeric.
|
||||
* Fixed [#971](https://github.com/rstudio/shiny/issues/971): `runApp()` would give confusing error if `port` was not numeric.
|
||||
|
||||
* Shiny now avoids using ports that Chrome deems unsafe. (#1222)
|
||||
* Shiny now avoids using ports that Chrome deems unsafe. ([#1222](https://github.com/rstudio/shiny/pull/1222))
|
||||
|
||||
* Added workaround for quartz graphics device resolution bug, where resolution is hard-coded to 72 ppi.
|
||||
|
||||
|
||||
124
R/app.R
124
R/app.R
@@ -227,6 +227,7 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
|
||||
onStart <- function() {
|
||||
oldwd <<- getwd()
|
||||
setwd(appDir)
|
||||
monitorHandle <<- initAutoReloadMonitor(appDir)
|
||||
# TODO: we should support hot reloading on global.R and R/*.R changes.
|
||||
if (getOption("shiny.autoload.r", TRUE)) {
|
||||
loadSupport(appDir, renv=sharedEnv, globalrenv=globalenv())
|
||||
@@ -234,17 +235,11 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
|
||||
if (file.exists(file.path.ci(appDir, "global.R")))
|
||||
sourceUTF8(file.path.ci(appDir, "global.R"))
|
||||
}
|
||||
monitorHandle <<- initAutoReloadMonitor(appDir)
|
||||
}
|
||||
onStop <- function() {
|
||||
setwd(oldwd)
|
||||
# It is possible that while calling appObj()$onStart() or loadingSupport, an error occured
|
||||
# This will cause `onStop` to be called.
|
||||
# The `oldwd` will exist, but `monitorHandle` is not a function yet.
|
||||
if (is.function(monitorHandle)) {
|
||||
monitorHandle()
|
||||
monitorHandle <<- NULL
|
||||
}
|
||||
monitorHandle()
|
||||
monitorHandle <<- NULL
|
||||
}
|
||||
|
||||
structure(
|
||||
@@ -291,9 +286,8 @@ initAutoReloadMonitor <- function(dir) {
|
||||
lastValue <- NULL
|
||||
observeLabel <- paste0("File Auto-Reload - '", basename(dir), "'")
|
||||
obs <- observe(label = observeLabel, {
|
||||
files <- sort_c(
|
||||
list.files(dir, pattern = filePattern, recursive = TRUE, ignore.case = TRUE)
|
||||
)
|
||||
files <- sort(list.files(dir, pattern = filePattern, recursive = TRUE,
|
||||
ignore.case = TRUE))
|
||||
times <- file.info(files)$mtime
|
||||
names(times) <- files
|
||||
|
||||
@@ -303,14 +297,14 @@ initAutoReloadMonitor <- function(dir) {
|
||||
} else if (!identical(lastValue, times)) {
|
||||
# We've changed!
|
||||
lastValue <<- times
|
||||
autoReloadCallbacks$invoke()
|
||||
for (session in appsByToken$values()) {
|
||||
session$reload()
|
||||
}
|
||||
}
|
||||
|
||||
invalidateLater(getOption("shiny.autoreload.interval", 500))
|
||||
})
|
||||
|
||||
onStop(obs$destroy)
|
||||
|
||||
obs$destroy
|
||||
}
|
||||
|
||||
@@ -331,49 +325,37 @@ initAutoReloadMonitor <- function(dir) {
|
||||
#' @details The files are sourced in alphabetical order (as determined by
|
||||
#' [list.files]). `global.R` is evaluated before the supporting R files in the
|
||||
#' `R/` directory.
|
||||
#' @param appDir The application directory. If `appDir` is `NULL` or
|
||||
#' not supplied, the nearest enclosing directory that is a Shiny app, starting
|
||||
#' with the current directory, is used.
|
||||
#' @param appDir The application directory
|
||||
#' @param renv The environmeny in which the files in the `R/` directory should
|
||||
#' be evaluated.
|
||||
#' @param globalrenv The environment in which `global.R` should be evaluated. If
|
||||
#' `NULL`, `global.R` will not be evaluated at all.
|
||||
#' @export
|
||||
loadSupport <- function(appDir=NULL, renv=new.env(parent=globalenv()), globalrenv=globalenv()){
|
||||
require(shiny)
|
||||
|
||||
if (is.null(appDir)) {
|
||||
appDir <- findEnclosingApp(".")
|
||||
}
|
||||
|
||||
loadSupport <- function(appDir, renv=new.env(parent=globalenv()), globalrenv=globalenv()){
|
||||
if (!is.null(globalrenv)){
|
||||
# Evaluate global.R, if it exists.
|
||||
globalPath <- file.path.ci(appDir, "global.R")
|
||||
if (file.exists(globalPath)){
|
||||
withr::with_dir(appDir, {
|
||||
sourceUTF8(basename(globalPath), envir=globalrenv)
|
||||
})
|
||||
if (file.exists(file.path.ci(appDir, "global.R"))){
|
||||
sourceUTF8(file.path.ci(appDir, "global.R"), envir=globalrenv)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
helpersDir <- file.path(appDir, "R")
|
||||
|
||||
disabled <- list.files(helpersDir, pattern="^_disable_autoload\\.r$", recursive=FALSE, ignore.case=TRUE)
|
||||
if (length(disabled) > 0){
|
||||
message("R/_disable_autoload.R detected; not loading the R/ directory automatically")
|
||||
return(invisible(renv))
|
||||
}
|
||||
|
||||
helpers <- list.files(helpersDir, pattern="\\.[rR]$", recursive=FALSE, full.names=TRUE)
|
||||
# Ensure files in R/ are sorted according to the 'C' locale before sourcing.
|
||||
# This convention is based on the default for packages. For details, see:
|
||||
# https://cran.r-project.org/doc/manuals/r-release/R-exts.html#The-DESCRIPTION-file
|
||||
helpers <- sort_c(helpers)
|
||||
helpers <- normalizePath(helpers)
|
||||
|
||||
withr::with_dir(appDir, {
|
||||
lapply(helpers, sourceUTF8, envir=renv)
|
||||
})
|
||||
if (length(helpers) > 0){
|
||||
message("Automatically loading ", length(helpers), " .R file",
|
||||
ifelse(length(helpers) != 1, "s", ""),
|
||||
" found in the R/ directory.\nSee https://rstd.io/shiny-autoload for more info.")
|
||||
}
|
||||
|
||||
lapply(helpers, sourceUTF8, envir=renv)
|
||||
|
||||
invisible(renv)
|
||||
}
|
||||
@@ -385,24 +367,21 @@ shinyAppDir_appR <- function(fileName, appDir, options=list())
|
||||
{
|
||||
fullpath <- file.path.ci(appDir, fileName)
|
||||
|
||||
# In an upcoming version of shiny, this option will go away.
|
||||
if (getOption("shiny.autoload.r", TRUE)) {
|
||||
# Create a child env which contains all the helpers and will be the shared parent
|
||||
# of the ui.R and server.R load.
|
||||
sharedEnv <- new.env(parent = globalenv())
|
||||
} else {
|
||||
sharedEnv <- globalenv()
|
||||
}
|
||||
|
||||
|
||||
# This sources app.R and caches the content. When appObj() is called but
|
||||
# app.R hasn't changed, it won't re-source the file. But if called and
|
||||
# app.R has changed, it'll re-source the file and return the result.
|
||||
appObj <- cachedFuncWithFile(appDir, fileName, case.sensitive = FALSE,
|
||||
function(appR) {
|
||||
wasDir <- setwd(appDir)
|
||||
on.exit(setwd(wasDir))
|
||||
|
||||
# TODO: we should support hot reloading on R/*.R changes.
|
||||
# In an upcoming version of shiny, this option will go away.
|
||||
if (getOption("shiny.autoload.r", TRUE)) {
|
||||
# Create a child env which contains all the helpers and will be the shared parent
|
||||
# of the ui.R and server.R load.
|
||||
sharedEnv <- new.env(parent = globalenv())
|
||||
loadSupport(appDir, renv=sharedEnv, globalrenv=NULL)
|
||||
} else {
|
||||
sharedEnv <- globalenv()
|
||||
}
|
||||
result <- sourceUTF8(fullpath, envir = new.env(parent = sharedEnv))
|
||||
|
||||
if (!is.shiny.appobj(result))
|
||||
@@ -446,23 +425,19 @@ shinyAppDir_appR <- function(fileName, appDir, options=list())
|
||||
onStart <- function() {
|
||||
oldwd <<- getwd()
|
||||
setwd(appDir)
|
||||
if (!is.null(appObj()$onStart)) appObj()$onStart()
|
||||
# TODO: we should support hot reloading on R/*.R changes.
|
||||
if (getOption("shiny.autoload.r", TRUE)) {
|
||||
loadSupport(appDir, renv=sharedEnv, globalrenv=NULL)
|
||||
}
|
||||
monitorHandle <<- initAutoReloadMonitor(appDir)
|
||||
invisible()
|
||||
if (!is.null(appObj()$onStart)) appObj()$onStart()
|
||||
}
|
||||
onStop <- function() {
|
||||
setwd(oldwd)
|
||||
# It is possible that while calling appObj()$onStart() or loadingSupport, an error occured
|
||||
# This will cause `onStop` to be called.
|
||||
# The `oldwd` will exist, but `monitorHandle` is not a function yet.
|
||||
if (is.function(monitorHandle)) {
|
||||
monitorHandle()
|
||||
monitorHandle <<- NULL
|
||||
}
|
||||
monitorHandle()
|
||||
monitorHandle <<- NULL
|
||||
}
|
||||
|
||||
appObjOptions <- appObj()$options
|
||||
|
||||
structure(
|
||||
list(
|
||||
# fallbackWWWDir is _not_ listed in staticPaths, because it needs to
|
||||
@@ -481,7 +456,7 @@ shinyAppDir_appR <- function(fileName, appDir, options=list())
|
||||
serverFuncSource = dynServerFuncSource,
|
||||
onStart = onStart,
|
||||
onStop = onStop,
|
||||
options = joinOptions(appObjOptions, options)
|
||||
options = options
|
||||
),
|
||||
class = "shiny.appobj"
|
||||
)
|
||||
@@ -531,25 +506,18 @@ is.shiny.appobj <- function(x) {
|
||||
}
|
||||
|
||||
#' @rdname shiny.appobj
|
||||
#' @param ... Ignored.
|
||||
#' @param ... Additional parameters to be passed to print.
|
||||
#' @export
|
||||
print.shiny.appobj <- function(x, ...) {
|
||||
runApp(x)
|
||||
}
|
||||
opts <- x$options %OR% list()
|
||||
opts <- opts[names(opts) %in%
|
||||
c("port", "launch.browser", "host", "quiet",
|
||||
"display.mode", "test.mode")]
|
||||
|
||||
# Joins two options objects (i.e. the `options` argument to shinyApp(),
|
||||
# shinyAppDir(), etc.). The values in `b` should take precedence over the values
|
||||
# in `a`. Given the current options available, it is safe to throw away any
|
||||
# values in `a` that are provided in `b`. But in the future, if new options are
|
||||
# introduced that need to be combined in some way instead of simply overwritten,
|
||||
# then this will be the place to do it. See the implementations of
|
||||
# print.shiny.appobj() and runApp() (for the latter, look specifically for
|
||||
# "findVal()") to determine the set of possible options.
|
||||
joinOptions <- function(a, b) {
|
||||
stopifnot(is.null(a) || is.list(a))
|
||||
stopifnot(is.null(b) || is.list(b))
|
||||
# Quote x and put runApp in quotes so that there's a nicer stack trace (#1851)
|
||||
args <- c(list(quote(x)), opts)
|
||||
|
||||
mergeVectors(a, b)
|
||||
do.call("runApp", args)
|
||||
}
|
||||
|
||||
#' @rdname shiny.appobj
|
||||
|
||||
227
R/app_template.R
227
R/app_template.R
@@ -3,34 +3,27 @@
|
||||
#' This function populates a directory with files for a Shiny application.
|
||||
#'
|
||||
#' In an interactive R session, this function will, by default, prompt the user
|
||||
#' to select which components to add to the application. Choices are
|
||||
#' which components to add to the application.
|
||||
#'
|
||||
#' ```
|
||||
#' 1: All
|
||||
#' 2: app.R : Main application file
|
||||
#' 3: R/example.R : Helper file with R code
|
||||
#' 4: R/example-module.R : Example module
|
||||
#' 5: tests/shinytest/ : Tests using the shinytest package
|
||||
#' 6: tests/testthat/ : Tests using the testthat package
|
||||
#' ```
|
||||
#'
|
||||
#' If option 1 is selected, the full example application including the
|
||||
#' following files and directories is created:
|
||||
#' The full example application includes the following files and directories:
|
||||
#'
|
||||
#' ```
|
||||
#' appdir/
|
||||
#' |- app.R
|
||||
#' |- R
|
||||
#' | |- example-module.R
|
||||
#' | `- example.R
|
||||
#' `- tests
|
||||
#' | |- my-module.R
|
||||
#' | |-- sort.R
|
||||
#' `-- tests
|
||||
#' |- server.R
|
||||
#' |- server
|
||||
#' | |- test-mymodule.R
|
||||
#' | `- test-server.R
|
||||
#' |- shinytest.R
|
||||
#' |- shinytest
|
||||
#' | `- mytest.R
|
||||
#' |- testthat.R
|
||||
#' `- testthat
|
||||
#' |- test-examplemodule.R
|
||||
#' |- test-server.R
|
||||
#' `-- testthat
|
||||
#' |- helper-load.R
|
||||
#' `- test-sort.R
|
||||
#' ```
|
||||
#'
|
||||
@@ -38,52 +31,52 @@
|
||||
#' * `app.R` is the main application file.
|
||||
#' * All files in the `R/` subdirectory are automatically sourced when the
|
||||
#' application is run.
|
||||
#' * `R/example.R` and `R/example-module.R` are automatically sourced when
|
||||
#' * `R/sort.R` and `R/my-module.R` are automatically sourced when
|
||||
#' the application is run. The first contains a function `lexical_sort()`,
|
||||
#' and the second contains code for module created by the
|
||||
#' [moduleServer()] function, which is used in the application.
|
||||
#' and the second contains code for a [Shiny module](moduleServer()) which
|
||||
#' is used in the application.
|
||||
#' * `tests/` contains various tests for the application. You may
|
||||
#' choose to use or remove any of them. They can be executed by the
|
||||
#' [runTests()] function.
|
||||
#' * `tests/server.R` is a test runner for test files in
|
||||
#' `tests/server/`.
|
||||
#' * `tests/server/test-mymodule.R` is a test for the module.
|
||||
#' * `tests/shinytest.R` is a test runner for test files in the
|
||||
#' `tests/shinytest/` directory.
|
||||
#' * `tests/shinytest/mytest.R` is a test that uses the
|
||||
#' [shinytest](https://rstudio.github.io/shinytest/) package to do
|
||||
#' snapshot-based testing.
|
||||
#' * `tests/testthat.R` is a test runner for test files in the
|
||||
#' `tests/testthat/` directory using the [testthat](https://testthat.r-lib.org/) package.
|
||||
#' * `tests/testthat/test-examplemodule.R` is a test for an application's module server function.
|
||||
#' * `tests/testthat/test-server.R` is a test for the application's server code
|
||||
#' * `tests/testthat/test-sort.R` is a test for a supporting function in the `R/` directory.
|
||||
#' `tests/testthat/` directory.
|
||||
#' * `tests/testthat/helper-load.R` is a helper script that is automatically
|
||||
#' loaded before running `test-mymodule.`R. (This is performed by the testthat
|
||||
#' package.)
|
||||
#' * `tests/testthat/test-sort.R` is a set of tests that use the
|
||||
#' [testthat](https://testthat.r-lib.org/) package for testing.
|
||||
#'
|
||||
#' @param path Path to create new shiny application template.
|
||||
#' @param examples Either one of "default", "ask", "all", or any combination of
|
||||
#' "app", "rdir", "module", "shinytest", and "testthat". In an
|
||||
#' "app", "rdir", "module", "shinytest", "testthat", and "server". In an
|
||||
#' interactive session, "default" falls back to "ask"; in a non-interactive
|
||||
#' session, "default" falls back to "all". With "ask", this function will
|
||||
#' prompt the user to select which template items will be added to the new app
|
||||
#' directory. With "all", all template items will be added to the app
|
||||
#' directory.
|
||||
#' @param dryrun If `TRUE`, don't actually write any files; just print out which
|
||||
#' files would be written.
|
||||
#'
|
||||
#' @export
|
||||
shinyAppTemplate <- function(path = NULL, examples = "default", dryrun = FALSE)
|
||||
shinyAppTemplate <- function(path = NULL, examples = "default")
|
||||
{
|
||||
if (is.null(path)) {
|
||||
stop("Please provide a `path`.")
|
||||
}
|
||||
|
||||
# =======================================================
|
||||
# Option handling
|
||||
# =======================================================
|
||||
|
||||
choices <- c(
|
||||
app = "app.R : Main application file",
|
||||
rdir = "R/example.R : Helper file with R code",
|
||||
module = "R/example-module.R : Example module",
|
||||
shinytest = "tests/shinytest/ : Tests using the shinytest package",
|
||||
testthat = "tests/testthat/ : Tests using the testthat package"
|
||||
app = "app.R : Main application file",
|
||||
rdir = "R/sort.R : Helper file with R code",
|
||||
module = "R/my-module.R : Example module",
|
||||
shinytest = "tests/shinytest/ : Tests using shinytest package",
|
||||
testthat = "tests/testthat/ : Tests using testthat",
|
||||
server = "tests/server/ : Tests of server and module code"
|
||||
)
|
||||
|
||||
if (identical(examples, "default")) {
|
||||
@@ -124,24 +117,6 @@ shinyAppTemplate <- function(path = NULL, examples = "default", dryrun = FALSE)
|
||||
return(invisible())
|
||||
}
|
||||
|
||||
if ("shinytest" %in% examples) {
|
||||
if (!is_available("shinytest", "1.4.0"))
|
||||
{
|
||||
message(
|
||||
"The tests/shinytest directory needs shinytest 1.4.0 or later to work properly."
|
||||
)
|
||||
if (is_available("shinytest")) {
|
||||
message("You currently have shinytest ",
|
||||
utils::packageVersion("shinytest"), " installed.")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
# =======================================================
|
||||
# Utility functions
|
||||
# =======================================================
|
||||
|
||||
# Check if a directory is empty, ignoring certain files
|
||||
dir_is_empty <- function(path) {
|
||||
files <- list.files(path, all.files = TRUE, no.. = TRUE)
|
||||
@@ -150,144 +125,106 @@ shinyAppTemplate <- function(path = NULL, examples = "default", dryrun = FALSE)
|
||||
return(length(files) != 0)
|
||||
}
|
||||
|
||||
# Helper to resolve paths relative to our template
|
||||
template_path <- function(...) {
|
||||
system.file("app_template", ..., package = "shiny")
|
||||
}
|
||||
|
||||
# Resolve path relative to destination
|
||||
dest_path <- function(...) {
|
||||
file.path(path, ...)
|
||||
}
|
||||
|
||||
mkdir <- function(path) {
|
||||
if (!dirExists(path)) {
|
||||
message("Creating ", ensure_trailing_slash(path))
|
||||
if (!dryrun) {
|
||||
dir.create(path, recursive = TRUE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Copy a file from the template directory to the destination directory. If the
|
||||
# file has templating code (it contains `{{` in the text), then run it through
|
||||
# the htmlTemplate().
|
||||
copy_file_one <- function(name) {
|
||||
from <- template_path(name)
|
||||
to <- dest_path(name)
|
||||
|
||||
message("Creating ", to)
|
||||
if (file.exists(to)) {
|
||||
stop(to, " already exists. Please remove it and try again.", call. = FALSE)
|
||||
}
|
||||
|
||||
if (!dryrun) {
|
||||
is_template <- any(grepl("{{", readLines(from), fixed = TRUE))
|
||||
|
||||
if (is_template) {
|
||||
writeChar(
|
||||
as.character(htmlTemplate(
|
||||
from,
|
||||
rdir = "rdir" %in% examples,
|
||||
module = "module" %in% examples
|
||||
)),
|
||||
con = to,
|
||||
eos = NULL
|
||||
)
|
||||
} else {
|
||||
file.copy(from, to)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Copy multiple files from template to destination.
|
||||
copy_file <- function(names) {
|
||||
for (name in names) {
|
||||
copy_file_one(name)
|
||||
}
|
||||
# Helper to resolve paths relative to our example
|
||||
example_path <- function(path) {
|
||||
system.file("app_template", path, package = "shiny")
|
||||
}
|
||||
|
||||
# Copy the files for a tests/ subdirectory
|
||||
copy_test_dir <- function(name) {
|
||||
files <- dir(template_path("tests"), recursive = TRUE)
|
||||
copy_test_dir <- function(name, with_rdir, with_module) {
|
||||
tests_dir <- file.path(path, "tests")
|
||||
dir.create(tests_dir, showWarnings = FALSE, recursive = TRUE)
|
||||
|
||||
files <- dir(example_path("tests"), recursive = TRUE)
|
||||
# Note: This is not the same as using dir(pattern = "^shinytest"), since
|
||||
# that will not match files inside of shinytest/.
|
||||
files <- files[grepl(paste0("^", name), files)]
|
||||
|
||||
# Filter out files that are not module files in the R directory.
|
||||
if (! "rdir" %in% examples) {
|
||||
# find all files in the testthat folder that are not module or server files
|
||||
is_r_folder_file <- (!grepl("module|server", basename(files))) & (dirname(files) == "testthat")
|
||||
files <- files[!is_r_folder_file]
|
||||
# Filter out files related to R/sort.R, if applicable.
|
||||
if (!with_rdir) {
|
||||
files <- files[!grepl("utils", files)]
|
||||
}
|
||||
|
||||
# Filter out module files, if applicable.
|
||||
if (! "module" %in% examples) {
|
||||
if (!with_module) {
|
||||
files <- files[!grepl("module", files)]
|
||||
}
|
||||
|
||||
mkdir(dest_path("tests"))
|
||||
|
||||
# Create any subdirectories if needed
|
||||
dirs <- setdiff(unique(dirname(files)), ".")
|
||||
for (dir in dirs) {
|
||||
mkdir(dest_path("tests", dir))
|
||||
dir.create(file.path(tests_dir, dir), showWarnings = FALSE, recursive = TRUE)
|
||||
}
|
||||
|
||||
copy_file(file.path("tests", files))
|
||||
file.copy(
|
||||
file.path(example_path("tests"), files),
|
||||
file.path(path, "tests", files)
|
||||
)
|
||||
}
|
||||
|
||||
# =======================================================
|
||||
# Main function
|
||||
# =======================================================
|
||||
|
||||
if (is.null(path)) {
|
||||
stop("`path` is missing.")
|
||||
}
|
||||
if (file.exists(path) && !dirExists(path)) {
|
||||
if (file.exists(path) && !dir.exists(path)) {
|
||||
stop(path, " exists but is not a directory.")
|
||||
}
|
||||
|
||||
if (dirExists(path) && dir_is_empty(path)) {
|
||||
if (dir.exists(path) && dir_is_empty(path)) {
|
||||
if (interactive()) {
|
||||
response <- readline(paste0(
|
||||
ensure_trailing_slash(path),
|
||||
" is not empty. Do you want to use this directory anyway? [y/n] "
|
||||
" is not empty. Do you want to create a Shiny app in this directory anyway? [y/n] "
|
||||
))
|
||||
if (tolower(response) != "y") {
|
||||
return(invisible())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mkdir(path)
|
||||
dir.create(path)
|
||||
}
|
||||
|
||||
# app.R - If "app", populate with example; otherwise use empty file.
|
||||
app_file <- file.path(path, "app.R")
|
||||
if ("app" %in% examples) {
|
||||
copy_file("app.R")
|
||||
if (file.exists(app_file)) {
|
||||
message("Not writing ", app_file, "because file already exists.")
|
||||
|
||||
} else {
|
||||
writeChar(
|
||||
as.character(htmlTemplate(
|
||||
example_path("app.R"),
|
||||
rdir = "rdir" %in% examples,
|
||||
module = "module" %in% examples
|
||||
)),
|
||||
con = app_file,
|
||||
eos = NULL
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
# R/ dir with non-module files
|
||||
# R/ dir with utils and/or module
|
||||
r_dir <- file.path(path, "R")
|
||||
if ("rdir" %in% examples) {
|
||||
files <- dir(template_path("R"))
|
||||
non_module_files <- files[!grepl("module.R$", files)]
|
||||
mkdir(dest_path("R"))
|
||||
copy_file(file.path("R", non_module_files))
|
||||
dir.create(r_dir, showWarnings = FALSE, recursive = TRUE)
|
||||
file.copy(example_path("R/sort.R"), r_dir, recursive = TRUE)
|
||||
}
|
||||
|
||||
# R/ dir with module files
|
||||
if ("module" %in% examples) {
|
||||
files <- dir(template_path("R"))
|
||||
module_files <- files[grepl("module.R$", files)]
|
||||
mkdir(dest_path("R"))
|
||||
copy_file(file.path("R", module_files))
|
||||
dir.create(r_dir, showWarnings = FALSE, recursive = TRUE)
|
||||
file.copy(example_path("R/my-module.R"), r_dir, recursive = TRUE)
|
||||
}
|
||||
|
||||
# tests/ dir
|
||||
if ("shinytest" %in% examples) {
|
||||
copy_test_dir("shinytest")
|
||||
copy_test_dir("shinytest", "rdir" %in% examples, "module" %in% examples)
|
||||
}
|
||||
if ("testthat" %in% examples) {
|
||||
copy_test_dir("testthat")
|
||||
copy_test_dir("testthat", "rdir" %in% examples, "module" %in% examples)
|
||||
}
|
||||
if ("server" %in% examples) {
|
||||
copy_test_dir("server", "rdir" %in% examples, "module" %in% examples)
|
||||
}
|
||||
if ("app" %in% examples) {
|
||||
message("Shiny application created at ", ensure_trailing_slash(path))
|
||||
}
|
||||
invisible()
|
||||
}
|
||||
|
||||
173
R/bootstrap.R
173
R/bootstrap.R
@@ -67,14 +67,11 @@ bootstrapLib <- function(theme = NULL) {
|
||||
),
|
||||
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)
|
||||
"accessibility/js/bootstrap-accessibility.min.js"
|
||||
),
|
||||
stylesheet = if (is.null(theme)) c(
|
||||
"css/bootstrap.min.css",
|
||||
# Safely adding accessibility plugin for screen readers and keyboard users; no break for sighted aspects (see https://github.com/paypal/bootstrap-accessibility-plugin)
|
||||
"accessibility/css/bootstrap-accessibility.css"
|
||||
# These shims are necessary for IE 8 compatibility
|
||||
"shim/html5shiv.min.js",
|
||||
"shim/respond.min.js"
|
||||
),
|
||||
stylesheet = if (is.null(theme)) "css/bootstrap.min.css",
|
||||
meta = list(viewport = "width=device-width, initial-scale=1")
|
||||
)
|
||||
}
|
||||
@@ -467,12 +464,14 @@ helpText <- function(...) {
|
||||
|
||||
#' Create a tab panel
|
||||
#'
|
||||
#' Create a tab panel that can be included within a [tabsetPanel()] or
|
||||
#' a [navbarPage()].
|
||||
#'
|
||||
#' @param title Display title for tab
|
||||
#' @param ... UI elements to include within the tab
|
||||
#' @param value The value that should be sent when `tabsetPanel` reports
|
||||
#' that this tab is selected. If omitted and `tabsetPanel` has an
|
||||
#' `id`, then the title will be used.
|
||||
#' `id`, then the title will be used..
|
||||
#' @param icon Optional icon to appear on the tab. This attribute is only
|
||||
#' valid when using a `tabPanel` within a [navbarPage()].
|
||||
#' @return A tab that can be passed to [tabsetPanel()]
|
||||
@@ -490,29 +489,12 @@ helpText <- function(...) {
|
||||
#' )
|
||||
#' )
|
||||
#' @export
|
||||
#' @describeIn tabPanel Create a tab panel that can be included within a [tabsetPanel()] or a [navbarPage()].
|
||||
tabPanel <- function(title, ..., value = title, icon = NULL) {
|
||||
div(
|
||||
class = "tab-pane",
|
||||
title = title,
|
||||
`data-value` = value,
|
||||
`data-icon-class` = iconClass(icon),
|
||||
...
|
||||
)
|
||||
}
|
||||
#' @export
|
||||
#' @describeIn tabPanel Create a tab panel that drops the title argument.
|
||||
#' This function should be used within `tabsetPanel(type = "hidden")`. See [tabsetPanel()] for example usage.
|
||||
tabPanelBody <- function(value, ..., icon = NULL) {
|
||||
if (
|
||||
!is.character(value) ||
|
||||
length(value) != 1 ||
|
||||
any(is.na(value)) ||
|
||||
nchar(value) == 0
|
||||
) {
|
||||
stop("`value` must be a single, non-empty string value")
|
||||
}
|
||||
tabPanel(title = NULL, ..., value = value, icon = icon)
|
||||
divTag <- div(class="tab-pane",
|
||||
title=title,
|
||||
`data-value`=value,
|
||||
`data-icon-class` = iconClass(icon),
|
||||
...)
|
||||
}
|
||||
|
||||
#' Create a tabset panel
|
||||
@@ -528,13 +510,8 @@ tabPanelBody <- function(value, ..., icon = NULL) {
|
||||
#' @param selected The `value` (or, if none was supplied, the `title`)
|
||||
#' of the tab that should be selected by default. If `NULL`, the first
|
||||
#' tab will be selected.
|
||||
#' @param type \describe{
|
||||
#' \item{`"tabs"`}{Standard tab look}
|
||||
#' \item{`"pills"`}{Selected tabs use the background fill color}
|
||||
#' \item{`"hidden"`}{Hides the selectable tabs. Use `type = "hidden"` in
|
||||
#' conjunction with [tabPanelBody()] and [updateTabsetPanel()] to control the
|
||||
#' active tab via other input controls. (See example below)}
|
||||
#' }
|
||||
#' @param type Use "tabs" for the standard look; Use "pills" for a more plain
|
||||
#' look where tabs are selected using a background fill color.
|
||||
#' @param position This argument is deprecated; it has been discontinued in
|
||||
#' Bootstrap 3.
|
||||
#' @return A tabset that can be passed to [mainPanel()]
|
||||
@@ -552,40 +529,11 @@ tabPanelBody <- function(value, ..., icon = NULL) {
|
||||
#' tabPanel("Table", tableOutput("table"))
|
||||
#' )
|
||||
#' )
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' sidebarLayout(
|
||||
#' sidebarPanel(
|
||||
#' radioButtons("controller", "Controller", 1:3, 1)
|
||||
#' ),
|
||||
#' mainPanel(
|
||||
#' tabsetPanel(
|
||||
#' id = "hidden_tabs",
|
||||
#' # Hide the tab values.
|
||||
#' # Can only switch tabs by using `updateTabsetPanel()`
|
||||
#' type = "hidden",
|
||||
#' tabPanelBody("panel1", "Panel 1 content"),
|
||||
#' tabPanelBody("panel2", "Panel 2 content"),
|
||||
#' tabPanelBody("panel3", "Panel 3 content")
|
||||
#' )
|
||||
#' )
|
||||
#' )
|
||||
#' )
|
||||
#'
|
||||
#' server <- function(input, output, session) {
|
||||
#' observeEvent(input$controller, {
|
||||
#' updateTabsetPanel(session, "hidden_tabs", selected = paste0("panel", input$controller))
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' if (interactive()) {
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#' @export
|
||||
tabsetPanel <- function(...,
|
||||
id = NULL,
|
||||
selected = NULL,
|
||||
type = c("tabs", "pills", "hidden"),
|
||||
type = c("tabs", "pills"),
|
||||
position = NULL) {
|
||||
if (!is.null(position)) {
|
||||
shinyDeprecated(msg = paste("tabsetPanel: argument 'position' is deprecated;",
|
||||
@@ -894,9 +842,42 @@ verbatimTextOutput <- function(outputId, placeholder = FALSE) {
|
||||
#' @rdname plotOutput
|
||||
#' @export
|
||||
imageOutput <- function(outputId, width = "100%", height="400px",
|
||||
click = NULL, dblclick = NULL, hover = NULL, brush = NULL,
|
||||
click = NULL, dblclick = NULL,
|
||||
hover = NULL, hoverDelay = NULL, hoverDelayType = NULL,
|
||||
brush = NULL,
|
||||
clickId = NULL, hoverId = NULL,
|
||||
inline = FALSE) {
|
||||
|
||||
if (!is.null(clickId)) {
|
||||
shinyDeprecated(
|
||||
msg = paste("The 'clickId' argument is deprecated. ",
|
||||
"Please use 'click' instead. ",
|
||||
"See ?imageOutput or ?plotOutput for more information."),
|
||||
version = "0.11.1"
|
||||
)
|
||||
click <- clickId
|
||||
}
|
||||
|
||||
if (!is.null(hoverId)) {
|
||||
shinyDeprecated(
|
||||
msg = paste("The 'hoverId' argument is deprecated. ",
|
||||
"Please use 'hover' instead. ",
|
||||
"See ?imageOutput or ?plotOutput for more information."),
|
||||
version = "0.11.1"
|
||||
)
|
||||
hover <- hoverId
|
||||
}
|
||||
|
||||
if (!is.null(hoverDelay) || !is.null(hoverDelayType)) {
|
||||
shinyDeprecated(
|
||||
msg = paste("The 'hoverDelay'and 'hoverDelayType' arguments are deprecated. ",
|
||||
"Please use 'hoverOpts' instead. ",
|
||||
"See ?imageOutput or ?plotOutput for more information."),
|
||||
version = "0.11.1"
|
||||
)
|
||||
hover <- hoverOpts(id = hover, delay = hoverDelay, delayType = hoverDelayType)
|
||||
}
|
||||
|
||||
style <- if (!inline) {
|
||||
paste("width:", validateCssUnit(width), ";", "height:", validateCssUnit(height))
|
||||
}
|
||||
@@ -1003,6 +984,14 @@ imageOutput <- function(outputId, width = "100%", height="400px",
|
||||
#' named list with `x` and `y` elements indicating the mouse
|
||||
#' position. To control the hover time or hover delay type, you must use
|
||||
#' [hoverOpts()].
|
||||
#' @param clickId Deprecated; use `click` instead. Also see the
|
||||
#' [clickOpts()] function.
|
||||
#' @param hoverId Deprecated; use `hover` instead. Also see the
|
||||
#' [hoverOpts()] function.
|
||||
#' @param hoverDelay Deprecated; use `hover` instead. Also see the
|
||||
#' [hoverOpts()] function.
|
||||
#' @param hoverDelayType Deprecated; use `hover` instead. Also see the
|
||||
#' [hoverOpts()] function.
|
||||
#' @param brush Similar to the `click` argument, this can be `NULL`
|
||||
#' (the default), a string, or an object created by the
|
||||
#' [brushOpts()] function. If you use a value like
|
||||
@@ -1186,12 +1175,16 @@ imageOutput <- function(outputId, width = "100%", height="400px",
|
||||
#' }
|
||||
#' @export
|
||||
plotOutput <- function(outputId, width = "100%", height="400px",
|
||||
click = NULL, dblclick = NULL, hover = NULL, brush = NULL,
|
||||
click = NULL, dblclick = NULL,
|
||||
hover = NULL, hoverDelay = NULL, hoverDelayType = NULL,
|
||||
brush = NULL,
|
||||
clickId = NULL, hoverId = NULL,
|
||||
inline = FALSE) {
|
||||
|
||||
# Result is the same as imageOutput, except for HTML class
|
||||
res <- imageOutput(outputId, width, height, click, dblclick,
|
||||
hover, brush, inline)
|
||||
hover, hoverDelay, hoverDelayType, brush,
|
||||
clickId, hoverId, inline)
|
||||
|
||||
res$attribs$class <- "shiny-plot-output"
|
||||
res
|
||||
@@ -1312,30 +1305,22 @@ uiOutput <- htmlOutput
|
||||
#' is assigned to.
|
||||
#' @param label The label that should appear on the button.
|
||||
#' @param class Additional CSS classes to apply to the tag, if any.
|
||||
#' @param icon An [icon()] to appear on the button. Default is `icon("download")`.
|
||||
#' @param ... Other arguments to pass to the container tag function.
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' ui <- fluidPage(
|
||||
#' downloadButton("downloadData", "Download")
|
||||
#' # In server.R:
|
||||
#' output$downloadData <- downloadHandler(
|
||||
#' filename = function() {
|
||||
#' paste('data-', Sys.Date(), '.csv', sep='')
|
||||
#' },
|
||||
#' content = function(con) {
|
||||
#' write.csv(data, con)
|
||||
#' }
|
||||
#' )
|
||||
#'
|
||||
#' server <- function(input, output) {
|
||||
#' # Our dataset
|
||||
#' data <- mtcars
|
||||
#'
|
||||
#' output$downloadData <- downloadHandler(
|
||||
#' filename = function() {
|
||||
#' paste("data-", Sys.Date(), ".csv", sep="")
|
||||
#' },
|
||||
#' content = function(file) {
|
||||
#' write.csv(data, file)
|
||||
#' }
|
||||
#' )
|
||||
#' }
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
#' # In ui.R:
|
||||
#' downloadLink('downloadData', 'Download')
|
||||
#' }
|
||||
#'
|
||||
#' @aliases downloadLink
|
||||
@@ -1343,15 +1328,13 @@ uiOutput <- htmlOutput
|
||||
#' @export
|
||||
downloadButton <- function(outputId,
|
||||
label="Download",
|
||||
class=NULL,
|
||||
...,
|
||||
icon = shiny::icon("download")) {
|
||||
class=NULL, ...) {
|
||||
aTag <- tags$a(id=outputId,
|
||||
class=paste('btn btn-default shiny-download-link', class),
|
||||
href='',
|
||||
target='_blank',
|
||||
download=NA,
|
||||
validateIcon(icon),
|
||||
icon("download"),
|
||||
label, ...)
|
||||
}
|
||||
|
||||
@@ -1375,7 +1358,7 @@ downloadLink <- function(outputId, label="Download", class=NULL, ...) {
|
||||
#'
|
||||
#' @param name Name of icon. Icons are drawn from the
|
||||
#' [Font Awesome Free](https://fontawesome.com/) (currently icons from
|
||||
#' the v5.13.0 set are supported with the v4 naming convention) and
|
||||
#' the v5.3.1 set are supported with the v4 naming convention) and
|
||||
#' [Glyphicons](http://getbootstrap.com/components/#glyphicons)
|
||||
#' libraries. Note that the "fa-" and "glyphicon-" prefixes should not be used
|
||||
#' in icon names (i.e. the "fa-calendar" icon should be referred to as
|
||||
@@ -1428,12 +1411,12 @@ icon <- function(name, class = NULL, lib = "font-awesome") {
|
||||
if (!is.null(class))
|
||||
iconClass <- paste(iconClass, class)
|
||||
|
||||
iconTag <- tags$i(class = iconClass, role = "presentation", `aria-label` = paste(name, "icon"))
|
||||
iconTag <- tags$i(class = iconClass)
|
||||
|
||||
# font-awesome needs an additional dependency (glyphicon is in bootstrap)
|
||||
if (lib == "font-awesome") {
|
||||
htmlDependencies(iconTag) <- htmlDependency(
|
||||
"font-awesome", "5.13.0", "www/shared/fontawesome", package = "shiny",
|
||||
"font-awesome", "5.3.1", "www/shared/fontawesome", package = "shiny",
|
||||
stylesheet = c(
|
||||
"css/all.min.css",
|
||||
"css/v4-shims.min.css"
|
||||
|
||||
77
R/cache-context.R
Normal file
77
R/cache-context.R
Normal file
@@ -0,0 +1,77 @@
|
||||
# A context object for tracking a cache that needs to be dirtied when a set of
|
||||
# files changes on disk. Each time the cache is dirtied, the set of files is
|
||||
# cleared. Therefore, the set of files needs to be re-built each time the cached
|
||||
# code executes. This approach allows for dynamic dependency graphs.
|
||||
CacheContext <- R6Class(
|
||||
'CacheContext',
|
||||
portable = FALSE,
|
||||
class = FALSE,
|
||||
public = list(
|
||||
.dirty = TRUE,
|
||||
# List of functions that return TRUE if dirty
|
||||
.tests = list(),
|
||||
|
||||
addDependencyFile = function(file) {
|
||||
if (.dirty)
|
||||
return()
|
||||
|
||||
file <- normalizePath(file)
|
||||
|
||||
mtime <- file.info(file)$mtime
|
||||
.tests <<- c(.tests, function() {
|
||||
newMtime <- try(file.info(file)$mtime, silent=TRUE)
|
||||
if (inherits(newMtime, 'try-error'))
|
||||
return(TRUE)
|
||||
return(!identical(mtime, newMtime))
|
||||
})
|
||||
invisible()
|
||||
},
|
||||
forceDirty = function() {
|
||||
.dirty <<- TRUE
|
||||
.tests <<- list()
|
||||
invisible()
|
||||
},
|
||||
isDirty = function() {
|
||||
if (.dirty)
|
||||
return(TRUE)
|
||||
|
||||
for (test in .tests) {
|
||||
if (test()) {
|
||||
forceDirty()
|
||||
return(TRUE)
|
||||
}
|
||||
}
|
||||
|
||||
return(FALSE)
|
||||
},
|
||||
reset = function() {
|
||||
.dirty <<- FALSE
|
||||
.tests <<- list()
|
||||
},
|
||||
with = function(func) {
|
||||
oldCC <- .currentCacheContext$cc
|
||||
.currentCacheContext$cc <- self
|
||||
on.exit(.currentCacheContext$cc <- oldCC)
|
||||
|
||||
return(func())
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
.currentCacheContext <- new.env()
|
||||
|
||||
# Indicates to Shiny that the given file path is part of the dependency graph
|
||||
# for whatever is currently executing (so far, only ui.R). By default, ui.R only
|
||||
# gets re-executed when it is detected to have changed; this function allows the
|
||||
# caller to indicate that it should also re-execute if the given file changes.
|
||||
#
|
||||
# If NULL or NA is given as the argument, then ui.R will re-execute next time.
|
||||
dependsOnFile <- function(filepath) {
|
||||
if (is.null(.currentCacheContext$cc))
|
||||
return()
|
||||
|
||||
if (is.null(filepath) || is.na(filepath))
|
||||
.currentCacheContext$cc$forceDirty()
|
||||
else
|
||||
.currentCacheContext$cc$addDependencyFile(filepath)
|
||||
}
|
||||
@@ -130,8 +130,8 @@
|
||||
#' if an object is in the cache, and then call `get(key)`, the object may
|
||||
#' be removed from the cache in between those two calls, and `get(key)`
|
||||
#' will throw an error. Instead of calling the two functions, it is better to
|
||||
#' simply call `get(key)`, and check that the returned object is not a
|
||||
#' `key_missing()` object, using `is.key_missing()`. This effectively tests for
|
||||
#' simply call `get(key)`, and use `tryCatch()` to handle the error
|
||||
#' that is thrown if the object is not in the cache. This effectively tests for
|
||||
#' existence and gets the object in one operation.
|
||||
#'
|
||||
#' It is also possible for one processes to prune objects at the same time that
|
||||
@@ -387,7 +387,7 @@ DiskCache <- R6Class("DiskCache",
|
||||
# is because it is expensive to find the size of the serialized object
|
||||
# before adding it.
|
||||
|
||||
private$log('prune')
|
||||
private$log(paste0('prune'))
|
||||
self$is_destroyed(throw = TRUE)
|
||||
|
||||
current_time <- Sys.time()
|
||||
@@ -407,11 +407,7 @@ DiskCache <- R6Class("DiskCache",
|
||||
rm_idx <- timediff > private$max_age
|
||||
if (any(rm_idx)) {
|
||||
private$log(paste0("prune max_age: Removing ", paste(info$name[rm_idx], collapse = ", ")))
|
||||
rm_success <- file.remove(info$name[rm_idx])
|
||||
# This maps rm_success back into the TRUEs in the rm_idx vector.
|
||||
# If (for example) rm_idx is c(F,T,F,T,T) and rm_success is c(T,F,T),
|
||||
# then this line modifies rm_idx to be c(F,T,F,F,T).
|
||||
rm_idx[rm_idx] <- rm_success
|
||||
file.remove(info$name[rm_idx])
|
||||
info <- info[!rm_idx, ]
|
||||
}
|
||||
}
|
||||
@@ -432,8 +428,7 @@ DiskCache <- R6Class("DiskCache",
|
||||
rm_idx <- seq_len(nrow(info)) > private$max_n
|
||||
private$log(paste0("prune max_n: Removing ", paste(info$name[rm_idx], collapse = ", ")))
|
||||
rm_success <- file.remove(info$name[rm_idx])
|
||||
rm_idx[rm_idx] <- rm_success
|
||||
info <- info[!rm_idx, ]
|
||||
info <- info[!rm_success, ]
|
||||
}
|
||||
|
||||
# 3. Remove files if cache is too large.
|
||||
@@ -443,8 +438,7 @@ DiskCache <- R6Class("DiskCache",
|
||||
rm_idx <- cum_size > private$max_size
|
||||
private$log(paste0("prune max_size: Removing ", paste(info$name[rm_idx], collapse = ", ")))
|
||||
rm_success <- file.remove(info$name[rm_idx])
|
||||
rm_idx[rm_idx] <- rm_success
|
||||
info <- info[!rm_idx, ]
|
||||
info <- info[!rm_success, ]
|
||||
}
|
||||
|
||||
private$prune_last_time <- as.numeric(current_time)
|
||||
@@ -561,7 +555,7 @@ DiskCache <- R6Class("DiskCache",
|
||||
if (is.null(private$logfile)) return()
|
||||
|
||||
text <- paste0(format(Sys.time(), "[%Y-%m-%d %H:%M:%OS3] DiskCache "), text)
|
||||
cat(text, sep = "\n", file = private$logfile, append = TRUE)
|
||||
writeLines(text, private$logfile)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
@@ -359,7 +359,7 @@ MemoryCache <- R6Class("MemoryCache",
|
||||
if (is.null(private$logfile)) return()
|
||||
|
||||
text <- paste0(format(Sys.time(), "[%Y-%m-%d %H:%M:%OS3] MemoryCache "), text)
|
||||
cat(text, sep = "\n", file = private$logfile, append = TRUE)
|
||||
writeLines(text, private$logfile)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
518
R/font-awesome.R
518
R/font-awesome.R
@@ -1,445 +1,75 @@
|
||||
font_awesome_brands <- c(
|
||||
"500px",
|
||||
"accessible-icon",
|
||||
"accusoft",
|
||||
"acquisitions-incorporated",
|
||||
"adn",
|
||||
"adobe",
|
||||
"adversal",
|
||||
"affiliatetheme",
|
||||
"airbnb",
|
||||
"algolia",
|
||||
"alipay",
|
||||
"amazon",
|
||||
"amazon-pay",
|
||||
"amilia",
|
||||
"android",
|
||||
"angellist",
|
||||
"angrycreative",
|
||||
"angular",
|
||||
"app-store",
|
||||
"app-store-ios",
|
||||
"apper",
|
||||
"apple",
|
||||
"apple-pay",
|
||||
"artstation",
|
||||
"asymmetrik",
|
||||
"atlassian",
|
||||
"audible",
|
||||
"autoprefixer",
|
||||
"avianex",
|
||||
"aviato",
|
||||
"aws",
|
||||
"bandcamp",
|
||||
"battle-net",
|
||||
"behance",
|
||||
"behance-square",
|
||||
"bimobject",
|
||||
"bitbucket",
|
||||
"bitcoin",
|
||||
"bity",
|
||||
"black-tie",
|
||||
"blackberry",
|
||||
"blogger",
|
||||
"blogger-b",
|
||||
"bluetooth",
|
||||
"bluetooth-b",
|
||||
"bootstrap",
|
||||
"btc",
|
||||
"buffer",
|
||||
"buromobelexperte",
|
||||
"buy-n-large",
|
||||
"buysellads",
|
||||
"canadian-maple-leaf",
|
||||
"cc-amazon-pay",
|
||||
"cc-amex",
|
||||
"cc-apple-pay",
|
||||
"cc-diners-club",
|
||||
"cc-discover",
|
||||
"cc-jcb",
|
||||
"cc-mastercard",
|
||||
"cc-paypal",
|
||||
"cc-stripe",
|
||||
"cc-visa",
|
||||
"centercode",
|
||||
"centos",
|
||||
"chrome",
|
||||
"chromecast",
|
||||
"cloudscale",
|
||||
"cloudsmith",
|
||||
"cloudversify",
|
||||
"codepen",
|
||||
"codiepie",
|
||||
"confluence",
|
||||
"connectdevelop",
|
||||
"contao",
|
||||
"cotton-bureau",
|
||||
"cpanel",
|
||||
"creative-commons",
|
||||
"creative-commons-by",
|
||||
"creative-commons-nc",
|
||||
"creative-commons-nc-eu",
|
||||
"creative-commons-nc-jp",
|
||||
"creative-commons-nd",
|
||||
"creative-commons-pd",
|
||||
"creative-commons-pd-alt",
|
||||
"creative-commons-remix",
|
||||
"creative-commons-sa",
|
||||
"creative-commons-sampling",
|
||||
"creative-commons-sampling-plus",
|
||||
"creative-commons-share",
|
||||
"creative-commons-zero",
|
||||
"critical-role",
|
||||
"css3",
|
||||
"css3-alt",
|
||||
"cuttlefish",
|
||||
"d-and-d",
|
||||
"d-and-d-beyond",
|
||||
"dailymotion",
|
||||
"dashcube",
|
||||
"delicious",
|
||||
"deploydog",
|
||||
"deskpro",
|
||||
"dev",
|
||||
"deviantart",
|
||||
"dhl",
|
||||
"diaspora",
|
||||
"digg",
|
||||
"digital-ocean",
|
||||
"discord",
|
||||
"discourse",
|
||||
"dochub",
|
||||
"docker",
|
||||
"draft2digital",
|
||||
"dribbble",
|
||||
"dribbble-square",
|
||||
"dropbox",
|
||||
"drupal",
|
||||
"dyalog",
|
||||
"earlybirds",
|
||||
"ebay",
|
||||
"edge",
|
||||
"elementor",
|
||||
"ello",
|
||||
"ember",
|
||||
"empire",
|
||||
"envira",
|
||||
"erlang",
|
||||
"ethereum",
|
||||
"etsy",
|
||||
"evernote",
|
||||
"expeditedssl",
|
||||
"facebook",
|
||||
"facebook-f",
|
||||
"facebook-messenger",
|
||||
"facebook-square",
|
||||
"fantasy-flight-games",
|
||||
"fedex",
|
||||
"fedora",
|
||||
"figma",
|
||||
"firefox",
|
||||
"firefox-browser",
|
||||
"first-order",
|
||||
"first-order-alt",
|
||||
"firstdraft",
|
||||
"flickr",
|
||||
"flipboard",
|
||||
"fly",
|
||||
"font-awesome",
|
||||
"font-awesome-alt",
|
||||
"font-awesome-flag",
|
||||
"font-awesome-logo-full",
|
||||
"fonticons",
|
||||
"fonticons-fi",
|
||||
"fort-awesome",
|
||||
"fort-awesome-alt",
|
||||
"forumbee",
|
||||
"foursquare",
|
||||
"free-code-camp",
|
||||
"freebsd",
|
||||
"fulcrum",
|
||||
"galactic-republic",
|
||||
"galactic-senate",
|
||||
"get-pocket",
|
||||
"gg",
|
||||
"gg-circle",
|
||||
"git",
|
||||
"git-alt",
|
||||
"git-square",
|
||||
"github",
|
||||
"github-alt",
|
||||
"github-square",
|
||||
"gitkraken",
|
||||
"gitlab",
|
||||
"gitter",
|
||||
"glide",
|
||||
"glide-g",
|
||||
"gofore",
|
||||
"goodreads",
|
||||
"goodreads-g",
|
||||
"google",
|
||||
"google-drive",
|
||||
"google-play",
|
||||
"google-plus",
|
||||
"google-plus-g",
|
||||
"google-plus-square",
|
||||
"google-wallet",
|
||||
"gratipay",
|
||||
"grav",
|
||||
"gripfire",
|
||||
"grunt",
|
||||
"gulp",
|
||||
"hacker-news",
|
||||
"hacker-news-square",
|
||||
"hackerrank",
|
||||
"hips",
|
||||
"hire-a-helper",
|
||||
"hooli",
|
||||
"hornbill",
|
||||
"hotjar",
|
||||
"houzz",
|
||||
"html5",
|
||||
"hubspot",
|
||||
"ideal",
|
||||
"imdb",
|
||||
"instagram",
|
||||
"instagram-square",
|
||||
"intercom",
|
||||
"internet-explorer",
|
||||
"invision",
|
||||
"ioxhost",
|
||||
"itch-io",
|
||||
"itunes",
|
||||
"itunes-note",
|
||||
"java",
|
||||
"jedi-order",
|
||||
"jenkins",
|
||||
"jira",
|
||||
"joget",
|
||||
"joomla",
|
||||
"js",
|
||||
"js-square",
|
||||
"jsfiddle",
|
||||
"kaggle",
|
||||
"keybase",
|
||||
"keycdn",
|
||||
"kickstarter",
|
||||
"kickstarter-k",
|
||||
"korvue",
|
||||
"laravel",
|
||||
"lastfm",
|
||||
"lastfm-square",
|
||||
"leanpub",
|
||||
"less",
|
||||
"line",
|
||||
"linkedin",
|
||||
"linkedin-in",
|
||||
"linode",
|
||||
"linux",
|
||||
"lyft",
|
||||
"magento",
|
||||
"mailchimp",
|
||||
"mandalorian",
|
||||
"markdown",
|
||||
"mastodon",
|
||||
"maxcdn",
|
||||
"mdb",
|
||||
"medapps",
|
||||
"medium",
|
||||
"medium-m",
|
||||
"medrt",
|
||||
"meetup",
|
||||
"megaport",
|
||||
"mendeley",
|
||||
"microblog",
|
||||
"microsoft",
|
||||
"mix",
|
||||
"mixcloud",
|
||||
"mixer",
|
||||
"mizuni",
|
||||
"modx",
|
||||
"monero",
|
||||
"napster",
|
||||
"neos",
|
||||
"nimblr",
|
||||
"node",
|
||||
"node-js",
|
||||
"npm",
|
||||
"ns8",
|
||||
"nutritionix",
|
||||
"odnoklassniki",
|
||||
"odnoklassniki-square",
|
||||
"old-republic",
|
||||
"opencart",
|
||||
"openid",
|
||||
"opera",
|
||||
"optin-monster",
|
||||
"orcid",
|
||||
"osi",
|
||||
"page4",
|
||||
"pagelines",
|
||||
"palfed",
|
||||
"patreon",
|
||||
"paypal",
|
||||
"penny-arcade",
|
||||
"periscope",
|
||||
"phabricator",
|
||||
"phoenix-framework",
|
||||
"phoenix-squadron",
|
||||
"php",
|
||||
"pied-piper",
|
||||
"pied-piper-alt",
|
||||
"pied-piper-hat",
|
||||
"pied-piper-pp",
|
||||
"pied-piper-square",
|
||||
"pinterest",
|
||||
"pinterest-p",
|
||||
"pinterest-square",
|
||||
"playstation",
|
||||
"product-hunt",
|
||||
"pushed",
|
||||
"python",
|
||||
"qq",
|
||||
"quinscape",
|
||||
"quora",
|
||||
"r-project",
|
||||
"raspberry-pi",
|
||||
"ravelry",
|
||||
"react",
|
||||
"reacteurope",
|
||||
"readme",
|
||||
"rebel",
|
||||
"red-river",
|
||||
"reddit",
|
||||
"reddit-alien",
|
||||
"reddit-square",
|
||||
"redhat",
|
||||
"renren",
|
||||
"replyd",
|
||||
"researchgate",
|
||||
"resolving",
|
||||
"rev",
|
||||
"rocketchat",
|
||||
"rockrms",
|
||||
"safari",
|
||||
"salesforce",
|
||||
"sass",
|
||||
"schlix",
|
||||
"scribd",
|
||||
"searchengin",
|
||||
"sellcast",
|
||||
"sellsy",
|
||||
"servicestack",
|
||||
"shirtsinbulk",
|
||||
"shopify",
|
||||
"shopware",
|
||||
"simplybuilt",
|
||||
"sistrix",
|
||||
"sith",
|
||||
"sketch",
|
||||
"skyatlas",
|
||||
"skype",
|
||||
"slack",
|
||||
"slack-hash",
|
||||
"slideshare",
|
||||
"snapchat",
|
||||
"snapchat-ghost",
|
||||
"snapchat-square",
|
||||
"soundcloud",
|
||||
"sourcetree",
|
||||
"speakap",
|
||||
"speaker-deck",
|
||||
"spotify",
|
||||
"squarespace",
|
||||
"stack-exchange",
|
||||
"stack-overflow",
|
||||
"stackpath",
|
||||
"staylinked",
|
||||
"steam",
|
||||
"steam-square",
|
||||
"steam-symbol",
|
||||
"sticker-mule",
|
||||
"strava",
|
||||
"stripe",
|
||||
"stripe-s",
|
||||
"studiovinari",
|
||||
"stumbleupon",
|
||||
"stumbleupon-circle",
|
||||
"superpowers",
|
||||
"supple",
|
||||
"suse",
|
||||
"swift",
|
||||
"symfony",
|
||||
"teamspeak",
|
||||
"telegram",
|
||||
"telegram-plane",
|
||||
"tencent-weibo",
|
||||
"the-red-yeti",
|
||||
"themeco",
|
||||
"themeisle",
|
||||
"think-peaks",
|
||||
"trade-federation",
|
||||
"trello",
|
||||
"tripadvisor",
|
||||
"tumblr",
|
||||
"tumblr-square",
|
||||
"twitch",
|
||||
"twitter",
|
||||
"twitter-square",
|
||||
"typo3",
|
||||
"uber",
|
||||
"ubuntu",
|
||||
"uikit",
|
||||
"umbraco",
|
||||
"uniregistry",
|
||||
"unity",
|
||||
"untappd",
|
||||
"ups",
|
||||
"usb",
|
||||
"usps",
|
||||
"ussunnah",
|
||||
"vaadin",
|
||||
"viacoin",
|
||||
"viadeo",
|
||||
"viadeo-square",
|
||||
"viber",
|
||||
"vimeo",
|
||||
"vimeo-square",
|
||||
"vimeo-v",
|
||||
"vine",
|
||||
"vk",
|
||||
"vnv",
|
||||
"vuejs",
|
||||
"waze",
|
||||
"weebly",
|
||||
"weibo",
|
||||
"weixin",
|
||||
"whatsapp",
|
||||
"whatsapp-square",
|
||||
"whmcs",
|
||||
"wikipedia-w",
|
||||
"windows",
|
||||
"wix",
|
||||
"wizards-of-the-coast",
|
||||
"wolf-pack-battalion",
|
||||
"wordpress",
|
||||
"wordpress-simple",
|
||||
"wpbeginner",
|
||||
"wpexplorer",
|
||||
"wpforms",
|
||||
"wpressr",
|
||||
"xbox",
|
||||
"xing",
|
||||
"xing-square",
|
||||
"y-combinator",
|
||||
"yahoo",
|
||||
"yammer",
|
||||
"yandex",
|
||||
"yandex-international",
|
||||
"yarn",
|
||||
"yelp",
|
||||
"yoast",
|
||||
"youtube",
|
||||
"youtube-square",
|
||||
"zhihu"
|
||||
)
|
||||
"500px", "accessible-icon", "accusoft", "adn", "adversal",
|
||||
"affiliatetheme", "algolia", "alipay", "amazon", "amazon-pay",
|
||||
"amilia", "android", "angellist", "angrycreative", "angular",
|
||||
"app-store", "app-store-ios", "apper", "apple", "apple-pay",
|
||||
"asymmetrik", "audible", "autoprefixer", "avianex", "aviato",
|
||||
"aws", "bandcamp", "behance", "behance-square", "bimobject",
|
||||
"bitbucket", "bitcoin", "bity", "black-tie", "blackberry", "blogger",
|
||||
"blogger-b", "bluetooth", "bluetooth-b", "btc", "buromobelexperte",
|
||||
"buysellads", "cc-amazon-pay", "cc-amex", "cc-apple-pay", "cc-diners-club",
|
||||
"cc-discover", "cc-jcb", "cc-mastercard", "cc-paypal", "cc-stripe",
|
||||
"cc-visa", "centercode", "chrome", "cloudscale", "cloudsmith",
|
||||
"cloudversify", "codepen", "codiepie", "connectdevelop", "contao",
|
||||
"cpanel", "creative-commons", "creative-commons-by", "creative-commons-nc",
|
||||
"creative-commons-nc-eu", "creative-commons-nc-jp", "creative-commons-nd",
|
||||
"creative-commons-pd", "creative-commons-pd-alt", "creative-commons-remix",
|
||||
"creative-commons-sa", "creative-commons-sampling", "creative-commons-sampling-plus",
|
||||
"creative-commons-share", "css3", "css3-alt", "cuttlefish", "d-and-d",
|
||||
"dashcube", "delicious", "deploydog", "deskpro", "deviantart",
|
||||
"digg", "digital-ocean", "discord", "discourse", "dochub", "docker",
|
||||
"draft2digital", "dribbble", "dribbble-square", "dropbox", "drupal",
|
||||
"dyalog", "earlybirds", "ebay", "edge", "elementor", "ello",
|
||||
"ember", "empire", "envira", "erlang", "ethereum", "etsy", "expeditedssl",
|
||||
"facebook", "facebook-f", "facebook-messenger", "facebook-square",
|
||||
"firefox", "first-order", "first-order-alt", "firstdraft", "flickr",
|
||||
"flipboard", "fly", "font-awesome", "font-awesome-alt", "font-awesome-flag",
|
||||
"font-awesome-logo-full", "fonticons", "fonticons-fi", "fort-awesome",
|
||||
"fort-awesome-alt", "forumbee", "foursquare", "free-code-camp",
|
||||
"freebsd", "fulcrum", "galactic-republic", "galactic-senate",
|
||||
"get-pocket", "gg", "gg-circle", "git", "git-square", "github",
|
||||
"github-alt", "github-square", "gitkraken", "gitlab", "gitter",
|
||||
"glide", "glide-g", "gofore", "goodreads", "goodreads-g", "google",
|
||||
"google-drive", "google-play", "google-plus", "google-plus-g",
|
||||
"google-plus-square", "google-wallet", "gratipay", "grav", "gripfire",
|
||||
"grunt", "gulp", "hacker-news", "hacker-news-square", "hackerrank",
|
||||
"hips", "hire-a-helper", "hooli", "hornbill", "hotjar", "houzz",
|
||||
"html5", "hubspot", "imdb", "instagram", "internet-explorer",
|
||||
"ioxhost", "itunes", "itunes-note", "java", "jedi-order", "jenkins",
|
||||
"joget", "joomla", "js", "js-square", "jsfiddle", "kaggle", "keybase",
|
||||
"keycdn", "kickstarter", "kickstarter-k", "korvue", "laravel",
|
||||
"lastfm", "lastfm-square", "leanpub", "less", "line", "linkedin",
|
||||
"linkedin-in", "linode", "linux", "lyft", "magento", "mailchimp",
|
||||
"mandalorian", "markdown", "mastodon", "maxcdn", "medapps", "medium",
|
||||
"medium-m", "medrt", "meetup", "megaport", "microsoft", "mix",
|
||||
"mixcloud", "mizuni", "modx", "monero", "napster", "neos", "nimblr",
|
||||
"nintendo-switch", "node", "node-js", "npm", "ns8", "nutritionix",
|
||||
"odnoklassniki", "odnoklassniki-square", "old-republic", "opencart",
|
||||
"openid", "opera", "optin-monster", "osi", "page4", "pagelines",
|
||||
"palfed", "patreon", "paypal", "periscope", "phabricator", "phoenix-framework",
|
||||
"phoenix-squadron", "php", "pied-piper", "pied-piper-alt", "pied-piper-hat",
|
||||
"pied-piper-pp", "pinterest", "pinterest-p", "pinterest-square",
|
||||
"playstation", "product-hunt", "pushed", "python", "qq", "quinscape",
|
||||
"quora", "r-project", "ravelry", "react", "readme", "rebel",
|
||||
"red-river", "reddit", "reddit-alien", "reddit-square", "rendact",
|
||||
"renren", "replyd", "researchgate", "resolving", "rev", "rocketchat",
|
||||
"rockrms", "safari", "sass", "schlix", "scribd", "searchengin",
|
||||
"sellcast", "sellsy", "servicestack", "shirtsinbulk", "shopware",
|
||||
"simplybuilt", "sistrix", "sith", "skyatlas", "skype", "slack",
|
||||
"slack-hash", "slideshare", "snapchat", "snapchat-ghost", "snapchat-square",
|
||||
"soundcloud", "speakap", "spotify", "squarespace", "stack-exchange",
|
||||
"stack-overflow", "staylinked", "steam", "steam-square", "steam-symbol",
|
||||
"sticker-mule", "strava", "stripe", "stripe-s", "studiovinari",
|
||||
"stumbleupon", "stumbleupon-circle", "superpowers", "supple",
|
||||
"teamspeak", "telegram", "telegram-plane", "tencent-weibo", "the-red-yeti",
|
||||
"themeco", "themeisle", "trade-federation", "trello", "tripadvisor",
|
||||
"tumblr", "tumblr-square", "twitch", "twitter", "twitter-square",
|
||||
"typo3", "uber", "uikit", "uniregistry", "untappd", "usb", "ussunnah",
|
||||
"vaadin", "viacoin", "viadeo", "viadeo-square", "viber", "vimeo",
|
||||
"vimeo-square", "vimeo-v", "vine", "vk", "vnv", "vuejs", "weebly",
|
||||
"weibo", "weixin", "whatsapp", "whatsapp-square", "whmcs", "wikipedia-w",
|
||||
"windows", "wix", "wolf-pack-battalion", "wordpress", "wordpress-simple",
|
||||
"wpbeginner", "wpexplorer", "wpforms", "xbox", "xing", "xing-square",
|
||||
"y-combinator", "yahoo", "yandex", "yandex-international", "yelp",
|
||||
"yoast", "youtube", "youtube-square", "zhihu"
|
||||
)
|
||||
|
||||
12
R/globals.R
12
R/globals.R
@@ -28,6 +28,14 @@ register_s3_method <- function(pkg, generic, class, fun = NULL) {
|
||||
}
|
||||
|
||||
register_upgrade_message <- function(pkg, version) {
|
||||
# Is an out-dated version of this package installed?
|
||||
needs_upgrade <- function() {
|
||||
if (system.file(package = pkg) == "")
|
||||
return(FALSE)
|
||||
if (utils::packageVersion(pkg) >= version)
|
||||
return(FALSE)
|
||||
TRUE
|
||||
}
|
||||
|
||||
msg <- sprintf(
|
||||
"This version of Shiny is designed to work with '%s' >= %s.
|
||||
@@ -35,7 +43,7 @@ register_upgrade_message <- function(pkg, version) {
|
||||
pkg, version, pkg
|
||||
)
|
||||
|
||||
if (pkg %in% loadedNamespaces() && !is_available(pkg, version)) {
|
||||
if (pkg %in% loadedNamespaces() && needs_upgrade()) {
|
||||
packageStartupMessage(msg)
|
||||
}
|
||||
|
||||
@@ -45,7 +53,7 @@ register_upgrade_message <- function(pkg, version) {
|
||||
setHook(
|
||||
packageEvent(pkg, "onLoad"),
|
||||
function(...) {
|
||||
if (!is_available(pkg, version)) packageStartupMessage(msg)
|
||||
if (needs_upgrade()) packageStartupMessage(msg)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
25
R/graph.R
25
R/graph.R
@@ -1,20 +1,31 @@
|
||||
is_installed <- function(package, version) {
|
||||
installedVersion <- tryCatch(utils::packageVersion(package), error = function(e) NA)
|
||||
!is.na(installedVersion) && installedVersion >= version
|
||||
}
|
||||
|
||||
# Check that the version of an suggested package satisfies the requirements
|
||||
#
|
||||
# @param package The name of the suggested package
|
||||
# @param version The version of the package
|
||||
check_suggested <- function(package, version = NULL) {
|
||||
check_suggested <- function(package, version, location) {
|
||||
|
||||
if (is_available(package, version)) {
|
||||
if (is_installed(package, version)) {
|
||||
return()
|
||||
}
|
||||
|
||||
missing_location <- missing(location)
|
||||
msg <- paste0(
|
||||
sQuote(package),
|
||||
if (is.na(version %OR% NA)) "" else paste0("(>= ", version, ")"),
|
||||
" must be installed for this functionality."
|
||||
if (is.na(version)) "" else paste0("(>= ", version, ")"),
|
||||
" must be installed for this functionality.",
|
||||
if (!missing_location)
|
||||
paste0(
|
||||
"\nPlease install the missing package: \n",
|
||||
" source(\"https://install-github.me/", location, "\")"
|
||||
)
|
||||
)
|
||||
|
||||
if (interactive()) {
|
||||
if (interactive() && missing_location) {
|
||||
message(msg, "\nWould you like to install it?")
|
||||
if (utils::menu(c("Yes", "No")) == 1) {
|
||||
return(utils::install.packages(package))
|
||||
@@ -179,10 +190,10 @@ RLog <- R6Class(
|
||||
paste0("names(", reactId, ")")
|
||||
},
|
||||
asListIdStr = function(reactId) {
|
||||
paste0("reactiveValuesToList(", reactId, ")")
|
||||
paste0("as.list(", reactId, ")")
|
||||
},
|
||||
asListAllIdStr = function(reactId) {
|
||||
paste0("reactiveValuesToList(", reactId, ", all.names = TRUE)")
|
||||
paste0("as.list(", reactId, ", all.names = TRUE)")
|
||||
},
|
||||
keyIdStr = function(reactId, key) {
|
||||
paste0(reactId, "$", key)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#' then the server will receive click events even when the mouse is outside
|
||||
#' the plotting area, as long as it is still inside the image.
|
||||
#' @export
|
||||
clickOpts <- function(id, clip = TRUE) {
|
||||
clickOpts <- function(id = NULL, clip = TRUE) {
|
||||
if (is.null(id))
|
||||
stop("id must not be NULL")
|
||||
|
||||
@@ -36,7 +36,7 @@ clickOpts <- function(id, clip = TRUE) {
|
||||
#' @param delay Maximum delay (in ms) between a pair clicks for them to be
|
||||
#' counted as a double-click.
|
||||
#' @export
|
||||
dblclickOpts <- function(id, clip = TRUE, delay = 400) {
|
||||
dblclickOpts <- function(id = NULL, clip = TRUE, delay = 400) {
|
||||
if (is.null(id))
|
||||
stop("id must not be NULL")
|
||||
|
||||
@@ -69,7 +69,7 @@ dblclickOpts <- function(id, clip = TRUE, delay = 400) {
|
||||
#' `NULL` when the mouse exits the plotting area. If `FALSE`, the
|
||||
#' value will stop changing when the cursor exits the plotting area.
|
||||
#' @export
|
||||
hoverOpts <- function(id, delay = 300,
|
||||
hoverOpts <- function(id = NULL, delay = 300,
|
||||
delayType = c("debounce", "throttle"), clip = TRUE,
|
||||
nullOutside = TRUE) {
|
||||
if (is.null(id))
|
||||
@@ -117,7 +117,7 @@ hoverOpts <- function(id, delay = 300,
|
||||
#' brush. Using `TRUE` is useful if you want to clear the brush whenever
|
||||
#' the plot is updated.
|
||||
#' @export
|
||||
brushOpts <- function(id, fill = "#9cf", stroke = "#036",
|
||||
brushOpts <- function(id = NULL, fill = "#9cf", stroke = "#036",
|
||||
opacity = 0.25, delay = 300,
|
||||
delayType = c("debounce", "throttle"), clip = TRUE,
|
||||
direction = c("xy", "x", "y"),
|
||||
|
||||
@@ -1,76 +1,59 @@
|
||||
#' Find rows of data selected on an interactive plot.
|
||||
#' Find rows of data that are selected by a brush
|
||||
#'
|
||||
#' @description
|
||||
#' `brushedPoints()` returns rows from a data frame which are under a brush.
|
||||
#' `nearPoints()` returns rows from a data frame which are near a click, hover,
|
||||
#' or double-click. Alternatively, set `allRows = TRUE` to return all rows from
|
||||
#' the input data with an additional column `selected_` that indicates which
|
||||
#' rows of the would be selected.
|
||||
#' This function returns rows from a data frame which are under a brush used
|
||||
#' with [plotOutput()].
|
||||
#'
|
||||
#' @section ggplot2:
|
||||
#' For plots created with ggplot2, it is not necessary to specify the
|
||||
#' column names to `xvar`, `yvar`, `panelvar1`, and `panelvar2` as that
|
||||
#' information can be automatically derived from the plot specification.
|
||||
#' It is also possible for this function to return all rows from the input data
|
||||
#' frame, but with an additional column `selected_`, which indicates which
|
||||
#' rows of the input data frame are selected by the brush (`TRUE` for
|
||||
#' selected, `FALSE` for not-selected). This is enabled by setting
|
||||
#' `allRows=TRUE` option.
|
||||
#'
|
||||
#' Note, however, that this will not work if you use a computed column, like
|
||||
#' `aes(speed/2, dist))`. Instead, we recommend that you modify the data
|
||||
#' The `xvar`, `yvar`, `panelvar1`, and `panelvar2`
|
||||
#' arguments specify which columns in the data correspond to the x variable, y
|
||||
#' variable, and panel variables of the plot. For example, if your plot is
|
||||
#' `plot(x=cars$speed, y=cars$dist)`, and your brush is named
|
||||
#' `"cars_brush"`, then you would use `brushedPoints(cars,
|
||||
#' input$cars_brush, "speed", "dist")`.
|
||||
#'
|
||||
#' For plots created with ggplot2, it should not be necessary to specify the
|
||||
#' column names; that information will already be contained in the brush,
|
||||
#' provided that variables are in the original data, and not computed. For
|
||||
#' example, with `ggplot(cars, aes(x=speed, y=dist)) + geom_point()`, you
|
||||
#' could use `brushedPoints(cars, input$cars_brush)`. If, however, you use
|
||||
#' a computed column, like `ggplot(cars, aes(x=speed/2, y=dist)) +
|
||||
#' geom_point()`, then it will not be able to automatically extract column names
|
||||
#' and filter on them. If you want to use this function to filter data, it is
|
||||
#' recommended that you not use computed columns; instead, modify the data
|
||||
#' first, and then make the plot with "raw" columns in the modified data.
|
||||
#'
|
||||
#' @section Brushing:
|
||||
#' If x or y column is a factor, then it will be coerced to an integer vector.
|
||||
#' If it is a character vector, then it will be coerced to a factor and then
|
||||
#' integer vector. This means that the brush will be considered to cover a
|
||||
#' given character/factor value when it covers the center value.
|
||||
#' If a specified x or y column is a factor, then it will be coerced to an
|
||||
#' integer vector. If it is a character vector, then it will be coerced to a
|
||||
#' factor and then integer vector. This means that the brush will be considered
|
||||
#' to cover a given character/factor value when it covers the center value.
|
||||
#'
|
||||
#' If the brush is operating in just the x or y directions (e.g., with
|
||||
#' `brushOpts(direction = "x")`, then this function will filter out points
|
||||
#' using just the x or y variable, whichever is appropriate.
|
||||
#'
|
||||
#' @returns
|
||||
#' A data frame based on `df`, containing the observations selected by the
|
||||
#' brush or near the click event. For `nearPoints()`, the rows will be sorted
|
||||
#' by distance to the event.
|
||||
#'
|
||||
#' If `allRows = TRUE`, then all rows will returned, along with a new
|
||||
#' `selected_` column that indicates whether or not the point was selected.
|
||||
#' The output from `nearPoints()` will no longer be sorted, but you can
|
||||
#' set `addDist = TRUE` to get an additional column that gives the pixel
|
||||
#' distance to the pointer.
|
||||
#'
|
||||
#' @param brush The data from a brush, such as `input$plot_brush`.
|
||||
#' @param df A data frame from which to select rows.
|
||||
#' @param brush,coordinfo The data from a brush or click/dblclick/hover event
|
||||
#' e.g. `input$plot_brush`, `input$plot_click`.
|
||||
#' @param xvar,yvar A string giving the name of the variable on the x or y axis.
|
||||
#' These are only required for base graphics, and must be the name of
|
||||
#' a column in `df`.
|
||||
#' @param panelvar1,panelvar2 A string giving the name of a panel variable.
|
||||
#' For expert use only; in most cases these will be automatically
|
||||
#' derived from the ggplot2 spec.
|
||||
#' @param xvar,yvar A string with the name of the variable on the x or y axis.
|
||||
#' This must also be the name of a column in `df`. If absent, then this
|
||||
#' function will try to infer the variable from the brush (only works for
|
||||
#' ggplot2).
|
||||
#' @param panelvar1,panelvar2 Each of these is a string with the name of a panel
|
||||
#' variable. For example, if with ggplot2, you facet on a variable called
|
||||
#' `cyl`, then you can use `"cyl"` here. However, specifying the
|
||||
#' panel variable should not be necessary with ggplot2; Shiny should be able
|
||||
#' to auto-detect the panel variable.
|
||||
#' @param allRows If `FALSE` (the default) return a data frame containing
|
||||
#' the selected rows. If `TRUE`, the input data frame will have a new
|
||||
#' column, `selected_`, which indicates whether the row was selected or not.
|
||||
#' @param threshold A maximum distance (in pixels) to the pointer location.
|
||||
#' Rows in the data frame will be selected if the distance to the pointer is
|
||||
#' less than `threshold`.
|
||||
#' @param maxpoints Maximum number of rows to return. If `NULL` (the default),
|
||||
#' will return all rows within the threshold distance.
|
||||
#' @param addDist If TRUE, add a column named `dist_` that contains the
|
||||
#' distance from the coordinate to the point, in pixels. When no pointer
|
||||
#' event has yet occurred, the value of `dist_` will be `NA`.
|
||||
#' column, `selected_`, which indicates whether the row was inside the
|
||||
#' brush (`TRUE`) or outside the brush (`FALSE`).
|
||||
#'
|
||||
#' @seealso [plotOutput()] for example usage.
|
||||
#' @export
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' # Note that in practice, these examples would need to go in reactives
|
||||
#' # or observers.
|
||||
#'
|
||||
#' # This would select all points within 5 pixels of the click
|
||||
#' nearPoints(mtcars, input$plot_click)
|
||||
#'
|
||||
#' # Select just the nearest point within 10 pixels of the click
|
||||
#' nearPoints(mtcars, input$plot_click, threshold = 10, maxpoints = 1)
|
||||
#'
|
||||
#' }
|
||||
brushedPoints <- function(df, brush, xvar = NULL, yvar = NULL,
|
||||
panelvar1 = NULL, panelvar2 = NULL,
|
||||
allRows = FALSE) {
|
||||
@@ -208,8 +191,56 @@ brushedPoints <- function(df, brush, xvar = NULL, yvar = NULL,
|
||||
# $ direction: chr "y"
|
||||
|
||||
|
||||
#' @export
|
||||
#' @rdname brushedPoints
|
||||
#'Find rows of data that are near a click/hover/double-click
|
||||
#'
|
||||
#'This function returns rows from a data frame which are near a click, hover, or
|
||||
#'double-click, when used with [plotOutput()]. The rows will be sorted
|
||||
#'by their distance to the mouse event.
|
||||
#'
|
||||
#'It is also possible for this function to return all rows from the input data
|
||||
#'frame, but with an additional column `selected_`, which indicates which
|
||||
#'rows of the input data frame are selected by the brush (`TRUE` for
|
||||
#'selected, `FALSE` for not-selected). This is enabled by setting
|
||||
#'`allRows=TRUE` option. If this is used, the resulting data frame will not
|
||||
#'be sorted by distance to the mouse event.
|
||||
#'
|
||||
#'The `xvar`, `yvar`, `panelvar1`, and `panelvar2` arguments
|
||||
#'specify which columns in the data correspond to the x variable, y variable,
|
||||
#'and panel variables of the plot. For example, if your plot is
|
||||
#'`plot(x=cars$speed, y=cars$dist)`, and your click variable is named
|
||||
#'`"cars_click"`, then you would use `nearPoints(cars,
|
||||
#'input$cars_brush, "speed", "dist")`.
|
||||
#'
|
||||
#'@inheritParams brushedPoints
|
||||
#'@param coordinfo The data from a mouse event, such as `input$plot_click`.
|
||||
#'@param threshold A maxmimum distance to the click point; rows in the data
|
||||
#' frame where the distance to the click is less than `threshold` will be
|
||||
#' returned.
|
||||
#'@param maxpoints Maximum number of rows to return. If NULL (the default),
|
||||
#' return all rows that are within the threshold distance.
|
||||
#'@param addDist If TRUE, add a column named `dist_` that contains the
|
||||
#' distance from the coordinate to the point, in pixels. When no mouse event
|
||||
#' has yet occured, the value of `dist_` will be `NA`.
|
||||
#'@param allRows If `FALSE` (the default) return a data frame containing
|
||||
#' the selected rows. If `TRUE`, the input data frame will have a new
|
||||
#' column, `selected_`, which indicates whether the row was inside the
|
||||
#' selected by the mouse event (`TRUE`) or not (`FALSE`).
|
||||
#'
|
||||
#'@seealso [plotOutput()] for more examples.
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' # Note that in practice, these examples would need to go in reactives
|
||||
#' # or observers.
|
||||
#'
|
||||
#' # This would select all points within 5 pixels of the click
|
||||
#' nearPoints(mtcars, input$plot_click)
|
||||
#'
|
||||
#' # Select just the nearest point within 10 pixels of the click
|
||||
#' nearPoints(mtcars, input$plot_click, threshold = 10, maxpoints = 1)
|
||||
#'
|
||||
#' }
|
||||
#'@export
|
||||
nearPoints <- function(df, coordinfo, xvar = NULL, yvar = NULL,
|
||||
panelvar1 = NULL, panelvar2 = NULL,
|
||||
threshold = 5, maxpoints = NULL, addDist = FALSE,
|
||||
|
||||
@@ -1,47 +1,17 @@
|
||||
startPNG <- function(filename, width, height, res, ...) {
|
||||
# shiny.useragg is an experimental option that isn't officially supported or
|
||||
# documented. It's here in the off chance that someone really wants
|
||||
# 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') %OR% FALSE) && is_available("ragg")) {
|
||||
pngfun <- ragg::agg_png
|
||||
} else if (capabilities("aqua")) {
|
||||
# i.e., png(type = 'quartz')
|
||||
# If quartz is available, use png() (which will default to quartz).
|
||||
# Otherwise, if the Cairo package is installed, use CairoPNG().
|
||||
# Finally, if neither quartz nor Cairo, use png().
|
||||
if (capabilities("aqua")) {
|
||||
pngfun <- grDevices::png
|
||||
} else if ((getOption('shiny.usecairo') %OR% TRUE) && is_available("Cairo")) {
|
||||
} else if ((getOption('shiny.usecairo') %OR% TRUE) &&
|
||||
nchar(system.file(package = "Cairo"))) {
|
||||
pngfun <- Cairo::CairoPNG
|
||||
} else {
|
||||
# i.e., png(type = 'cairo')
|
||||
pngfun <- grDevices::png
|
||||
}
|
||||
|
||||
args <- rlang::list2(filename=filename, width=width, height=height, res=res, ...)
|
||||
|
||||
# Set a smarter default for the device's bg argument (based on thematic's global state).
|
||||
# Note that, technically, this is really only needed for CairoPNG, since the other
|
||||
# devices allow their bg arg to be overridden by par(bg=...), which thematic does prior
|
||||
# to plot-time, but it shouldn't hurt to inform other the device directly as well
|
||||
if (is.null(args$bg) && isNamespaceLoaded("thematic")) {
|
||||
# TODO: use :: once thematic is on CRAN
|
||||
args$bg <- utils::getFromNamespace("thematic_get_option", "thematic")("bg", "white")
|
||||
# auto vals aren't resolved until plot time, so if we see one, resolve it
|
||||
if (isTRUE("auto" == args$bg)) {
|
||||
args$bg <- getCurrentOutputInfo()[["bg"]]()
|
||||
}
|
||||
}
|
||||
|
||||
# Handle both bg and background device arg
|
||||
# https://github.com/r-lib/ragg/issues/35
|
||||
fmls <- names(formals(pngfun))
|
||||
if (("background" %in% fmls) && (!"bg" %in% fmls)) {
|
||||
if (is.null(args$background)) {
|
||||
args$background <- args$bg
|
||||
}
|
||||
args$bg <- NULL
|
||||
}
|
||||
|
||||
do.call(pngfun, args)
|
||||
pngfun(filename=filename, width=width, height=height, res=res, ...)
|
||||
# Call plot.new() so that even if no plotting operations are performed at
|
||||
# least we have a blank background. N.B. we need to set the margin to 0
|
||||
# temporarily before plot.new() because when the plot size is small (e.g.
|
||||
|
||||
@@ -110,10 +110,6 @@ dateInput <- function(inputId, label, value = NULL, min = NULL, max = NULL,
|
||||
shinyInputLabel(inputId, label),
|
||||
tags$input(type = "text",
|
||||
class = "form-control",
|
||||
# `aria-labelledby` attribute is required for accessibility to avoid doubled labels (#2951).
|
||||
`aria-labelledby` = paste0(inputId, "-label"),
|
||||
# title attribute is announced for screen readers for date format.
|
||||
title = paste("Date format:", format),
|
||||
`data-date-language` = language,
|
||||
`data-date-week-start` = weekstart,
|
||||
`data-date-format` = format,
|
||||
|
||||
@@ -100,10 +100,6 @@ dateRangeInput <- function(inputId, label, start = NULL, end = NULL,
|
||||
tags$input(
|
||||
class = "form-control",
|
||||
type = "text",
|
||||
# `aria-labelledby` attribute is required for accessibility to avoid doubled labels (#2951).
|
||||
`aria-labelledby` = paste0(inputId, "-label"),
|
||||
# title attribute is announced for screen readers for date format.
|
||||
title = paste("Date format:", format),
|
||||
`data-date-language` = language,
|
||||
`data-date-week-start` = weekstart,
|
||||
`data-date-format` = format,
|
||||
@@ -122,10 +118,6 @@ dateRangeInput <- function(inputId, label, start = NULL, end = NULL,
|
||||
tags$input(
|
||||
class = "form-control",
|
||||
type = "text",
|
||||
# `aria-labelledby` attribute is required for accessibility to avoid doubled labels (#2951).
|
||||
`aria-labelledby` = paste0(inputId, "-label"),
|
||||
# title attribute is announced for screen readers for date format.
|
||||
title = paste("Date format:", format),
|
||||
`data-date-language` = language,
|
||||
`data-date-week-start` = weekstart,
|
||||
`data-date-format` = format,
|
||||
|
||||
@@ -91,8 +91,7 @@ fileInput <- function(inputId, label, multiple = FALSE, accept = NULL,
|
||||
id = inputId,
|
||||
name = inputId,
|
||||
type = "file",
|
||||
# Don't use "display: none;" style, which causes keyboard accessibility issue; instead use the following workaround: https://css-tricks.com/places-its-tempting-to-use-display-none-but-dont/
|
||||
style = "position: absolute !important; top: -99999px !important; left: -99999px !important;",
|
||||
style = "display: none;",
|
||||
`data-restore` = restoredValue
|
||||
)
|
||||
|
||||
|
||||
@@ -183,20 +183,16 @@ selectizeInput <- function(inputId, ..., options = NULL, width = NULL) {
|
||||
|
||||
# given a select input and its id, selectize it
|
||||
selectizeIt <- function(inputId, select, options, nonempty = FALSE) {
|
||||
# Make sure accessibility plugin is included
|
||||
if (!('selectize-plugin-a11y' %in% options$plugins)) {
|
||||
options$plugins <- c(options$plugins, list('selectize-plugin-a11y'))
|
||||
}
|
||||
|
||||
res <- checkAsIs(options)
|
||||
|
||||
selectizeDep <- htmlDependency(
|
||||
"selectize", "0.12.4", c(href = "shared/selectize"),
|
||||
"selectize", "0.11.2", c(href = "shared/selectize"),
|
||||
stylesheet = "css/selectize.bootstrap3.css",
|
||||
head = format(tagList(
|
||||
tags$script(src = 'shared/selectize/js/selectize.min.js'),
|
||||
# Accessibility plugin for screen readers (https://github.com/SLMNBJ/selectize-plugin-a11y):
|
||||
tags$script(src = 'shared/selectize/accessibility/js/selectize-plugin-a11y.min.js')
|
||||
HTML('<!--[if lt IE 9]>'),
|
||||
tags$script(src = 'shared/selectize/js/es5-shim.min.js'),
|
||||
HTML('<![endif]-->'),
|
||||
tags$script(src = 'shared/selectize/js/selectize.min.js')
|
||||
))
|
||||
)
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@ shinyInputLabel <- function(inputId, label = NULL) {
|
||||
label,
|
||||
class = "control-label",
|
||||
class = if (is.null(label)) "shiny-label-null",
|
||||
# `id` attribute is required for `aria-labelledby` used by screen readers:
|
||||
id = paste0(inputId, "-label"),
|
||||
`for` = inputId
|
||||
)
|
||||
}
|
||||
|
||||
@@ -14,26 +14,7 @@
|
||||
# returns `NULL`, or an `httpResponse`.
|
||||
#
|
||||
## ------------------------------------------------------------------------
|
||||
|
||||
#' Create an HTTP response object
|
||||
#'
|
||||
#' @param status HTTP status code for the response.
|
||||
#' @param content_type The value for the `Content-Type` header.
|
||||
#' @param content The body of the response, given as a single-element character
|
||||
#' vector (will be encoded as UTF-8) or a raw vector.
|
||||
#' @param headers A named list of additional headers to include. Do not include
|
||||
#' `Content-Length` (as it is automatically calculated) or `Content-Type` (the
|
||||
#' `content_type` argument is used instead).
|
||||
#'
|
||||
#' @examples
|
||||
#' httpResponse(status = 405L,
|
||||
#' content_type = "text/plain",
|
||||
#' content = "The requested method was not allowed"
|
||||
#' )
|
||||
#'
|
||||
#' @keywords internal
|
||||
#' @export
|
||||
httpResponse <- function(status = 200L,
|
||||
httpResponse <- function(status = 200,
|
||||
content_type = "text/html; charset=UTF-8",
|
||||
content = "",
|
||||
headers = list()) {
|
||||
@@ -330,32 +311,16 @@ HandlerManager <- R6Class("HandlerManager",
|
||||
},
|
||||
call = .httpServer(
|
||||
function (req) {
|
||||
hybrid_chain(
|
||||
hybrid_chain(
|
||||
withCallingHandlers(withLogErrors(handlers$invoke(req)),
|
||||
error = function(cond) {
|
||||
sanitizeErrors <- getOption('shiny.sanitize.errors', FALSE)
|
||||
if (inherits(cond, 'shiny.custom.error') || !sanitizeErrors) {
|
||||
stop(cond$message, call. = FALSE)
|
||||
} else {
|
||||
stop(paste("An error has occurred. Check your logs or",
|
||||
"contact the app author for clarification."),
|
||||
call. = FALSE)
|
||||
}
|
||||
}
|
||||
),
|
||||
catch = function(err) {
|
||||
httpResponse(status = 500L,
|
||||
content_type = "text/html",
|
||||
content = as.character(htmltools::htmlTemplate(
|
||||
system.file("template", "error.html", package = "shiny"),
|
||||
message = conditionMessage(err)
|
||||
))
|
||||
)
|
||||
withCallingHandlers(withLogErrors(handlers$invoke(req)),
|
||||
error = function(cond) {
|
||||
sanitizeErrors <- getOption('shiny.sanitize.errors', FALSE)
|
||||
if (inherits(cond, 'shiny.custom.error') || !sanitizeErrors) {
|
||||
stop(cond$message, call. = FALSE)
|
||||
} else {
|
||||
stop(paste("An error has occurred. Check your logs or",
|
||||
"contact the app author for clarification."),
|
||||
call. = FALSE)
|
||||
}
|
||||
),
|
||||
function(resp) {
|
||||
maybeInjectAutoreload(resp)
|
||||
}
|
||||
)
|
||||
},
|
||||
@@ -425,22 +390,6 @@ HandlerManager <- R6Class("HandlerManager",
|
||||
)
|
||||
)
|
||||
|
||||
maybeInjectAutoreload <- function(resp) {
|
||||
if (getOption("shiny.autoreload", FALSE) &&
|
||||
isTRUE(grepl("^text/html($|;)", resp$content_type)) &&
|
||||
is.character(resp$content)) {
|
||||
|
||||
resp$content <- gsub(
|
||||
"</head>",
|
||||
"<script src=\"shared/shiny-autoreload.js\"></script>\n</head>",
|
||||
resp$content,
|
||||
fixed = TRUE
|
||||
)
|
||||
}
|
||||
|
||||
resp
|
||||
}
|
||||
|
||||
# Safely get the Content-Length of a Rook response, or NULL if the length cannot
|
||||
# be determined for whatever reason (probably malformed response$content).
|
||||
# If deleteOwnedContent is TRUE, then the function should delete response
|
||||
|
||||
471
R/mock-session.R
471
R/mock-session.R
@@ -70,195 +70,52 @@ extract <- function(promise) {
|
||||
}
|
||||
|
||||
#' @noRd
|
||||
mapNames <- function(func, vals) {
|
||||
mapNames <- function(func, ...) {
|
||||
vals <- list(...)
|
||||
names(vals) <- vapply(names(vals), func, character(1))
|
||||
vals
|
||||
}
|
||||
|
||||
#' Returns a noop implementation of the public method `name` of ShinySession.
|
||||
#' @include shiny.R
|
||||
#' @noRd
|
||||
makeNoop <- function(name, msg = paste0(name, " is a noop.")) {
|
||||
if (!(name %in% names(ShinySession$public_methods)))
|
||||
stop(name, " is not public method of ShinySession.")
|
||||
impl <- ShinySession$public_methods[[name]]
|
||||
body(impl) <- rlang::expr({
|
||||
# Force arguments
|
||||
!!lapply(formalArgs(impl), rlang::sym)
|
||||
# Evade "no visible binding" note for reference to `private`
|
||||
(!!as.symbol("private"))$noopWarn(!!name, !!msg)
|
||||
invisible()
|
||||
})
|
||||
impl
|
||||
}
|
||||
|
||||
#' Accepts a series of symbols as arguments and generates corresponding noop
|
||||
#' implementations.
|
||||
#' @noRd
|
||||
makeWarnNoops <- function(...) {
|
||||
methods <- as.character(list(...))
|
||||
names(methods) <- methods
|
||||
lapply(methods, makeNoop)
|
||||
}
|
||||
|
||||
#' Returns an implementation of a ShinySession public method that signals an
|
||||
#' error.
|
||||
#' @include shiny.R
|
||||
#' @noRd
|
||||
makeError <- function(name, msg = paste0(name, " is for internal use only.")) {
|
||||
if (!(name %in% names(ShinySession$public_methods)))
|
||||
stop(name, " is not public method of ShinySession.")
|
||||
impl <- ShinySession$public_methods[[name]]
|
||||
body(impl) <- rlang::expr({
|
||||
base::stop(!!msg)
|
||||
})
|
||||
impl
|
||||
}
|
||||
|
||||
#' Accepts a series of named arguments. Each name corresponds to a ShinySession
|
||||
#' public method that should signal an error, and each argument corresponds to
|
||||
#' an error message.
|
||||
#' @noRd
|
||||
makeErrors <- function(...) {
|
||||
errors <- rlang::list2(...)
|
||||
mapply(makeError, names(errors), errors, USE.NAMES = TRUE, SIMPLIFY = FALSE)
|
||||
}
|
||||
|
||||
#' @noRd
|
||||
makeExtraMethods <- function() {
|
||||
c(makeWarnNoops(
|
||||
"allowReconnect",
|
||||
"decrementBusyCount",
|
||||
"doBookmark",
|
||||
"exportTestValues",
|
||||
"flushOutput",
|
||||
"getBookmarkExclude",
|
||||
"getTestSnapshotUrl",
|
||||
"incrementBusyCount",
|
||||
"manageHiddenOutputs",
|
||||
"manageInputs",
|
||||
"onBookmark",
|
||||
"onBookmarked",
|
||||
"onInputReceived",
|
||||
"onRestore",
|
||||
"onRestored",
|
||||
"outputOptions",
|
||||
"reactlog",
|
||||
# TODO Consider implementing this. Would require a new method like
|
||||
# session$getDataObj() to access in a test expression.
|
||||
"registerDataObj",
|
||||
"reload",
|
||||
"resetBrush",
|
||||
"sendBinaryMessage",
|
||||
"sendChangeTabVisibility",
|
||||
"sendCustomMessage",
|
||||
"sendInputMessage",
|
||||
"sendInsertTab",
|
||||
"sendInsertUI",
|
||||
"sendModal",
|
||||
"sendNotification",
|
||||
"sendProgress",
|
||||
"sendRemoveTab",
|
||||
"sendRemoveUI",
|
||||
"setBookmarkExclude",
|
||||
"setShowcase",
|
||||
"showProgress",
|
||||
"updateQueryString"
|
||||
), makeErrors(
|
||||
`@uploadEnd` = "for internal use only",
|
||||
`@uploadInit` = "for internal use only",
|
||||
`@uploadieFinish` = "for internal use only",
|
||||
createBookmarkObservers = "for internal use only",
|
||||
dispatch = "for internal use only",
|
||||
handleRequest = "for internal use only",
|
||||
requestFlush = "for internal use only",
|
||||
saveFileUrl = "for internal use only",
|
||||
startTiming = "for internal use only",
|
||||
wsClosed = "for internal use only"
|
||||
))
|
||||
}
|
||||
|
||||
#' @description Adds generated instance methods to a MockShinySession instance.
|
||||
#' Note that `lock_objects = FALSE` must be set in the call to `R6Class()`
|
||||
#' that produced the generator object of the instance.
|
||||
#' @param instance instance of an R6 object, generally a `MockShinySession`.
|
||||
#' @param methods named list of method names to method implementation functions.
|
||||
#' In our typical usage, each function is derived from a public method of
|
||||
#' `ShinySession`. The environment of each implementation function is set to
|
||||
#' `instance$.__enclos_env` before the method is added.
|
||||
#' @noRd
|
||||
addGeneratedInstanceMethods <- function(instance, methods = makeExtraMethods()) {
|
||||
mapply(function(name, impl) {
|
||||
environment(impl) <- instance$.__enclos_env__
|
||||
instance[[name]] <- impl
|
||||
}, names(methods), methods)
|
||||
}
|
||||
|
||||
#' Mock Shiny Session
|
||||
#'
|
||||
#' @description An R6 class suitable for testing purposes. Simulates, to the
|
||||
#' extent possible, the behavior of the `ShinySession` class. The `session`
|
||||
#' parameter provided to Shiny server functions and modules is an instance of
|
||||
#' a `ShinySession` in normal operation.
|
||||
#'
|
||||
#' Most kinds of module and server testing do not require this class be
|
||||
#' instantiated manually. See instead [testServer()].
|
||||
#'
|
||||
#' In order to support advanced usage, instances of `MockShinySession` are
|
||||
#' **unlocked** so that public methods and fields of instances may be
|
||||
#' modified. For example, in order to test authentication workflows, the
|
||||
#' `user` or `groups` fields may be overridden. Modified instances of
|
||||
#' `MockShinySession` may then be passed explicitly as the `session` argument
|
||||
#' of [testServer()].
|
||||
#' @description
|
||||
#' An R6 class suitable for testing that simulates the `session` parameter
|
||||
#' provided to Shiny server functions or modules.
|
||||
#'
|
||||
#' @include timer.R
|
||||
#' @export
|
||||
MockShinySession <- R6Class(
|
||||
'MockShinySession',
|
||||
portable = FALSE,
|
||||
lock_objects = FALSE,
|
||||
public = list(
|
||||
#' @field env The environment associated with the session.
|
||||
env = NULL,
|
||||
#' @field returned The value returned by the module under test.
|
||||
#' @field returned The value returned by the module.
|
||||
returned = NULL,
|
||||
#' @field singletons Hardcoded as empty. Needed for rendering HTML (i.e. renderUI).
|
||||
#' @field singletons Hardcoded as empty. Needed for rendering HTML (i.e. renderUI)
|
||||
singletons = character(0),
|
||||
#' @field clientData Mock client data that always returns a size for plots.
|
||||
#' @field clientData Mock client data that always returns a size for plots
|
||||
clientData = structure(list(), class="mockclientdata"),
|
||||
#' @field output The shinyoutputs associated with the session.
|
||||
#' @description No-op
|
||||
#' @param logEntry Not used
|
||||
reactlog = function(logEntry){},
|
||||
#' @description No-op
|
||||
incrementBusyCount = function(){},
|
||||
#' @field output The shinyoutputs associated with the session
|
||||
output = NULL,
|
||||
#' @field input The reactive inputs associated with the session.
|
||||
#' @field input The reactive inputs associated with the session
|
||||
input = NULL,
|
||||
#' @field userData An environment initialized as empty.
|
||||
userData = NULL,
|
||||
#' @field progressStack A stack of progress objects.
|
||||
#' @field progressStack A stack of progress objects
|
||||
progressStack = 'Stack',
|
||||
#' @field token On a real `ShinySession`, used to identify this instance in URLs.
|
||||
token = 'character',
|
||||
#' @field cache The session cache MemoryCache.
|
||||
cache = NULL,
|
||||
#' @field appcache The app cache MemoryCache.
|
||||
appcache = NULL,
|
||||
#' @field restoreContext Part of bookmarking support in a real
|
||||
#' `ShinySession` but always `NULL` for a `MockShinySession`.
|
||||
restoreContext = NULL,
|
||||
#' @field groups Character vector of groups associated with an authenticated
|
||||
#' user. Always `NULL` for a `MockShinySesion`.
|
||||
groups = NULL,
|
||||
#' @field user The username of an authenticated user. Always `NULL` for a
|
||||
#' `MockShinySession`.
|
||||
user = NULL,
|
||||
|
||||
#' @description Create a new MockShinySession.
|
||||
#' @description Create a new MockShinySession
|
||||
initialize = function() {
|
||||
private$.input <- ReactiveValues$new(dedupe = FALSE, label = "input")
|
||||
private$flushCBs <- Callbacks$new()
|
||||
private$flushedCBs <- Callbacks$new()
|
||||
private$endedCBs <- Callbacks$new()
|
||||
|
||||
private$file_generators <- fastmap()
|
||||
|
||||
private$timer <- MockableTimerCallbacks$new()
|
||||
self$progressStack <- Stack$new()
|
||||
|
||||
@@ -271,15 +128,6 @@ MockShinySession <- R6Class(
|
||||
|
||||
# Create a read-only copy of the inputs reactive.
|
||||
self$input <- .createReactiveValues(private$.input, readonly = TRUE)
|
||||
|
||||
self$token <- createUniqueId(16)
|
||||
self$cache <- MemoryCache$new()
|
||||
self$appcache <- MemoryCache$new()
|
||||
|
||||
# Adds various generated noop and error-producing method implementations.
|
||||
# Note that noop methods can be configured to produce warnings by setting
|
||||
# the option shiny.mocksession.warn = TRUE; see $noopWarn() for details.
|
||||
addGeneratedInstanceMethods(self)
|
||||
},
|
||||
#' @description Define a callback to be invoked before a reactive flush
|
||||
#' @param fun The function to invoke
|
||||
@@ -316,24 +164,16 @@ MockShinySession <- R6Class(
|
||||
},
|
||||
|
||||
#' @description Returns `FALSE` if the session has not yet been closed
|
||||
isEnded = function(){ private$was_closed },
|
||||
isEnded = function(){ private$closed },
|
||||
#' @description Returns `FALSE` if the session has not yet been closed
|
||||
isClosed = function(){ private$was_closed },
|
||||
isClosed = function(){ private$closed },
|
||||
#' @description Closes the session
|
||||
close = function(){
|
||||
for (output in private$output) {
|
||||
output$suspend()
|
||||
}
|
||||
withReactiveDomain(self, {
|
||||
private$endedCBs$invoke(onError = printError, ..stacktraceon = TRUE)
|
||||
})
|
||||
private$was_closed <- TRUE
|
||||
},
|
||||
close = function(){ private$closed <- TRUE },
|
||||
|
||||
#FIXME: this is wrong. Will need to be more complex.
|
||||
#' @description Unsophisticated mock implementation that merely invokes
|
||||
# the given callback immediately.
|
||||
#' @param callback The callback to be invoked.
|
||||
#' the given callback immediately.
|
||||
#' @param callback The callback ato be invoked.
|
||||
cycleStartAction = function(callback){ callback() },
|
||||
|
||||
#' @description Base64-encode the given file. Needed for image rendering.
|
||||
@@ -350,17 +190,14 @@ MockShinySession <- R6Class(
|
||||
return(paste('data:', contentType, ';base64,', b64, sep=''))
|
||||
},
|
||||
|
||||
#' @description Sets reactive values associated with the `session$inputs`
|
||||
#' object and flushes the reactives.
|
||||
#' @param ... The inputs to set. These arguments are processed with
|
||||
#' [rlang::list2()] and so are _[dynamic][rlang::dyn-dots]_. Input names
|
||||
#' may not be duplicated.
|
||||
#' @description Sets reactive values associated with the `session$inputs` object
|
||||
#' and flushes the reactives.
|
||||
#' @param ... The inputs to set.
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' session$setInputs(x=1, y=2)
|
||||
#' }
|
||||
#' s <- MockShinySession$new()
|
||||
#' s$setInputs(x=1, y=2)
|
||||
setInputs = function(...) {
|
||||
vals <- rlang::dots_list(..., .homonyms = "error")
|
||||
vals <- list(...)
|
||||
mapply(names(vals), vals, FUN = function(name, value) {
|
||||
private$.input$set(name, value)
|
||||
})
|
||||
@@ -368,10 +205,8 @@ MockShinySession <- R6Class(
|
||||
},
|
||||
|
||||
#' @description An internal method which shouldn't be used by others.
|
||||
#' Schedules `callback` for execution after some number of `millis`
|
||||
#' milliseconds.
|
||||
#' @param millis The number of milliseconds on which to schedule a callback
|
||||
#' @param callback The function to schedule.
|
||||
#' @param callback The function to schedule
|
||||
.scheduleTask = function(millis, callback) {
|
||||
id <- private$timer$schedule(millis, callback)
|
||||
|
||||
@@ -410,17 +245,15 @@ MockShinySession <- R6Class(
|
||||
},
|
||||
|
||||
#' @description An internal method which shouldn't be used by others.
|
||||
#' @return Elapsed time in milliseconds.
|
||||
.now = function() {
|
||||
# Returns elapsed time in milliseconds
|
||||
private$timer$getElapsed()
|
||||
},
|
||||
|
||||
#' @description An internal method which shouldn't be used by others.
|
||||
#' Defines an output in a way that sets private$currentOutputName
|
||||
#' appropriately.
|
||||
#' @param name The name of the output.
|
||||
#' @param func The render definition.
|
||||
#' @param label Not used.
|
||||
#' @param name The name of the output
|
||||
#' @param func The render definition
|
||||
#' @param label Not used
|
||||
defineOutput = function(name, func, label) {
|
||||
force(name)
|
||||
|
||||
@@ -437,7 +270,7 @@ MockShinySession <- R6Class(
|
||||
# We could just stash the promise, but we get an "unhandled promise error". This bypasses
|
||||
prom <- NULL
|
||||
tryCatch({
|
||||
v <- private$withCurrentOutput(name, func(self, name))
|
||||
v <- func(self, name)
|
||||
if (!promises::is.promise(v)){
|
||||
# Make our sync value into a promise
|
||||
prom <- promises::promise(function(resolve, reject){ resolve(v) })
|
||||
@@ -460,11 +293,8 @@ MockShinySession <- R6Class(
|
||||
private$outs[[name]] <- list(obs = obs, func = func, promise = NULL)
|
||||
},
|
||||
|
||||
#' @description An internal method which shouldn't be used by others. Forces
|
||||
#' evaluation of any reactive dependencies of the output function.
|
||||
#' @param name The name of the output.
|
||||
#' @return The return value of the function responsible for rendering the
|
||||
#' output.
|
||||
#' @description An internal method which shouldn't be used by others.
|
||||
#' @param name The name of the output
|
||||
getOutput = function(name) {
|
||||
# Unlike the real outputs, we're going to return the last value rather than the unevaluated function
|
||||
if (is.null(private$outs[[name]])) {
|
||||
@@ -485,17 +315,73 @@ MockShinySession <- R6Class(
|
||||
v <- extract(private$outs[[name]]$promise)
|
||||
if (!is.null(v$err)){
|
||||
stop(v$err)
|
||||
} else if (private$file_generators$has(self$ns(name))) {
|
||||
download <- private$file_generators$get(self$ns(name))
|
||||
private$renderFile(self$ns(name), download)
|
||||
} else {
|
||||
v$val
|
||||
}
|
||||
},
|
||||
|
||||
#' @description No-op
|
||||
#' @param name Not used
|
||||
#' @param data Not used
|
||||
#' @param filterFunc Not used
|
||||
registerDataObj = function(name, data, filterFunc) {},
|
||||
#' @description No-op
|
||||
#' @param value Not used
|
||||
allowReconnect = function(value) {},
|
||||
#' @description No-op
|
||||
reload = function() {},
|
||||
#' @description No-op
|
||||
#' @param brushId Not used
|
||||
resetBrush = function(brushId) {
|
||||
warning("session$brush isn't meaningfully mocked on the MockShinySession")
|
||||
},
|
||||
#' @description No-op
|
||||
#' @param type Not used
|
||||
#' @param message Not used
|
||||
sendCustomMessage = function(type, message) {},
|
||||
#' @description No-op
|
||||
#' @param type Not used
|
||||
#' @param message Not used
|
||||
sendBinaryMessage = function(type, message) {},
|
||||
#' @description No-op
|
||||
#' @param inputId Not used
|
||||
#' @param message Not used
|
||||
sendInputMessage = function(inputId, message) {},
|
||||
#' @description No-op
|
||||
#' @param names Not used
|
||||
setBookmarkExclude = function(names) {
|
||||
warning("Bookmarking isn't meaningfully mocked in MockShinySession")
|
||||
},
|
||||
#' @description No-op
|
||||
getBookmarkExclude = function() {
|
||||
warning("Bookmarking isn't meaningfully mocked in MockShinySession")
|
||||
},
|
||||
#' @description No-op
|
||||
#' @param fun Not used
|
||||
onBookmark = function(fun) {},
|
||||
#' @description No-op
|
||||
#' @param fun Not used
|
||||
onBookmarked = function(fun) {},
|
||||
#' @description No-op
|
||||
doBookmark = function() {
|
||||
warning("Bookmarking isn't meaningfully mocked in MockShinySession")
|
||||
},
|
||||
#' @description No-op
|
||||
#' @param fun Not used
|
||||
onRestore = function(fun) {},
|
||||
#' @description No-op
|
||||
#' @param fun Not used
|
||||
onRestored = function(fun) {},
|
||||
#' @description No-op
|
||||
exportTestValues = function() {},
|
||||
#' @description No-op
|
||||
#' @param input Not used
|
||||
#' @param output Not used
|
||||
#' @param export Not used
|
||||
#' @param format Not used
|
||||
getTestSnapshotUrl = function(input=TRUE, output=TRUE, export=TRUE, format="json") {},
|
||||
#' @description Returns the given id prefixed by this namespace's id.
|
||||
#' @param id The id to prefix with a namespace id.
|
||||
#' @return The id with a namespace prefix.
|
||||
#' @param id The id to modify.
|
||||
ns = function(id) {
|
||||
NS(private$nsPrefix, id)
|
||||
},
|
||||
@@ -505,7 +391,6 @@ MockShinySession <- R6Class(
|
||||
},
|
||||
#' @description Create and return a namespace-specific session proxy.
|
||||
#' @param namespace Character vector indicating a namespace.
|
||||
#' @return A new session proxy.
|
||||
makeScope = function(namespace) {
|
||||
ns <- NS(namespace)
|
||||
createSessionProxy(
|
||||
@@ -514,196 +399,49 @@ MockShinySession <- R6Class(
|
||||
output = structure(.createOutputWriter(self, ns = ns), class = "shinyoutput"),
|
||||
makeScope = function(namespace) self$makeScope(ns(namespace)),
|
||||
ns = function(namespace) ns(namespace),
|
||||
setInputs = function(...) {
|
||||
self$setInputs(!!!mapNames(ns, rlang::dots_list(..., .homonyms = "error")))
|
||||
}
|
||||
setInputs = function(...) do.call(self$setInputs, mapNames(ns, ...))
|
||||
)
|
||||
},
|
||||
#' @description Set the environment associated with a testServer() call, but
|
||||
#' only if it has not previously been set. This ensures that only the
|
||||
#' environment of the outermost module under test is the one retained. In
|
||||
#' other words, the first assignment wins.
|
||||
#' @description Set the environment associated with a testServer() call.
|
||||
#' @param env The environment to retain.
|
||||
#' @return The provided `env`.
|
||||
setEnv = function(env) {
|
||||
if (is.null(self$env)) {
|
||||
stopifnot(all(c("input", "output", "session") %in% ls(env)))
|
||||
self$env <- env
|
||||
}
|
||||
self$env <- env
|
||||
},
|
||||
#' @description Set the value returned by the module call and proactively
|
||||
#' flush. Note that this method may be called multiple times if modules
|
||||
#' are nested. The last assignment, corresponding to an invocation of
|
||||
#' setReturned() in the outermost module, wins.
|
||||
#' @description Set the value returned by the module call and proactively flush.
|
||||
#' @param value The value returned from the module
|
||||
#' @return The provided `value`.
|
||||
setReturned = function(value) {
|
||||
self$returned <- value
|
||||
private$flush()
|
||||
value
|
||||
},
|
||||
#' @description Get the value returned by the module call.
|
||||
#' @return The value returned by the module call
|
||||
getReturned = function() self$returned,
|
||||
#' @description Generate a distinct character identifier for use as a proxy
|
||||
#' @description Return a distinct character identifier for use as a proxy
|
||||
#' namespace.
|
||||
#' @return A character identifier unique to the current session.
|
||||
genId = function() {
|
||||
private$idCounter <- private$idCounter + 1
|
||||
paste0("proxy", private$idCounter)
|
||||
},
|
||||
#' @description Provides a way to access the root `MockShinySession` from
|
||||
#' any descendant proxy.
|
||||
#' @return The root `MockShinySession`.
|
||||
rootScope = function() {
|
||||
self
|
||||
},
|
||||
#' @description Called by observers when a reactive expression errors.
|
||||
#' @param e An error object.
|
||||
unhandledError = function(e) {
|
||||
self$close()
|
||||
},
|
||||
#' @description Freeze a value until the flush cycle completes.
|
||||
#' @param x A `ReactiveValues` object.
|
||||
#' @param name The name of a reactive value within `x`.
|
||||
freezeValue = function(x, name) {
|
||||
if (!is.reactivevalues(x))
|
||||
stop("x must be a reactivevalues object")
|
||||
|
||||
impl <- .subset2(x, 'impl')
|
||||
key <- .subset2(x, 'ns')(name)
|
||||
impl$freeze(key)
|
||||
self$onFlushed(function() impl$thaw(key))
|
||||
},
|
||||
#' @description Registers the given callback to be invoked when the session
|
||||
#' is closed (i.e. the connection to the client has been severed). The
|
||||
#' return value is a function which unregisters the callback. If multiple
|
||||
#' callbacks are registered, the order in which they are invoked is not
|
||||
#' guaranteed.
|
||||
#' @param sessionEndedCallback Function to call when the session ends.
|
||||
onSessionEnded = function(sessionEndedCallback) {
|
||||
self$onEnded(sessionEndedCallback)
|
||||
},
|
||||
#' @description Associated a downloadable file with the session.
|
||||
#' @param name The un-namespaced output name to associate with the
|
||||
#' downloadable file.
|
||||
#' @param filename A string or function designating the name of the file.
|
||||
#' @param contentType A string of the content type of the file. Not used by
|
||||
#' `MockShinySession`.
|
||||
#' @param content A function that takes a single argument file that is a
|
||||
#' file path (string) of a nonexistent temp file, and writes the content
|
||||
#' to that file path. (Reactive values and functions may be used from this
|
||||
#' function.)
|
||||
registerDownload = function(name, filename, contentType, content) {
|
||||
private$file_generators$set(self$ns(name), list(
|
||||
filename = if (is.function(filename)) filename else function() filename,
|
||||
content = content
|
||||
))
|
||||
},
|
||||
#' @description Get information about the output that is currently being
|
||||
#' executed.
|
||||
#' @return A list with with the `name` of the output. If no output is
|
||||
#' currently being executed, this will return `NULL`.
|
||||
getCurrentOutputInfo = function() {
|
||||
name <- private$currentOutputName
|
||||
if (is.null(name)) NULL else list(name = name)
|
||||
}
|
||||
),
|
||||
private = list(
|
||||
# @field .input Internal ReactiveValues object for normal input sent from client.
|
||||
.input = NULL,
|
||||
# @field flushCBs `Callbacks` called before flush.
|
||||
flushCBs = NULL,
|
||||
# @field flushedCBs `Callbacks` called after flush.
|
||||
flushedCBs = NULL,
|
||||
# @field endedCBs `Callbacks` called when session ends.
|
||||
endedCBs = NULL,
|
||||
# @field timer `MockableTimerCallbacks` called at particular times.
|
||||
timer = NULL,
|
||||
# @field was_closed Set to `TRUE` once the session is closed.
|
||||
was_closed = FALSE,
|
||||
# @field outs List of namespaced output names.
|
||||
closed = FALSE,
|
||||
outs = list(),
|
||||
# @field nsPrefix Prefix with which to namespace inputs and outputs.
|
||||
nsPrefix = "mock-session",
|
||||
# @field idCounter Incremented every time `$genId()` is called.
|
||||
idCounter = 0,
|
||||
# @field file_generators Map of namespaced output names to lists with
|
||||
# `filename` and `output` elements, each a function. Updated by
|
||||
# `$registerDownload()` and read by `$getOutput()`. Files are generated
|
||||
# on demand when the output is accessed.
|
||||
file_generators = NULL,
|
||||
# @field currentOutputName Namespaced name of the currently executing
|
||||
#' output, or `NULL` if no output is currently executing.
|
||||
currentOutputName = NULL,
|
||||
|
||||
# @description Writes a downloadable file to disk. If the `content` function
|
||||
# associated with a download handler does not write a file, an error is
|
||||
# signaled. Created files are deleted upon session close.
|
||||
# @param name The eamespaced output name associated with the downloadable
|
||||
# file.
|
||||
# @param download List with two names, `filename` and `content`. Both should
|
||||
# be functions. `filename` should take no arguments and return a string.
|
||||
# `content` should accept a path argument and create a file at that path.
|
||||
# @return A path to a temp file.
|
||||
renderFile = function(name, download) {
|
||||
# We make our own tempdir here because it's not safe to delete the result
|
||||
# of tempdir().
|
||||
tmpd <- tempfile()
|
||||
dir.create(tmpd, recursive = TRUE)
|
||||
self$onSessionEnded(function() unlink(tmpd, recursive = TRUE))
|
||||
file <- file.path(tmpd, download$filename())
|
||||
download$content(file)
|
||||
if (!file.exists(file))
|
||||
error("downloadHandler for ", name, " did not write a file.")
|
||||
file
|
||||
},
|
||||
|
||||
# @description Calls `shiny:::flushReact()` and executes all callbacks
|
||||
# related to reactivity.
|
||||
flush = function(){
|
||||
isolate(private$flushCBs$invoke(..stacktraceon = TRUE))
|
||||
shiny:::flushReact() # namespace to avoid calling our own method
|
||||
isolate(private$flushedCBs$invoke(..stacktraceon = TRUE))
|
||||
later::run_now()
|
||||
},
|
||||
|
||||
# @description Produces a warning if the option `shiny.mocksession.warn` is
|
||||
# unset and not `FALSE`.
|
||||
# @param name The name of the mocked method.
|
||||
# @param msg A message describing why the method is not implemented.
|
||||
noopWarn = function(name, msg) {
|
||||
if (getOption("shiny.mocksession.warn", FALSE) == FALSE)
|
||||
return(invisible())
|
||||
out <- paste0(name, " is not fully implemented by MockShinySession: ", msg)
|
||||
out <- paste0(out, "\n", "To disable messages like this, run `options(shiny.mocksession.warn=FALSE)`")
|
||||
warning(out, call. = FALSE)
|
||||
},
|
||||
|
||||
# @description Binds a domain to `expr` and uses `createVarPromiseDomain()`
|
||||
# to ensure `private$currentOutputName` is set to `name` around any of
|
||||
# the promise's callbacks. Domains are something like dynamic scopes but
|
||||
# for promise chains instead of the call stack.
|
||||
# @return A promise.
|
||||
withCurrentOutput = function(name, expr) {
|
||||
if (!is.null(private$currentOutputName)) {
|
||||
stop("Nested calls to withCurrentOutput() are not allowed.")
|
||||
}
|
||||
|
||||
promises::with_promise_domain(
|
||||
createVarPromiseDomain(private, "currentOutputName", name),
|
||||
expr
|
||||
)
|
||||
}
|
||||
),
|
||||
active = list(
|
||||
#' @field files For internal use only.
|
||||
files = function() stop("$files is for internal use only."),
|
||||
#' @field downloads For internal use only.
|
||||
downloads = function() stop("$downloads is for internal use only."),
|
||||
#' @field closed Deprecated in `ShinySession` and signals an error.
|
||||
closed = function() stop("$closed is deprecated"),
|
||||
#' @field session Deprecated in ShinySession and signals an error.
|
||||
session = function() stop("$session is deprecated"),
|
||||
#' @field request An empty environment where the request should be. The request isn't meaningfully mocked currently.
|
||||
request = function(value) {
|
||||
if (!missing(value)){
|
||||
@@ -714,3 +452,4 @@ MockShinySession <- R6Class(
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
29
R/modules.R
29
R/modules.R
@@ -44,13 +44,14 @@ createSessionProxy <- function(parentSession, ...) {
|
||||
#' <http://shiny.rstudio.com/articles/modules.html> to learn more.
|
||||
#'
|
||||
#' Starting in Shiny 1.5.0, we recommend using `moduleServer` instead of
|
||||
#' [`callModule()`], because the syntax is a little easier
|
||||
#' to understand, and modules created with `moduleServer` can be tested with
|
||||
#' [`testServer()`].
|
||||
#' `callModule`, because the syntax is a little easier to understand, and
|
||||
#' modules created with `moduleServer` can be tested with [`testServer()`].
|
||||
#'
|
||||
#' @param module A Shiny module server function.
|
||||
#' @param id An ID string that corresponds with the ID used to call the module's
|
||||
#' UI function.
|
||||
#' @param ... For `callModule`, additional parameters to pass to module server
|
||||
#' function.
|
||||
#' @param session Session from which to make a child scope (the default should
|
||||
#' almost always be used).
|
||||
#'
|
||||
@@ -133,30 +134,14 @@ moduleServer <- function(id, module, session = getDefaultReactiveDomain()) {
|
||||
if (inherits(session, "MockShinySession")) {
|
||||
body(module) <- rlang::expr({
|
||||
session$setEnv(base::environment())
|
||||
!!body(module)
|
||||
session$setReturned({ !!!body(module) })
|
||||
})
|
||||
session$setReturned(callModule(module, id, session = session))
|
||||
} else {
|
||||
callModule(module, id, session = session)
|
||||
}
|
||||
callModule(module, id, session = session)
|
||||
}
|
||||
|
||||
|
||||
#' Invoke a Shiny module
|
||||
#'
|
||||
#' Note: As of Shiny 1.5.0, we recommend using [`moduleServer()`] instead of
|
||||
#' [`callModule()`], because the syntax is a little easier
|
||||
#' to understand, and modules created with `moduleServer` can be tested with
|
||||
#' [`testServer()`].
|
||||
#'
|
||||
#' @param module A Shiny module server function
|
||||
#' @param id An ID string that corresponds with the ID used to call the module's
|
||||
#' UI function
|
||||
#' @param ... Additional parameters to pass to module server function
|
||||
#' @param session Session from which to make a child scope (the default should
|
||||
#' almost always be used)
|
||||
#'
|
||||
#' @return The return value, if any, from executing the module server function
|
||||
#' @rdname moduleServer
|
||||
#' @export
|
||||
callModule <- function(module, id, ..., session = getDefaultReactiveDomain()) {
|
||||
if (!inherits(session, c("ShinySession", "session_proxy", "MockShinySession"))) {
|
||||
|
||||
@@ -150,8 +150,6 @@
|
||||
#' `"app"` (the default), `"session"`, or a cache object like
|
||||
#' a [diskCache()]. See the Cache Scoping section for more
|
||||
#' information.
|
||||
#' @param width,height not used. They are specified via the argument
|
||||
#' `sizePolicy`.
|
||||
#'
|
||||
#' @seealso See [renderPlot()] for the regular, non-cached version of
|
||||
#' this function. For more about configuring caches, see
|
||||
@@ -297,10 +295,7 @@ renderCachedPlot <- function(expr,
|
||||
res = 72,
|
||||
cache = "app",
|
||||
...,
|
||||
alt = "Plot object",
|
||||
outputArgs = list(),
|
||||
width = NULL,
|
||||
height = NULL
|
||||
outputArgs = list()
|
||||
) {
|
||||
|
||||
# This ..stacktraceon is matched by a ..stacktraceoff.. when plotFunc
|
||||
@@ -312,11 +307,6 @@ renderCachedPlot <- function(expr,
|
||||
|
||||
args <- list(...)
|
||||
|
||||
if (!is.null(width) || !is.null(height)) {
|
||||
warning("Unused argument(s) 'width' and/or 'height'. ",
|
||||
"'sizePolicy' is used instead.")
|
||||
}
|
||||
|
||||
cacheKeyExpr <- substitute(cacheKeyExpr)
|
||||
# The real cache key we'll use also includes width, height, res, pixelratio.
|
||||
# This is just the part supplied by the user.
|
||||
@@ -351,14 +341,6 @@ renderCachedPlot <- function(expr,
|
||||
# values get filled by an observer below.
|
||||
fitDims <- reactiveValues(width = NULL, height = NULL)
|
||||
|
||||
# Make sure alt param to be reactive function
|
||||
if (is.reactive(alt))
|
||||
altWrapper <- alt
|
||||
else if (is.function(alt))
|
||||
altWrapper <- reactive({ alt() })
|
||||
else
|
||||
altWrapper <- function() { alt }
|
||||
|
||||
resizeObserver <- NULL
|
||||
ensureResizeObserver <- function() {
|
||||
if (!is.null(resizeObserver))
|
||||
@@ -405,8 +387,6 @@ renderCachedPlot <- function(expr,
|
||||
isolate({
|
||||
width <- fitDims$width
|
||||
height <- fitDims$height
|
||||
# Make sure alt text to be reactive function
|
||||
alt <- altWrapper()
|
||||
})
|
||||
|
||||
pixelratio <- session$clientData$pixelratio %OR% 1
|
||||
@@ -418,7 +398,6 @@ renderCachedPlot <- function(expr,
|
||||
func = isolatedFunc,
|
||||
width = width,
|
||||
height = height,
|
||||
alt = alt,
|
||||
pixelratio = pixelratio,
|
||||
res = res
|
||||
),
|
||||
@@ -455,7 +434,6 @@ renderCachedPlot <- function(expr,
|
||||
function(userCacheKeyResult) {
|
||||
width <- fitDims$width
|
||||
height <- fitDims$height
|
||||
alt <- altWrapper()
|
||||
pixelratio <- session$clientData$pixelratio %OR% 1
|
||||
|
||||
key <- digest::digest(list(outputName, userCacheKeyResult, width, height, res, pixelratio), "xxhash64")
|
||||
@@ -471,7 +449,6 @@ renderCachedPlot <- function(expr,
|
||||
plotObj = plotObj,
|
||||
width = width,
|
||||
height = height,
|
||||
alt = alt,
|
||||
pixelratio = pixelratio
|
||||
))
|
||||
}
|
||||
@@ -494,7 +471,6 @@ renderCachedPlot <- function(expr,
|
||||
plotObj = drawReactiveResult,
|
||||
width = width,
|
||||
height = height,
|
||||
alt = alt,
|
||||
pixelratio = pixelratio
|
||||
)
|
||||
}
|
||||
@@ -504,7 +480,6 @@ renderCachedPlot <- function(expr,
|
||||
hybrid_chain(possiblyAsyncResult, function(result) {
|
||||
width <- result$width
|
||||
height <- result$height
|
||||
alt <- result$alt
|
||||
pixelratio <- result$pixelratio
|
||||
|
||||
# Three possibilities when we get here:
|
||||
@@ -525,7 +500,6 @@ renderCachedPlot <- function(expr,
|
||||
result$plotObj,
|
||||
width,
|
||||
height,
|
||||
alt,
|
||||
pixelratio,
|
||||
res
|
||||
),
|
||||
|
||||
@@ -36,12 +36,6 @@
|
||||
#' @param res Resolution of resulting plot, in pixels per inch. This value is
|
||||
#' passed to [grDevices::png()]. Note that this affects the resolution of PNG
|
||||
#' rendering in R; it won't change the actual ppi of the browser.
|
||||
#' @param alt Alternate text for the HTML `<img>` tag
|
||||
#' if it cannot be displayed or viewed (i.e., the user uses a screen reader).
|
||||
#' In addition to a character string, the value may be a reactive expression
|
||||
#' (or a function referencing reactive values) that returns a character string.
|
||||
#' NULL or "" is not recommended because those should be limited to decorative images
|
||||
#' (the default is "Plot object").
|
||||
#' @param ... Arguments to be passed through to [grDevices::png()].
|
||||
#' These can be used to set the width, height, background color, etc.
|
||||
#' @param env The environment in which to evaluate `expr`.
|
||||
@@ -57,10 +51,9 @@
|
||||
#' call to [plotOutput()] when `renderPlot` is used in an
|
||||
#' interactive R Markdown document.
|
||||
#' @export
|
||||
renderPlot <- function(expr, width = 'auto', height = 'auto', res = 72, ...,
|
||||
alt = "Plot object",
|
||||
env = parent.frame(), quoted = FALSE,
|
||||
execOnResize = FALSE, outputArgs = list()
|
||||
renderPlot <- function(expr, width='auto', height='auto', res=72, ...,
|
||||
env=parent.frame(), quoted=FALSE,
|
||||
execOnResize=FALSE, outputArgs=list()
|
||||
) {
|
||||
# This ..stacktraceon is matched by a ..stacktraceoff.. when plotFunc
|
||||
# is called
|
||||
@@ -82,13 +75,6 @@ renderPlot <- function(expr, width = 'auto', height = 'auto', res = 72, ...,
|
||||
else
|
||||
heightWrapper <- function() { height }
|
||||
|
||||
if (is.reactive(alt))
|
||||
altWrapper <- alt
|
||||
else if (is.function(alt))
|
||||
altWrapper <- reactive({ alt() })
|
||||
else
|
||||
altWrapper <- function() { alt }
|
||||
|
||||
getDims <- function() {
|
||||
width <- widthWrapper()
|
||||
height <- heightWrapper()
|
||||
@@ -126,7 +112,6 @@ renderPlot <- function(expr, width = 'auto', height = 'auto', res = 72, ...,
|
||||
func = func,
|
||||
width = dims$width,
|
||||
height = dims$height,
|
||||
alt = altWrapper(),
|
||||
pixelratio = pixelratio,
|
||||
res = res
|
||||
), args))
|
||||
@@ -155,7 +140,7 @@ renderPlot <- function(expr, width = 'auto', height = 'auto', res = 72, ...,
|
||||
dims <- getDims()
|
||||
pixelratio <- session$clientData$pixelratio %OR% 1
|
||||
result <- do.call("resizeSavedPlot", c(
|
||||
list(name, shinysession, result, dims$width, dims$height, altWrapper(), pixelratio, res),
|
||||
list(name, shinysession, result, dims$width, dims$height, pixelratio, res),
|
||||
args
|
||||
))
|
||||
|
||||
@@ -174,17 +159,12 @@ renderPlot <- function(expr, width = 'auto', height = 'auto', res = 72, ...,
|
||||
markRenderFunction(outputFunc, renderFunc, outputArgs = outputArgs)
|
||||
}
|
||||
|
||||
resizeSavedPlot <- function(name, session, result, width, height, alt, pixelratio, res, ...) {
|
||||
resizeSavedPlot <- function(name, session, result, width, height, pixelratio, res, ...) {
|
||||
if (result$img$width == width && result$img$height == height &&
|
||||
result$pixelratio == pixelratio && result$res == res) {
|
||||
return(result)
|
||||
}
|
||||
|
||||
if (isNamespaceLoaded("showtext")) {
|
||||
showtextOpts <- showtext::showtext_opts(dpi = res*pixelratio)
|
||||
on.exit({showtext::showtext_opts(showtextOpts)}, add = TRUE)
|
||||
}
|
||||
|
||||
coordmap <- NULL
|
||||
outfile <- plotPNG(function() {
|
||||
grDevices::replayPlot(result$recordedPlot)
|
||||
@@ -196,7 +176,6 @@ resizeSavedPlot <- function(name, session, result, width, height, alt, pixelrati
|
||||
src = session$fileUrl(name, outfile, contentType = "image/png"),
|
||||
width = width,
|
||||
height = height,
|
||||
alt = alt,
|
||||
coordmap = coordmap,
|
||||
error = attr(coordmap, "error", exact = TRUE)
|
||||
)
|
||||
@@ -204,7 +183,7 @@ resizeSavedPlot <- function(name, session, result, width, height, alt, pixelrati
|
||||
result
|
||||
}
|
||||
|
||||
drawPlot <- function(name, session, func, width, height, alt, pixelratio, res, ...) {
|
||||
drawPlot <- function(name, session, func, width, height, pixelratio, res, ...) {
|
||||
# 1. Start PNG
|
||||
# 2. Enable displaylist recording
|
||||
# 3. Call user-defined func
|
||||
@@ -221,17 +200,6 @@ drawPlot <- function(name, session, func, width, height, alt, pixelratio, res, .
|
||||
domain <- createGraphicsDevicePromiseDomain(device)
|
||||
grDevices::dev.control(displaylist = "enable")
|
||||
|
||||
# In some cases (at least when `png(type='cairo')), showtext's font
|
||||
# rendering needs to know about the device's resolution to work properly.
|
||||
# I don't see any immediate harm in setting the dpi option for any device,
|
||||
# but it's worth noting that the option doesn't currently work with CairoPNG.
|
||||
# https://github.com/yixuan/showtext/issues/33
|
||||
showtextOpts <- if (isNamespaceLoaded("showtext")) {
|
||||
showtext::showtext_opts(dpi = res*pixelratio)
|
||||
} else {
|
||||
NULL
|
||||
}
|
||||
|
||||
hybrid_chain(
|
||||
hybrid_chain(
|
||||
promises::with_promise_domain(domain, {
|
||||
@@ -278,9 +246,6 @@ drawPlot <- function(name, session, func, width, height, alt, pixelratio, res, .
|
||||
}),
|
||||
finally = function() {
|
||||
grDevices::dev.off(device)
|
||||
if (length(showtextOpts)) {
|
||||
showtext::showtext_opts(showtextOpts)
|
||||
}
|
||||
}
|
||||
),
|
||||
function(result) {
|
||||
@@ -288,7 +253,6 @@ drawPlot <- function(name, session, func, width, height, alt, pixelratio, res, .
|
||||
src = session$fileUrl(name, outfile, contentType='image/png'),
|
||||
width = width,
|
||||
height = height,
|
||||
alt = alt,
|
||||
coordmap = result$coordmap,
|
||||
# Get coordmap error message if present
|
||||
error = attr(result$coordmap, "error", exact = TRUE)
|
||||
|
||||
@@ -5,15 +5,14 @@ inputHandlers <- Map$new()
|
||||
#'
|
||||
#' Adds an input handler for data of this type. When called, Shiny will use the
|
||||
#' function provided to refine the data passed back from the client (after being
|
||||
#' deserialized by jsonlite) before making it available in the `input` variable
|
||||
#' of the `server.R` file.
|
||||
#' deserialized by jsonlite) before making it available in the `input`
|
||||
#' variable of the `server.R` file.
|
||||
#'
|
||||
#' This function will register the handler for the duration of the R process
|
||||
#' (unless Shiny is explicitly reloaded). For that reason, the `type` used
|
||||
#' should be very specific to this package to minimize the risk of colliding
|
||||
#' with another Shiny package which might use this data type name. We recommend
|
||||
#' the format of "packageName.widgetName". It should be called from the
|
||||
#' package's `.onLoad()` function.
|
||||
#' the format of "packageName.widgetName".
|
||||
#'
|
||||
#' Currently Shiny registers the following handlers: `shiny.matrix`,
|
||||
#' `shiny.number`, and `shiny.date`.
|
||||
@@ -21,20 +20,23 @@ inputHandlers <- Map$new()
|
||||
#' The `type` of a custom Shiny Input widget will be deduced using the
|
||||
#' `getType()` JavaScript function on the registered Shiny inputBinding.
|
||||
#' @param type The type for which the handler should be added --- should be a
|
||||
#' single-element character vector.
|
||||
#' single-element character vector.
|
||||
#' @param fun The handler function. This is the function that will be used to
|
||||
#' parse the data delivered from the client before it is available in the
|
||||
#' `input` variable. The function will be called with the following three
|
||||
#' parameters: \enumerate{ \item{The value of this input as provided by the
|
||||
#' client, deserialized using jsonlite.} \item{The `shinysession` in which the
|
||||
#' input exists.} \item{The name of the input.} }
|
||||
#' @param force If `TRUE`, will overwrite any existing handler without warning.
|
||||
#' If `FALSE`, will throw an error if this class already has a handler
|
||||
#' defined.
|
||||
#' parameters:
|
||||
#' \enumerate{
|
||||
#' \item{The value of this input as provided by the client, deserialized
|
||||
#' using jsonlite.}
|
||||
#' \item{The `shinysession` in which the input exists.}
|
||||
#' \item{The name of the input.}
|
||||
#' }
|
||||
#' @param force If `TRUE`, will overwrite any existing handler without
|
||||
#' warning. If `FALSE`, will throw an error if this class already has
|
||||
#' a handler defined.
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' # Register an input handler which rounds a input number to the nearest integer
|
||||
#' # In a package, this should be called from the .onLoad function.
|
||||
#' registerInputHandler("mypackage.validint", function(x, shinysession, name) {
|
||||
#' if (is.null(x)) return(NA)
|
||||
#' round(x)
|
||||
|
||||
44
R/server.R
44
R/server.R
@@ -71,8 +71,7 @@ registerClient <- function(client) {
|
||||
#' @export
|
||||
addResourcePath <- function(prefix, directoryPath) {
|
||||
if (length(prefix) != 1) stop("prefix must be of length 1")
|
||||
if (grepl("^\\.+$", prefix)) stop("prefix can't be composed of dots only")
|
||||
if (!grepl('[a-z0-9\\-_.]+$', prefix, ignore.case = TRUE, perl = TRUE)) {
|
||||
if (!grepl('^[a-z0-9\\-_][a-z0-9\\-_.]*$', prefix, ignore.case = TRUE, perl = TRUE)) {
|
||||
stop("addResourcePath called with invalid prefix; please see documentation")
|
||||
}
|
||||
if (prefix %in% c('shared')) {
|
||||
@@ -280,8 +279,6 @@ decodeMessage <- function(data) {
|
||||
return(mainMessage)
|
||||
}
|
||||
|
||||
autoReloadCallbacks <- Callbacks$new()
|
||||
|
||||
createAppHandlers <- function(httpHandlers, serverFuncSource) {
|
||||
appvars <- new.env()
|
||||
appvars$server <- NULL
|
||||
@@ -307,22 +304,6 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
|
||||
return(TRUE)
|
||||
}
|
||||
|
||||
if (identical(ws$request$PATH_INFO, "/autoreload/")) {
|
||||
if (!getOption("shiny.autoreload", FALSE)) {
|
||||
ws$close()
|
||||
return(TRUE)
|
||||
}
|
||||
|
||||
callbackHandle <- autoReloadCallbacks$register(function() {
|
||||
ws$send("autoreload")
|
||||
ws$close()
|
||||
})
|
||||
ws$onClose(function() {
|
||||
callbackHandle()
|
||||
})
|
||||
return(TRUE)
|
||||
}
|
||||
|
||||
if (!is.null(getOption("shiny.observer.error", NULL))) {
|
||||
warning(
|
||||
call. = FALSE,
|
||||
@@ -787,15 +768,6 @@ runApp <- function(appDir=getwd(),
|
||||
shinyOptions(cache = MemoryCache$new())
|
||||
}
|
||||
|
||||
# Invoke user-defined onStop callbacks, before the application's internal
|
||||
# onStop callbacks.
|
||||
on.exit({
|
||||
.globals$onStopCallbacks$invoke()
|
||||
.globals$onStopCallbacks <- Callbacks$new()
|
||||
}, add = TRUE)
|
||||
|
||||
require(shiny)
|
||||
|
||||
appParts <- as.shiny.appobj(appDir)
|
||||
|
||||
# The lines below set some of the app's running options, which
|
||||
@@ -909,6 +881,8 @@ runApp <- function(appDir=getwd(),
|
||||
setShowcaseDefault(1)
|
||||
}
|
||||
|
||||
require(shiny)
|
||||
|
||||
# determine port if we need to
|
||||
if (is.null(port)) {
|
||||
|
||||
@@ -947,6 +921,13 @@ runApp <- function(appDir=getwd(),
|
||||
}
|
||||
}
|
||||
|
||||
# Invoke user-defined onStop callbacks, before the application's internal
|
||||
# onStop callbacks.
|
||||
on.exit({
|
||||
.globals$onStopCallbacks$invoke()
|
||||
.globals$onStopCallbacks <- Callbacks$new()
|
||||
}, add = TRUE)
|
||||
|
||||
# Extract appOptions (which is a list) and store them as shinyOptions, for
|
||||
# this app. (This is the only place we have to store settings that are
|
||||
# accessible both the UI and server portion of the app.)
|
||||
@@ -1056,6 +1037,8 @@ stopApp <- function(returnValue = invisible()) {
|
||||
#'
|
||||
#' @param example The name of the example to run, or `NA` (the default) to
|
||||
#' list the available examples.
|
||||
#' @param port The TCP port that the application should listen on. Defaults to
|
||||
#' choosing a random port.
|
||||
#' @param launch.browser If true, the system's default web browser will be
|
||||
#' launched automatically after the app is started. Defaults to true in
|
||||
#' interactive sessions only.
|
||||
@@ -1064,7 +1047,6 @@ stopApp <- function(returnValue = invisible()) {
|
||||
#' @param display.mode The mode in which to display the example. Defaults to
|
||||
#' `showcase`, but may be set to `normal` to see the example without
|
||||
#' code or commentary.
|
||||
#' @inheritParams runApp
|
||||
#'
|
||||
#' @examples
|
||||
#' ## Only run this example in interactive R sessions
|
||||
@@ -1080,7 +1062,7 @@ stopApp <- function(returnValue = invisible()) {
|
||||
#' }
|
||||
#' @export
|
||||
runExample <- function(example=NA,
|
||||
port=getOption("shiny.port"),
|
||||
port=NULL,
|
||||
launch.browser=getOption('shiny.launch.browser',
|
||||
interactive()),
|
||||
host=getOption('shiny.host', '127.0.0.1'),
|
||||
|
||||
@@ -65,7 +65,7 @@ getShinyOption <- function(name, default = NULL) {
|
||||
#' [runApp()] for more information.}
|
||||
#' \item{shiny.jquery.version (defaults to `3`)}{The major version of jQuery to use.
|
||||
#' Currently only values of `3` or `1` are supported. If `1`, then jQuery 1.12.4 is used. If `3`,
|
||||
#' then jQuery 3.5.1 is used.}
|
||||
#' then jQuery 3.4.1 is used.}
|
||||
#' \item{shiny.json.digits (defaults to `16`)}{The number of digits to use when converting
|
||||
#' numbers to JSON format to send to the client web browser.}
|
||||
#' \item{shiny.launch.browser (defaults to `interactive()`)}{A boolean which controls the default behavior
|
||||
|
||||
122
R/shiny.R
122
R/shiny.R
@@ -474,22 +474,8 @@ ShinySession <- R6Class(
|
||||
|
||||
if (!is.null(params$input)) {
|
||||
|
||||
# The isolate and reactiveValuesToList calls are being executed
|
||||
# in a non-reactive context, but will produce output in the reactlog
|
||||
# Seeing new, unlabelled reactives ONLY when calling shinytest is
|
||||
# jarring / frustrating to debug.
|
||||
# Since labeling these values is not currently supported in reactlog,
|
||||
# it is better to hide them.
|
||||
# Hopefully we can replace this with something like
|
||||
# `with_reactlog_group("shinytest", {})`, which would visibily explain
|
||||
# why the new reactives are added when calling shinytest
|
||||
withr::with_options(
|
||||
list(shiny.reactlog = FALSE),
|
||||
{
|
||||
allInputs <- isolate(
|
||||
reactiveValuesToList(self$input, all.names = TRUE)
|
||||
)
|
||||
}
|
||||
allInputs <- isolate(
|
||||
reactiveValuesToList(self$input, all.names = TRUE)
|
||||
)
|
||||
|
||||
# If params$input is "1", return all; otherwise return just the
|
||||
@@ -1333,13 +1319,6 @@ ShinySession <- R6Class(
|
||||
|
||||
# If we don't already have width for this output info, see if it's
|
||||
# present, and if so, add it.
|
||||
|
||||
# Note that all the following clientData values (which are reactiveValues)
|
||||
# are wrapped in reactive() so that users can take a dependency on particular
|
||||
# output info (i.e., just depend on width/height, or just depend on bg, fg, etc).
|
||||
# To put it another way, if getCurrentOutputInfo() simply returned a list of values
|
||||
# from self$clientData, than anything that calls getCurrentOutputInfo() would take
|
||||
# a reactive dependency on all of these values.
|
||||
if (! ("width" %in% names(tmp_info)) ) {
|
||||
width_name <- paste0("output_", name, "_width")
|
||||
if (width_name %in% cd_names()) {
|
||||
@@ -1358,42 +1337,6 @@ ShinySession <- R6Class(
|
||||
}
|
||||
}
|
||||
|
||||
# parseCssColors() currently errors out if you hand it any NAs
|
||||
# This'll make sure we're always working with a string (and if
|
||||
# that string isn't a valid CSS color, will return NA)
|
||||
# https://github.com/rstudio/htmltools/issues/161
|
||||
parse_css_colors <- function(x) {
|
||||
htmltools::parseCssColors(x %OR% "", mustWork = FALSE)
|
||||
}
|
||||
|
||||
bg <- paste0("output_", name, "_bg")
|
||||
if (bg %in% cd_names()) {
|
||||
tmp_info$bg <- reactive({
|
||||
parse_css_colors(self$clientData[[bg]])
|
||||
})
|
||||
}
|
||||
|
||||
fg <- paste0("output_", name, "_fg")
|
||||
if (fg %in% cd_names()) {
|
||||
tmp_info$fg <- reactive({
|
||||
parse_css_colors(self$clientData[[fg]])
|
||||
})
|
||||
}
|
||||
|
||||
accent <- paste0("output_", name, "_accent")
|
||||
if (accent %in% cd_names()) {
|
||||
tmp_info$accent <- reactive({
|
||||
parse_css_colors(self$clientData[[accent]])
|
||||
})
|
||||
}
|
||||
|
||||
font <- paste0("output_", name, "_font")
|
||||
if (font %in% cd_names()) {
|
||||
tmp_info$font <- reactive({
|
||||
self$clientData[[font]]
|
||||
})
|
||||
}
|
||||
|
||||
private$outputInfo[[name]] <- tmp_info
|
||||
private$outputInfo[[name]]
|
||||
},
|
||||
@@ -2156,69 +2099,16 @@ outputOptions <- function(x, name, ...) {
|
||||
}
|
||||
|
||||
|
||||
#' Get output information
|
||||
#' Get information about the output that is currently being executed.
|
||||
#'
|
||||
#' Returns information about the currently executing output, including its `name` (i.e., `outputId`);
|
||||
#' and in some cases, relevant sizing and styling information.
|
||||
#' @return A list with information about the current output, including the
|
||||
#' `name` of the output. If no output is currently being executed, this will
|
||||
#' return `NULL`.
|
||||
#'
|
||||
#' @param session The current Shiny session.
|
||||
#'
|
||||
#' @return `NULL` if called outside of an output context; otherwise,
|
||||
#' a list which includes:
|
||||
#' * The `name` of the output (reported for any output).
|
||||
#' * If the output is a `plotOutput()` or `imageOutput()`, then:
|
||||
#' * `height`: a reactive expression which returns the height in pixels.
|
||||
#' * `width`: a reactive expression which returns the width in pixels.
|
||||
#' * If the output is a `plotOutput()`, `imageOutput()`, or contains a `shiny-report-theme` class, then:
|
||||
#' * `bg`: a reactive expression which returns the background color.
|
||||
#' * `fg`: a reactive expression which returns the foreground color.
|
||||
#' * `accent`: a reactive expression which returns the hyperlink color.
|
||||
#' * `font`: a reactive expression which returns a list of font information, including:
|
||||
#' * `families`: a character vector containing the CSS `font-family` property.
|
||||
#' * `size`: a character string containing the CSS `font-size` property
|
||||
#'
|
||||
#' @export
|
||||
#' @examples
|
||||
#'
|
||||
#' if (interactive()) {
|
||||
#' shinyApp(
|
||||
#' fluidPage(
|
||||
#' tags$style(HTML("body {background-color: black; color: white; }")),
|
||||
#' tags$style(HTML("body a {color: purple}")),
|
||||
#' tags$style(HTML("#info {background-color: teal; color: orange; }")),
|
||||
#' plotOutput("p"),
|
||||
#' "Computed CSS styles for the output named info:",
|
||||
#' tagAppendAttributes(
|
||||
#' textOutput("info"),
|
||||
#' class = "shiny-report-theme"
|
||||
#' )
|
||||
#' ),
|
||||
#' function(input, output) {
|
||||
#' output$p <- renderPlot({
|
||||
#' info <- getCurrentOutputInfo()
|
||||
#' par(bg = info$bg(), fg = info$fg(), col.axis = info$fg(), col.main = info$fg())
|
||||
#' plot(1:10, col = info$accent(), pch = 19)
|
||||
#' title("A simple R plot that uses its CSS styling")
|
||||
#' })
|
||||
#' output$info <- renderText({
|
||||
#' info <- getCurrentOutputInfo()
|
||||
#' jsonlite::toJSON(
|
||||
#' list(
|
||||
#' bg = info$bg(),
|
||||
#' fg = info$fg(),
|
||||
#' accent = info$accent(),
|
||||
#' font = info$font()
|
||||
#' ),
|
||||
#' auto_unbox = TRUE
|
||||
#' )
|
||||
#' })
|
||||
#' }
|
||||
#' )
|
||||
#' }
|
||||
#'
|
||||
#'
|
||||
getCurrentOutputInfo <- function(session = getDefaultReactiveDomain()) {
|
||||
if (is.null(session)) return(NULL)
|
||||
session$getCurrentOutputInfo()
|
||||
}
|
||||
|
||||
|
||||
25
R/shinyui.R
25
R/shinyui.R
@@ -24,7 +24,7 @@ withMathJax <- function(...) {
|
||||
)
|
||||
}
|
||||
|
||||
renderPage <- function(ui, showcase=0, testMode=FALSE) {
|
||||
renderPage <- function(ui, connection, showcase=0, testMode=FALSE) {
|
||||
# If the ui is a NOT complete document (created by htmlTemplate()), then do some
|
||||
# preprocessing and make sure it's a complete document.
|
||||
if (!inherits(ui, "html_document")) {
|
||||
@@ -46,7 +46,7 @@ renderPage <- function(ui, showcase=0, testMode=FALSE) {
|
||||
version <- getOption("shiny.jquery.version", 3)
|
||||
if (version == 3) {
|
||||
return(htmlDependency(
|
||||
"jquery", "3.5.1",
|
||||
"jquery", "3.4.1",
|
||||
c(href = "shared"),
|
||||
script = "jquery.min.js"
|
||||
))
|
||||
@@ -77,7 +77,7 @@ renderPage <- function(ui, showcase=0, testMode=FALSE) {
|
||||
}
|
||||
|
||||
html <- renderDocument(ui, shiny_deps, processDep = createWebDependency)
|
||||
enc2utf8(paste(collapse = "\n", html))
|
||||
writeUTF8(html, con = connection)
|
||||
}
|
||||
|
||||
#' Create a Shiny UI handler
|
||||
@@ -101,18 +101,16 @@ uiHttpHandler <- function(ui, uiPattern = "^/$") {
|
||||
|
||||
force(ui)
|
||||
|
||||
allowed_methods <- "GET"
|
||||
if (is.function(ui)) {
|
||||
allowed_methods <- attr(ui, "http_methods_supported", exact = TRUE) %OR% allowed_methods
|
||||
}
|
||||
|
||||
function(req) {
|
||||
if (!isTRUE(req$REQUEST_METHOD %in% allowed_methods))
|
||||
if (!identical(req$REQUEST_METHOD, 'GET'))
|
||||
return(NULL)
|
||||
|
||||
if (!isTRUE(grepl(uiPattern, req$PATH_INFO)))
|
||||
return(NULL)
|
||||
|
||||
textConn <- file(open = "w+")
|
||||
on.exit(close(textConn))
|
||||
|
||||
showcaseMode <- .globals$showcaseDefault
|
||||
if (.globals$showcaseOverride) {
|
||||
mode <- showcaseModeOfReq(req)
|
||||
@@ -152,11 +150,8 @@ uiHttpHandler <- function(ui, uiPattern = "^/$") {
|
||||
if (is.null(uiValue))
|
||||
return(NULL)
|
||||
|
||||
if (inherits(uiValue, "httpResponse")) {
|
||||
return(uiValue)
|
||||
} else {
|
||||
html <- renderPage(uiValue, showcaseMode, testMode)
|
||||
return(httpResponse(200, content=html))
|
||||
}
|
||||
renderPage(uiValue, textConn, showcaseMode, testMode)
|
||||
html <- paste(readLines(textConn, encoding = 'UTF-8'), collapse='\n')
|
||||
return(httpResponse(200, content=enc2utf8(html)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,10 +198,7 @@ markOutputAttrs <- function(renderFunc, snapshotExclude = NULL,
|
||||
#' @param deleteFile Should the file in `func()$src` be deleted after
|
||||
#' it is sent to the client browser? Generally speaking, if the image is a
|
||||
#' temp file generated within `func`, then this should be `TRUE`;
|
||||
#' if the image is not a temp file, this should be `FALSE`. (For backward
|
||||
#' compatibility reasons, if this argument is missing, a warning will be
|
||||
#' emitted, and if the file is in the temp directory it will be deleted. In
|
||||
#' the future, this warning will become an error.)
|
||||
#' if the image is not a temp file, this should be `FALSE`.
|
||||
#' @param outputArgs A list of arguments to be passed through to the implicit
|
||||
#' call to [imageOutput()] when `renderImage` is used in an
|
||||
#' interactive R Markdown document.
|
||||
@@ -274,53 +271,15 @@ markOutputAttrs <- function(renderFunc, snapshotExclude = NULL,
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
renderImage <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
deleteFile, outputArgs=list()) {
|
||||
deleteFile=TRUE, outputArgs=list()) {
|
||||
installExprFunction(expr, "func", env, quoted)
|
||||
|
||||
# missing() must be used directly within the function with the given arg
|
||||
if (missing(deleteFile)) {
|
||||
deleteFile <- NULL
|
||||
}
|
||||
|
||||
# Tracks whether we've reported the `deleteFile` warning yet; we don't want to
|
||||
# do it on every invalidation (though we will end up doing it at least once
|
||||
# per output per session).
|
||||
warned <- FALSE
|
||||
|
||||
createRenderFunction(func,
|
||||
transform = function(imageinfo, session, name, ...) {
|
||||
shouldDelete <- deleteFile
|
||||
|
||||
# jcheng 2020-05-08
|
||||
#
|
||||
# Until Shiny 1.5.0, the default for deleteFile was, incredibly, TRUE.
|
||||
# Changing it to default to FALSE might cause existing Shiny apps to pile
|
||||
# up images in their temp directory (for long lived R processes). Not
|
||||
# having a default (requiring explicit value) is the right long-term move,
|
||||
# but would break today's apps.
|
||||
#
|
||||
# Compromise we decided on was to eventually require TRUE/FALSE, but for
|
||||
# now, change the default behavior to only delete temp files; and emit a
|
||||
# warning encouraging people to not rely on the default.
|
||||
if (is.null(shouldDelete)) {
|
||||
shouldDelete <- isTRUE(try(silent = TRUE,
|
||||
file.exists(imageinfo$src) && isTemp(imageinfo$src, mustExist = TRUE)
|
||||
))
|
||||
|
||||
if (!warned) {
|
||||
warned <<- TRUE
|
||||
warning("The renderImage output named '",
|
||||
getCurrentOutputInfo()$name,
|
||||
"' is missing the deleteFile argument; as of Shiny 1.5.0, you must ",
|
||||
"use deleteFile=TRUE or deleteFile=FALSE. (This warning will ",
|
||||
"become an error in a future version of Shiny.)",
|
||||
call. = FALSE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldDelete) {
|
||||
on.exit(unlink(imageinfo$src), add = TRUE)
|
||||
# Should the file be deleted after being sent? If .deleteFile not set or if
|
||||
# TRUE, then delete; otherwise don't delete.
|
||||
if (deleteFile) {
|
||||
on.exit(unlink(imageinfo$src))
|
||||
}
|
||||
|
||||
# If contentType not specified, autodetect based on extension
|
||||
@@ -336,71 +295,36 @@ renderImage <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
imageOutput, outputArgs)
|
||||
}
|
||||
|
||||
# TODO: If we ever take a dependency on fs, it'd be great to replace this with
|
||||
# fs::path_has_parent().
|
||||
isTemp <- function(path, tempDir = tempdir(), mustExist) {
|
||||
if (!isTRUE(mustExist)) {
|
||||
# jcheng 2020-05-11: I added mustExist just to make it totally obvious that
|
||||
# the path must exist. We don't support the case where the file doesn't
|
||||
# exist because it makes normalizePath unusable, and it's a bit scary
|
||||
# security-wise to compare paths without normalization. Using fs would fix
|
||||
# this as it knows how to normalize paths that don't exist.
|
||||
stop("isTemp(mustExist=FALSE) is not implemented")
|
||||
}
|
||||
|
||||
if (mustExist && !file.exists(path)) {
|
||||
stop("path does not exist")
|
||||
}
|
||||
|
||||
if (nchar(tempDir) == 0 || !dir.exists(tempDir)) {
|
||||
# This should never happen, but just to be super paranoid...
|
||||
stop("invalid temp dir")
|
||||
}
|
||||
|
||||
path <- normalizePath(path, winslash = "/", mustWork = mustExist)
|
||||
|
||||
tempDir <- normalizePath(tempDir, winslash = "/", mustWork = TRUE)
|
||||
if (path == tempDir) {
|
||||
return(FALSE)
|
||||
}
|
||||
|
||||
tempDir <- ensure_trailing_slash(tempDir)
|
||||
if (path == tempDir) {
|
||||
return(FALSE)
|
||||
}
|
||||
|
||||
return(substr(path, 1, nchar(tempDir)) == tempDir)
|
||||
}
|
||||
|
||||
#' Text Output
|
||||
#' Printable Output
|
||||
#'
|
||||
#' @description
|
||||
#' `renderPrint()` prints the result of `expr`, while `renderText()` pastes it
|
||||
#' together into a single string. `renderPrint()` is equivalent to [print()];
|
||||
#' `renderText()` is equivalent to [cat()]. Both functions capture all other
|
||||
#' printed output generated while evaluating `expr`.
|
||||
#' Makes a reactive version of the given function that captures any printed
|
||||
#' output, and also captures its printable result (unless
|
||||
#' [base::invisible()]), into a string. The resulting function is suitable
|
||||
#' for assigning to an `output` slot.
|
||||
#'
|
||||
#' `renderPrint()` is usually paired with [verbatimTextOutput()];
|
||||
#' `renderText()` is usually paired with [textOutput()].
|
||||
#'
|
||||
#' @details
|
||||
#' The corresponding HTML output tag can be anything (though `pre` is
|
||||
#' recommended if you need a monospace font and whitespace preserved) and should
|
||||
#' have the CSS class name `shiny-text-output`.
|
||||
#'
|
||||
#' @return
|
||||
#' For `renderPrint()`, note the given expression returns `NULL` then `NULL`
|
||||
#' will actually be visible in the output. To display nothing, make your
|
||||
#' function return [invisible()].
|
||||
#' The result of executing `func` will be printed inside a
|
||||
#' [utils::capture.output()] call.
|
||||
#'
|
||||
#' @param expr An expression to evaluate.
|
||||
#' @param env The environment in which to evaluate `expr`. For expert use only.
|
||||
#' Note that unlike most other Shiny output functions, if the given function
|
||||
#' returns `NULL` then `NULL` will actually be visible in the output.
|
||||
#' To display nothing, make your function return [base::invisible()].
|
||||
#'
|
||||
#' @param expr An expression that may print output and/or return a printable R
|
||||
#' object.
|
||||
#' @param env The environment in which to evaluate `expr`.
|
||||
#' @param quoted Is `expr` a quoted expression (with `quote()`)? This
|
||||
#' is useful if you want to save an expression in a variable.
|
||||
#' @param width Width of printed output.
|
||||
#' @param width The value for `[options][base::options]('width')`.
|
||||
#' @param outputArgs A list of arguments to be passed through to the implicit
|
||||
#' call to [verbatimTextOutput()] or [textOutput()] when the functions are
|
||||
#' used in an interactive RMarkdown document.
|
||||
#' call to [verbatimTextOutput()] when `renderPrint` is used
|
||||
#' in an interactive R Markdown document.
|
||||
#' @seealso [renderText()] for displaying the value returned from a
|
||||
#' function, instead of the printed output.
|
||||
#'
|
||||
#' @example res/text-example.R
|
||||
#' @export
|
||||
@@ -476,10 +400,35 @@ createRenderPrintPromiseDomain <- function(width) {
|
||||
)
|
||||
}
|
||||
|
||||
#' Text Output
|
||||
#'
|
||||
#' Makes a reactive version of the given function that also uses
|
||||
#' [base::cat()] to turn its result into a single-element character
|
||||
#' vector.
|
||||
#'
|
||||
#' The corresponding HTML output tag can be anything (though `pre` is
|
||||
#' recommended if you need a monospace font and whitespace preserved) and should
|
||||
#' have the CSS class name `shiny-text-output`.
|
||||
#'
|
||||
#' The result of executing `func` will passed to `cat`, inside a
|
||||
#' [utils::capture.output()] call.
|
||||
#'
|
||||
#' @param expr An expression that returns an R object that can be used as an
|
||||
#' argument to `cat`.
|
||||
#' @param env The environment in which to evaluate `expr`.
|
||||
#' @param quoted Is `expr` a quoted expression (with `quote()`)? This
|
||||
#' is useful if you want to save an expression in a variable.
|
||||
#' @param outputArgs A list of arguments to be passed through to the implicit
|
||||
#' call to [textOutput()] when `renderText` is used in an
|
||||
#' interactive R Markdown document.
|
||||
#' @param sep A separator passed to `cat` to be appended after each
|
||||
#' element.
|
||||
#'
|
||||
#' @seealso [renderPrint()] for capturing the print output of a
|
||||
#' function, rather than the returned text value.
|
||||
#'
|
||||
#' @example res/text-example.R
|
||||
#' @export
|
||||
#' @rdname renderPrint
|
||||
renderText <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
outputArgs=list(), sep=" ") {
|
||||
installExprFunction(expr, "func", env, quoted)
|
||||
@@ -578,7 +527,7 @@ renderUI <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
#' if (interactive()) {
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' downloadButton("downloadData", "Download")
|
||||
#' downloadLink("downloadData", "Download")
|
||||
#' )
|
||||
#'
|
||||
#' server <- function(input, output) {
|
||||
|
||||
@@ -104,18 +104,20 @@ navTabsDropdown <- function(files) {
|
||||
|
||||
tabContentHelper <- function(files, path, language) {
|
||||
lapply(files, function(file) {
|
||||
tags$div(class=paste("tab-pane",
|
||||
with(tags,
|
||||
div(class=paste("tab-pane",
|
||||
if (tolower(file) %in% c("app.r", "server.r")) " active"
|
||||
else "",
|
||||
sep=""),
|
||||
id=paste(gsub(".", "_", file, fixed=TRUE),
|
||||
"_code", sep=""),
|
||||
tags$pre(class="shiny-code",
|
||||
pre(class="shiny-code",
|
||||
# we need to prevent the indentation of <code> ... </code>
|
||||
HTML(format(tags$code(
|
||||
class=paste0("language-", language),
|
||||
paste(readUTF8(file.path.ci(path, file)), collapse="\n")
|
||||
), indent = FALSE))))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
142
R/test-server.R
142
R/test-server.R
@@ -1,3 +1,15 @@
|
||||
# Create a "data mask" suitable for passing to rlang::eval_tidy. Bindings in
|
||||
# `env` and bindings in the parent of `env` are merged into a single named list.
|
||||
# Bindings in `env` take precedence over bindings in the parent of `env`.
|
||||
#' @noRd
|
||||
makeMask <- function(env) {
|
||||
stopifnot(length(rlang::env_parents(env)) > 1)
|
||||
child <- as.list(env)
|
||||
parent <- as.list(rlang::env_parent(env))
|
||||
parent_only <- setdiff(names(parent), names(child))
|
||||
append(child, parent[parent_only])
|
||||
}
|
||||
|
||||
#' @noRd
|
||||
isModuleServer <- function(x) {
|
||||
is.function(x) && names(formals(x))[1] == "id"
|
||||
@@ -10,23 +22,17 @@ isModuleServer <- function(x) {
|
||||
#' modules.
|
||||
#' @param app The path to an application or module to test. In addition to
|
||||
#' paths, applications may be represented by any object suitable for coercion
|
||||
#' to an `appObj` by [`as.shiny.appobj`]. Application server functions must
|
||||
#' include a `session` argument in order to be tested. If `app` is `NULL` or
|
||||
#' not supplied, the nearest enclosing directory that is a Shiny app, starting
|
||||
#' with the current directory, is used.
|
||||
#' @param expr Test code containing expectations. The objects from inside the
|
||||
#' server function environment will be made available in the environment of
|
||||
#' the test expression (this is done using a data mask with
|
||||
#' [rlang::eval_tidy()]). This includes the parameters of the server function
|
||||
#' (e.g. `input`, `output`, and `session`), along with any other values
|
||||
#' created inside of the server function.
|
||||
#' @param args Additional arguments to pass to the module function. If `app` is
|
||||
#' a module, and no `id` argument is provided, one will be generated and
|
||||
#' supplied automatically.
|
||||
#' @param session The [`MockShinySession`] object to use as the [reactive
|
||||
#' domain][shiny::domains]. The same session object is used as the domain both
|
||||
#' during invocation of the server or module under test and during evaluation
|
||||
#' of `expr`.
|
||||
#' to an `appObj` by `as.shiny.appobj`. Application server functions must
|
||||
#' include a `session` argument in order to be tested.
|
||||
#' @param expr Test code containing expectations. The test expression will run
|
||||
#' in the server function environment, meaning that the parameters of the
|
||||
#' server function (e.g. `input`, `output`, and `session`) will be available
|
||||
#' along with any other values created inside of the server function.
|
||||
#' @param ... Additional arguments to pass to the module function. These
|
||||
#' arguments are processed with [rlang::list2()] and so are
|
||||
#' _[dynamic][rlang::dyn-dots]_. If `app` is a module, and no `id` argument is
|
||||
#' provided, one will be generated and supplied automatically.
|
||||
#' @return The result of evaluating `expr`.
|
||||
#' @include mock-session.R
|
||||
#' @rdname testServer
|
||||
#' @examples
|
||||
@@ -41,7 +47,7 @@ isModuleServer <- function(x) {
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' testServer(server, args = list(multiplier = 2), {
|
||||
#' testServer(server, {
|
||||
#' session$setInputs(x = 1)
|
||||
#' # You're also free to use third-party
|
||||
#' # testing packages like testthat:
|
||||
@@ -53,77 +59,61 @@ isModuleServer <- function(x) {
|
||||
#' stopifnot(myreactive() == 4)
|
||||
#' stopifnot(output$txt == "I am 4")
|
||||
#' # Any additional arguments, below, are passed along to the module.
|
||||
#' })
|
||||
#' }, multiplier = 2)
|
||||
#' @export
|
||||
testServer <- function(app = NULL, expr, args = list(), session = MockShinySession$new()) {
|
||||
testServer <- function(app, expr, ...) {
|
||||
|
||||
require(shiny)
|
||||
args <- rlang::list2(...)
|
||||
|
||||
if (!is.null(getDefaultReactiveDomain()))
|
||||
stop("testServer() is for use only within tests and may not indirectly call itself.")
|
||||
session <- getDefaultReactiveDomain()
|
||||
|
||||
quosure <- rlang::enquo(expr)
|
||||
if (inherits(session, "MockShinySession"))
|
||||
stop("Test expressions may not call testServer()")
|
||||
if (inherits(session, "session_proxy")
|
||||
&& inherits(get("parent", envir = session), "MockShinySession"))
|
||||
stop("Modules may not call testServer()")
|
||||
|
||||
session <- MockShinySession$new()
|
||||
on.exit(if (!session$isClosed()) session$close())
|
||||
|
||||
withMockContext <- function(expr) {
|
||||
isolate(
|
||||
withReactiveDomain(session, {
|
||||
withr::with_options(list(`shiny.allowoutputreads` = TRUE), {
|
||||
withLocalOptions({
|
||||
# Sets a cache for renderCachedPlot() with cache = "app" to use.
|
||||
shinyOptions("cache" = session$appcache)
|
||||
expr
|
||||
})
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (isModuleServer(app)) {
|
||||
if (!("id" %in% names(args)))
|
||||
args[["id"]] <- session$genId()
|
||||
# app is presumed to be a module, and modules may take additional arguments,
|
||||
# so splice in any args.
|
||||
withMockContext(rlang::exec(app, !!!args))
|
||||
|
||||
# If app is a module, then we must use both the module function's immediate
|
||||
# environment and also its enclosing environment to construct the mask.
|
||||
parent_clone <- rlang::env_clone(parent.env(session$env))
|
||||
clone <- rlang::env_clone(session$env, parent_clone)
|
||||
mask <- rlang::new_data_mask(clone, parent_clone)
|
||||
withMockContext(rlang::eval_tidy(quosure, mask, rlang::caller_env()))
|
||||
} else {
|
||||
if (is.null(app)) {
|
||||
app <- findEnclosingApp(".")
|
||||
}
|
||||
|
||||
appobj <- as.shiny.appobj(app)
|
||||
if (!is.null(appobj$onStart))
|
||||
appobj$onStart()
|
||||
# Ensure appobj$onStop() is called, and the current directory is restored,
|
||||
# regardless of whether invoking the server function is successful.
|
||||
tryCatch({
|
||||
server <- appobj$serverFuncSource()
|
||||
if (! "session" %in% names(formals(server)))
|
||||
stop("Tested application server functions must declare input, output, and session arguments.")
|
||||
body(server) <- rlang::expr({
|
||||
session$setEnv(base::environment())
|
||||
!!body(server)
|
||||
})
|
||||
if (length(args))
|
||||
stop("Arguments were provided to a server function.")
|
||||
withMockContext(server(input = session$input, output = session$output, session = session))
|
||||
}, finally = {
|
||||
if (!is.null(appobj$onStop))
|
||||
appobj$onStop()
|
||||
server <- appobj$serverFuncSource()
|
||||
if (! "session" %in% names(formals(server)))
|
||||
stop("Tested application server functions must declare input, output, and session arguments.")
|
||||
body(server) <- rlang::expr({
|
||||
session$setEnv(base::environment())
|
||||
!!!body(server)
|
||||
})
|
||||
|
||||
# If app is a server, we use only the server function's immediate
|
||||
# environment to construct the mask.
|
||||
mask <- rlang::new_data_mask(rlang::env_clone(session$env))
|
||||
withMockContext(rlang::eval_tidy(quosure, mask, rlang::caller_env()))
|
||||
app <- function() {
|
||||
session$setReturned(server(input = session$input, output = session$output, session = session))
|
||||
}
|
||||
if (length(args))
|
||||
message("Discarding unused arguments to server function")
|
||||
}
|
||||
|
||||
invisible()
|
||||
isolate(
|
||||
withReactiveDomain(
|
||||
session,
|
||||
withr::with_options(list(`shiny.allowoutputreads` = TRUE), {
|
||||
rlang::exec(app, !!!args)
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
stopifnot(all(c("input", "output", "session") %in% ls(session$env)))
|
||||
|
||||
quosure <- rlang::enquo(expr)
|
||||
|
||||
isolate(
|
||||
withReactiveDomain(
|
||||
session,
|
||||
withr::with_options(list(`shiny.allowoutputreads` = TRUE), {
|
||||
rlang::eval_tidy(quosure, makeMask(session$env), rlang::caller_env())
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
206
R/test.R
206
R/test.R
@@ -2,47 +2,35 @@
|
||||
#'
|
||||
#' @param file Name of the test runner file, a character vector of length 1.
|
||||
#' @param pass Whether or not the test passed, a logical vector of length 1.
|
||||
#' @param result Value (wrapped in a list) obtained by evaluating `file`.
|
||||
#' This can also by any errors signaled when evaluating the `file`.
|
||||
#' @param result Value (wrapped in a list) obtained by evaluating `file` or `NA`
|
||||
#' if no value was obtained, such as with `shinytest`.
|
||||
#' @param error Error, if any, (and wrapped in a list) that was signaled during
|
||||
#' evaluation of `file`.
|
||||
#'
|
||||
#' @return A 1-row data frame representing a single test run. `result` and
|
||||
#' is a "list column", or a column that contains list elements.
|
||||
#' `error` are "list columns", or columns that may contain list elements.
|
||||
#' @noRd
|
||||
result_row <- function(file, pass, result) {
|
||||
result_row <- function(file, pass, result, error) {
|
||||
stopifnot(is.list(result))
|
||||
stopifnot(is.list(error))
|
||||
df <- data.frame(
|
||||
file = file,
|
||||
pass = pass,
|
||||
result = I(result),
|
||||
error = I(error),
|
||||
stringsAsFactors = FALSE
|
||||
)
|
||||
class(df) <- c("shiny_runtests", class(df))
|
||||
class(df) <- c("shinytestrun", class(df))
|
||||
df
|
||||
}
|
||||
|
||||
#' Check to see if the given directory contains at least one script, and that
|
||||
#' all scripts in the directory are shinytest scripts.
|
||||
#' Scans for the magic string of `app <- ShinyDriver$new(` as an indicator that
|
||||
#' this is a shinytest.
|
||||
#' Check to see if the given text is a shinytest
|
||||
#' Scans for the magic string of `app <- ShinyDriver$new(` as an indicator that this is a shinytest.
|
||||
#' Brought in from shinytest to avoid having to export this function.
|
||||
#' @noRd
|
||||
is_legacy_shinytest_dir <- function(path){
|
||||
is_shinytest_script <- function(file) {
|
||||
if (!file.exists(file)) {
|
||||
return(FALSE)
|
||||
}
|
||||
|
||||
text <- readLines(file, warn = FALSE)
|
||||
any(
|
||||
grepl("app\\s*<-\\s*ShinyDriver\\$new\\(", text, perl=TRUE)
|
||||
)
|
||||
}
|
||||
|
||||
files <- dir(path, full.names = TRUE)
|
||||
files <- files[!file.info(files)$isdir]
|
||||
if (length(files) == 0) {
|
||||
return(FALSE)
|
||||
}
|
||||
all(vapply(files, is_shinytest_script, logical(1)))
|
||||
isShinyTest <- function(text){
|
||||
lines <- grepl("app\\s*<-\\s*ShinyDriver\\$new\\(", text, perl=TRUE)
|
||||
any(lines)
|
||||
}
|
||||
|
||||
#' Runs the tests associated with this Shiny app
|
||||
@@ -55,135 +43,99 @@ is_legacy_shinytest_dir <- function(path){
|
||||
#' @param filter If not `NULL`, only tests with file names matching this regular
|
||||
#' expression will be executed. Matching is performed on the file name
|
||||
#' including the extension.
|
||||
#' @param assert Logical value which determines if an error should be thrown if any error is captured.
|
||||
#' @param envir Parent testing environment in which to base the individual testing environments.
|
||||
#'
|
||||
#' @return A data frame classed with the supplemental class `"shiny_runtests"`.
|
||||
#' @return A data frame classed with the supplemental class `"shinytestrun"`.
|
||||
#' The data frame has the following columns:
|
||||
#'
|
||||
#' | **Name** | **Type** | **Meaning** |
|
||||
#' | :-- | :-- | :-- |
|
||||
#' | `file` | `character(1)` | File name of the runner script in `tests/` that was sourced. |
|
||||
#' | `pass` | `logical(1)` | Whether or not the runner script signaled an error when sourced. |
|
||||
#' | `result` | any or `NA` | The return value of the runner |
|
||||
#' | `result` | any or `NA` | The return value of the runner, or `NA` if `pass == FALSE`. |
|
||||
#' | `error` | any or `NA` | The error signaled by the runner, or `NA` if `pass == TRUE`. |
|
||||
#'
|
||||
#' @details Historically, [shinytest](https://rstudio.github.io/shinytest/)
|
||||
#' recommended placing tests at the top-level of the `tests/` directory.
|
||||
#' This older folder structure is not supported by runTests.
|
||||
#' Please see [shinyAppTemplate()] for more details.
|
||||
#' recommended placing tests at the top-level of the `tests/` directory. In
|
||||
#' order to support that model, `testApp` first checks to see if the `.R`
|
||||
#' files in the `tests/` directory are all shinytests; if so, just calls out
|
||||
#' to [shinytest::testApp()].
|
||||
#' @export
|
||||
runTests <- function(
|
||||
appDir = ".",
|
||||
filter = NULL,
|
||||
assert = TRUE,
|
||||
envir = globalenv()
|
||||
) {
|
||||
# similar to runApp()
|
||||
# Allows shiny's functions to be available in the UI, server, and test code
|
||||
runTests <- function(appDir=".", filter=NULL){
|
||||
require(shiny)
|
||||
|
||||
testsDir <- file.path(appDir, "tests")
|
||||
if (!dirExists(testsDir)) {
|
||||
if (!dirExists(testsDir)){
|
||||
stop("No tests directory found: ", testsDir)
|
||||
}
|
||||
runners <- list.files(testsDir, pattern="\\.r$", ignore.case = TRUE)
|
||||
|
||||
if (length(runners) == 0) {
|
||||
if (length(runners) == 0){
|
||||
message("No test runners found in ", testsDir)
|
||||
return(result_row(character(0), logical(0), list()))
|
||||
return(result_row(character(0), logical(0), list(), list()))
|
||||
}
|
||||
|
||||
if (!is.null(filter)) {
|
||||
if (!is.null(filter)){
|
||||
runners <- runners[grepl(filter, runners)]
|
||||
}
|
||||
if (length(runners) == 0) {
|
||||
if (length(runners) == 0){
|
||||
stop("No test runners matched the given filter: '", filter, "'")
|
||||
}
|
||||
|
||||
# Inspect each runner to see if it appears to be a shinytest
|
||||
isST <- vapply(runners, function(r){
|
||||
text <- readLines(file.path(testsDir, r), warn = FALSE)
|
||||
isShinyTest(text)
|
||||
}, logical(1))
|
||||
|
||||
# See the @details section of the runTests() docs above for why this branch exists.
|
||||
if (is_legacy_shinytest_dir(testsDir)) {
|
||||
stop(
|
||||
"It appears that the .R files in ", testsDir, " are all shinytests.",
|
||||
" This is not supported by `shiny::runTests()`.",
|
||||
"\nPlease see `?shinytest::migrateShinytestDir` to migrate your shinytest file structure to the new format (requires shinytest 1.4.0 or above).",
|
||||
"\nSee `?shiny::shinyAppTemplate` for an example of the new testing file structure."
|
||||
)
|
||||
if (all(isST)){
|
||||
# just call out to shinytest
|
||||
# We don't need to message/warn here since shinytest already does it.
|
||||
if (!requireNamespace("shinytest", quietly=TRUE) ){
|
||||
stop("It appears that the .R files in ", testsDir,
|
||||
" are all shinytests, but shinytest is not installed.")
|
||||
}
|
||||
|
||||
if (!getOption("shiny.autoload.r", TRUE)) {
|
||||
warning("You've disabled `shiny.autoload.r` via an option but this is not passed through to shinytest. Consider using a _disable_autoload.R file as described at https://rstd.io/shiny-autoload")
|
||||
}
|
||||
|
||||
return(do.call(rbind, lapply(shinytest::testApp(appDir)[["results"]], function(r) {
|
||||
error <- if (r[["pass"]]) NA else simpleError("Unknown shinytest error")
|
||||
result_row(r[["name"]], r[["pass"]], list(NA), list(error))
|
||||
})))
|
||||
}
|
||||
|
||||
renv <- new.env(parent = envir)
|
||||
|
||||
# Otherwise source all the runners -- each in their own environment.
|
||||
ret <- do.call(rbind, lapply(runners, function(r) {
|
||||
pass <- FALSE
|
||||
result <-
|
||||
tryCatch({
|
||||
env <- new.env(parent = renv)
|
||||
withr::with_dir(testsDir, {
|
||||
ret <- sourceUTF8(r, envir = env)
|
||||
})
|
||||
pass <- TRUE
|
||||
ret
|
||||
}, error = function(err) {
|
||||
message("Error in ", r, "\n", err)
|
||||
err
|
||||
})
|
||||
result_row(file.path(testsDir, r), pass, list(result))
|
||||
}))
|
||||
|
||||
if (isTRUE(assert)) {
|
||||
if (!all(ret$pass)) {
|
||||
stop("Shiny App Test Failures detected in\n", paste0("* ", runtest_pretty_file(ret$file[!ret$pass]), collapse = "\n"), call. = FALSE)
|
||||
testenv <- new.env(parent=globalenv())
|
||||
renv <- new.env(parent=testenv)
|
||||
if (getOption("shiny.autoload.r", TRUE)) {
|
||||
loadSupport(appDir, renv=renv, globalrenv=testenv)
|
||||
} else if (file.exists.ci(file.path(appDir, "server.R"))){
|
||||
# then check for global.R to load
|
||||
if (file.exists(file.path.ci(appDir, "global.R"))){
|
||||
sourceUTF8(file.path.ci(appDir, "global.R"))
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
|
||||
runtest_pretty_file <- function(f) {
|
||||
test_folder <- dirname(f)
|
||||
app_folder <- dirname(test_folder)
|
||||
file.path(
|
||||
basename(app_folder),
|
||||
basename(test_folder),
|
||||
basename(f)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
#' @export
|
||||
print.shiny_runtests <- function(x, ..., reporter = "summary") {
|
||||
|
||||
cat("Shiny App Test Results\n")
|
||||
|
||||
|
||||
if (any(x$pass)) {
|
||||
# TODO in future... use clisymbols::symbol$tick and crayon green
|
||||
cat("* Success\n")
|
||||
mapply(
|
||||
x$file,
|
||||
x$pass,
|
||||
x$result,
|
||||
FUN = function(file, pass, result) {
|
||||
if (!pass) return()
|
||||
# print(result)
|
||||
cat(" - ", runtest_pretty_file(file), "\n", sep = "")
|
||||
}
|
||||
)
|
||||
}
|
||||
if (any(!x$pass)) {
|
||||
# TODO in future... use clisymbols::symbol$cross and crayon red
|
||||
cat("* Failure\n")
|
||||
mapply(
|
||||
x$file,
|
||||
x$pass,
|
||||
x$result,
|
||||
FUN = function(file, pass, result) {
|
||||
if (pass) return()
|
||||
cat(" - ", runtest_pretty_file(file), "\n", sep = "")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
invisible(x)
|
||||
oldwd <- getwd()
|
||||
on.exit({
|
||||
setwd(oldwd)
|
||||
}, add=TRUE)
|
||||
|
||||
setwd(testsDir)
|
||||
|
||||
# Otherwise source all the runners -- each in their own environment.
|
||||
return(do.call(rbind, lapply(runners, function(r) {
|
||||
result <- NA
|
||||
error <- NA
|
||||
pass <- FALSE
|
||||
tryCatch({
|
||||
env <- new.env(parent = renv)
|
||||
result <- sourceUTF8(r, envir = env)
|
||||
pass <- TRUE
|
||||
}, error = function(e) {
|
||||
error <<- e
|
||||
})
|
||||
result_row(r, pass, list(result), list(error))
|
||||
})))
|
||||
}
|
||||
|
||||
@@ -126,15 +126,13 @@ updateCheckboxInput <- function(session, inputId, label = NULL, value = NULL) {
|
||||
#' if (interactive()) {
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' actionButton("update", "Update other buttons and link"),
|
||||
#' actionButton("update", "Update other buttons"),
|
||||
#' br(),
|
||||
#' actionButton("goButton", "Go"),
|
||||
#' br(),
|
||||
#' actionButton("goButton2", "Go 2", icon = icon("area-chart")),
|
||||
#' br(),
|
||||
#' actionButton("goButton3", "Go 3"),
|
||||
#' br(),
|
||||
#' actionLink("goLink", "Go Link")
|
||||
#' actionButton("goButton3", "Go 3")
|
||||
#' )
|
||||
#'
|
||||
#' server <- function(input, output, session) {
|
||||
@@ -155,26 +153,17 @@ updateCheckboxInput <- function(session, inputId, label = NULL, value = NULL) {
|
||||
#' # unchaged and changes its label
|
||||
#' updateActionButton(session, "goButton3",
|
||||
#' label = "New label 3")
|
||||
#'
|
||||
#' # Updates goLink's label and icon
|
||||
#' updateActionButton(session, "goLink",
|
||||
#' label = "New link label",
|
||||
#' icon = icon("link"))
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#' @rdname updateActionButton
|
||||
#' @export
|
||||
updateActionButton <- function(session, inputId, label = NULL, icon = NULL) {
|
||||
if (!is.null(icon)) icon <- as.character(validateIcon(icon))
|
||||
message <- dropNulls(list(label=label, icon=icon))
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
#' @rdname updateActionButton
|
||||
#' @export
|
||||
updateActionLink <- updateActionButton
|
||||
|
||||
|
||||
#' Change the value of a date input on the client
|
||||
|
||||
57
R/utils.R
57
R/utils.R
@@ -210,16 +210,6 @@ sortByName <- function(x) {
|
||||
x[order(names(x))]
|
||||
}
|
||||
|
||||
# Sort a vector. If a character vector, sort using C locale, which is consistent
|
||||
# across platforms. Note that radix sort uses C locale according to ?sort.
|
||||
sort_c <- function(x, ...) {
|
||||
# Use UTF-8 encoding, because if encoding is "unknown" for non-ASCII
|
||||
# characters, the sort() will throw an error.
|
||||
if (is.character(x))
|
||||
x <- enc2utf8(x)
|
||||
sort(x, method = "radix", ...)
|
||||
}
|
||||
|
||||
# Wrapper around list2env with a NULL check. In R <3.2.0, if an empty unnamed
|
||||
# list is passed to list2env(), it errors. But an empty named list is OK. For
|
||||
# R >=3.2.0, this wrapper is not necessary.
|
||||
@@ -1831,7 +1821,7 @@ cat_line <- function(...) {
|
||||
cat(paste(..., "\n", collapse = ""))
|
||||
}
|
||||
|
||||
select_menu <- function(choices, title = NULL, msg = "Enter one or more numbers (with spaces), or an empty line to exit: \n")
|
||||
select_menu <- function(choices, title = NULL, msg = "Enter one or more numbers (with spaces), or an empty line to exit: \n")
|
||||
{
|
||||
if (!is.null(title)) {
|
||||
cat(title, "\n", sep = "")
|
||||
@@ -1848,48 +1838,3 @@ select_menu <- function(choices, title = NULL, msg = "Enter one or more numbers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#' @noRd
|
||||
isAppDir <- function(path) {
|
||||
|
||||
if (file.exists(file.path.ci(path, "app.R")))
|
||||
return(TRUE)
|
||||
|
||||
if (file.exists(file.path.ci(path, "server.R"))
|
||||
&& file.exists(file.path.ci(path, "ui.R")))
|
||||
return(TRUE)
|
||||
|
||||
FALSE
|
||||
}
|
||||
|
||||
# Borrowed from rprojroot which borrowed from devtools
|
||||
#' @noRd
|
||||
is_root <- function(path) {
|
||||
identical(
|
||||
normalizePath(path, winslash = "/"),
|
||||
normalizePath(dirname(path), winslash = "/")
|
||||
)
|
||||
}
|
||||
|
||||
#' @noRd
|
||||
findEnclosingApp <- function(path = ".") {
|
||||
orig_path <- path
|
||||
path <- normalizePath(path, winslash = "/", mustWork = TRUE)
|
||||
repeat {
|
||||
if (isAppDir(path))
|
||||
return(path)
|
||||
if (is_root(path))
|
||||
stop("Shiny app not found at ", orig_path, " or in any parent directory.")
|
||||
path <- dirname(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)
|
||||
}
|
||||
|
||||
15
README.md
15
README.md
@@ -1,11 +1,8 @@
|
||||
# shiny <img src="man/figures/logo.png" align="right" width=120 height=139 alt="" />
|
||||
# Shiny
|
||||
|
||||
<!-- badges: start -->
|
||||
[](https://CRAN.R-project.org/package=shiny)
|
||||
[](https://github.com/rstudio/shiny/actions)
|
||||
[](https://community.rstudio.com/new-topic?category=shiny&tags=shiny)
|
||||
*Travis:* [](https://travis-ci.org/rstudio/shiny)
|
||||
|
||||
<!-- badges: end -->
|
||||
*AppVeyor:* [](https://ci.appveyor.com/project/rstudio/shiny)
|
||||
|
||||
Shiny is a new package from RStudio that makes it incredibly easy to build interactive web applications with R.
|
||||
|
||||
@@ -37,9 +34,9 @@ install.packages("shiny")
|
||||
To install the latest development builds directly from GitHub, run this instead:
|
||||
|
||||
```r
|
||||
if (!require("remotes"))
|
||||
install.packages("remotes")
|
||||
remotes::install_github("rstudio/shiny")
|
||||
if (!require("devtools"))
|
||||
install.packages("devtools")
|
||||
devtools::install_github("rstudio/shiny")
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
54
appveyor.yml
Normal file
54
appveyor.yml
Normal file
@@ -0,0 +1,54 @@
|
||||
# DO NOT CHANGE the "init" and "install" sections below
|
||||
|
||||
# Download script file from GitHub
|
||||
init:
|
||||
ps: |
|
||||
$ErrorActionPreference = "Stop"
|
||||
Invoke-WebRequest http://raw.github.com/krlmlr/r-appveyor/master/scripts/appveyor-tool.ps1 -OutFile "..\appveyor-tool.ps1"
|
||||
Import-Module '..\appveyor-tool.ps1'
|
||||
|
||||
install:
|
||||
ps: Bootstrap
|
||||
|
||||
cache:
|
||||
# Bust library cache every time the description file changes
|
||||
# as appveyor cache can not be busted manually
|
||||
# This helps get around errors such as "can't update curl because it's already loaded"
|
||||
# when trying to update the existing cache
|
||||
# PR: https://github.com/rstudio/shiny/pull/2722
|
||||
- C:\RLibrary -> DESCRIPTION
|
||||
|
||||
# Adapt as necessary starting from here
|
||||
|
||||
build_script:
|
||||
- travis-tool.sh install_deps
|
||||
|
||||
test_script:
|
||||
- travis-tool.sh run_tests
|
||||
|
||||
on_failure:
|
||||
- 7z a failure.zip *.Rcheck\*
|
||||
- appveyor PushArtifact failure.zip
|
||||
|
||||
artifacts:
|
||||
- path: '*.Rcheck\**\*.log'
|
||||
name: Logs
|
||||
|
||||
- path: '*.Rcheck\**\*.out'
|
||||
name: Logs
|
||||
|
||||
- path: '*.Rcheck\**\*.fail'
|
||||
name: Logs
|
||||
|
||||
- path: '*.Rcheck\**\*.Rout'
|
||||
name: Logs
|
||||
|
||||
- path: '\*_*.tar.gz'
|
||||
name: Bits
|
||||
|
||||
- path: '\*_*.zip'
|
||||
name: Bits
|
||||
|
||||
environment:
|
||||
global:
|
||||
USE_RTOOLS: true
|
||||
@@ -1,5 +1,5 @@
|
||||
exampleModuleUI <- function(id, label = "Counter") {
|
||||
# All uses of Shiny input/output IDs in the UI must be namespaced,
|
||||
mymoduleUI <- function(id, label = "Counter") {
|
||||
# Al uses of Shiny input/output IDs in the UI must be namespaced,
|
||||
# as in ns("x").
|
||||
ns <- NS(id)
|
||||
tagList(
|
||||
@@ -8,7 +8,7 @@ exampleModuleUI <- function(id, label = "Counter") {
|
||||
)
|
||||
}
|
||||
|
||||
exampleModuleServer <- function(id) {
|
||||
mymoduleServer <- function(id) {
|
||||
# moduleServer() wraps a function to create the server component of a
|
||||
# module.
|
||||
moduleServer(
|
||||
@@ -3,18 +3,14 @@ ui <- fluidPage(
|
||||
# These blocks of code are processed with htmlTemplate()
|
||||
if (isTRUE(module)) {
|
||||
' # ======== Modules ========
|
||||
# exampleModuleUI is defined in R/example-module.R
|
||||
wellPanel(
|
||||
h2("Modules example"),
|
||||
exampleModuleUI("examplemodule1", "Click counter #1"),
|
||||
exampleModuleUI("examplemodule2", "Click counter #2")
|
||||
),
|
||||
# mymoduleUI is defined in R/my-module.R
|
||||
mymoduleUI("mymodule1", "Click counter #1"),
|
||||
mymoduleUI("mymodule2", "Click counter #2"),
|
||||
# =========================
|
||||
'
|
||||
}
|
||||
}}
|
||||
wellPanel(
|
||||
h2("Sorting example"),
|
||||
sliderInput("size", "Data size", min = 5, max = 20, value = 10),
|
||||
{{
|
||||
if (isTRUE(rdir)) {
|
||||
@@ -31,9 +27,9 @@ server <- function(input, output, session) {
|
||||
{{
|
||||
if (isTRUE(module)) {
|
||||
' # ======== Modules ========
|
||||
# exampleModuleServer is defined in R/example-module.R
|
||||
exampleModuleServer("examplemodule1")
|
||||
exampleModuleServer("examplemodule2")
|
||||
# mymoduleServer is defined in R/my-module.R
|
||||
mymoduleServer("mymodule1")
|
||||
mymoduleServer("mymodule2")
|
||||
# =========================
|
||||
'
|
||||
}
|
||||
@@ -41,7 +37,7 @@ if (isTRUE(module)) {
|
||||
data <- reactive({
|
||||
{{
|
||||
if (isTRUE(rdir)) {
|
||||
' # lexical_sort from R/example.R
|
||||
' # lexical_sort from R/sort.R
|
||||
lexical_sort(seq_len(input$size))'
|
||||
} else {
|
||||
' sort(seq_len(input$size))'
|
||||
|
||||
6
inst/app_template/tests/server.R
Normal file
6
inst/app_template/tests/server.R
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
files <- list.files("./server", full.names = FALSE)
|
||||
origwd <- getwd()
|
||||
setwd("./server")
|
||||
on.exit(setwd(origwd), add=TRUE)
|
||||
lapply(files, source, local=environment())
|
||||
@@ -1,6 +1,7 @@
|
||||
# Use testthat just for expectations
|
||||
library(testthat)
|
||||
|
||||
context("mymoduleServer")
|
||||
|
||||
# See ?testServer for more information
|
||||
testServer(mymoduleServer, {
|
||||
# Set initial value of a button
|
||||
session$setInputs(button = 0)
|
||||
@@ -1,11 +1,11 @@
|
||||
# Use testthat just for expectations
|
||||
context("App")
|
||||
library(testthat)
|
||||
|
||||
testServer(expr = {
|
||||
testServer('../..', {
|
||||
# Set the `size` slider and check the output
|
||||
session$setInputs(size = 6)
|
||||
expect_equal(output$sequence, "1 2 3 4 5 6")
|
||||
|
||||
session$setInputs(size = 12)
|
||||
expect_equal(output$sequence, paste0(lexical_sort(1:12), collapse = " "))
|
||||
expect_equal(output$sequence, "1 2 3 4 5 6 7 8 9 10 11 12")
|
||||
})
|
||||
@@ -2,11 +2,6 @@ app <- ShinyDriver$new("../../")
|
||||
app$snapshotInit("mytest")
|
||||
|
||||
app$snapshot()
|
||||
{{
|
||||
if (isTRUE(module)) {
|
||||
'
|
||||
app$setInputs(`examplemodule1-button` = "click")
|
||||
app$setInputs(`examplemodule1-button` = "click")
|
||||
app$snapshot()'
|
||||
}
|
||||
}}
|
||||
app$setInputs(`mymodule1-button` = "click")
|
||||
app$setInputs(`mymodule1-button` = "click")
|
||||
app$snapshot()
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
library(testthat)
|
||||
|
||||
test_dir(
|
||||
"./testthat",
|
||||
# Run in the app's environment containing all support methods.
|
||||
env = shiny::loadSupport(),
|
||||
# Display the regular progress output and throw an error if any test error is found
|
||||
reporter = c("progress", "fail")
|
||||
)
|
||||
# Run in the "current" environment, because shiny::runTests() is going to
|
||||
# provision a new environment that's just for our test. And we'll want access to
|
||||
# the supporting files that were already loaded into that env.
|
||||
testthat::test_dir("./testthat", env = environment())
|
||||
|
||||
11
inst/app_template/tests/testthat/helper-load.R
Normal file
11
inst/app_template/tests/testthat/helper-load.R
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
# The RStudio IDE offers a "Run Tests" button when it sees testthat tests but it runs
|
||||
# in its own environment/process. Which means that any helpers we've loaded into our
|
||||
# environment won't be visible. So we add this helper not because it's actually needed
|
||||
# in the typical `shiny::runTests` workflow, but to make that IDE button work.
|
||||
# Once the IDE adds proper support for this style, we'll be able to drop these files.
|
||||
#
|
||||
# Note that this may redundantly source the files in your R/ dir depending on your
|
||||
# workflow.
|
||||
library(shiny)
|
||||
shiny::loadSupport("../../", renv = globalenv())
|
||||
@@ -1,18 +0,0 @@
|
||||
context("exampleModuleServer")
|
||||
|
||||
# See ?testServer for more information
|
||||
testServer(exampleModuleServer, {
|
||||
# Set initial value of a button
|
||||
session$setInputs(button = 0)
|
||||
|
||||
# Check the value of the reactiveVal `count()`
|
||||
expect_equal(count(), 1)
|
||||
# Check the value of the renderText()
|
||||
expect_equal(output$out, "1")
|
||||
|
||||
# Simulate a click
|
||||
session$setInputs(button = 1)
|
||||
|
||||
expect_equal(count(), 2)
|
||||
expect_equal(output$out, "2")
|
||||
})
|
||||
@@ -1,20 +0,0 @@
|
||||
context("app")
|
||||
|
||||
testServer(expr = {
|
||||
# Set the `size` slider and check the output
|
||||
session$setInputs(size = 6)
|
||||
expect_equal(output$sequence, "1 2 3 4 5 6")
|
||||
{{
|
||||
if (isTRUE(rdir)) {
|
||||
'
|
||||
session$setInputs(size = 12)
|
||||
expect_equal(output$sequence, "1 10 11 12 2 3 4 5 6 7 8 9")
|
||||
'
|
||||
} else {
|
||||
'
|
||||
session$setInputs(size = 12)
|
||||
expect_equal(output$sequence, "1 2 3 4 5 6 7 8 9 10 11 12")
|
||||
'
|
||||
}
|
||||
}}
|
||||
})
|
||||
@@ -1,6 +1,4 @@
|
||||
# Test the lexical_sort function from R/example.R
|
||||
context("sort")
|
||||
|
||||
# Test the lexical_sort function from R/utils.R
|
||||
test_that("Lexical sorting works", {
|
||||
expect_equal(lexical_sort(c(1, 2, 3)), c(1, 2, 3))
|
||||
expect_equal(lexical_sort(c(1, 2, 3, 13, 11, 21)), c(1, 11, 13, 2, 21, 3))
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>An error has occurred</title>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h1>An error has occurred!</h1>
|
||||
<p>{{message}}</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
.btn:focus{outline:dotted 2px #000}div.active:focus{outline:dotted 1px #000}a:focus{outline:dotted 1px #000}.close:hover,.close:focus{outline:dotted 1px #000}.nav>li>a:hover,.nav>li>a:focus{outline:dotted 1px #000}.carousel-indicators li,.carousel-indicators li.active{height:18px;width:18px;border-width:2px;position:relative;box-shadow:0px 0px 0px 1px #808080}.carousel-indicators.active li{background-color:rgba(100,149,253,0.6)}.carousel-indicators.active li.active{background-color:white}.carousel-tablist-highlight{display:block;position:absolute;outline:2px solid transparent;background-color:transparent;box-shadow:0px 0px 0px 1px transparent}.carousel-tablist-highlight.focus{outline:2px solid #6495ED;background-color:rgba(0,0,0,0.4)}a.carousel-control:focus{outline:2px solid #6495ED;background-image:linear-gradient(to right, transparent 0px, rgba(0,0,0,0.5) 100%);box-shadow:0px 0px 0px 1px #000000}.carousel-pause-button{position:absolute;top:-30em;left:-300em;display:block}.carousel-pause-button.focus{top:0.5em;left:0.5em}.carousel:hover .carousel-caption,.carousel.contrast .carousel-caption{background-color:rgba(0,0,0,0.5);z-index:10}.alert-success{color:#2d4821}.alert-info{color:#214c62}.alert-warning{color:#6c4a00;background-color:#f9f1c6}.alert-danger{color:#d2322d}.alert-danger:hover{color:#a82824}
|
||||
File diff suppressed because one or more lines are too long
7
inst/www/shared/bootstrap/shim/html5shiv.min.js
vendored
Normal file
7
inst/www/shared/bootstrap/shim/html5shiv.min.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
|
||||
*/
|
||||
// Only run this code in IE 8
|
||||
if (!!window.navigator.userAgent.match("MSIE 8")) {
|
||||
!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.2",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="<xyz></xyz>",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b)}(this,document);
|
||||
};
|
||||
8
inst/www/shared/bootstrap/shim/respond.min.js
vendored
Normal file
8
inst/www/shared/bootstrap/shim/respond.min.js
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl
|
||||
* Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT
|
||||
* */
|
||||
|
||||
// Only run this code in IE 8
|
||||
if (!!window.navigator.userAgent.match("MSIE 8")) {
|
||||
!function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­<style media="'+a+'"> #mq-test-1 { width: 42px; }</style>',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b<s.length;b++){var c=s[b],e=c.href,f=c.media,g=c.rel&&"stylesheet"===c.rel.toLowerCase();e&&g&&!o[e]&&(c.styleSheet&&c.styleSheet.rawCssText?(v(c.styleSheet.rawCssText,e,f),o[e]=!0):(!/^([a-zA-Z:]*\/\/)/.test(e)&&!r||e.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&("//"===e.substring(0,2)&&(e=a.location.protocol+e),d.push({href:e,media:f})))}w()};x(),c.update=x,c.getEmValue=t,a.addEventListener?a.addEventListener("resize",b,!1):a.attachEvent&&a.attachEvent("onresize",b)}}(this);
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
773
inst/www/shared/fontawesome/css/all.css
vendored
773
inst/www/shared/fontawesome/css/all.css
vendored
File diff suppressed because it is too large
Load Diff
4
inst/www/shared/fontawesome/css/all.min.css
vendored
4
inst/www/shared/fontawesome/css/all.min.css
vendored
File diff suppressed because one or more lines are too long
30
inst/www/shared/fontawesome/css/v4-shims.css
vendored
30
inst/www/shared/fontawesome/css/v4-shims.css
vendored
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
|
||||
* Font Awesome Free 5.3.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
.fa.fa-glass:before {
|
||||
@@ -154,12 +154,6 @@
|
||||
.fa.fa-mail-forward:before {
|
||||
content: "\f064"; }
|
||||
|
||||
.fa.fa-expand:before {
|
||||
content: "\f424"; }
|
||||
|
||||
.fa.fa-compress:before {
|
||||
content: "\f422"; }
|
||||
|
||||
.fa.fa-eye {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-weight: 400; }
|
||||
@@ -753,19 +747,19 @@
|
||||
content: "\f15d"; }
|
||||
|
||||
.fa.fa-sort-alpha-desc:before {
|
||||
content: "\f881"; }
|
||||
content: "\f15e"; }
|
||||
|
||||
.fa.fa-sort-amount-asc:before {
|
||||
content: "\f160"; }
|
||||
|
||||
.fa.fa-sort-amount-desc:before {
|
||||
content: "\f884"; }
|
||||
content: "\f161"; }
|
||||
|
||||
.fa.fa-sort-numeric-asc:before {
|
||||
content: "\f162"; }
|
||||
|
||||
.fa.fa-sort-numeric-desc:before {
|
||||
content: "\f886"; }
|
||||
content: "\f163"; }
|
||||
|
||||
.fa.fa-youtube-square {
|
||||
font-family: 'Font Awesome 5 Brands';
|
||||
@@ -1061,6 +1055,9 @@
|
||||
.fa.fa-automobile:before {
|
||||
content: "\f1b9"; }
|
||||
|
||||
.fa.fa-cab:before {
|
||||
content: "\f1ba"; }
|
||||
|
||||
.fa.fa-envelope-o {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-weight: 400; }
|
||||
@@ -1068,10 +1065,6 @@
|
||||
.fa.fa-envelope-o:before {
|
||||
content: "\f0e0"; }
|
||||
|
||||
.fa.fa-spotify {
|
||||
font-family: 'Font Awesome 5 Brands';
|
||||
font-weight: 400; }
|
||||
|
||||
.fa.fa-deviantart {
|
||||
font-family: 'Font Awesome 5 Brands';
|
||||
font-weight: 400; }
|
||||
@@ -1784,6 +1777,10 @@
|
||||
.fa.fa-map-o:before {
|
||||
content: "\f279"; }
|
||||
|
||||
.fa.fa-commenting {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-weight: 400; }
|
||||
|
||||
.fa.fa-commenting:before {
|
||||
content: "\f4ad"; }
|
||||
|
||||
@@ -2168,5 +2165,6 @@
|
||||
font-family: 'Font Awesome 5 Brands';
|
||||
font-weight: 400; }
|
||||
|
||||
.fa.fa-cab:before {
|
||||
content: "\f1ba"; }
|
||||
.fa.fa-spotify {
|
||||
font-family: 'Font Awesome 5 Brands';
|
||||
font-weight: 400; }
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 699 KiB After Width: | Height: | Size: 633 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 139 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 876 KiB After Width: | Height: | Size: 665 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -20,24 +20,16 @@ David Serduke <davidserduke@gmail.com>
|
||||
Richard D. Worth <rdworth@gmail.com>
|
||||
Scott González <scott.gonzalez@gmail.com>
|
||||
Ariel Flesler <aflesler@gmail.com>
|
||||
Cheah Chu Yeow <chuyeow@gmail.com>
|
||||
Andrew Chalkley <andrew@chalkley.org>
|
||||
Fabio Buffoni <fabio.buffoni@bitmaster.it>
|
||||
Stefan Bauckmeier <stefan@bauckmeier.de>
|
||||
Jon Evans <jon@springyweb.com>
|
||||
TJ Holowaychuk <tj@vision-media.ca>
|
||||
Riccardo De Agostini <rdeago@gmail.com>
|
||||
Michael Bensoussan <mickey@seesmic.com>
|
||||
Louis-Rémi Babé <lrbabe@gmail.com>
|
||||
Robert Katić <robert.katic@gmail.com>
|
||||
Damian Janowski <damian.janowski@gmail.com>
|
||||
Anton Kovalyov <anton@kovalyov.net>
|
||||
Dušan B. Jovanovic <dbjdbj@gmail.com>
|
||||
Louis-Rémi Babé <lrbabe@gmail.com>
|
||||
Earle Castledine <mrspeaker@gmail.com>
|
||||
Damian Janowski <damian.janowski@gmail.com>
|
||||
Rich Dougherty <rich@rd.gen.nz>
|
||||
Kim Dalsgaard <kim@kimdalsgaard.com>
|
||||
Andrea Giammarchi <andrea.giammarchi@gmail.com>
|
||||
Fabian Jakobs <fabian.jakobs@web.de>
|
||||
Mark Gibson <jollytoad@gmail.com>
|
||||
Karl Swedberg <kswedberg@gmail.com>
|
||||
Justin Meyer <justinbmeyer@gmail.com>
|
||||
@@ -45,10 +37,9 @@ Ben Alman <cowboy@rj3.net>
|
||||
James Padolsey <cla@padolsey.net>
|
||||
David Petersen <public@petersendidit.com>
|
||||
Batiste Bieler <batiste.bieler@gmail.com>
|
||||
Jake Archibald <jake.archibald@bbc.co.uk>
|
||||
Alexander Farkas <info@corrupt-system.de>
|
||||
Filipe Fortes <filipe@fortes.com>
|
||||
Rick Waldron <waldron.rick@gmail.com>
|
||||
Filipe Fortes <filipe@fortes.com>
|
||||
Neeraj Singh <neerajdotname@gmail.com>
|
||||
Paul Irish <paul.irish@gmail.com>
|
||||
Iraê Carvalho <irae@irae.pro.br>
|
||||
@@ -56,24 +47,23 @@ Matt Curry <matt@pseudocoder.com>
|
||||
Michael Monteleone <michael@michaelmonteleone.net>
|
||||
Noah Sloan <noah.sloan@gmail.com>
|
||||
Tom Viner <github@viner.tv>
|
||||
J. Ryan Stinnett <jryans@gmail.com>
|
||||
Douglas Neiner <doug@dougneiner.com>
|
||||
Adam J. Sontag <ajpiano@ajpiano.com>
|
||||
Heungsub Lee <h@subl.ee>
|
||||
Dave Reed <dareed@microsoft.com>
|
||||
Ralph Whitbeck <ralph.whitbeck@gmail.com>
|
||||
Carl Fürstenberg <azatoth@gmail.com>
|
||||
Jacob Wright <jacwright@gmail.com>
|
||||
Ralph Whitbeck <ralph.whitbeck@gmail.com>
|
||||
J. Ryan Stinnett <jryans@gmail.com>
|
||||
unknown <Igen005@.upcorp.ad.uprr.com>
|
||||
temp01 <temp01irc@gmail.com>
|
||||
Heungsub Lee <h@subl.ee>
|
||||
Colin Snover <github.com@zetafleet.com>
|
||||
Jared Grippe <jared@deadlyicon.com>
|
||||
Ryan W Tenney <ryan@10e.us>
|
||||
Alex Sexton <AlexSexton@gmail.com>
|
||||
Pinhook <contact@pinhooklabs.com>
|
||||
Ron Otten <r.j.g.otten@gmail.com>
|
||||
Jephte Clain <Jephte.Clain@univ-reunion.fr>
|
||||
Anton Matzneller <obhvsbypqghgc@gmail.com>
|
||||
Alex Sexton <AlexSexton@gmail.com>
|
||||
Dan Heberden <danheberden@gmail.com>
|
||||
Henri Wiechers <hwiechers@gmail.com>
|
||||
Russell Holbrook <russell.holbrook@patch.com>
|
||||
@@ -83,19 +73,21 @@ Scott Jehl <scottjehl@gmail.com>
|
||||
James Burke <jrburke@gmail.com>
|
||||
Jonas Pfenniger <jonas@pfenniger.name>
|
||||
Xavi Ramirez <xavi.rmz@gmail.com>
|
||||
Jared Grippe <jared@deadlyicon.com>
|
||||
Sylvester Keil <sylvester@keil.or.at>
|
||||
Brandon Sterne <bsterne@mozilla.com>
|
||||
Mathias Bynens <mathias@qiwi.be>
|
||||
Lee Carpenter <elcarpie@gmail.com>
|
||||
Timmy Willison <4timmywil@gmail.com>
|
||||
Corey Frang <gnarf37@gmail.com>
|
||||
Digitalxero <digitalxero>
|
||||
Anton Kovalyov <anton@kovalyov.net>
|
||||
David Murdoch <david@davidmurdoch.com>
|
||||
Josh Varner <josh.varner@gmail.com>
|
||||
Charles McNulty <cmcnulty@kznf.com>
|
||||
Jordan Boesch <jboesch26@gmail.com>
|
||||
Jess Thrysoee <jess@thrysoee.dk>
|
||||
Michael Murray <m@murz.net>
|
||||
Lee Carpenter <elcarpie@gmail.com>
|
||||
Alexis Abril <me@alexisabril.com>
|
||||
Rob Morgan <robbym@gmail.com>
|
||||
John Firebaugh <john_firebaugh@bigfix.com>
|
||||
@@ -115,13 +107,12 @@ Timo Tijhof <krinklemail@gmail.com>
|
||||
Steen Nielsen <swinedk@gmail.com>
|
||||
Anton Ryzhov <anton@ryzhov.me>
|
||||
Shi Chuan <shichuanr@gmail.com>
|
||||
Matt Mueller <mattmuelle@gmail.com>
|
||||
Berker Peksag <berker.peksag@gmail.com>
|
||||
Toby Brain <tobyb@freshview.com>
|
||||
Matt Mueller <mattmuelle@gmail.com>
|
||||
Justin <drakefjustin@gmail.com>
|
||||
Daniel Herman <daniel.c.herman@gmail.com>
|
||||
Oleg Gaidarenko <markelog@gmail.com>
|
||||
Rock Hymas <rock@fogcreek.com>
|
||||
Richard Gibson <richard.gibson@gmail.com>
|
||||
Rafaël Blais Masson <rafbmasson@gmail.com>
|
||||
cmc3cn <59194618@qq.com>
|
||||
@@ -133,7 +124,6 @@ Andrew E Monat <amonat@gmail.com>
|
||||
Oskari <admin@o-programs.com>
|
||||
Joao Henrique de Andrade Bruni <joaohbruni@yahoo.com.br>
|
||||
tsinha <tsinha@Anthonys-MacBook-Pro.local>
|
||||
Dominik D. Geyer <dominik.geyer@gmail.com>
|
||||
Matt Farmer <matt@frmr.me>
|
||||
Trey Hunner <treyhunner@gmail.com>
|
||||
Jason Moon <jmoon@socialcast.com>
|
||||
@@ -142,180 +132,164 @@ Kris Borchers <kris.borchers@gmail.com>
|
||||
Vladimir Zhuravlev <private.face@gmail.com>
|
||||
Jacob Thornton <jacobthornton@gmail.com>
|
||||
Chad Killingsworth <chadkillingsworth@missouristate.edu>
|
||||
Vitya Muhachev <vic99999@yandex.ru>
|
||||
Nowres Rafid <nowres.rafed@gmail.com>
|
||||
David Benjamin <davidben@mit.edu>
|
||||
Alan Plum <github@ap.apsq.de>
|
||||
Uri Gilad <antishok@gmail.com>
|
||||
Chris Faulkner <thefaulkner@gmail.com>
|
||||
Marcel Greter <marcel.greter@ocbnet.ch>
|
||||
Elijah Manor <elijah.manor@gmail.com>
|
||||
Daniel Chatfield <chatfielddaniel@gmail.com>
|
||||
Daniel Gálvez <dgalvez@editablething.com>
|
||||
Nikita Govorov <nikita.govorov@gmail.com>
|
||||
Wesley Walser <waw325@gmail.com>
|
||||
Mike Pennisi <mike@mikepennisi.com>
|
||||
Matthias Jäggli <matthias.jaeggli@gmail.com>
|
||||
Devin Cooper <cooper.semantics@gmail.com>
|
||||
Markus Staab <markus.staab@redaxo.de>
|
||||
Dave Riddle <david@joyvuu.com>
|
||||
Callum Macrae <callum@lynxphp.com>
|
||||
Jonathan Sampson <jjdsampson@gmail.com>
|
||||
Benjamin Truyman <bentruyman@gmail.com>
|
||||
Jay Merrifield <fracmak@gmail.com>
|
||||
James Huston <james@jameshuston.net>
|
||||
Sai Lung Wong <sai.wong@huffingtonpost.com>
|
||||
Erick Ruiz de Chávez <erickrdch@gmail.com>
|
||||
David Bonner <dbonner@cogolabs.com>
|
||||
Allen J Schmidt Jr <cobrasoft@gmail.com>
|
||||
Akintayo Akinwunmi <aakinwunmi@judge.com>
|
||||
MORGAN <morgan@morgangraphics.com>
|
||||
Ismail Khair <ismail.khair@gmail.com>
|
||||
Carl Danley <carldanley@gmail.com>
|
||||
Mike Petrovich <michael.c.petrovich@gmail.com>
|
||||
Greg Lavallee <greglavallee@wapolabs.com>
|
||||
Daniel Gálvez <dgalvez@editablething.com>
|
||||
Sai Lung Wong <sai.wong@huffingtonpost.com>
|
||||
Tom H Fuertes <TomFuertes@gmail.com>
|
||||
Roland Eckl <eckl.roland@googlemail.com>
|
||||
Yiming He <yiminghe@gmail.com>
|
||||
Jay Merrifield <fracmak@gmail.com>
|
||||
Allen J Schmidt Jr <cobrasoft@gmail.com>
|
||||
Jonathan Sampson <jjdsampson@gmail.com>
|
||||
Marcel Greter <marcel.greter@ocbnet.ch>
|
||||
Matthias Jäggli <matthias.jaeggli@gmail.com>
|
||||
David Fox <dfoxinator@gmail.com>
|
||||
Bennett Sorbo <bsorbo@gmail.com>
|
||||
Yiming He <yiminghe@gmail.com>
|
||||
Devin Cooper <cooper.semantics@gmail.com>
|
||||
Paul Ramos <paul.b.ramos@gmail.com>
|
||||
Rod Vagg <rod@vagg.org>
|
||||
Bennett Sorbo <bsorbo@gmail.com>
|
||||
Sebastian Burkhard <sebi.burkhard@gmail.com>
|
||||
Zachary Adam Kaplan <razic@viralkitty.com>
|
||||
Adam Coulombe <me@adam.co>
|
||||
nanto_vi <nanto@moon.email.ne.jp>
|
||||
nanto <nanto@moon.email.ne.jp>
|
||||
Danil Somsikov <danilasomsikov@gmail.com>
|
||||
Ryunosuke SATO <tricknotes.rs@gmail.com>
|
||||
Diego Tres <diegotres@gmail.com>
|
||||
Jean Boussier <jean.boussier@gmail.com>
|
||||
Adam Coulombe <me@adam.co>
|
||||
Andrew Plummer <plummer.andrew@gmail.com>
|
||||
Mark Raddatz <mraddatz@gmail.com>
|
||||
Pascal Borreli <pascal@borreli.com>
|
||||
Isaac Z. Schlueter <i@izs.me>
|
||||
Karl Sieburg <ksieburg@yahoo.com>
|
||||
Pascal Borreli <pascal@borreli.com>
|
||||
Nguyen Phuc Lam <ruado1987@gmail.com>
|
||||
Dmitry Gusev <dmitry.gusev@gmail.com>
|
||||
Steven Benner <admin@stevenbenner.com>
|
||||
Li Xudong <istonelee@gmail.com>
|
||||
Michał Gołębiowski-Owczarek <m.goleb@gmail.com>
|
||||
Renato Oliveira dos Santos <ros3@cin.ufpe.br>
|
||||
Frederic Junod <frederic.junod@camptocamp.com>
|
||||
Li Xudong <istonelee@gmail.com>
|
||||
Steven Benner <admin@stevenbenner.com>
|
||||
Tom H Fuertes <tomfuertes@gmail.com>
|
||||
Mitch Foley <mitch@thefoley.net>
|
||||
Renato Oliveira dos Santos <ros3@cin.ufpe.br>
|
||||
ros3cin <ros3@cin.ufpe.br>
|
||||
Kyle Robinson Young <kyle@dontkry.com>
|
||||
John Paul <john@johnkpaul.com>
|
||||
Jason Bedard <jason+jquery@jbedard.ca>
|
||||
Kyle Robinson Young <kyle@dontkry.com>
|
||||
Chris Talkington <chris@talkingtontech.com>
|
||||
Eddie Monge <eddie@eddiemonge.com>
|
||||
Terry Jones <terry@jon.es>
|
||||
Jason Merino <jasonmerino@gmail.com>
|
||||
Dan Burzo <danburzo@gmail.com>
|
||||
Jeremy Dunck <jdunck@gmail.com>
|
||||
Chris Price <price.c@gmail.com>
|
||||
Guy Bedford <guybedford@gmail.com>
|
||||
njhamann <njhamann@gmail.com>
|
||||
Goare Mao <mygoare@gmail.com>
|
||||
Amey Sakhadeo <me@ameyms.com>
|
||||
Mike Sidorov <mikes.ekb@gmail.com>
|
||||
Anthony Ryan <anthonyryan1@gmail.com>
|
||||
Lihan Li <frankieteardrop@gmail.com>
|
||||
Dominik D. Geyer <dominik.geyer@gmail.com>
|
||||
George Kats <katsgeorgeek@gmail.com>
|
||||
Dongseok Paeng <dongseok83.paeng@lge.com>
|
||||
Lihan Li <frankieteardrop@gmail.com>
|
||||
Ronny Springer <springer.ronny@gmail.com>
|
||||
Ilya Kantor <iliakan@gmail.com>
|
||||
Marian Sollmann <marian.sollmann@cargomedia.ch>
|
||||
Chris Antaki <ChrisAntaki@gmail.com>
|
||||
Marian Sollmann <marian.sollmann@cargomedia.ch>
|
||||
njhamann <njhamann@gmail.com>
|
||||
Ilya Kantor <iliakan@gmail.com>
|
||||
David Hong <d.hong@me.com>
|
||||
John Paul <john@johnkpaul.com>
|
||||
Jakob Stoeck <jakob@pokermania.de>
|
||||
Christopher Jones <chris@cjqed.com>
|
||||
Forbes Lindesay <forbes@lindesay.co.uk>
|
||||
S. Andrew Sheppard <andrew@wq.io>
|
||||
Leonardo Balter <leonardo.balter@gmail.com>
|
||||
Rodrigo Rosenfeld Rosas <rr.rosas@gmail.com>
|
||||
Daniel Husar <dano.husar@gmail.com>
|
||||
Philip Jägenstedt <philip@foolip.org>
|
||||
John Hoven <hovenj@gmail.com>
|
||||
Roman Reiß <me@silverwind.io>
|
||||
Benjy Cui <benjytrys@gmail.com>
|
||||
Rodrigo Rosenfeld Rosas <rr.rosas@gmail.com>
|
||||
John Hoven <hovenj@gmail.com>
|
||||
Philip Jägenstedt <philip@foolip.org>
|
||||
Christian Kosmowski <ksmwsk@gmail.com>
|
||||
David Corbacho <davidcorbacho@gmail.com>
|
||||
Liang Peng <poppinlp@gmail.com>
|
||||
TJ VanToll <tj.vantoll@gmail.com>
|
||||
Aurelio De Rosa <aurelioderosa@gmail.com>
|
||||
Senya Pugach <upisfree@outlook.com>
|
||||
Dan Hart <danhart@notonthehighstreet.com>
|
||||
Aurelio De Rosa <aurelioderosa@gmail.com>
|
||||
Nazar Mokrynskyi <nazar@mokrynskyi.com>
|
||||
Benjamin Tan <demoneaux@gmail.com>
|
||||
Amit Merchant <bullredeyes@gmail.com>
|
||||
Jason Bedard <jason+github@jbedard.ca>
|
||||
Veaceslav Grimalschi <grimalschi@yandex.ru>
|
||||
Richard McDaniel <rm0026@uah.edu>
|
||||
Arthur Verschaeve <contact@arthurverschaeve.be>
|
||||
Shivaji Varma <contact@shivajivarma.com>
|
||||
Ben Toews <mastahyeti@gmail.com>
|
||||
Dan Hart <danhart@notonthehighstreet.com>
|
||||
Bin Xin <rhyzix@gmail.com>
|
||||
Neftaly Hernandez <neftaly.hernandez@gmail.com>
|
||||
T.J. Crowder <tj.crowder@farsightsoftware.com>
|
||||
Nicolas HENRY <icewil@gmail.com>
|
||||
David Corbacho <davidcorbacho@gmail.com>
|
||||
Veaceslav Grimalschi <grimalschi@yandex.ru>
|
||||
Daniel Husar <dano.husar@gmail.com>
|
||||
Frederic Hemberger <mail@frederic-hemberger.de>
|
||||
Victor Homyakov <vkhomyackov@gmail.com>
|
||||
Ben Toews <mastahyeti@gmail.com>
|
||||
Aditya Raghavan <araghavan3@gmail.com>
|
||||
Victor Homyakov <vkhomyackov@gmail.com>
|
||||
Shivaji Varma <contact@shivajivarma.com>
|
||||
Nicolas HENRY <icewil@gmail.com>
|
||||
Anne-Gaelle Colom <coloma@westminster.ac.uk>
|
||||
Leonardo Braga <leonardo.braga@gmail.com>
|
||||
George Mauer <gmauer@gmail.com>
|
||||
Leonardo Braga <leonardo.braga@gmail.com>
|
||||
Stephen Edgar <stephen@netweb.com.au>
|
||||
Thomas Tortorini <thomastortorini@gmail.com>
|
||||
Jörn Wagner <joern.wagner@explicatis.com>
|
||||
Jon Hester <jon.d.hester@gmail.com>
|
||||
Colin Frick <colin@bash.li>
|
||||
Winston Howes <winstonhowes@gmail.com>
|
||||
Jon Hester <jon.d.hester@gmail.com>
|
||||
Alexander O'Mara <me@alexomara.com>
|
||||
Chris Rebert <github@rebertia.com>
|
||||
Bastian Buchholz <buchholz.bastian@googlemail.com>
|
||||
Mu Haibao <mhbseal@163.com>
|
||||
Calvin Metcalf <calvin.metcalf@gmail.com>
|
||||
Arthur Stolyar <nekr.fabula@gmail.com>
|
||||
Calvin Metcalf <calvin.metcalf@gmail.com>
|
||||
Mu Haibao <mhbseal@163.com>
|
||||
Richard McDaniel <rm0026@uah.edu>
|
||||
Chris Rebert <github@rebertia.com>
|
||||
Gabriel Schulhof <gabriel.schulhof@intel.com>
|
||||
Gilad Peleg <giladp007@gmail.com>
|
||||
Julian Alexander Murillo <julian.alexander.murillo@gmail.com>
|
||||
Kevin Kirsche <Kev.Kirsche+GitHub@gmail.com>
|
||||
Martin Naumann <martin@geekonaut.de>
|
||||
Yongwoo Jeon <yongwoo.jeon@navercorp.com>
|
||||
John-David Dalton <john.david.dalton@gmail.com>
|
||||
Marek Lewandowski <m.lewandowski@cksource.com>
|
||||
Bruno Pérel <brunoperel@gmail.com>
|
||||
Daniel Nill <daniellnill@gmail.com>
|
||||
Reed Loden <reed@reedloden.com>
|
||||
Daniel Nill <daniellnill@gmail.com>
|
||||
Yongwoo Jeon <yongwoo.jeon@navercorp.com>
|
||||
Sean Henderson <seanh.za@gmail.com>
|
||||
Gary Ye <garysye@gmail.com>
|
||||
Richard Kraaijenhagen <stdin+git@riichard.com>
|
||||
Connor Atherton <c.liam.atherton@gmail.com>
|
||||
Gary Ye <garysye@gmail.com>
|
||||
Christian Grete <webmaster@christiangrete.com>
|
||||
Tom von Clef <thomas.vonclef@gmail.com>
|
||||
Liza Ramo <liza.h.ramo@gmail.com>
|
||||
Julian Alexander Murillo <julian.alexander.murillo@gmail.com>
|
||||
Joelle Fleurantin <joasqueeniebee@gmail.com>
|
||||
Steve Mao <maochenyan@gmail.com>
|
||||
Jon Dufresne <jon.dufresne@gmail.com>
|
||||
Jae Sung Park <alberto.park@gmail.com>
|
||||
Josh Soref <apache@soref.com>
|
||||
Saptak Sengupta <saptak013@gmail.com>
|
||||
Henry Wong <henryw4k@gmail.com>
|
||||
Jun Sun <klsforever@gmail.com>
|
||||
Josh Soref <apache@soref.com>
|
||||
Henry Wong <henryw4k@gmail.com>
|
||||
Jon Dufresne <jon.dufresne@gmail.com>
|
||||
Martijn W. van der Lee <martijn@vanderlee.com>
|
||||
Devin Wilson <dwilson6.github@gmail.com>
|
||||
Damian Senn <jquery@topaxi.codes>
|
||||
Steve Mao <maochenyan@gmail.com>
|
||||
Zack Hall <zackhall@outlook.com>
|
||||
Vitaliy Terziev <vitaliyterziev@gmail.com>
|
||||
Todor Prikumov <tono_pr@abv.bg>
|
||||
Bernhard M. Wiedemann <jquerybmw@lsmod.de>
|
||||
Todor Prikumov <tono_pr@abv.bg>
|
||||
Jha Naman <createnaman@gmail.com>
|
||||
Alexander Lisianoi <all3fox@gmail.com>
|
||||
William Robinet <william.robinet@conostix.com>
|
||||
Alexander Lisianoi <all3fox@gmail.com>
|
||||
Vitaliy Terziev <vitaliyterziev@gmail.com>
|
||||
Joe Trumbull <trumbull.j@gmail.com>
|
||||
Alexander K <xpyro@ya.ru>
|
||||
Damian Senn <jquery@topaxi.codes>
|
||||
Ralin Chimev <ralin.chimev@gmail.com>
|
||||
Felipe Sateler <fsateler@gmail.com>
|
||||
Christophe Tafani-Dereeper <christophetd@hotmail.fr>
|
||||
@@ -323,35 +297,25 @@ Manoj Kumar <nithmanoj@gmail.com>
|
||||
David Broder-Rodgers <broder93@gmail.com>
|
||||
Alex Louden <alex@louden.com>
|
||||
Alex Padilla <alexonezero@outlook.com>
|
||||
karan-96 <karanbatra96@gmail.com>
|
||||
南漂一卒 <shiy007@qq.com>
|
||||
Erik Lax <erik@datahack.se>
|
||||
karan-96 <karanbatra96@gmail.com>
|
||||
Boom Lee <teabyii@gmail.com>
|
||||
Andreas Solleder <asol@num42.de>
|
||||
CDAGaming <cstack2011@yahoo.com>
|
||||
Pierre Spring <pierre@nelm.io>
|
||||
Shashanka Nataraj <shashankan.10@gmail.com>
|
||||
CDAGaming <cstack2011@yahoo.com>
|
||||
Erik Lax <erik@datahack.se>
|
||||
Matan Kotler-Berkowitz <205matan@gmail.com>
|
||||
Jordan Beland <jordan.beland@gmail.com>
|
||||
Henry Zhu <hi@henryzoo.com>
|
||||
Saptak Sengupta <saptak013@gmail.com>
|
||||
Nilton Cesar <niltoncms@gmail.com>
|
||||
basil.belokon <basil.belokon@gmail.com>
|
||||
Andrey Meshkov <ay.meshkov@gmail.com>
|
||||
tmybr11 <tomas.perone@gmail.com>
|
||||
Luis Emilio Velasco Sanchez <emibloque@gmail.com>
|
||||
Ed S <ejsanders@gmail.com>
|
||||
Bert Zhang <enbo@users.noreply.github.com>
|
||||
Sébastien Règne <regseb@users.noreply.github.com>
|
||||
wartmanm <3869625+wartmanm@users.noreply.github.com>
|
||||
Siddharth Dungarwal <sd5869@gmail.com>
|
||||
abnud1 <ahmad13932013@hotmail.com>
|
||||
Andrei Fangli <andrei_fangli@outlook.com>
|
||||
Marja Hölttä <marja.holtta@gmail.com>
|
||||
abnud1 <ahmad13932013@hotmail.com>
|
||||
buddh4 <mail@jharrer.de>
|
||||
Hoang <dangkyokhoang@gmail.com>
|
||||
Wonseop Kim <wonseop.kim@samsung.com>
|
||||
Pat O'Callaghan <patocallaghan@gmail.com>
|
||||
JuanMa Ruiz <ruizjuanma@gmail.com>
|
||||
Ahmed.S.ElAfifi <ahmed.s.elafifi@gmail.com>
|
||||
Sean Robinson <sean.robinson@scottsdalecc.edu>
|
||||
Christian Oliff <christianoliff@pm.me>
|
||||
|
||||
1238
inst/www/shared/jquery.js
vendored
1238
inst/www/shared/jquery.js
vendored
File diff suppressed because it is too large
Load Diff
4
inst/www/shared/jquery.min.js
vendored
4
inst/www/shared/jquery.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,151 +0,0 @@
|
||||
/* global Selectize */
|
||||
Selectize.define("selectize-plugin-a11y", function (options) {
|
||||
var self = this;
|
||||
var KEY_RETURN = 13;
|
||||
|
||||
if (typeof self.accessibility === "undefined") {
|
||||
self.accessibility = {};
|
||||
}
|
||||
|
||||
self.accessibility.helpers = {
|
||||
randomId: function (len) {
|
||||
var str = "",
|
||||
strLength = len || 10,
|
||||
base = "abcdefghijklmnopqrstuvwxyz0123456789",
|
||||
baseLength = base.length;
|
||||
|
||||
for (var i = 0; i < strLength; i++) {
|
||||
str += base[Math.floor(baseLength * Math.random())];
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
self.accessibility.liveRegion = {
|
||||
$region: "",
|
||||
speak: function (msg) {
|
||||
var $container = $("<div></div>");
|
||||
$container.text(msg);
|
||||
this.$region.html($container);
|
||||
},
|
||||
domListener: function () {
|
||||
var observer = new MutationObserver(function (mutations) {
|
||||
mutations.forEach(function (mutation) {
|
||||
var $target = $(mutation.target);
|
||||
if ($target.hasClass("items")) {
|
||||
if ($target.hasClass("dropdown-active")) {
|
||||
// open
|
||||
self.$control_input.attr("aria-expanded", "true");
|
||||
var kids = self.$dropdown_content[0].children;
|
||||
for (i = 0; i < kids.length; i++) {
|
||||
var attrs = kids[i].attributes;
|
||||
if (!attrs.role) {
|
||||
kids[i].setAttribute("role", "option");
|
||||
}
|
||||
if (!attrs.id) {
|
||||
kids[i].setAttribute("id", self.accessibility.helpers.randomId());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// close
|
||||
self.$control_input.attr("aria-expanded", "false");
|
||||
self.$control_input.removeAttr("aria-activedescendant");
|
||||
}
|
||||
} else {
|
||||
// option change
|
||||
if ($target.hasClass("active")) {
|
||||
if (!!$target.attr("data-value")) { // eslint-disable-line no-extra-boolean-cast
|
||||
self.$control_input.attr(
|
||||
"aria-activedescendant",
|
||||
$target.attr("id")
|
||||
);
|
||||
self.accessibility.liveRegion.speak($target.text(), 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
observer.observe(self.$dropdown[0], {
|
||||
attributes: true,
|
||||
attributeFilter: ["class"],
|
||||
subtree: true,
|
||||
attributeOldValue: true
|
||||
});
|
||||
|
||||
observer.observe(self.$control[0], {
|
||||
attributes: true,
|
||||
attributeFilter: ["class"]
|
||||
});
|
||||
|
||||
observer.observe(self.$control_input[0], {
|
||||
attributes: true,
|
||||
attributeFilter: ["value"]
|
||||
});
|
||||
},
|
||||
setAttributes: function () {
|
||||
this.$region.attr({
|
||||
"aria-live": "assertive",
|
||||
role: "log",
|
||||
"aria-relevant": "additions",
|
||||
"aria-atomic": "true"
|
||||
});
|
||||
},
|
||||
setStyles: function () {
|
||||
this.$region.css({
|
||||
position: "absolute",
|
||||
width: "1px",
|
||||
height: "1px",
|
||||
"margin-top": "-1px",
|
||||
clip: "rect(1px, 1px, 1px, 1px)",
|
||||
overflow: "hidden"
|
||||
});
|
||||
},
|
||||
init: function () {
|
||||
this.$region = $("<div>");
|
||||
this.setAttributes();
|
||||
this.setStyles();
|
||||
$("body").append(this.$region);
|
||||
this.domListener();
|
||||
}
|
||||
};
|
||||
|
||||
this.setup = (function () {
|
||||
var original = self.setup;
|
||||
return function () {
|
||||
original.apply(this, arguments);
|
||||
var inputId = self.accessibility.helpers.randomId(),
|
||||
listboxId = self.accessibility.helpers.randomId();
|
||||
|
||||
self.$control.on("keydown", function (e) {
|
||||
if (e.keyCode === KEY_RETURN) {
|
||||
$(this).click();
|
||||
}
|
||||
});
|
||||
|
||||
self.$control_input.attr({
|
||||
role: "combobox",
|
||||
"aria-expanded": "false",
|
||||
haspopup: "listbox",
|
||||
"aria-owns": listboxId,
|
||||
"aria-label": self.$wrapper
|
||||
.closest("[data-accessibility-selectize-label]")
|
||||
.attr("data-accessibility-selectize-label")
|
||||
});
|
||||
|
||||
self.$dropdown_content.attr({
|
||||
role: "listbox",
|
||||
id: listboxId
|
||||
});
|
||||
self.accessibility.liveRegion.init();
|
||||
};
|
||||
})();
|
||||
|
||||
this.destroy = (function () {
|
||||
var original = self.destroy;
|
||||
return function () {
|
||||
self.accessibility.liveRegion.$region.remove();
|
||||
return original.apply(this, arguments);
|
||||
};
|
||||
})();
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
Selectize.define("selectize-plugin-a11y",function(t){var e,a,s=this;void 0===s.accessibility&&(s.accessibility={}),s.accessibility.helpers={randomId:function(t){for(var i="",e=t||10,a="abcdefghijklmnopqrstuvwxyz0123456789",r=a.length,s=0;s<e;s++)i+=a[Math.floor(r*Math.random())];return i}},s.accessibility.liveRegion={$region:"",speak:function(t){var i=$("<div></div>");i.text(t),this.$region.html(i)},domListener:function(){var t=new MutationObserver(function(t){t.forEach(function(t){var e=$(t.target);if(e.hasClass("items"))if(e.hasClass("dropdown-active")){s.$control_input.attr("aria-expanded","true");var a=s.$dropdown_content[0].children;for(i=0;i<a.length;i++){var r=a[i].attributes;r.role||a[i].setAttribute("role","option"),r.id||a[i].setAttribute("id",s.accessibility.helpers.randomId())}}else s.$control_input.attr("aria-expanded","false"),s.$control_input.removeAttr("aria-activedescendant");else e.hasClass("active")&&e.attr("data-value")&&(s.$control_input.attr("aria-activedescendant",e.attr("id")),s.accessibility.liveRegion.speak(e.text(),500))})});t.observe(s.$dropdown[0],{attributes:!0,attributeFilter:["class"],subtree:!0,attributeOldValue:!0}),t.observe(s.$control[0],{attributes:!0,attributeFilter:["class"]}),t.observe(s.$control_input[0],{attributes:!0,attributeFilter:["value"]})},setAttributes:function(){this.$region.attr({"aria-live":"assertive",role:"log","aria-relevant":"additions","aria-atomic":"true"})},setStyles:function(){this.$region.css({position:"absolute",width:"1px",height:"1px","margin-top":"-1px",clip:"rect(1px, 1px, 1px, 1px)",overflow:"hidden"})},init:function(){this.$region=$("<div>"),this.setAttributes(),this.setStyles(),$("body").append(this.$region),this.domListener()}},this.setup=(e=s.setup,function(){e.apply(this,arguments);s.accessibility.helpers.randomId();var t=s.accessibility.helpers.randomId();s.$control.on("keydown",function(t){13===t.keyCode&&$(this).click()}),s.$control_input.attr({role:"combobox","aria-expanded":"false",haspopup:"listbox","aria-owns":t,"aria-label":s.$wrapper.closest("[data-accessibility-selectize-label]").attr("data-accessibility-selectize-label")}),s.$dropdown_content.attr({role:"listbox",id:t}),s.accessibility.liveRegion.init()}),this.destroy=(a=s.destroy,function(){return s.accessibility.liveRegion.$region.remove(),a.apply(this,arguments)})});
|
||||
17
inst/www/shared/selectize/js/es5-shim.min.js
vendored
Normal file
17
inst/www/shared/selectize/js/es5-shim.min.js
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
(function(o){"function"==typeof define?define(o):"function"==typeof YUI?YUI.add("es5",o):o()})(function(){function o(){}function v(a){a=+a;a!==a?a=0:0!==a&&(a!==1/0&&a!==-(1/0))&&(a=(0<a||-1)*Math.floor(Math.abs(a)));return a}function s(a){var b=typeof a;return null===a||"undefined"===b||"boolean"===b||"number"===b||"string"===b}Function.prototype.bind||(Function.prototype.bind=function(a){var b=this;if("function"!=typeof b)throw new TypeError("Function.prototype.bind called on incompatible "+b);
|
||||
var d=q.call(arguments,1),c=function(){if(this instanceof c){var e=b.apply(this,d.concat(q.call(arguments)));return Object(e)===e?e:this}return b.apply(a,d.concat(q.call(arguments)))};b.prototype&&(o.prototype=b.prototype,c.prototype=new o,o.prototype=null);return c});var k=Function.prototype.call,p=Object.prototype,q=Array.prototype.slice,h=k.bind(p.toString),t=k.bind(p.hasOwnProperty);t(p,"__defineGetter__")&&(k.bind(p.__defineGetter__),k.bind(p.__defineSetter__),k.bind(p.__lookupGetter__),k.bind(p.__lookupSetter__));
|
||||
if(2!=[1,2].splice(0).length){var y=Array.prototype.splice;Array.prototype.splice=function(a,b){return arguments.length?y.apply(this,[a===void 0?0:a,b===void 0?this.length-a:b].concat(q.call(arguments,2))):[]}}if(1!=[].unshift(0)){var z=Array.prototype.unshift;Array.prototype.unshift=function(){z.apply(this,arguments);return this.length}}Array.isArray||(Array.isArray=function(a){return h(a)=="[object Array]"});var k=Object("a"),l="a"!=k[0]||!(0 in k);Array.prototype.forEach||(Array.prototype.forEach=
|
||||
function(a,b){var d=n(this),c=l&&h(this)=="[object String]"?this.split(""):d,e=-1,f=c.length>>>0;if(h(a)!="[object Function]")throw new TypeError;for(;++e<f;)e in c&&a.call(b,c[e],e,d)});Array.prototype.map||(Array.prototype.map=function(a,b){var d=n(this),c=l&&h(this)=="[object String]"?this.split(""):d,e=c.length>>>0,f=Array(e);if(h(a)!="[object Function]")throw new TypeError(a+" is not a function");for(var g=0;g<e;g++)g in c&&(f[g]=a.call(b,c[g],g,d));return f});Array.prototype.filter||(Array.prototype.filter=
|
||||
function(a,b){var d=n(this),c=l&&h(this)=="[object String]"?this.split(""):d,e=c.length>>>0,f=[],g;if(h(a)!="[object Function]")throw new TypeError(a+" is not a function");for(var i=0;i<e;i++)if(i in c){g=c[i];a.call(b,g,i,d)&&f.push(g)}return f});Array.prototype.every||(Array.prototype.every=function(a,b){var d=n(this),c=l&&h(this)=="[object String]"?this.split(""):d,e=c.length>>>0;if(h(a)!="[object Function]")throw new TypeError(a+" is not a function");for(var f=0;f<e;f++)if(f in c&&!a.call(b,c[f],
|
||||
f,d))return false;return true});Array.prototype.some||(Array.prototype.some=function(a,b){var d=n(this),c=l&&h(this)=="[object String]"?this.split(""):d,e=c.length>>>0;if(h(a)!="[object Function]")throw new TypeError(a+" is not a function");for(var f=0;f<e;f++)if(f in c&&a.call(b,c[f],f,d))return true;return false});Array.prototype.reduce||(Array.prototype.reduce=function(a){var b=n(this),d=l&&h(this)=="[object String]"?this.split(""):b,c=d.length>>>0;if(h(a)!="[object Function]")throw new TypeError(a+
|
||||
" is not a function");if(!c&&arguments.length==1)throw new TypeError("reduce of empty array with no initial value");var e=0,f;if(arguments.length>=2)f=arguments[1];else{do{if(e in d){f=d[e++];break}if(++e>=c)throw new TypeError("reduce of empty array with no initial value");}while(1)}for(;e<c;e++)e in d&&(f=a.call(void 0,f,d[e],e,b));return f});Array.prototype.reduceRight||(Array.prototype.reduceRight=function(a){var b=n(this),d=l&&h(this)=="[object String]"?this.split(""):b,c=d.length>>>0;if(h(a)!=
|
||||
"[object Function]")throw new TypeError(a+" is not a function");if(!c&&arguments.length==1)throw new TypeError("reduceRight of empty array with no initial value");var e,c=c-1;if(arguments.length>=2)e=arguments[1];else{do{if(c in d){e=d[c--];break}if(--c<0)throw new TypeError("reduceRight of empty array with no initial value");}while(1)}do c in this&&(e=a.call(void 0,e,d[c],c,b));while(c--);return e});if(!Array.prototype.indexOf||-1!=[0,1].indexOf(1,2))Array.prototype.indexOf=function(a){var b=l&&
|
||||
h(this)=="[object String]"?this.split(""):n(this),d=b.length>>>0;if(!d)return-1;var c=0;arguments.length>1&&(c=v(arguments[1]));for(c=c>=0?c:Math.max(0,d+c);c<d;c++)if(c in b&&b[c]===a)return c;return-1};if(!Array.prototype.lastIndexOf||-1!=[0,1].lastIndexOf(0,-3))Array.prototype.lastIndexOf=function(a){var b=l&&h(this)=="[object String]"?this.split(""):n(this),d=b.length>>>0;if(!d)return-1;var c=d-1;arguments.length>1&&(c=Math.min(c,v(arguments[1])));for(c=c>=0?c:d-Math.abs(c);c>=0;c--)if(c in b&&
|
||||
a===b[c])return c;return-1};if(!Object.keys){var w=!0,x="toString toLocaleString valueOf hasOwnProperty isPrototypeOf propertyIsEnumerable constructor".split(" "),A=x.length,r;for(r in{toString:null})w=!1;Object.keys=function(a){if(typeof a!="object"&&typeof a!="function"||a===null)throw new TypeError("Object.keys called on a non-object");var b=[],d;for(d in a)t(a,d)&&b.push(d);if(w)for(d=0;d<A;d++){var c=x[d];t(a,c)&&b.push(c)}return b}}if(!Date.prototype.toISOString||-1===(new Date(-621987552E5)).toISOString().indexOf("-000001"))Date.prototype.toISOString=
|
||||
function(){var a,b,d,c;if(!isFinite(this))throw new RangeError("Date.prototype.toISOString called on non-finite value.");c=this.getUTCFullYear();a=this.getUTCMonth();c=c+Math.floor(a/12);a=[(a%12+12)%12+1,this.getUTCDate(),this.getUTCHours(),this.getUTCMinutes(),this.getUTCSeconds()];c=(c<0?"-":c>9999?"+":"")+("00000"+Math.abs(c)).slice(0<=c&&c<=9999?-4:-6);for(b=a.length;b--;){d=a[b];d<10&&(a[b]="0"+d)}return c+"-"+a.slice(0,2).join("-")+"T"+a.slice(2).join(":")+"."+("000"+this.getUTCMilliseconds()).slice(-3)+
|
||||
"Z"};r=!1;try{r=Date.prototype.toJSON&&null===(new Date(NaN)).toJSON()&&-1!==(new Date(-621987552E5)).toJSON().indexOf("-000001")&&Date.prototype.toJSON.call({toISOString:function(){return true}})}catch(H){}r||(Date.prototype.toJSON=function(){var a=Object(this),b;a:if(s(a))b=a;else{b=a.valueOf;if(typeof b==="function"){b=b.call(a);if(s(b))break a}b=a.toString;if(typeof b==="function"){b=b.call(a);if(s(b))break a}throw new TypeError;}if(typeof b==="number"&&!isFinite(b))return null;b=a.toISOString;
|
||||
if(typeof b!="function")throw new TypeError("toISOString property is not callable");return b.call(a)});var g=Date,m=function(a,b,d,c,e,f,h){var i=arguments.length;if(this instanceof g){i=i==1&&String(a)===a?new g(m.parse(a)):i>=7?new g(a,b,d,c,e,f,h):i>=6?new g(a,b,d,c,e,f):i>=5?new g(a,b,d,c,e):i>=4?new g(a,b,d,c):i>=3?new g(a,b,d):i>=2?new g(a,b):i>=1?new g(a):new g;i.constructor=m;return i}return g.apply(this,arguments)},u=function(a,b){var d=b>1?1:0;return B[b]+Math.floor((a-1969+d)/4)-Math.floor((a-
|
||||
1901+d)/100)+Math.floor((a-1601+d)/400)+365*(a-1970)},C=RegExp("^(\\d{4}|[+-]\\d{6})(?:-(\\d{2})(?:-(\\d{2})(?:T(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.(\\d{3}))?)?(Z|(?:([-+])(\\d{2}):(\\d{2})))?)?)?)?$"),B=[0,31,59,90,120,151,181,212,243,273,304,334,365],j;for(j in g)m[j]=g[j];m.now=g.now;m.UTC=g.UTC;m.prototype=g.prototype;m.prototype.constructor=m;m.parse=function(a){var b=C.exec(a);if(b){var d=Number(b[1]),c=Number(b[2]||1)-1,e=Number(b[3]||1)-1,f=Number(b[4]||0),h=Number(b[5]||0),i=Number(b[6]||
|
||||
0),j=Number(b[7]||0),m=!b[4]||b[8]?0:Number(new g(1970,0)),k=b[9]==="-"?1:-1,l=Number(b[10]||0),b=Number(b[11]||0);if(f<(h>0||i>0||j>0?24:25)&&h<60&&i<60&&j<1E3&&c>-1&&c<12&&l<24&&b<60&&e>-1&&e<u(d,c+1)-u(d,c)){d=((u(d,c)+e)*24+f+l*k)*60;d=((d+h+b*k)*60+i)*1E3+j+m;if(-864E13<=d&&d<=864E13)return d}return NaN}return g.parse.apply(this,arguments)};Date=m;Date.now||(Date.now=function(){return(new Date).getTime()});if("0".split(void 0,0).length){var D=String.prototype.split;String.prototype.split=function(a,
|
||||
b){return a===void 0&&b===0?[]:D.apply(this,arguments)}}if("".substr&&"b"!=="0b".substr(-1)){var E=String.prototype.substr;String.prototype.substr=function(a,b){return E.call(this,a<0?(a=this.length+a)<0?0:a:a,b)}}j="\t\n\x0B\f\r \u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\ufeff";if(!String.prototype.trim||j.trim()){j="["+j+"]";var F=RegExp("^"+j+j+"*"),G=RegExp(j+j+"*$");String.prototype.trim=function(){if(this===void 0||this===
|
||||
null)throw new TypeError("can't convert "+this+" to object");return String(this).replace(F,"").replace(G,"")}}var n=function(a){if(a==null)throw new TypeError("can't convert "+a+" to object");return Object(a)}});
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
})();
|
||||
@@ -407,10 +407,3 @@ pre.shiny-text-output {
|
||||
color: #aaa;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
|
||||
/* Hidden tabPanels */
|
||||
.nav-hidden {
|
||||
/* override anything bootstrap sets for `.nav` */
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
(function () {
|
||||
var $ = jQuery;
|
||||
var exports = window.Shiny = window.Shiny || {};
|
||||
exports.version = "1.5.0.9001"; // Version number inserted by Grunt
|
||||
exports.version = "1.4.0.9002"; // Version number inserted by Grunt
|
||||
|
||||
var origPushState = window.history.pushState;
|
||||
|
||||
@@ -5322,7 +5322,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
_getLabel: function _getLabel(obj) {
|
||||
// If <label><input /><span>label text</span></label>
|
||||
if (obj.parentNode.tagName === "LABEL") {
|
||||
return $(obj.parentNode).find('span').text().trim();
|
||||
return $.trim($(obj.parentNode).find('span').text());
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -5420,7 +5420,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
_getLabel: function _getLabel(obj) {
|
||||
// If <label><input /><span>label text</span></label>
|
||||
if (obj.parentNode.tagName === "LABEL") {
|
||||
return $(obj.parentNode).find('span').text().trim();
|
||||
return $.trim($(obj.parentNode).find('span').text());
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -6312,65 +6312,6 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
}
|
||||
});
|
||||
|
||||
function getComputedBgColor(el) {
|
||||
if (!el) {
|
||||
// Top of document, can't recurse further
|
||||
return null;
|
||||
}
|
||||
|
||||
var bgColor = getStyle(el, "background-color");
|
||||
var 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.
|
||||
var 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;
|
||||
} // Compute the color property of an a tag, scoped within the element
|
||||
|
||||
|
||||
function getComputedLinkColor(el) {
|
||||
var a = document.createElement("a");
|
||||
a.href = "/";
|
||||
var 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);
|
||||
var linkColor = getStyle(a, "color");
|
||||
el.removeChild(div);
|
||||
return linkColor;
|
||||
}
|
||||
|
||||
function getComputedFont(el) {
|
||||
var fontFamily = getStyle(el, "font-family");
|
||||
var 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 id = getIdFromEl(this);
|
||||
initialValues['.clientdata_output_' + id + '_bg'] = getComputedBgColor(this);
|
||||
initialValues['.clientdata_output_' + id + '_fg'] = getStyle(this, "color");
|
||||
initialValues['.clientdata_output_' + id + '_accent'] = getComputedLinkColor(this);
|
||||
initialValues['.clientdata_output_' + id + '_font'] = getComputedFont(this);
|
||||
});
|
||||
|
||||
function doSendImageSize() {
|
||||
$('.shiny-image-output, .shiny-plot-output, .shiny-report-size').each(function () {
|
||||
var id = getIdFromEl(this);
|
||||
@@ -6380,13 +6321,6 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
||||
inputs.setInput('.clientdata_output_' + id + '_height', this.offsetHeight);
|
||||
}
|
||||
});
|
||||
$('.shiny-image-output, .shiny-plot-output, .shiny-report-theme').each(function () {
|
||||
var id = getIdFromEl(this);
|
||||
inputs.setInput('.clientdata_output_' + id + '_bg', getComputedBgColor(this));
|
||||
inputs.setInput('.clientdata_output_' + id + '_fg', getStyle(this, "color"));
|
||||
inputs.setInput('.clientdata_output_' + id + '_accent', getComputedLinkColor(this));
|
||||
inputs.setInput('.clientdata_output_' + id + '_font', getComputedFont(this));
|
||||
});
|
||||
$('.shiny-bound-output').each(function () {
|
||||
var $this = $(this),
|
||||
binding = $this.data('shiny-output-binding');
|
||||
|
||||
File diff suppressed because one or more lines are too long
1
inst/www/shared/shiny.min.css
vendored
1
inst/www/shared/shiny.min.css
vendored
File diff suppressed because one or more lines are too long
4
inst/www/shared/shiny.min.js
vendored
4
inst/www/shared/shiny.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -4,20 +4,8 @@
|
||||
\alias{MockShinySession}
|
||||
\title{Mock Shiny Session}
|
||||
\description{
|
||||
An R6 class suitable for testing purposes. Simulates, to the
|
||||
extent possible, the behavior of the \code{ShinySession} class. The \code{session}
|
||||
parameter provided to Shiny server functions and modules is an instance of
|
||||
a \code{ShinySession} in normal operation.
|
||||
|
||||
Most kinds of module and server testing do not require this class be
|
||||
instantiated manually. See instead \code{\link[=testServer]{testServer()}}.
|
||||
|
||||
In order to support advanced usage, instances of \code{MockShinySession} are
|
||||
\strong{unlocked} so that public methods and fields of instances may be
|
||||
modified. For example, in order to test authentication workflows, the
|
||||
\code{user} or \code{groups} fields may be overridden. Modified instances of
|
||||
\code{MockShinySession} may then be passed explicitly as the \code{session} argument
|
||||
of \code{\link[=testServer]{testServer()}}.
|
||||
An R6 class suitable for testing that simulates the \code{session} parameter
|
||||
provided to Shiny server functions or modules.
|
||||
}
|
||||
\examples{
|
||||
|
||||
@@ -25,57 +13,33 @@ of \code{\link[=testServer]{testServer()}}.
|
||||
## Method `MockShinySession$setInputs`
|
||||
## ------------------------------------------------
|
||||
|
||||
\dontrun{
|
||||
session$setInputs(x=1, y=2)
|
||||
}
|
||||
s <- MockShinySession$new()
|
||||
s$setInputs(x=1, y=2)
|
||||
}
|
||||
\section{Public fields}{
|
||||
\if{html}{\out{<div class="r6-fields">}}
|
||||
\describe{
|
||||
\item{\code{env}}{The environment associated with the session.}
|
||||
|
||||
\item{\code{returned}}{The value returned by the module under test.}
|
||||
\item{\code{returned}}{The value returned by the module.}
|
||||
|
||||
\item{\code{singletons}}{Hardcoded as empty. Needed for rendering HTML (i.e. renderUI).}
|
||||
\item{\code{singletons}}{Hardcoded as empty. Needed for rendering HTML (i.e. renderUI)}
|
||||
|
||||
\item{\code{clientData}}{Mock client data that always returns a size for plots.}
|
||||
\item{\code{clientData}}{Mock client data that always returns a size for plots}
|
||||
|
||||
\item{\code{output}}{The shinyoutputs associated with the session.}
|
||||
\item{\code{output}}{The shinyoutputs associated with the session}
|
||||
|
||||
\item{\code{input}}{The reactive inputs associated with the session.}
|
||||
\item{\code{input}}{The reactive inputs associated with the session}
|
||||
|
||||
\item{\code{userData}}{An environment initialized as empty.}
|
||||
|
||||
\item{\code{progressStack}}{A stack of progress objects.}
|
||||
|
||||
\item{\code{token}}{On a real \code{ShinySession}, used to identify this instance in URLs.}
|
||||
|
||||
\item{\code{cache}}{The session cache MemoryCache.}
|
||||
|
||||
\item{\code{appcache}}{The app cache MemoryCache.}
|
||||
|
||||
\item{\code{restoreContext}}{Part of bookmarking support in a real
|
||||
\code{ShinySession} but always \code{NULL} for a \code{MockShinySession}.}
|
||||
|
||||
\item{\code{groups}}{Character vector of groups associated with an authenticated
|
||||
user. Always \code{NULL} for a \code{MockShinySesion}.}
|
||||
|
||||
\item{\code{user}}{The username of an authenticated user. Always \code{NULL} for a
|
||||
\code{MockShinySession}.}
|
||||
\item{\code{progressStack}}{A stack of progress objects}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
\section{Active bindings}{
|
||||
\if{html}{\out{<div class="r6-active-bindings">}}
|
||||
\describe{
|
||||
\item{\code{files}}{For internal use only.}
|
||||
|
||||
\item{\code{downloads}}{For internal use only.}
|
||||
|
||||
\item{\code{closed}}{Deprecated in \code{ShinySession} and signals an error.}
|
||||
|
||||
\item{\code{session}}{Deprecated in ShinySession and signals an error.}
|
||||
|
||||
\item{\code{request}}{An empty environment where the request should be. The request isn't meaningfully mocked currently.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
@@ -83,6 +47,8 @@ user. Always \code{NULL} for a \code{MockShinySesion}.}
|
||||
\section{Methods}{
|
||||
\subsection{Public methods}{
|
||||
\itemize{
|
||||
\item \href{#method-reactlog}{\code{MockShinySession$reactlog()}}
|
||||
\item \href{#method-incrementBusyCount}{\code{MockShinySession$incrementBusyCount()}}
|
||||
\item \href{#method-new}{\code{MockShinySession$new()}}
|
||||
\item \href{#method-onFlush}{\code{MockShinySession$onFlush()}}
|
||||
\item \href{#method-onFlushed}{\code{MockShinySession$onFlushed()}}
|
||||
@@ -98,6 +64,22 @@ user. Always \code{NULL} for a \code{MockShinySesion}.}
|
||||
\item \href{#method-.now}{\code{MockShinySession$.now()}}
|
||||
\item \href{#method-defineOutput}{\code{MockShinySession$defineOutput()}}
|
||||
\item \href{#method-getOutput}{\code{MockShinySession$getOutput()}}
|
||||
\item \href{#method-registerDataObj}{\code{MockShinySession$registerDataObj()}}
|
||||
\item \href{#method-allowReconnect}{\code{MockShinySession$allowReconnect()}}
|
||||
\item \href{#method-reload}{\code{MockShinySession$reload()}}
|
||||
\item \href{#method-resetBrush}{\code{MockShinySession$resetBrush()}}
|
||||
\item \href{#method-sendCustomMessage}{\code{MockShinySession$sendCustomMessage()}}
|
||||
\item \href{#method-sendBinaryMessage}{\code{MockShinySession$sendBinaryMessage()}}
|
||||
\item \href{#method-sendInputMessage}{\code{MockShinySession$sendInputMessage()}}
|
||||
\item \href{#method-setBookmarkExclude}{\code{MockShinySession$setBookmarkExclude()}}
|
||||
\item \href{#method-getBookmarkExclude}{\code{MockShinySession$getBookmarkExclude()}}
|
||||
\item \href{#method-onBookmark}{\code{MockShinySession$onBookmark()}}
|
||||
\item \href{#method-onBookmarked}{\code{MockShinySession$onBookmarked()}}
|
||||
\item \href{#method-doBookmark}{\code{MockShinySession$doBookmark()}}
|
||||
\item \href{#method-onRestore}{\code{MockShinySession$onRestore()}}
|
||||
\item \href{#method-onRestored}{\code{MockShinySession$onRestored()}}
|
||||
\item \href{#method-exportTestValues}{\code{MockShinySession$exportTestValues()}}
|
||||
\item \href{#method-getTestSnapshotUrl}{\code{MockShinySession$getTestSnapshotUrl()}}
|
||||
\item \href{#method-ns}{\code{MockShinySession$ns()}}
|
||||
\item \href{#method-flushReact}{\code{MockShinySession$flushReact()}}
|
||||
\item \href{#method-makeScope}{\code{MockShinySession$makeScope()}}
|
||||
@@ -105,20 +87,41 @@ user. Always \code{NULL} for a \code{MockShinySesion}.}
|
||||
\item \href{#method-setReturned}{\code{MockShinySession$setReturned()}}
|
||||
\item \href{#method-getReturned}{\code{MockShinySession$getReturned()}}
|
||||
\item \href{#method-genId}{\code{MockShinySession$genId()}}
|
||||
\item \href{#method-rootScope}{\code{MockShinySession$rootScope()}}
|
||||
\item \href{#method-unhandledError}{\code{MockShinySession$unhandledError()}}
|
||||
\item \href{#method-freezeValue}{\code{MockShinySession$freezeValue()}}
|
||||
\item \href{#method-onSessionEnded}{\code{MockShinySession$onSessionEnded()}}
|
||||
\item \href{#method-registerDownload}{\code{MockShinySession$registerDownload()}}
|
||||
\item \href{#method-getCurrentOutputInfo}{\code{MockShinySession$getCurrentOutputInfo()}}
|
||||
\item \href{#method-clone}{\code{MockShinySession$clone()}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-reactlog"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-reactlog}{}}}
|
||||
\subsection{Method \code{reactlog()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$reactlog(logEntry)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{logEntry}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-incrementBusyCount"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-incrementBusyCount}{}}}
|
||||
\subsection{Method \code{incrementBusyCount()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$incrementBusyCount()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-new"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-new}{}}}
|
||||
\subsection{Method \code{new()}}{
|
||||
Create a new MockShinySession.
|
||||
Create a new MockShinySession
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$new()}\if{html}{\out{</div>}}
|
||||
}
|
||||
@@ -214,6 +217,7 @@ Closes the session
|
||||
\if{latex}{\out{\hypertarget{method-cycleStartAction}{}}}
|
||||
\subsection{Method \code{cycleStartAction()}}{
|
||||
Unsophisticated mock implementation that merely invokes
|
||||
the given callback immediately.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$cycleStartAction(callback)}\if{html}{\out{</div>}}
|
||||
}
|
||||
@@ -221,7 +225,7 @@ Unsophisticated mock implementation that merely invokes
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{callback}}{The callback to be invoked.}
|
||||
\item{\code{callback}}{The callback ato be invoked.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
@@ -251,8 +255,8 @@ Base64-encode the given file. Needed for image rendering.
|
||||
\if{html}{\out{<a id="method-setInputs"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-setInputs}{}}}
|
||||
\subsection{Method \code{setInputs()}}{
|
||||
Sets reactive values associated with the \code{session$inputs}
|
||||
object and flushes the reactives.
|
||||
Sets reactive values associated with the \code{session$inputs} object
|
||||
and flushes the reactives.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$setInputs(...)}\if{html}{\out{</div>}}
|
||||
}
|
||||
@@ -260,17 +264,14 @@ object and flushes the reactives.
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{...}}{The inputs to set. These arguments are processed with
|
||||
\code{\link[rlang:list2]{rlang::list2()}} and so are \emph{\link[rlang:dyn-dots]{dynamic}}. Input names
|
||||
may not be duplicated.}
|
||||
\item{\code{...}}{The inputs to set.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
\subsection{Examples}{
|
||||
\if{html}{\out{<div class="r example copy">}}
|
||||
\preformatted{\dontrun{
|
||||
session$setInputs(x=1, y=2)
|
||||
}
|
||||
\preformatted{s <- MockShinySession$new()
|
||||
s$setInputs(x=1, y=2)
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
|
||||
@@ -282,8 +283,6 @@ session$setInputs(x=1, y=2)
|
||||
\if{latex}{\out{\hypertarget{method-.scheduleTask}{}}}
|
||||
\subsection{Method \code{.scheduleTask()}}{
|
||||
An internal method which shouldn't be used by others.
|
||||
Schedules \code{callback} for execution after some number of \code{millis}
|
||||
milliseconds.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$.scheduleTask(millis, callback)}\if{html}{\out{</div>}}
|
||||
}
|
||||
@@ -293,7 +292,7 @@ milliseconds.
|
||||
\describe{
|
||||
\item{\code{millis}}{The number of milliseconds on which to schedule a callback}
|
||||
|
||||
\item{\code{callback}}{The function to schedule.}
|
||||
\item{\code{callback}}{The function to schedule}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
@@ -324,17 +323,12 @@ An internal method which shouldn't be used by others.
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$.now()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Returns}{
|
||||
Elapsed time in milliseconds.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-defineOutput"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-defineOutput}{}}}
|
||||
\subsection{Method \code{defineOutput()}}{
|
||||
An internal method which shouldn't be used by others.
|
||||
Defines an output in a way that sets private$currentOutputName
|
||||
appropriately.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$defineOutput(name, func, label)}\if{html}{\out{</div>}}
|
||||
}
|
||||
@@ -342,11 +336,11 @@ appropriately.
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{name}}{The name of the output.}
|
||||
\item{\code{name}}{The name of the output}
|
||||
|
||||
\item{\code{func}}{The render definition.}
|
||||
\item{\code{func}}{The render definition}
|
||||
|
||||
\item{\code{label}}{Not used.}
|
||||
\item{\code{label}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
@@ -355,8 +349,7 @@ appropriately.
|
||||
\if{html}{\out{<a id="method-getOutput"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-getOutput}{}}}
|
||||
\subsection{Method \code{getOutput()}}{
|
||||
An internal method which shouldn't be used by others. Forces
|
||||
evaluation of any reactive dependencies of the output function.
|
||||
An internal method which shouldn't be used by others.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$getOutput(name)}\if{html}{\out{</div>}}
|
||||
}
|
||||
@@ -364,13 +357,274 @@ evaluation of any reactive dependencies of the output function.
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{name}}{The name of the output.}
|
||||
\item{\code{name}}{The name of the output}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
\subsection{Returns}{
|
||||
The return value of the function responsible for rendering the
|
||||
output.
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-registerDataObj"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-registerDataObj}{}}}
|
||||
\subsection{Method \code{registerDataObj()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$registerDataObj(name, data, filterFunc)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{name}}{Not used}
|
||||
|
||||
\item{\code{data}}{Not used}
|
||||
|
||||
\item{\code{filterFunc}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-allowReconnect"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-allowReconnect}{}}}
|
||||
\subsection{Method \code{allowReconnect()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$allowReconnect(value)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{value}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-reload"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-reload}{}}}
|
||||
\subsection{Method \code{reload()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$reload()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-resetBrush"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-resetBrush}{}}}
|
||||
\subsection{Method \code{resetBrush()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$resetBrush(brushId)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{brushId}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-sendCustomMessage"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-sendCustomMessage}{}}}
|
||||
\subsection{Method \code{sendCustomMessage()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$sendCustomMessage(type, message)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{type}}{Not used}
|
||||
|
||||
\item{\code{message}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-sendBinaryMessage"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-sendBinaryMessage}{}}}
|
||||
\subsection{Method \code{sendBinaryMessage()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$sendBinaryMessage(type, message)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{type}}{Not used}
|
||||
|
||||
\item{\code{message}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-sendInputMessage"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-sendInputMessage}{}}}
|
||||
\subsection{Method \code{sendInputMessage()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$sendInputMessage(inputId, message)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{inputId}}{Not used}
|
||||
|
||||
\item{\code{message}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-setBookmarkExclude"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-setBookmarkExclude}{}}}
|
||||
\subsection{Method \code{setBookmarkExclude()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$setBookmarkExclude(names)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{names}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-getBookmarkExclude"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-getBookmarkExclude}{}}}
|
||||
\subsection{Method \code{getBookmarkExclude()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$getBookmarkExclude()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-onBookmark"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-onBookmark}{}}}
|
||||
\subsection{Method \code{onBookmark()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$onBookmark(fun)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{fun}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-onBookmarked"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-onBookmarked}{}}}
|
||||
\subsection{Method \code{onBookmarked()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$onBookmarked(fun)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{fun}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-doBookmark"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-doBookmark}{}}}
|
||||
\subsection{Method \code{doBookmark()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$doBookmark()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-onRestore"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-onRestore}{}}}
|
||||
\subsection{Method \code{onRestore()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$onRestore(fun)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{fun}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-onRestored"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-onRestored}{}}}
|
||||
\subsection{Method \code{onRestored()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$onRestored(fun)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{fun}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-exportTestValues"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-exportTestValues}{}}}
|
||||
\subsection{Method \code{exportTestValues()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$exportTestValues()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-getTestSnapshotUrl"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-getTestSnapshotUrl}{}}}
|
||||
\subsection{Method \code{getTestSnapshotUrl()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$getTestSnapshotUrl(
|
||||
input = TRUE,
|
||||
output = TRUE,
|
||||
export = TRUE,
|
||||
format = "json"
|
||||
)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{input}}{Not used}
|
||||
|
||||
\item{\code{output}}{Not used}
|
||||
|
||||
\item{\code{export}}{Not used}
|
||||
|
||||
\item{\code{format}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
@@ -385,13 +639,10 @@ Returns the given id prefixed by this namespace's id.
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{id}}{The id to prefix with a namespace id.}
|
||||
\item{\code{id}}{The id to modify.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
\subsection{Returns}{
|
||||
The id with a namespace prefix.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-flushReact"></a>}}
|
||||
@@ -419,18 +670,12 @@ Create and return a namespace-specific session proxy.
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
\subsection{Returns}{
|
||||
A new session proxy.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-setEnv"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-setEnv}{}}}
|
||||
\subsection{Method \code{setEnv()}}{
|
||||
Set the environment associated with a testServer() call, but
|
||||
only if it has not previously been set. This ensures that only the
|
||||
environment of the outermost module under test is the one retained. In
|
||||
other words, the first assignment wins.
|
||||
Set the environment associated with a testServer() call.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$setEnv(env)}\if{html}{\out{</div>}}
|
||||
}
|
||||
@@ -442,18 +687,12 @@ other words, the first assignment wins.
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
\subsection{Returns}{
|
||||
The provided \code{env}.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-setReturned"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-setReturned}{}}}
|
||||
\subsection{Method \code{setReturned()}}{
|
||||
Set the value returned by the module call and proactively
|
||||
flush. Note that this method may be called multiple times if modules
|
||||
are nested. The last assignment, corresponding to an invocation of
|
||||
setReturned() in the outermost module, wins.
|
||||
Set the value returned by the module call and proactively flush.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$setReturned(value)}\if{html}{\out{</div>}}
|
||||
}
|
||||
@@ -465,9 +704,6 @@ setReturned() in the outermost module, wins.
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
\subsection{Returns}{
|
||||
The provided \code{value}.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-getReturned"></a>}}
|
||||
@@ -478,138 +714,17 @@ Get the value returned by the module call.
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$getReturned()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Returns}{
|
||||
The value returned by the module call
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-genId"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-genId}{}}}
|
||||
\subsection{Method \code{genId()}}{
|
||||
Generate a distinct character identifier for use as a proxy
|
||||
Return a distinct character identifier for use as a proxy
|
||||
namespace.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$genId()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Returns}{
|
||||
A character identifier unique to the current session.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-rootScope"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-rootScope}{}}}
|
||||
\subsection{Method \code{rootScope()}}{
|
||||
Provides a way to access the root \code{MockShinySession} from
|
||||
any descendant proxy.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$rootScope()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Returns}{
|
||||
The root \code{MockShinySession}.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-unhandledError"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-unhandledError}{}}}
|
||||
\subsection{Method \code{unhandledError()}}{
|
||||
Called by observers when a reactive expression errors.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$unhandledError(e)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{e}}{An error object.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-freezeValue"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-freezeValue}{}}}
|
||||
\subsection{Method \code{freezeValue()}}{
|
||||
Freeze a value until the flush cycle completes.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$freezeValue(x, name)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{x}}{A \code{ReactiveValues} object.}
|
||||
|
||||
\item{\code{name}}{The name of a reactive value within \code{x}.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-onSessionEnded"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-onSessionEnded}{}}}
|
||||
\subsection{Method \code{onSessionEnded()}}{
|
||||
Registers the given callback to be invoked when the session
|
||||
is closed (i.e. the connection to the client has been severed). The
|
||||
return value is a function which unregisters the callback. If multiple
|
||||
callbacks are registered, the order in which they are invoked is not
|
||||
guaranteed.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$onSessionEnded(sessionEndedCallback)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{sessionEndedCallback}}{Function to call when the session ends.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-registerDownload"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-registerDownload}{}}}
|
||||
\subsection{Method \code{registerDownload()}}{
|
||||
Associated a downloadable file with the session.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$registerDownload(name, filename, contentType, content)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{name}}{The un-namespaced output name to associate with the
|
||||
downloadable file.}
|
||||
|
||||
\item{\code{filename}}{A string or function designating the name of the file.}
|
||||
|
||||
\item{\code{contentType}}{A string of the content type of the file. Not used by
|
||||
\code{MockShinySession}.}
|
||||
|
||||
\item{\code{content}}{A function that takes a single argument file that is a
|
||||
file path (string) of a nonexistent temp file, and writes the content
|
||||
to that file path. (Reactive values and functions may be used from this
|
||||
function.)}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-getCurrentOutputInfo"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-getCurrentOutputInfo}{}}}
|
||||
\subsection{Method \code{getCurrentOutputInfo()}}{
|
||||
Get information about the output that is currently being
|
||||
executed.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$getCurrentOutputInfo()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Returns}{
|
||||
A list with with the \code{name} of the output. If no output is
|
||||
currently being executed, this will return \code{NULL}.
|
||||
output, or \code{NULL} if no output is currently executing.
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-clone"></a>}}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
\title{Create an object representing brushing options}
|
||||
\usage{
|
||||
brushOpts(
|
||||
id,
|
||||
id = NULL,
|
||||
fill = "#9cf",
|
||||
stroke = "#036",
|
||||
opacity = 0.25,
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
% Please edit documentation in R/image-interact.R
|
||||
\name{brushedPoints}
|
||||
\alias{brushedPoints}
|
||||
\alias{nearPoints}
|
||||
\title{Find rows of data selected on an interactive plot.}
|
||||
\title{Find rows of data that are selected by a brush}
|
||||
\usage{
|
||||
brushedPoints(
|
||||
df,
|
||||
@@ -14,103 +13,64 @@ brushedPoints(
|
||||
panelvar2 = NULL,
|
||||
allRows = FALSE
|
||||
)
|
||||
|
||||
nearPoints(
|
||||
df,
|
||||
coordinfo,
|
||||
xvar = NULL,
|
||||
yvar = NULL,
|
||||
panelvar1 = NULL,
|
||||
panelvar2 = NULL,
|
||||
threshold = 5,
|
||||
maxpoints = NULL,
|
||||
addDist = FALSE,
|
||||
allRows = FALSE
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{df}{A data frame from which to select rows.}
|
||||
|
||||
\item{brush, coordinfo}{The data from a brush or click/dblclick/hover event
|
||||
e.g. \code{input$plot_brush}, \code{input$plot_click}.}
|
||||
\item{brush}{The data from a brush, such as \code{input$plot_brush}.}
|
||||
|
||||
\item{xvar, yvar}{A string giving the name of the variable on the x or y axis.
|
||||
These are only required for base graphics, and must be the name of
|
||||
a column in \code{df}.}
|
||||
\item{xvar, yvar}{A string with the name of the variable on the x or y axis.
|
||||
This must also be the name of a column in \code{df}. If absent, then this
|
||||
function will try to infer the variable from the brush (only works for
|
||||
ggplot2).}
|
||||
|
||||
\item{panelvar1, panelvar2}{A string giving the name of a panel variable.
|
||||
For expert use only; in most cases these will be automatically
|
||||
derived from the ggplot2 spec.}
|
||||
\item{panelvar1, panelvar2}{Each of these is a string with the name of a panel
|
||||
variable. For example, if with ggplot2, you facet on a variable called
|
||||
\code{cyl}, then you can use \code{"cyl"} here. However, specifying the
|
||||
panel variable should not be necessary with ggplot2; Shiny should be able
|
||||
to auto-detect the panel variable.}
|
||||
|
||||
\item{allRows}{If \code{FALSE} (the default) return a data frame containing
|
||||
the selected rows. If \code{TRUE}, the input data frame will have a new
|
||||
column, \code{selected_}, which indicates whether the row was selected or not.}
|
||||
|
||||
\item{threshold}{A maximum distance (in pixels) to the pointer location.
|
||||
Rows in the data frame will be selected if the distance to the pointer is
|
||||
less than \code{threshold}.}
|
||||
|
||||
\item{maxpoints}{Maximum number of rows to return. If \code{NULL} (the default),
|
||||
will return all rows within the threshold distance.}
|
||||
|
||||
\item{addDist}{If TRUE, add a column named \code{dist_} that contains the
|
||||
distance from the coordinate to the point, in pixels. When no pointer
|
||||
event has yet occurred, the value of \code{dist_} will be \code{NA}.}
|
||||
}
|
||||
\value{
|
||||
A data frame based on \code{df}, containing the observations selected by the
|
||||
brush or near the click event. For \code{nearPoints()}, the rows will be sorted
|
||||
by distance to the event.
|
||||
|
||||
If \code{allRows = TRUE}, then all rows will returned, along with a new
|
||||
\code{selected_} column that indicates whether or not the point was selected.
|
||||
The output from \code{nearPoints()} will no longer be sorted, but you can
|
||||
set \code{addDist = TRUE} to get an additional column that gives the pixel
|
||||
distance to the pointer.
|
||||
column, \code{selected_}, which indicates whether the row was inside the
|
||||
brush (\code{TRUE}) or outside the brush (\code{FALSE}).}
|
||||
}
|
||||
\description{
|
||||
\code{brushedPoints()} returns rows from a data frame which are under a brush.
|
||||
\code{nearPoints()} returns rows from a data frame which are near a click, hover,
|
||||
or double-click. Alternatively, set \code{allRows = TRUE} to return all rows from
|
||||
the input data with an additional column \code{selected_} that indicates which
|
||||
rows of the would be selected.
|
||||
This function returns rows from a data frame which are under a brush used
|
||||
with \code{\link[=plotOutput]{plotOutput()}}.
|
||||
}
|
||||
\section{ggplot2}{
|
||||
\details{
|
||||
It is also possible for this function to return all rows from the input data
|
||||
frame, but with an additional column \code{selected_}, which indicates which
|
||||
rows of the input data frame are selected by the brush (\code{TRUE} for
|
||||
selected, \code{FALSE} for not-selected). This is enabled by setting
|
||||
\code{allRows=TRUE} option.
|
||||
|
||||
For plots created with ggplot2, it is not necessary to specify the
|
||||
column names to \code{xvar}, \code{yvar}, \code{panelvar1}, and \code{panelvar2} as that
|
||||
information can be automatically derived from the plot specification.
|
||||
The \code{xvar}, \code{yvar}, \code{panelvar1}, and \code{panelvar2}
|
||||
arguments specify which columns in the data correspond to the x variable, y
|
||||
variable, and panel variables of the plot. For example, if your plot is
|
||||
\code{plot(x=cars$speed, y=cars$dist)}, and your brush is named
|
||||
\code{"cars_brush"}, then you would use \code{brushedPoints(cars, input$cars_brush, "speed", "dist")}.
|
||||
|
||||
Note, however, that this will not work if you use a computed column, like
|
||||
\verb{aes(speed/2, dist))}. Instead, we recommend that you modify the data
|
||||
For plots created with ggplot2, it should not be necessary to specify the
|
||||
column names; that information will already be contained in the brush,
|
||||
provided that variables are in the original data, and not computed. For
|
||||
example, with \code{ggplot(cars, aes(x=speed, y=dist)) + geom_point()}, you
|
||||
could use \code{brushedPoints(cars, input$cars_brush)}. If, however, you use
|
||||
a computed column, like \code{ggplot(cars, aes(x=speed/2, y=dist)) + geom_point()}, then it will not be able to automatically extract column names
|
||||
and filter on them. If you want to use this function to filter data, it is
|
||||
recommended that you not use computed columns; instead, modify the data
|
||||
first, and then make the plot with "raw" columns in the modified data.
|
||||
}
|
||||
|
||||
\section{Brushing}{
|
||||
|
||||
If x or y column is a factor, then it will be coerced to an integer vector.
|
||||
If it is a character vector, then it will be coerced to a factor and then
|
||||
integer vector. This means that the brush will be considered to cover a
|
||||
given character/factor value when it covers the center value.
|
||||
If a specified x or y column is a factor, then it will be coerced to an
|
||||
integer vector. If it is a character vector, then it will be coerced to a
|
||||
factor and then integer vector. This means that the brush will be considered
|
||||
to cover a given character/factor value when it covers the center value.
|
||||
|
||||
If the brush is operating in just the x or y directions (e.g., with
|
||||
\code{brushOpts(direction = "x")}, then this function will filter out points
|
||||
using just the x or y variable, whichever is appropriate.
|
||||
}
|
||||
|
||||
\examples{
|
||||
\dontrun{
|
||||
# Note that in practice, these examples would need to go in reactives
|
||||
# or observers.
|
||||
|
||||
# This would select all points within 5 pixels of the click
|
||||
nearPoints(mtcars, input$plot_click)
|
||||
|
||||
# Select just the nearest point within 10 pixels of the click
|
||||
nearPoints(mtcars, input$plot_click, threshold = 10, maxpoints = 1)
|
||||
|
||||
}
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=plotOutput]{plotOutput()}} for example usage.
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/modules.R
|
||||
\name{callModule}
|
||||
\alias{callModule}
|
||||
\title{Invoke a Shiny module}
|
||||
\usage{
|
||||
callModule(module, id, ..., session = getDefaultReactiveDomain())
|
||||
}
|
||||
\arguments{
|
||||
\item{module}{A Shiny module server function}
|
||||
|
||||
\item{id}{An ID string that corresponds with the ID used to call the module's
|
||||
UI function}
|
||||
|
||||
\item{...}{Additional parameters to pass to module server function}
|
||||
|
||||
\item{session}{Session from which to make a child scope (the default should
|
||||
almost always be used)}
|
||||
}
|
||||
\value{
|
||||
The return value, if any, from executing the module server function
|
||||
}
|
||||
\description{
|
||||
Note: As of Shiny 1.5.0, we recommend using \code{\link[=moduleServer]{moduleServer()}} instead of
|
||||
\code{\link[=callModule]{callModule()}}, because the syntax is a little easier
|
||||
to understand, and modules created with \code{moduleServer} can be tested with
|
||||
\code{\link[=testServer]{testServer()}}.
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
\alias{clickOpts}
|
||||
\title{Create an object representing click options}
|
||||
\usage{
|
||||
clickOpts(id, clip = TRUE)
|
||||
clickOpts(id = NULL, clip = TRUE)
|
||||
}
|
||||
\arguments{
|
||||
\item{id}{Input value name. For example, if the value is \code{"plot_click"},
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
\alias{dblclickOpts}
|
||||
\title{Create an object representing double-click options}
|
||||
\usage{
|
||||
dblclickOpts(id, clip = TRUE, delay = 400)
|
||||
dblclickOpts(id = NULL, clip = TRUE, delay = 400)
|
||||
}
|
||||
\arguments{
|
||||
\item{id}{Input value name. For example, if the value is
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user