Compare commits

...

534 Commits

Author SHA1 Message Date
Carson
561625b270 soft-deprecate bootstrapPage() and basicPage(). Fixes #2594 2019-09-10 17:48:13 -05:00
Carson Sievert
2e0221ecfd Merge pull request #2592 from rstudio/set-min-max
Exit early if date parsing fails
2019-09-09 13:55:08 -07:00
Carson
aeded79544 yarn build 2019-09-09 15:43:41 -05:00
Carson
c0a7958e77 update news 2019-09-09 15:18:41 -05:00
Carson
431b194ec2 Exit early if date parsing fails in _setMin() and _setMax(), closes #2591 2019-09-09 13:48:18 -05:00
Joe Cheng
29d24d7e08 Merge pull request #2586 from rstudio/resourcePathChangesOption
Suppress resource path changes by default
2019-09-06 13:20:35 -07:00
Winston Chang
3b04c642ae Bump httpuv version dependency 2019-09-06 11:47:58 -05:00
Winston Chang
609fc5b0c0 Fix typo 2019-09-06 11:46:58 -05:00
Winston Chang
2a8c79b577 Merge pull request #2588 from rstudio/wch-slider-phantom
Add try-catch to ion.rangeSlider
2019-09-06 10:52:25 -05:00
Winston Chang
043316e40f Clarify comments 2019-09-06 10:52:08 -05:00
Joe Cheng
c9d8b987d4 Merge pull request #2522 from rstudio/wch-fix-reactivepoll-leak
Fix reactivePoll leak
2019-09-05 16:44:57 -07:00
Joe Cheng
33c5a5c665 Fix unit test 2019-09-05 16:12:07 -07:00
Winston Chang
29c90ba163 Code review fixes 2019-09-05 16:12:07 -07:00
Winston Chang
8c19450b10 Use safer method to remove observer 2019-09-05 16:11:47 -07:00
Winston Chang
89c97458c4 Update NEWS 2019-09-05 16:11:47 -07:00
Winston Chang
02be516902 Use safer finalizer for reactivePoll 2019-09-05 16:11:47 -07:00
Winston Chang
47ada300ea Fix reactivePoll leak 2019-09-05 16:11:19 -07:00
Carson
6f9c621774 Always suppress for now 2019-09-05 17:22:56 -05:00
Winston Chang
0310fe3b68 Automate patches for ion.rangeSlider 2019-09-05 16:00:56 -05:00
Winston Chang
7144a6e4b7 In ion.rangeSlider, wrap removeProp() in try-catch. Closes #2587 2019-09-05 15:53:37 -05:00
Carson
1c8071a96f Add shiny.resourcePathChanges option to get more information about resource path changes for a given prefix.
Closes #2584
2019-09-05 13:44:21 -05:00
Carson Sievert
ff5377da9e Merge pull request #2583 from rstudio/carson_resource_warning
Fix spacing in warning for resource paths conflicting with www/ subdirs
2019-09-04 11:27:09 -07:00
Carson
7aee84eb05 Fix spacing in warning for resource paths conflicting with www/ subdirs 2019-09-04 11:52:31 -05:00
Joe Cheng
769c32fd38 Merge pull request #2580 from hadley/shinyApp-docs
shinyApp() documentation improvements.
2019-09-04 08:12:17 -07:00
Joe Cheng
d05b89cfb3 Merge pull request #2581 from hadley/shinyApp-args
Clarify required argumented to shinyApp()
2019-09-04 08:08:44 -07:00
Hadley Wickham
f1f18a2334 Clarify required argumented to shinyApp()
This does technically change the interface as `shinyApp(server = function(input, output) ...))` would have previously worked, but it didn't generate a useful app.

Fixes #2462
2019-09-04 09:57:19 -05:00
Hadley Wickham
afc556f801 More details about server argument
Fixes #2508
2019-09-04 09:46:57 -05:00
Hadley Wickham
7f240839fc Document shiny.appobj in own file
Fixes #2458
2019-09-04 09:44:48 -05:00
Joe Cheng
8d0a6274cb Merge pull request #2579 from rstudio/wch-fix-sleep
Remove Sys.sleep in runApp()
2019-09-03 14:11:29 -07:00
Winston Chang
91cab10ff8 Remove Sys.sleep in runApp() 2019-09-03 15:09:51 -05:00
Joe Cheng
5828ea7426 Merge pull request #2577 from hadley/print-methods
Implement two missing print methods
2019-09-03 08:14:51 -07:00
Joe Cheng
80ba147168 Merge pull request #2566 from rstudio/fix-statichandler-backslash
Disallow backslash in staticHandler paths
2019-09-03 07:50:35 -07:00
Joe Cheng
f85479ba11 Merge pull request #2570 from rstudio/joe/misc/update-node-deps
Upgrade yarn dependencies
2019-09-03 07:49:51 -07:00
Joe Cheng
a23c5f151f Merge pull request #2569 from hadley/pageWithSidebar
Soft-deprecate pageWithSidebar() and headerPanel()
2019-09-03 07:49:29 -07:00
Joe Cheng
cab3601474 Merge pull request #2568 from hadley/doc-combine
Document all sidebar UI components together
2019-09-03 07:48:44 -07:00
Joe Cheng
cf330fcd58 Merge pull request #2575 from hadley/options-docs
Combine shinyOptions and shiny-options
2019-09-03 07:47:32 -07:00
Hadley Wickham
eb0162dccf Add basic print method for shiny.render.function
So at least the user isn't exposed to a bunch on internals
2019-09-03 09:17:02 -05:00
Hadley Wickham
a415aed7e6 Implement print.reactivevalues 2019-09-03 09:13:43 -05:00
Hadley Wickham
9f6014dc0b Remove from index 2019-09-02 09:14:20 -05:00
Hadley Wickham
21b0d38b57 Minor formatting tweaks 2019-09-02 09:14:15 -05:00
Hadley Wickham
1ec7f22b5f Alphabetise options 2019-09-02 09:03:46 -05:00
Hadley Wickham
346c5e4a4c Merge shinyOptions and shiny-options
Including an alias so that ?"shiny-options" will continue to work.

Fixes #2544
2019-09-02 08:59:58 -05:00
Joe Cheng
c9a0f0a713 Merge pull request #2567 from hadley/reactive-plot-size-docs
Clarify the three inputs to width/height
2019-08-29 16:24:46 -07:00
Joe Cheng
8bbc38dc8a Upgrade yarn dependencies
This prevents GitHub from complaining about a security vulnerability
in es-lint.
2019-08-29 12:27:29 -07:00
Hadley Wickham
96494a22f9 Soft-deprecate pageWithSidebar() and headerPanel()
Fixes #2452
2019-08-29 08:27:36 -05:00
Hadley Wickham
0813789e2a Cross-link tabPanel() to navbarPage() 2019-08-29 08:15:19 -05:00
Hadley Wickham
98ca820ab1 Document sidebarPanel() and mainPanel() with sidebarLayout() 2019-08-29 08:09:22 -05:00
Hadley Wickham
81ca9d9f29 Co-locate all sidebarLayout() functions 2019-08-29 08:00:40 -05:00
Hadley Wickham
16fe0019f9 Clarify the three inputs to width/height 2019-08-29 07:30:09 -05:00
Barret Schloerke
5fa650ab75 Merge pull request #2559 from rstudio/barret-trim-showcase-info
Reduce the information sent to shiny showcase
2019-08-28 17:27:16 -04:00
Winston Chang
564c2a0f16 Disallow backslash in staicHandler paths 2019-08-28 15:42:09 -05:00
Jeff Allen
99ac85f06a Merge pull request #2547 from rstudio/jeff/feature/helpers
Automatically load 'helpers' in R/ directory at runtime
2019-08-28 16:19:07 +00:00
trestletech
fc30ad0935 Fix upper-case test 2019-08-28 11:05:04 -05:00
trestletech
aadf2eb609 Merge remote-tracking branch 'origin/master' into jeff/feature/helpers 2019-08-28 10:29:32 -05:00
trestletech
68f778e423 autload 2019-08-28 10:26:31 -05:00
trestletech
0066cff652 - update NEWS
- only source global in server.R mode
- only use intermediary environment if opted-in to autoloading.
2019-08-28 10:20:42 -05:00
trestletech
f872a0c80a Only use loadSupport if opted-in to autload.r 2019-08-28 10:15:11 -05:00
Barret Schloerke
d9478142b1 Merge pull request #2561 from rstudio/barret-fix-master
Fix master docs
2019-08-27 17:38:27 -04:00
Winston Chang
5eced59961 Rebuild JS files 2019-08-27 16:31:29 -05:00
Winston Chang
3e1862cd51 Merge pull request #2526 from rstudio/setDateBounds
Set min/max date after date value when they land on the same day
2019-08-27 16:30:49 -05:00
Barret Schloerke
7271609850 ignore any node_modules-like named folder 2019-08-27 17:24:25 -04:00
Barret Schloerke
f24337bb3b add messages in htmltools script to notify about version number 2019-08-27 17:14:51 -04:00
trestletech
6167247ea2 Fix default param 2019-08-27 15:59:27 -05:00
Barret Schloerke
0332e52501 remove unneeded method roxygen tag 2019-08-27 16:50:33 -04:00
trestletech
0c23f78ab7 Make loading helpers opt-in. 2019-08-27 15:29:58 -05:00
Barret Schloerke
7624449644 add import htmltools statement 2019-08-27 16:17:20 -04:00
Barret Schloerke
97309e8c4c execute tools/updateHtmltoolsMan.R 2019-08-27 14:44:39 -04:00
Barret Schloerke
a1e78214db Create updateHtmltoolsMan.R 2019-08-27 14:34:33 -04:00
Barret Schloerke
1a57b3296b use htmltools remote 2019-08-27 14:32:25 -04:00
trestletech
7c10fc3514 Merge remote-tracking branch 'origin/master' into jeff/feature/helpers 2019-08-27 12:54:06 -05:00
Jeff Allen
494ef42aa8 Clarify docs 2019-08-27 17:46:17 +00:00
Carson
8a54d216c6 better news description 2019-08-27 10:47:10 -05:00
Carson
896a20d76d set start/end date after setting date if they land on the same day, closes #2335 2019-08-27 10:30:38 -05:00
Barret Schloerke
a26510b02f copy in latest man files from htmltools
* fix staticdocs test
* remove hasTagAttributes and getTagAttributes from man file
2019-08-26 15:30:27 -04:00
Barret Schloerke
1465f1d237 add roxygen tag to enforce s3 method for checking 2019-08-26 15:27:36 -04:00
Joe Cheng
21b18d107a Merge pull request #2166 from rstudio/unexport-knit-print
Un-export knit_print methods from htmltools
2019-08-26 14:29:16 -04:00
Joe Cheng
cc2173c587 Merge pull request #2555 from rstudio/wch-fix-invalidatelater-leak
Fix invalidateLater memory leak
2019-08-26 13:51:34 -04:00
Winston Chang
71fe821ae9 Update NEWS 2019-08-26 11:28:45 -05:00
Winston Chang
3ffab69ad6 Register shiny's knit_print methods on load 2019-08-26 11:28:19 -05:00
Winston Chang
58a662bd35 Merge branch 'master' into unexport-knit-print 2019-08-26 11:04:40 -05:00
Barret Schloerke
eb55c256c7 only send requried information across wire to browser for showcase mode 2019-08-26 10:52:27 -04:00
Winston Chang
b07e553b9e Merge pull request #2557 from rstudio/barret-upgrade-bootstrap-jqui
Upgrade Bootstrap (v3.4.1) and jQuery (v3.4.1)
2019-08-23 12:19:16 -05:00
Winston Chang
2d61709de3 Update reactiveValuesToList documentation 2019-08-23 11:48:29 -05:00
Barret Schloerke
1352e1d92d move news item to library updates and state 'resolved' 2019-08-23 12:47:40 -04:00
Barret Schloerke
b595c3b902 update htmldeps versions for jquery and bootstrap 2019-08-23 12:47:20 -04:00
Barret Schloerke
76efb01c4c add news item about upgrading bootstrap and jquery versions 2019-08-23 11:06:09 -04:00
Winston Chang
0078945b79 Fix link 2019-08-23 10:00:55 -05:00
Barret Schloerke
70d8ef0b8e update license info for bootstrap and jquery 2019-08-23 11:00:06 -04:00
Barret Schloerke
9a1f7cba68 change jquery v1.12.4 -> v3.4.1 2019-08-23 10:37:21 -04:00
Barret Schloerke
39e14acffe change bootstrap v3.3.7 -> v3.4.1 2019-08-23 10:36:16 -04:00
Alan Dipert
a6149390a0 Fix selectInput/selectizeInput handling character(1) options (#2540)
* selectInput/selectizeInput: Fix handling of character(1) choices

* Re-document

* Add .github to .Rbuildignore

* Expand comment, don't import stats

* Add test for 013-selectize regression

* Expand comment

* Split listify into series of passes

* Thouroughly overhaul and comment choicesWithNames()

* No recursion

* Comment new "flat" choice processing machinery

* Remove unneccesary test of choice tree with depth > 2

* Test for choices idiomatically

* Tweak comment for asCharacter

* Comment odd test, add a new test for single-item list

* Handle empty non-lists correctly, add test

* Add test ensuring empty lists come back named

* Add comment/assertion stipulating processGroupedChoices() takes only lists
2019-08-22 21:23:38 -05:00
Carson Sievert
33cdc75810 Throw an informative warning if a subdirectory of www clashes with another static path (#2434)
* Throw an informative warning if a subdirectory of www clashes with another static path, fixes #2433

* check all pairwise combinations of resource mappings

* Check for www subdir conflicts at startApp() time

* fix warning message

* review feedback; update news
2019-08-22 16:58:41 -05:00
Winston Chang
13f229089d Merge pull request #2459 from rstudio/resources
Introduce removeResourcePath()
2019-08-22 16:13:13 -05:00
Winston Chang
2dbb0fca85 Merge branch 'master' into resources 2019-08-22 16:12:58 -05:00
Winston Chang
c7a8a4e30f Update NEWS 2019-08-22 16:05:08 -05:00
Winston Chang
dc6f1a0c10 Fixes for R CMD check 2019-08-22 15:21:04 -05:00
Winston Chang
178872d651 Update fastmap version dependency 2019-08-20 13:23:55 -05:00
Barret Schloerke
e3c15493a2 Merge pull request #2545 from rstudio/barret-bug-dynamic-tab
Fix selected dynamic tab in deployed environments
2019-08-19 14:35:31 -04:00
Barret Schloerke
3f22e5da2d add news item 2019-08-19 14:24:42 -04:00
Winston Chang
39ee4513c6 Fix invalidateLater memory leak. Closes #2267 2019-08-19 12:21:12 -05:00
Barret Schloerke
598898f0a1 use boostrap url stripper regex to remove url before looking for relative tag location 2019-08-15 11:29:50 -04:00
trestletech
052e783638 Update to new signature in test. 2019-08-14 14:35:19 -05:00
trestletech
d2deda238a Move global.R sourcing into an exported load function 2019-08-14 14:25:05 -05:00
trestletech
7317a8304f Only load top-level R files in R/
Ignores nested directories to better follow R package conventions. We want to align well so that this structure is portable to golem.
2019-08-12 15:03:50 -05:00
trestletech
5ea9d70fb4 Require capitalized R/ dir. 2019-08-12 14:59:16 -05:00
Jeff Allen
a73e0998bc Correct mistake around app.R in global 2019-08-09 16:22:03 +00:00
trestletech
51befe3e27 Add news
and other minor changes from self-review
2019-08-08 15:55:08 -05:00
trestletech
37569a291b Fix options test 2019-08-08 14:48:19 -05:00
trestletech
7fe973145d Test ui/server/app/global sourcing. 2019-08-08 11:44:01 -05:00
trestletech
da3fc276fd Revert "DI the source function for testing."
This reverts commit c2dfea18c4.
2019-08-08 09:35:07 -05:00
trestletech
4c0af8b1c0 Revert "Break master 😈"
This reverts commit f65f7b2f1b.
2019-08-07 15:42:38 -05:00
trestletech
f65f7b2f1b Break master 😈
This reverts commit 545b6c1247.
2019-08-07 15:29:50 -05:00
trestletech
33c86ed6a7 Encrypt with --org
https://github.com/travis-ci/travis-ci/issues/7837
2019-08-07 14:55:44 -05:00
trestletech
545b6c1247 Revert "Break master 😈"
This reverts commit 1b0e37f371.
2019-08-07 14:09:05 -05:00
trestletech
1b0e37f371 Break master 😈 2019-08-07 14:06:55 -05:00
trestletech
97e00721e9 Add Travis Slack notifications on fail 2019-08-07 13:46:19 -05:00
Barret Schloerke
3c43301edb remove print statement 2019-08-07 12:45:20 -04:00
Barret Schloerke
51cbb67a96 use new RegExp 2019-08-07 12:21:38 -04:00
Barret Schloerke
2e2bd80416 remove leading url when removing relative url 2019-08-07 11:06:03 -04:00
Barret Schloerke
86389ff7a3 add print to debug appendTab 2019-08-07 10:44:59 -04:00
trestletech
c2dfea18c4 DI the source function for testing. 2019-08-06 14:08:25 -05:00
trestletech
4be6bbc681 Load helpers into isolated environment
And scaffold out the tests.
2019-08-06 10:49:38 -05:00
trestletech
cfc0ff9cc7 Fix expectations. 2019-08-06 09:10:18 -05:00
trestletech
b4c6ba6962 Add dynamically-generated case-sensitive test. 2019-08-06 09:05:02 -05:00
trestletech
dc3ed2f79b Support case-agnostic r/ dir loading 2019-08-05 17:33:07 -05:00
trestletech
5d95c7a9cb Load helpers in R/ on app startup
No support yet for case-sensitive file systems when loading the dir.
2019-08-05 17:25:14 -05:00
Alan Dipert
6821ca6238 Merge pull request #2543 from rstudio/buildignore-github
Add .github to .Rbuildignore
2019-08-05 14:32:20 -07:00
Alan Dipert
167dc0a259 Add .github to .Rbuildignore 2019-08-05 20:45:27 +00:00
Winston Chang
fa9fa68693 Re-document 2019-08-05 15:22:12 -05:00
Winston Chang
353615da89 Remove fastmap from Remotes because it is on CRAN 2019-07-29 11:01:01 -05:00
Winston Chang
51de558675 Rebuild JS objects 2019-07-26 10:41:21 -05:00
Winston Chang
174fc1dda1 Update JS build dependencies 2019-07-26 10:41:07 -05:00
Carson Sievert
7caeb60c47 add some historical context in the comment 2019-07-15 16:53:51 -05:00
Carson Sievert
e3aba1b5ff Introduce removeResourcePath() & throw message if the local path of a resource path has changed 2019-07-15 16:53:51 -05:00
Alan Dipert
1a8b36f06d selectInput: improve factor handling (#2524)
* selectInput: handle factor choices, fixes #2515

* Handle complex vectors

* Improve tests

* New item

* Update selectInput() docs to mention factors

* Un-S3-ify listify

* Bracketify the if/else

* Moar Brackets

* Fix travis: we hope

* Use existing asNamedVector function

* Better implementation of asNamedVector

* Clarify comments
2019-07-15 14:51:13 -05:00
Winston Chang
250303790c Bump version to 1.3.2.9001 2019-07-09 13:56:39 -05:00
Winston Chang
e20544659a Merge pull request #2523 from rstudio/joe/feature/readable-outputs
Support reading of Shiny outputs
2019-07-09 12:19:54 -05:00
Joe Cheng
ad7692ed34 output$xxx should return the actual func passed in
...not the frankenstein one we create to clean up the stack trace
2019-07-06 13:20:44 -07:00
Joe Cheng
f9144a4be3 Make output reading compatible with modules 2019-07-05 19:22:41 -07:00
Winston Chang
0fc3b90efb tham -> than 2019-07-03 20:52:15 -05:00
Winston Chang
25ccc8a77a Merge pull request #2484 from rstudio/weakref
Use weakrefs for reactive value to reactive expression dependencies
2019-07-03 20:49:33 -05:00
Winston Chang
da18390f3e Import and re-export fastmap::key_missing (#2517)
* Import and re-export fastmap::key_missing

* Fix for staticdocs index
2019-07-03 15:45:42 -05:00
Winston Chang
b392bf8298 Update rlang dependency info 2019-07-03 15:37:16 -05:00
Joe Cheng
73c42ebeaf Merge pull request #2516 from rstudio/fix-shinyapp-doc
Remove outdated information for shinyApp()
2019-07-03 10:41:28 -07:00
Winston Chang
eb45f7fcba Remove outdated information for shinyApp() 2019-06-28 22:31:29 -05:00
Jeff Allen
5fdca29448 Clarify interaction between width/height and CSS/templates (#2504)
* Clarify interaction between width/height and CSS/templates

* Reword given that height is less likely to be specified in CSS.
2019-06-25 10:56:57 -05:00
Jeff Allen
048c4006e4 en dashes -> em dashes (#2513) 2019-06-25 10:46:52 -05:00
Jeff Allen
6d10a2dafb Merge pull request #2510 from rstudio/jeff-md
Convert docs to MD
2019-06-25 13:53:07 +00:00
trestletech
d6c421f8de Fix more hyperlinks 2019-06-20 12:03:56 -05:00
trestletech
ac4adcc62c Fix shinyOptions hyperlinking 2019-06-20 11:51:45 -05:00
trestletech
bc8465d284 Commit generated MD with new links, not manually reviewed. 2019-06-19 15:50:47 -05:00
trestletech
7fc497eeb8 Auto-generated link conversation, not manually reviewed. 2019-06-19 15:46:13 -05:00
trestletech
633817e3d5 Manually escape another % that wasn't getting rendered properly. 2019-06-19 15:43:16 -05:00
trestletech
09dee9670a Manually escape one % sign that wasn't getting escaped? 2019-06-19 15:42:24 -05:00
trestletech
631debbec4 Restore one \code{} block that contains inner backticks 2019-06-19 15:38:47 -05:00
trestletech
4e57bc2161 Fix syntax error with double-backticks. 2019-06-19 15:32:01 -05:00
trestletech
a111e36867 Accept harmless RD changes. 2019-06-19 15:30:55 -05:00
trestletech
152bd5841c Accept whitespace-only RD changes. 2019-06-19 15:29:54 -05:00
trestletech
ecefdcd951 Convert R to MD
Used roxygen2md::roxygen2md(scope="simple")

Not manually reviewed.
2019-06-19 15:28:03 -05:00
trestletech
a976cfa98d Remove escaping for % in preparation for MD conversion
Obtained by running `sed -i "" -E "s/^(.*)\\\%(.*)$/\1%\2/g" *` in the R
directory on a Mac.
2019-06-19 15:13:25 -05:00
trestletech
df70d7708d Result of running roxygen2md(scope = "none")
Enables markdown. Only observed one non-whitespace difference on a line
that had used backticks previously which were previously not being
parsed as a code block.

-explicitly using the `title` parameter of the top-level page function.
+explicitly using the \code{title} parameter of the top-level page function.
2019-06-19 14:47:04 -05:00
Jeff Allen
c558d95e3b Merge pull request #2507 from rstudio/jeff-ci-docs
Test for Roxygen doc & JS changes in Travis
2019-06-19 19:24:11 +00:00
trestletech
3bd4825d4b Write errors to stderr. 2019-06-19 11:34:59 -05:00
trestletech
67cdcedd4e Better bash 2019-06-19 11:04:39 -05:00
trestletech
384116a76f Specify node version 2019-06-19 10:22:55 -05:00
trestletech
12e91ae643 Script to check for JS. 2019-06-19 10:09:21 -05:00
trestletech
7c56d277da Try matrix build for Roxygen check. 2019-06-18 20:20:25 -05:00
trestletech
579a4592b8 Test for Roxygen doc changes in Travis
I'd prefer to do the doc check prior to the package check so that we can
fail fast in light of trivial errors, but I worry about the side-effects
of installing devtools and roxygen2 on our tests, so I'm punting those
tasks until after our CMD check.

It may be possible to parallelize this work adjacent to our package
check (and only do it on one version of R rather than all three), but I
haven't explored that yet.

Failures in the `after_script`s don't fail the build, surprisingly. The
`|| travis_terminate 1` accomplishes that.  `travis_terminate` taken
from
https://github.com/travis-ci/travis-ci/issues/1574#issuecomment-164094347

`git clean` incantations found in
https://issues.jenkins-ci.org/browse/JENKINS-31924
2019-06-18 16:48:22 -05:00
Jeff Allen
9dad5e6362 Define optgroup when using (#2502) 2019-06-18 16:06:29 -05:00
Jeff Allen
f51b5421f2 Add sep argument to renderText (#2497)
* Add sep argument to renderText

Closes #2469

* Add link to PR

* Regenerate docs
2019-06-14 16:26:28 -05:00
Winston Chang
387907ea32 Upgrade JS build dependencies 2019-06-14 16:18:53 -05:00
trestletech
b5ca1d48e0 Add link to renderCachedPlot.
Closes #2476
2019-06-14 14:22:49 -05:00
trestletech
396f170738 Bundle deprecated reactive functions into a single file. 2019-06-14 14:20:31 -05:00
Jeff Allen
5514039d42 M0ar README words
Give a bit more detail about validating install, and avoid calling things easy.
2019-06-14 14:18:44 -05:00
Winston Chang
c90c4f3673 Suppress stack traces in tests 2019-06-14 10:59:45 -05:00
Winston Chang
41758858cf Fix react logging for reactiveValues 2019-06-14 10:58:50 -05:00
Joe Cheng
3ff507e6b8 Merge pull request #2493 from rstudio/jeff-internal-docs
Mark deprecated functions as internal
2019-06-13 14:34:03 -07:00
trestletech
5199371025 Mark deprecated functions as internal.
This prevents them from being listed in the documentation index. Closes #2482.
2019-06-13 15:47:20 -05:00
Winston Chang
26ad773f77 Switch from fastmap to rlang for weakref functions 2019-06-13 14:52:14 -05:00
Winston Chang
9e133e0ecc More memory leak tests 2019-06-11 20:21:16 -05:00
Winston Chang
c4e7099229 Reactive expressions keep reference to context 2019-06-11 19:19:52 -05:00
Winston Chang
56062628f2 Use Dependents in ReactiveValues 2019-06-11 19:19:31 -05:00
Winston Chang
48a3a1dabb Use weak references for dependents of reactive values 2019-06-10 20:43:28 -05:00
Winston Chang
ca3c2b3e26 Use weak references for reactive contexts 2019-06-06 13:35:56 -05:00
Winston Chang
d35c5c8320 Merge pull request #2479 from rstudio/wch-consistent-reactive-order
Ensure that observers fire in consistent order
2019-06-06 13:22:05 -05:00
Winston Chang
749a582296 Update NEWS 2019-06-04 16:12:12 -05:00
Winston Chang
6310406430 Add tests for observer order 2019-06-04 16:07:04 -05:00
Winston Chang
d26d339f97 Ensure that dependents are sorted 2019-06-04 14:02:36 -05:00
Winston Chang
17afce6fa1 Merge pull request #2429 from rstudio/wch-fastmap
Use fastmap as backing store for Map class
2019-05-31 15:39:34 -05:00
Joe Cheng
d3aa601798 Make Shiny outputs (optionally) readable 2019-05-31 09:25:27 -07:00
Winston Chang
8f24d667d6 Unquote key 2019-05-30 15:12:15 -05:00
Winston Chang
5cd4588ef2 Use grep(value=TRUE) 2019-05-30 14:38:05 -05:00
Winston Chang
b0a1821d95 Rebuild shiny.js 2019-05-30 14:32:52 -05:00
Winston Chang
6b835f70e6 Merge pull request #2460 from rstudio/wch-disable-plot-drag
Disable dragging of plots with any interactions enabled
2019-05-30 14:32:00 -05:00
Winston Chang
308bc76ac6 Disable dragging of plots with any interactions. Closes #1393, #2223 2019-05-29 15:16:11 -05:00
Winston Chang
fd843509a1 Fix NEWS formatting 2019-05-29 11:59:13 -05:00
Winston Chang
7691cfdadb Merge pull request #2446 from nteetor/master
New target for `shiny:inputchanged` event
2019-05-29 11:57:55 -05:00
nteetor
1aa9368e54 Update inputchanged news item with pr number, move to improvements 2019-05-23 20:11:44 -04:00
nteetor
180e852fee Trigger shiny:inputchanged event on related input element (#2442) 2019-05-22 20:01:33 -04:00
Alan Dipert
547edd7e32 Fix feature request template 2019-05-21 11:14:02 -07:00
Winston Chang
0b46c63c31 Fix testthat version number 2019-05-16 16:43:01 -05:00
Carson Sievert
9b69ce1988 yarn build 2019-05-14 16:44:33 -05:00
Carson Sievert
57cc44f662 Coordmap info should retain discrete limits (#2410)
* ggplot2 input brushes should retain discrete range mapping, and be imposed in brushedPoints(), closes #1433

* simplify logic and reduce required storage

* get nearPoints() working as well, cleanup

* only remember scale range if ggplot is facet with a free discrete axis

* Use the scale limits (before the range) since the former is specified, that's what is actually shown on the plot

also, introduce within_brush() helper to consistently handle missing values produced by asNumber()

* also use scale limits in older versions of ggplot2

* DRY

* discrete_mapping -> discrete_limits; better comments

* update test expectation

* a couple unit tests

* update comment to reflect new coordmap data structure

* use unlink() not rm()

* add some tests for specifying scale limits and labels

* Use get_limits() if available

* update news

* better name and comment for new asNumber() argument
2019-05-14 16:34:00 -05:00
Carson Sievert
4eaa9c7ea9 Don't match text inputs with a trailing '-selectized' in their id, fixes #2396 (#2418)
* Don't match text inputs with a trailing '-selectized' in their id, fixes #2396

* update news

* parentheses
2019-05-14 16:26:32 -05:00
Winston Chang
0b6cdcc826 fastmap moved to r-lib 2019-05-14 11:46:57 -05:00
Winston Chang
7bc0a0ca39 Fix tests that assumed names in a specific order 2019-05-14 10:41:06 -05:00
Winston Chang
1ef2074a10 Fastmap objects can now be saved and loaded 2019-05-14 10:36:37 -05:00
Winston Chang
0747b2a72a fastmap: exists() was renamed to has() 2019-05-14 10:35:50 -05:00
Alan Dipert
64b3095f2c Removed redundant section of issue template 2019-05-10 10:07:30 -07:00
Winston Chang
ab82af122f Merge pull request #2436 from rstudio/alan/issue-templates
Add issue templates
2019-05-10 12:05:35 -05:00
Alan Dipert
54fccf2e7c Incorporate feedback from @wch 2019-05-10 10:01:03 -07:00
Alan Dipert
05e953db3a Improve bug report template 2019-05-10 08:28:05 -07:00
Alan Dipert
f726835850 Add issue templates 2019-05-09 23:04:19 -07:00
Winston Chang
38d2809131 Convert MemoryCache to use fastmap 2019-05-09 10:20:33 -05:00
Winston Chang
d7718991a6 Import fastmap::fastmap 2019-05-09 10:20:33 -05:00
Winston Chang
32c2bff6eb Convert ReactiveValues$.metadata to use Map 2019-05-09 10:20:33 -05:00
Winston Chang
555ede03ed Convert ReactiveValues$.values to use Map 2019-05-08 20:33:52 -05:00
Winston Chang
2a6f218700 Convert ReactiveValues$.dependents to use Map 2019-05-08 20:33:52 -05:00
Winston Chang
b087c19b52 Use fastmap as backing store for Map class 2019-05-08 20:33:52 -05:00
Carson Sievert
6fed1c60ac update news (should've been done in #2404) 2019-05-08 16:36:46 -05:00
Carson Sievert
b10f2a5291 yarn build 2019-05-08 16:30:59 -05:00
Winston Chang
a4a49a354e Merge pull request #2404 from rstudio/inputRateName
Fix issue with input rate policies
2019-05-08 16:24:02 -05:00
Carson Sievert
ead23528ca doSetInput calls setInput (duh) so should have name and type 2019-05-08 16:19:21 -05:00
Carson Sievert
b8644949cc camelCase for consistency; clarify comment 2019-05-08 16:19:13 -05:00
Carson Sievert
b88e3a64f2 comment on the difference between name_type and name 2019-05-08 16:19:07 -05:00
Carson Sievert
2871b423fd rename name arg to name_type where relevant in input decorators...
this will help to highlight when you should call a method with just the input name instead of both the name and the type
2019-05-08 16:19:02 -05:00
Carson Sievert
562fafbc39 pass inputName to immediateCall() and normalCall() 2019-05-08 16:18:55 -05:00
Carson Sievert
191e0874f8 type is only relevant for public methods setInput() and setRatePolicy()
change the name of these arguments to reflect this (name_type)
2019-05-08 16:18:47 -05:00
Carson Sievert
fa5ff7bfa5 Consistently ignore input type in all InputRateDecorator methods 2019-05-08 16:18:39 -05:00
Carson Sievert
82e80ccdeb InputRateDecorator's setInput method needs to strip of the input's ttype before looking up the input's rate policy, closes #2387 2019-05-08 16:18:19 -05:00
Carson Sievert
ff84cf5a18 update news (#2428) 2019-05-08 16:08:20 -05:00
Winston Chang
44843a7768 Merge pull request #2406 from rstudio/null-label
Input label updating
2019-05-08 15:52:09 -05:00
Carson Sievert
68eeb338da Have input labels always include 'control-label' class 2019-05-08 15:15:09 -05:00
Carson Sievert
ea54c17902 merge with master 2019-05-08 15:10:22 -05:00
Barret Schloerke
d5ad7eed40 Merge pull request #2424 from rstudio/joe/bugfix/reactive-value-not-changing
Fix rstudio/reactlog#36: Changes to reactive values not displaying accurately
2019-05-08 12:01:08 -04:00
Joe Cheng
c2430cd3f4 Update NEWS 2019-05-07 09:33:59 -07:00
Joe Cheng
8a0731493f Fix rstudio/reactlog#36: Changes to reactive values not displaying accurately 2019-05-07 09:30:59 -07:00
Carson Sievert
07e2b80b5d merge with master; fix NEWS conflicts 2019-05-03 17:21:06 -05:00
Carson Sievert
1311e1fca2 have class come before the for attribute 2019-05-03 17:16:17 -05:00
Winston Chang
e6c2133520 Merge pull request #2416 from rstudio/updateSliderInput
getSliderType() should be able to handle NULL min/max/value, fixes #2250
2019-05-03 16:54:57 -05:00
Carson Sievert
3d6f734ff2 update comment 2019-05-03 15:58:45 -05:00
Carson Sievert
e0eaa58779 update news 2019-05-03 15:57:55 -05:00
Carson Sievert
ced6622b25 Have getSliderType() return '' early if min, max, and value are NULL 2019-05-03 15:54:48 -05:00
Carson Sievert
2d2cf96f5e missed input_binding_slider.js 2019-05-03 15:48:47 -05:00
Carson Sievert
370f1b51ee Inputs now always supply a <label> tag with a special CSS class for hiding NULL labels
This helps to simplify the updating logic on the client
2019-05-03 15:38:57 -05:00
Winston Chang
67d3a504ae Merge pull request #2403 from rstudio/dateFormat
Consistent approach to coercing and formatting date strings
2019-05-03 10:58:06 -05:00
Carson Sievert
34ee48ef93 update news 2019-05-02 10:46:08 -05:00
Carson Sievert
c61a585e79 getSliderType() should be able to handle NULL min/max/value, fixes #2250 2019-05-01 19:41:26 -05:00
Carson Sievert
09388c9f07 Apply label updating logic all relevant input labels 2019-05-01 18:55:36 -05:00
Carson Sievert
b1bc78dad3 fix news link 2019-05-01 11:19:40 -05:00
Carson Sievert
a5a0f23c3a Use jQuery's text() method for proper escaping when inserting data.label string 2019-04-30 17:33:26 -05:00
Carson Sievert
4c50c064d3 make return value of dateYMD() slightly more clear 2019-04-30 17:08:13 -05:00
Carson Sievert
a63f271300 update news 2019-04-30 17:00:34 -05:00
Carson Sievert
08b22ff550 update NEWS 2019-04-30 17:00:34 -05:00
Carson Sievert
b04133bf65 Include argName with warning when length > 1 2019-04-30 17:00:34 -05:00
Carson Sievert
3602358d2c fix typo in warning message 2019-04-30 17:00:34 -05:00
Carson Sievert
67b0416eba Throw informative warning if date coercion fails and original input 2019-04-30 17:00:34 -05:00
Carson Sievert
f8d69ecb1f Consistent approach to coercing and formatting date strings, closes #2402 2019-04-30 17:00:34 -05:00
Carson Sievert
5e8bc204c1 make sure to remove label tag from DOM if label is updated to NULL 2019-04-26 19:08:15 -05:00
Carson Sievert
938332d646 Have textInput()'s receiveMessage method insert a label tag if one is needed, closes #868 2019-04-26 16:56:53 -05:00
Winston Chang
386078d441 Merge tag 'v1.3.2' 2019-04-23 14:07:37 -05:00
Winston Chang
4d778faaf4 Bump version to 1.3.2 2019-04-18 11:51:16 -05:00
Winston Chang
3055cf5602 Update NEWS 2019-04-18 11:49:09 -05:00
Joe Cheng
36373ba28b Merge pull request #2386 from rstudio/joe/bugfix/subapp-routing
Fix #2385: R Markdown documents containing subapps not rendering properly
2019-04-18 08:49:56 -07:00
Joe Cheng
1415b57181 Add sys.www.root to createAppHandlers, so that subapps can access /shared/* 2019-04-16 18:40:29 -07:00
Joe Cheng
65d4a4e906 Add comments 2019-04-16 18:12:09 -07:00
Joe Cheng
0abe221227 Use v1.3.1.9000 2019-04-14 17:21:26 -07:00
Joe Cheng
1b8d822226 Fix #2385: R Markdown documents containing subapps not rendering properly 2019-04-14 17:19:17 -07:00
Winston Chang
bc8fbd60d7 Bump version to 1.3.1.9000 2019-04-12 11:13:32 -05:00
Winston Chang
4c332eac9a Merge tag 'v1.3.1'
Shiny 1.3.1 on CRAN
2019-04-12 11:12:59 -05:00
Joe Cheng
f5392d77dc Merge pull request #2382 from rstudio/fix-index-html
Fix serving of www/index.html
2019-04-11 11:43:44 -07:00
Winston Chang
1e88990a0b Fix serving of www/index.html. Closes #2380 2019-04-11 11:57:48 -05:00
Joe Cheng
de4c7567d0 Manually bump the version numbers in shiny.js and shiny.min.js
Normally this would be where we grunt, but for this hotfix we
need to avoid the changes that went in at the end of 1.3.0 that
were accidentally left out of the built JS.
2019-04-10 11:42:42 -07:00
Joe Cheng
aff33dd023 Bump version to 1.3.1 2019-04-10 11:35:05 -07:00
Barret Schloerke
a287ebe324 Minimize str usage in rlog$valueStr (#2377)
* return early if loggin is disabled

* do not allow str to recurse

* add news item for #2377

* change "  " to " "

* Not a "world-ending performance issue"
2019-04-10 11:27:29 -07:00
Winston Chang
583a8d1001 Merge pull request #2353 from rstudio/fix-verbatim-text-wrap
Don't wrap text in verbatimTextOuput in Safari
2019-04-08 16:20:18 -05:00
Winston Chang
36a808add0 Update NEWS 2019-04-08 13:22:57 -05:00
Winston Chang
f651d4a274 Don't wrap text in verbatimTextOuput in Safari. Closes #2233 2019-04-08 13:20:20 -05:00
Winston Chang
f6e8e645f2 Bump version to 1.3.0.9000 2019-04-08 13:19:57 -05:00
Winston Chang
b4d2f88b74 Merge tag 'v1.3.0'
Shiny v1.3.0 on CRAN
2019-04-08 12:01:05 -05:00
Winston Chang
c524a736bd Re-document 2019-03-29 17:25:14 -05:00
Winston Chang
cdf3bf18f0 Fix broken URL 2019-03-29 16:55:41 -05:00
Winston Chang
b21bdacb4f Remove reactlog from Remotes 2019-03-27 13:42:25 -05:00
Winston Chang
92019b5ba3 Merge pull request #2361 from rstudio/fix-svg-foreignobject
Fix #2348, #2329, #1817: bugs triggered by networkD3 sankey plot
2019-03-27 13:40:15 -05:00
Alan Dipert
908d635063 Fix #2349, #2329, #1817: bugs triggered by networkD3 sankey plot
* All of these were caused by the presence of multiple body tags on the
page, which happened because networkD3's sankey plot generates SVGs
containing body tags via SVG's foreignObject tag
* In various places, the 'body' jQuery selector string is used under the
assumption there is only one 'body' tag on the page. The presence of
multiple 'body' tags breaks reliant code in strange ways.
* The fix was to use document.body or 'body:first' instead of 'body'.
2019-03-27 11:36:19 -07:00
Alan Dipert
20329feb7f Improve bootstrap-datepicker update tools, add docs 2019-03-26 20:33:42 -07:00
Alan Dipert
4cd92a1cd9 Add 'Fix datepicker DST bug' as patch
- Original commit: 0683b79
2019-03-26 20:33:32 -07:00
Alan Dipert
8ca3397c5d Improve bootstrap-datepicker update script 2019-03-26 20:33:20 -07:00
Alan Dipert
05cd79481e Re-import bootstrap-datepicker 1.6.4 2019-03-26 17:34:47 -07:00
Winston Chang
c0f1905785 Remove httpuv and reactlog from remotes 2019-03-26 15:18:00 -05:00
Alan Dipert
9afc06028d Restore intuitive bookmarking behavior (#2360)
* Adding flushPending() to ShinySession's flushOutput() restores intuitive bookmarking behavior

* Check that restoreContext is present

* Update NEWS
2019-03-26 15:08:34 -05:00
Barret Schloerke
7b6cc50238 Merge branch 'master' into rc-v1.3.0
* master:
  fix shortString is NA or NULL logic
  add coverage for situation where label might be na or NULL
  increase default length of label to 250chars from 100chars
  make sure labels are short for reactlog
2019-03-01 15:45:19 -05:00
Barret Schloerke
722b1d0258 Merge pull request #2345 from rstudio/short_reactlog_labels
Shorter reactlog labels
2019-03-01 14:43:59 -06:00
Barret Schloerke
93d3b78ac1 fix shortString is NA or NULL logic 2019-03-01 15:22:40 -05:00
Barret Schloerke
69e82f6e0e add coverage for situation where label might be na or NULL 2019-03-01 14:57:05 -05:00
Barret Schloerke
1f83a6db7b increase default length of label to 250chars from 100chars 2019-03-01 14:34:21 -05:00
Barret Schloerke
8f37951e14 make sure labels are short for reactlog 2019-03-01 14:30:23 -05:00
Joe Cheng
e1f4d43926 Merge pull request #2342 from rstudio/reactlog-cran
Reactlog github location removed
2019-02-27 10:31:08 -08:00
Joe Cheng
eb6139276f Merge pull request #2343 from rstudio/fix-resource-path
addResourcePath: create staticPath object immediately. Fixes #2339
2019-02-27 10:27:09 -08:00
Winston Chang
f18c426151 addResourcePath: create staticPath object immediately. Fixes #2339 2019-02-27 11:12:55 -06:00
Barret Schloerke
e46debb6d1 remove github location for reactlog and clean up flow of check_suggested 2019-02-27 09:19:46 -05:00
Barret Schloerke
d8b8739cb8 use httpuv rc-v1.5.0 branch 2019-02-26 16:57:28 -05:00
Barret Schloerke
9fd8eefa59 Merge branch 'master' into rc-v1.3.0
* master:
  Make sure the is.na() check in %AND% looks for length-1 input
2019-02-26 16:21:24 -05:00
Barret Schloerke
fd2af06a53 run grunt 2019-02-26 16:21:01 -05:00
Barret Schloerke
48f945ba7f use reactlog rc-1.0.0 branch 2019-02-26 16:19:24 -05:00
Barret Schloerke
6d59f88a76 bump news and description versions to 1.3.0 2019-02-26 16:19:07 -05:00
Joe Cheng
8b94d4626d Merge pull request #2338 from rstudio/fix-and
Make sure the is.na() check in %AND% looks for length-1 input
2019-02-26 13:11:39 -08:00
Winston Chang
d7d8e78e42 Make sure the is.na() check in %AND% looks for length-1 input
This is to avoid errors with R CMD check on R-devel like this:
https://travis-ci.org/rstudio/shiny/jobs/498880293
2019-02-26 14:32:41 -06:00
Joe Cheng
9755f86f53 Merge pull request #2327 from rstudio/staticpath-exclude
Exclude "session" from static path serving
2019-02-26 12:17:30 -08:00
Winston Chang
599a3ee82f Simplify session placement 2019-02-26 13:02:23 -06:00
Winston Chang
c790346490 Merge pull request #2284 from chasemc/patch-1
Fix typo
2019-02-21 14:04:40 -06:00
Joe Cheng
68cf3a9111 Merge pull request #2311 from rstudio/bookmark-dot
Bookmarking: restore inputs that have a leading dot
2019-02-21 11:56:40 -08:00
Barret Schloerke
59221dfcf2 bump dev version of reactlog. remove reactlog::reactlog_add_shiny_resource_paths() 2019-02-15 15:52:05 -06:00
Winston Chang
020413a206 Always exclude /session from static paths 2019-02-15 15:08:26 -06:00
Winston Chang
a343e9ebdf Use excludeStaticPath() function 2019-02-14 21:15:20 -06:00
Winston Chang
c304efee36 Exclude "session" from static path serving. Fixes #2325 2019-02-12 20:28:47 -06:00
Winston Chang
95173f676d Merge pull request #2319 from rstudio/joe/misc/constant-time-check
Add constant time check for shared secret
2019-02-11 15:22:54 -06:00
Joe Cheng
87d1db1f2b Fix test 2019-02-11 10:02:40 -08:00
Barret Schloerke
d445f384c7 Merge pull request #2315 from rstudio/reactlogShow
Add methods: reactlog, reactlogShow, and reactlogReset. Deprecate showReactLog
2019-02-07 15:05:05 -06:00
Joe Cheng
59dd4b0721 Code review feedback
- Rename sharedSecret variables to checkSharedSecret
- Don't perform the digest::digest(). This just means the timing could
  give away the length of the secret, but that's OK, there's enough
  entropy in the secret even if you know its length.
2019-02-05 14:33:04 -08:00
Joe Cheng
d73c91d4a7 Add unit tests for shared secret check 2019-02-04 14:19:02 -08:00
Joe Cheng
665a66522e Add constant time check for shared secret 2019-02-04 13:19:47 -08:00
Barret Schloerke
ba1efa65fa update man file name to reactlog from showReactLog in inst/staticdocs/index.r 2019-02-01 16:29:03 -05:00
Barret Schloerke
64a74692b9 document time for reactlogShow 2019-02-01 16:05:33 -05:00
Barret Schloerke
46cd285dd0 update docs by removing showReactLog/reactlogShow (to reactlog) as much as possible 2019-01-30 16:01:22 -05:00
Barret Schloerke
bcac115c3d Add methods: reactlog, reactlogShow, and reactlogReset. Depricate showReactLog
Update links to help file to not use `showReactLog`, but `reactlogShow`
Use updated reactlog pkg function api of reactlog_*. This may fail right now, but rerun travis when the reactlog code is merged into master.
2019-01-30 12:20:22 -05:00
Winston Chang
77ddb2c8c2 Bookmarking: restore inputs that have a leading dot. Fixes #2308 2019-01-23 12:05:24 -06:00
Barret Schloerke
8ae31eb998 Merge pull request #2107 from schloerke/barret/reactlog
Upgraded reactlog logging and support for shinyreactlog rendering
2019-01-11 13:19:14 -05:00
Barret Schloerke
7551a6ae1d add stats:: to setNames function calls
helps pass R CMD check
2019-01-11 13:04:39 -05:00
Barret Schloerke
93be659b1b merge Remotes 2019-01-11 12:45:59 -05:00
Barret Schloerke
3327878fc2 merged from master 2019-01-11 12:33:31 -05:00
Winston Chang
0b25c7f3c1 Merge pull request #2280 from rstudio/static-file
Use httpuv static file serving
2019-01-11 10:56:52 -06:00
Barret Schloerke
b606ba4dd7 added news item for reactlog 2019-01-11 09:43:25 -05:00
Chase Clark
0269bc810c Fix typo
"...use the JavaScript library selectize.js (https://github.com/selectize/selectize.js) ~~ to~~ instead of the basic select..."
2018-12-17 12:59:57 -06:00
Barret Schloerke
f2775f2c1d update rLog$msg output tests 2018-12-14 16:18:20 -05:00
Barret Schloerke
f06274aec6 fixed bad argument placement 2018-12-14 16:01:25 -05:00
Barret Schloerke
dfa686a3e0 always display the first n chars in a rLog$valueChange or rLog$define
capture the value in a try statement of capture.output of str
2018-12-14 15:51:31 -05:00
Barret Schloerke
fe679b5de5 add reactId to rLog$invalidateLater 2018-12-14 15:49:45 -05:00
Barret Schloerke
aa1eb0410c add force option to retrieving reactive info 2018-12-14 14:10:34 -05:00
Barret Schloerke
1b06bab7ee add define observer to rLog 2018-12-14 12:05:37 -05:00
Barret Schloerke
0f13056aa2 fix rLog$reset to work as an installed package. added a dummy context reactId (different from noReactId) 2018-12-14 12:05:22 -05:00
Barret Schloerke
beecf60db7 use rLog$reset() instead of initializeReactlog() due to changing global binding error 2018-12-13 17:04:12 -05:00
Barret Schloerke
160a2013bc fix broken test 2018-12-13 16:52:44 -05:00
Barret Schloerke
b8c636e87e move the actual setting of the reactiveValues key higher in set command for accurate logging 2018-12-13 16:52:35 -05:00
Barret Schloerke
add40e5926 when calling rlog$define, set a value 2018-12-13 16:51:32 -05:00
Barret Schloerke
960e7f3b24 fix .globals binding issue 2018-12-13 16:50:13 -05:00
Barret Schloerke
3e749f36e8 turn off logging of value in console 2018-12-13 16:50:03 -05:00
Barret Schloerke
8198d99309 add rlog$invalidateLater(runningCtxId, millis, domain) 2018-12-13 14:49:27 -05:00
Barret Schloerke
81de1c8ed4 remove setLabel from ReactiveValues 2018-12-13 14:42:25 -05:00
Barret Schloerke
3eb55e9d9b update reactiveValues set comments 2018-12-13 14:34:58 -05:00
Barret Schloerke
6b6ac86aea async start stop rLog should use domain = self 2018-12-13 14:33:29 -05:00
Barret Schloerke
1b45e70cbb use rLog$noReactId constant 2018-12-13 14:32:40 -05:00
Barret Schloerke
929f7ec235 document 2018-12-13 13:26:49 -05:00
Barret Schloerke
cf28d7e470 init testing for msg logging 2018-12-13 13:26:42 -05:00
Barret Schloerke
b0a00108f3 log, action, then perform invalidate action 2018-12-13 13:26:26 -05:00
Barret Schloerke
01151fc7f8 dummy context should be created every time. allow for id to be passed in 2018-12-13 13:26:12 -05:00
Barret Schloerke
bf8dbc38c7 add a noReactId label and init rLog method 2018-12-13 13:25:53 -05:00
Barret Schloerke
ae0d4d9353 add a default reactId for contexts for clearer msg logs and rLogs 2018-12-13 10:48:32 -05:00
Barret Schloerke
43ec4ae238 add helper functions for msg logger. 2018-12-13 10:48:02 -05:00
Barret Schloerke
c568a8cabe when updating a value for reactVal or a reactValues key, the context should not be recorded 2018-12-13 10:46:12 -05:00
Barret Schloerke
423bdd8b6b read reactlog version from description file 2018-12-12 11:27:17 -05:00
Barret Schloerke
1e19ff65e6 fix bad comma usage 2018-12-12 11:04:36 -05:00
Barret Schloerke
a9cf632f53 markTime -> userMark; queueEmpty -> idle 2018-12-12 10:58:28 -05:00
Barret Schloerke
fddf94a341 this check is already covered 2018-12-11 17:23:22 -05:00
Barret Schloerke
203168d261 dec/increment with integers 2018-12-11 17:23:13 -05:00
Barret Schloerke
0e3c3536f8 no need to store messages 2018-12-11 17:22:59 -05:00
Barret Schloerke
45b2b7e24f use curly brackets for all function defs 2018-12-11 17:22:33 -05:00
Barret Schloerke
88f177b065 use class brackets for R6 def 2018-12-11 17:22:09 -05:00
Barret Schloerke
ea7a8dd3ad consistent naming 2018-12-11 17:14:30 -05:00
Barret Schloerke
dda8f92494 remove writeReactLog 2018-12-11 17:13:09 -05:00
Barret Schloerke
26211802cd spelling and comments 2018-12-11 17:12:59 -05:00
Barret Schloerke
b4bef0d32c use reactlog::reactlog_add_shiny_resource_paths 2018-12-11 17:12:38 -05:00
Winston Chang
a8bf203067 Grunt 2018-12-10 14:34:27 -06:00
Winston Chang
624dd2e99d Bump version to 1.2.0.9001 2018-12-10 14:19:48 -06:00
Barret Schloerke
26a136a6e8 check_suggested now takes a github location and a source install script 2018-12-04 14:38:14 -05:00
Winston Chang
2d57ffa546 Update NEWS 2018-12-03 12:13:54 -06:00
Winston Chang
428b81a6d9 Use httpuv master branch 2018-12-03 12:10:04 -06:00
Barret Schloerke
f24c12fdfb shinyreactlog -> reactlog 2018-11-30 16:02:00 -05:00
Barret Schloerke
9a345d191b merge in master 2018-11-27 10:33:11 -05:00
Winston Chang
fec706d134 Add headers for static serving 2018-11-20 12:25:46 -06:00
Winston Chang
c338448997 Use shiny-shared-secret validation for static files 2018-11-20 12:25:46 -06:00
Winston Chang
956c1cb1a7 Use setStaticPath instead of setStaticPaths 2018-11-20 12:25:46 -06:00
Winston Chang
8831b4da9e Use static serving for app's own assets 2018-11-20 12:25:46 -06:00
Winston Chang
f8bd60dcd7 Use httpuv static serving 2018-11-20 12:25:46 -06:00
Winston Chang
6a373b585c Merge pull request #2248 from rstudio/fix-selectize-label
Make updateSelectizeInput() work with labels again
2018-11-15 17:04:16 -06:00
Winston Chang
54480e2510 Merge branch 'master' into fix-selectize-label 2018-11-15 17:03:58 -06:00
Joe Cheng
83f73603db Merge pull request #2257 from colearendt/fix-htmltools-dep
fix dependency version since htmltools 0.3.6 is used
2018-11-15 14:39:37 -08:00
Joe Cheng
2b10f192ba Merge pull request #2261 from rstudio/joe/bugfix/async-rendercachedplot
Fix #2247: Async cached plots raise "Error in !: invalid argument type" error
2018-11-15 14:39:07 -08:00
Winston Chang
775d5289cb Grunt 2018-11-15 15:23:35 -06:00
Winston Chang
e6c66352a7 Update NEWS 2018-11-15 15:23:35 -06:00
Winston Chang
77afd73ee1 Use new selectize suffix. Fixes #2245 2018-11-15 15:23:35 -06:00
Winston Chang
5ac96a40aa Remove QuitChildProcessesOnExit: Default option 2018-11-15 15:10:54 -06:00
Winston Chang
2fea0e2598 Don't byte-compile when doing local install in RStudio 2018-11-15 15:08:49 -06:00
Joe Cheng
2b64949cbe Fix #2247: Async cached plots raise "Error in !: invalid argument type" error 2018-11-14 16:45:40 -08:00
Cole Arendt
918d57f25e fix dependency version since htmltools 0.3.6 is used 2018-11-11 15:04:44 -05:00
Joe Cheng
5e2b40d3a9 Bump version for development 2018-11-02 13:11:04 -07:00
Joe Cheng
979ef4bd43 Merge remote-tracking branch 'origin/v1.2-rc' 2018-11-02 13:10:48 -07:00
Winston Chang
6ede0194c6 Update license information in README 2018-10-25 12:15:57 -05:00
Barret Schloerke
9963ba6cf5 merge master 2018-09-18 12:26:57 -04:00
Barret Schloerke
21ff005c1a remove display param from MessageLogger 2018-09-18 10:59:31 -04:00
Barret Schloerke
206b9135f1 if reactlog console option is set, display, or display is display is true 2018-09-18 10:24:48 -04:00
Barret Schloerke
5449de1a67 use shinyreactlog pkg directly 2018-09-18 10:24:17 -04:00
Barret Schloerke
47c61756e6 log create context with srcref and srcfile 2018-09-18 10:24:01 -04:00
Barret Schloerke
f28900f8ca merged master 2018-09-10 12:50:42 -04:00
Barret Schloerke
e0c15c42d7 do not depend on null reactid values 2018-09-05 10:40:45 -04:00
Winston Chang
a6dade846e Un-export knit_print methods from htmltools 2018-08-23 15:51:53 -05:00
Barret Schloerke
facef1d23c do not set shiny.reactlog option by default 2018-07-09 22:23:28 -04:00
Barret Schloerke
cdb446375c turn all active isLogging bindings into functions 2018-07-09 22:22:49 -04:00
Barret Schloerke
6f7b2887aa fix parameters for shinyreactlog (session_token) 2018-07-06 11:58:37 -04:00
Barret Schloerke
bc8ae063dd add new option for shinyreactlog messages in the console 2018-07-06 11:58:16 -04:00
Barret Schloerke
003dc39d76 add shinyreactlog as remote 2018-06-22 10:49:57 -04:00
Barret Schloerke
0b04c28011 move renderReactLog calculation above addResourcePath 2018-06-21 16:54:23 -04:00
Barret Schloerke
31854ad9e8 add reactlog resource path when calling for reactlog 2018-06-21 15:24:17 -04:00
Barret Schloerke
4304e92f0d use self$ for all fn calls within rLog to avoid any conflicts 2018-06-21 15:23:33 -04:00
Barret Schloerke
44736cefbf allow for null context id in dependents 2018-06-21 15:22:46 -04:00
Barret Schloerke
a807449f28 remove old temp files 2018-06-21 15:22:16 -04:00
Barret Schloerke
ae9d38b59c remove old .graph methods and use shinyreactlog pkg for rendering 2018-06-21 10:31:12 -04:00
Barret Schloerke
05e50c1b98 use original yarn lock file 2018-06-20 15:54:07 -04:00
Barret Schloerke
e11004da7b remove _ignore folder 2018-06-20 15:51:17 -04:00
Barret Schloerke
97ee7b5d96 clean up tools readme to use yarn over global grunt-cli install 2018-06-20 15:47:16 -04:00
Barret Schloerke
6c6e2573aa remove a LOT of files in favor of github.com/schloerke/shinyreactlog
still need hooks to shinyreactlog pkg
2018-06-20 15:34:22 -04:00
Barret Schloerke
8992827f21 merged master 2018-06-20 14:11:22 -04:00
Barret Schloerke
893b9c1b38 merged master -> barret/reactlog 2018-06-19 09:24:39 -04:00
Barret Schloerke
6f8166ca0f add todo 2018-06-14 16:06:12 -05:00
Barret Schloerke
64db035d77 simplify colors
no active green or grey. input invalidate should be grey
2018-06-08 15:38:03 -04:00
Barret Schloerke
cb051e4254 less case "STRING" and more case OBJ.VALUE 2018-06-08 15:20:51 -04:00
Barret Schloerke
20e9c2901d reduced hoverStatusOnNodeIds arg requirements and internals 2018-06-08 15:17:33 -04:00
Barret Schloerke
ce4b391495 add more flow classes and remove $FlowExpectError 2018-06-08 15:16:43 -04:00
Barret Schloerke
7d932f5b18 fix searching on nodes 2018-06-08 11:43:37 -04:00
Barret Schloerke
56c8c08e08 move mapValues to a util file 2018-06-08 11:42:37 -04:00
Barret Schloerke
13ef25c0b5 bump flow threshold to 95 percent 2018-06-08 11:41:10 -04:00
Barret Schloerke
6abfa5bf80 default log with marks 2018-06-06 16:14:54 -04:00
Barret Schloerke
20ae8e4f8b fix eslint and prettier clashes 2018-06-06 16:14:51 -04:00
Barret Schloerke
f595c5d504 first pass at a user time mark 2018-06-06 16:14:47 -04:00
Barret Schloerke
972779253c update enter exit to not be off by one 2018-06-06 16:14:42 -04:00
Barret Schloerke
9179a241e9 first pass at reactlog mark 2018-06-06 16:14:31 -04:00
Barret Schloerke
85e7e89ad9 fix babel options bug in grunt config 2018-06-06 16:14:26 -04:00
Barret Schloerke
9f5bc00c89 add simpler lint then watch script 2018-05-31 14:00:55 -04:00
Barret Schloerke
0ab842e3c5 add cranwhales log 2018-05-31 14:00:32 -04:00
Barret Schloerke
3a0a3e49dc use log states from dictionary, rather than copies 2018-05-31 13:58:45 -04:00
Barret Schloerke
438b1c043e set app data as log 2018-05-31 13:57:56 -04:00
Barret Schloerke
6d13b65e7c export rlog object not as default 2018-05-31 13:57:27 -04:00
Barret Schloerke
423d41ee0e fix console bug 2018-05-31 13:55:59 -04:00
Barret Schloerke
1b61d9bc51 first pass at freeze/thaw in rlog 2018-05-31 13:55:32 -04:00
Barret Schloerke
bf0c3d42db copy all rlog files to the temp directory 2018-05-31 13:53:58 -04:00
Barret Schloerke
5394a68314 attempt to load rlog_data as a trycatch to work with showReactLog() 2018-05-31 13:53:46 -04:00
Barret Schloerke
b0063399bb fix freeze/thaw rlog'ing 2018-05-30 14:14:57 -04:00
Barret Schloerke
724c6b7656 Merge branch 'master' into barret/reactlog
* master:
  runApp: add support for IPv6 addresses
  Bump version to *.9000. Back to work!
  Bump version to 1.1.0
  Bump httpuv version and add NEWS note
  Fix #2061: Tests failing on Windows due to rounding errors
  Take dependency on later >=0.7.2
2018-05-29 18:26:24 -04:00
Barret Schloerke
0530cbcd0f make sure log works with an empty log 2018-05-29 18:22:26 -04:00
Barret Schloerke
6e2bba1513 up the flow type percentage 2018-05-29 18:16:40 -04:00
Barret Schloerke
89ac5d7c42 ignore yarn error 2018-05-29 18:16:08 -04:00
Barret Schloerke
dd68722b66 first pass at cyto flow types 2018-05-29 12:01:36 -04:00
Barret Schloerke
933d5db2ab flow more files 2018-05-29 09:59:19 -04:00
Barret Schloerke
0386ed6409 add index file for updateGraph to gather all exports for easy import 2018-05-29 09:34:06 -04:00
Barret Schloerke
d3c14bf416 first pass at flowtype 2018-05-29 09:33:02 -04:00
Barret Schloerke
2a224ce9fb add babel plugin transform class properties within yarn 2018-05-29 09:32:53 -04:00
Barret Schloerke
78322525b7 add flow, flow scripts and update grunt-babel 2018-05-29 09:25:39 -04:00
Barret Schloerke
5b7c9c205e remove rlog grunt tasks in favor of config files 2018-05-29 09:24:44 -04:00
Barret Schloerke
07ac70a460 add lodash flow types 2018-05-29 09:23:30 -04:00
Barret Schloerke
3629f806a2 add jquery flow types 2018-05-29 09:23:21 -04:00
Barret Schloerke
72fc43c738 add a flow config
only for rlog src
make all lints warnings
any suppress comment starts with "\\ $Flow"
and if strict (currently none) do recommended strict things
2018-05-29 09:23:06 -04:00
Barret Schloerke
df38f0be3f clean up lint config 2018-05-23 17:02:26 -04:00
Barret Schloerke
808684c2a8 remove unused dep and script 2018-05-23 17:00:57 -04:00
Barret Schloerke
69ed3a7751 working graph with es6 modules 2018-05-23 16:40:24 -04:00
Barret Schloerke
68556caa9a first pass at distributed files. graph loads, not all perfect 2018-05-23 12:02:59 -04:00
Barret Schloerke
bb8ea8053b prettier and build script updates 2018-05-22 11:44:49 -04:00
Barret Schloerke
6f01e6edf1 first pass at sep classes 2018-05-22 10:38:10 -04:00
Barret Schloerke
66a74d16ff lints 2018-05-22 10:01:07 -04:00
Barret Schloerke
0e525f5aeb add Console module 2018-05-22 09:59:52 -04:00
Barret Schloerke
86007c466d copy in react_graph into index.js to start pruning into multiple files 2018-05-17 16:56:21 -04:00
Barret Schloerke
7b39b79183 added prettier config for rlog 2018-05-17 16:42:02 -04:00
Barret Schloerke
7f453aa6f6 add local rlog .eslintrc.js 2018-05-17 16:38:59 -04:00
Barret Schloerke
f36052ffeb add test files 2018-05-17 16:38:34 -04:00
Barret Schloerke
d35db11f43 add gitignore in rlog 2018-05-17 16:19:20 -04:00
Barret Schloerke
173e5d3f97 prettier and lints 2018-05-17 16:18:05 -04:00
Barret Schloerke
bcebf737c3 move node_modules and grunt file to root dir 2018-05-17 16:13:13 -04:00
Barret Schloerke
5280b72b85 add different log files for rlog to check 2018-05-15 12:21:29 -04:00
Barret Schloerke
a4dfe7138e search regex implemented to update filtered data on getGraph.atStep(k) 2018-05-14 16:01:04 -04:00
Barret Schloerke
b9960bad1a next, prev, next cycle, prev cycle implemented within new search 2018-05-14 15:34:35 -04:00
Barret Schloerke
e1d7805396 massive sweep on how filtering and hovering is done. Commiting. regex filter is unfinished 2018-05-11 14:59:50 -04:00
Barret Schloerke
ce6f993f0e add filter by name 2018-05-10 10:54:14 -04:00
Barret Schloerke
aa1d94e6c9 first pass double click filter 2018-05-09 10:42:45 -04:00
Barret Schloerke
00a6092836 remove TODO 2018-05-09 10:41:45 -04:00
Barret Schloerke
f6372faa23 for future... make animation a setting 2018-05-04 11:10:01 -04:00
Barret Schloerke
1a5e266d26 Drastically improve performance by not re-rendering the layout on a layout that isn't changing 2018-05-04 11:02:33 -04:00
Barret Schloerke
2e4a107201 fix hover and sticky hover to be stable throughout transitions
all items use `hoverKey`. edges are supplying their ghostKey so that all edges share the same hoverKey.  if A --> B then all edges from A to B will behave the same way.
2018-05-04 11:01:31 -04:00
Barret Schloerke
2559496ded first pass at hover highlight. need to move to graph data object and not cyto object 2018-05-03 13:27:20 -04:00
Barret Schloerke
d3aa82fc5d clean up graph addEntry wrapper 2018-05-03 13:26:53 -04:00
Barret Schloerke
704605918d update layout options 2018-05-03 13:26:35 -04:00
Barret Schloerke
7e8116888b add alt shift arrows navigation and prev / next step calculations 2018-05-03 13:25:25 -04:00
Barret Schloerke
e0f4bbd20d skip adding entries if the reactId is rNoCtx 2018-05-02 15:12:21 -04:00
Barret Schloerke
5ae2d5a24b Allow for isolate calls to have no context and input name changes to have no context 2018-05-02 15:11:58 -04:00
Barret Schloerke
8648737a7a fix missing period bug 2018-05-02 15:11:19 -04:00
Barret Schloerke
6e090d5112 active enter and value change now pulse and use ActiveStateStatus helper 2018-05-02 11:25:08 -04:00
Barret Schloerke
2207e561f2 fix progress bar tick leaking right bug 2018-05-02 11:24:33 -04:00
Barret Schloerke
b9cd5b572b first pass at ActiveStateStatus class with invalidate 2018-05-02 11:04:30 -04:00
Barret Schloerke
344c6f3ee7 use graph style and do not animate graph style 2018-05-02 11:03:52 -04:00
Barret Schloerke
f6f2c0ed56 first pass at cacheing graphs. wait for now 2018-05-02 11:01:01 -04:00
Barret Schloerke
ec7a66a966 make edges shades of grey 2018-05-02 11:00:34 -04:00
Barret Schloerke
23ca428a01 add cycle markers in the timeline 2018-05-02 10:58:47 -04:00
Barret Schloerke
eb9f251e34 add nav buttons 2018-05-02 10:58:15 -04:00
Barret Schloerke
394d875eb4 valuechange addressed when an isolateInvalidateEnd is called
invalidate end also sets color to a 'done' grey
2018-05-01 14:12:02 -04:00
Barret Schloerke
4cc6403867 do not double log observable set invalidation 2018-05-01 14:09:09 -04:00
Barret Schloerke
9d5fa773f3 add classes and colors for different states of a graph
* reactive key value change until invalidate end has finished
* latest enter is darker green than others
* mousedown added to timeline click
* mousedown and mouse movement added to timeline
2018-05-01 10:08:37 -04:00
Barret Schloerke
075ca49a1f log that invalidation has occured when an input value changes a key 2018-05-01 10:06:13 -04:00
Barret Schloerke
9564f1d871 invalidate rlog namesDeps on value change 2018-05-01 10:05:36 -04:00
Barret Schloerke
cf546a47b6 on rlog object definition, do not trigger a value change 2018-05-01 10:05:23 -04:00
Barret Schloerke
d3a4f35170 merge master --> reactlog 2018-04-30 11:25:03 -04:00
Barret Schloerke
f450aea449 allow for skipping to next cycle by holding altKey and arrow L/R 2018-04-30 11:21:15 -04:00
Barret Schloerke
aed308b259 styles added to animation in cyto nodes 2018-04-30 11:20:45 -04:00
Barret Schloerke
714dffc943 set up ghost edge and use classes in cyto graph 2018-04-30 11:04:13 -04:00
Barret Schloerke
f8a173efbd first pass at cytoscape.js graph 2018-04-30 10:06:59 -04:00
Barret Schloerke
70e7822dd1 be clear in action name provided in log and give dependsOnRemove a ctxId 2018-04-30 10:06:40 -04:00
Barret Schloerke
452631550a single quotes to double quotes 2018-04-24 11:34:12 -04:00
Barret Schloerke
a14266b452 add freeze and thaw to logger 2018-04-24 11:34:00 -04:00
Barret Schloerke
ceb19c7573 use an rLog object to do all logging 2018-04-24 10:49:16 -04:00
Barret Schloerke
7336d327b3 first pass at adding domain to all rlog functions 2018-04-18 11:49:11 -04:00
Barret Schloerke
c9c5225a6a add rlogAsyncStart and rlogAsyncStop 2018-04-17 10:58:20 -04:00
Barret Schloerke
e1060bf537 isolate calls should be handled differently than regular rlog calls 2018-04-17 10:10:01 -04:00
Barret Schloerke
392e42a55d clean up when reactivevalues are defined and updated in rlog 2018-04-17 10:09:34 -04:00
Barret Schloerke
b974e41148 add test app for rlog 2018-04-17 10:09:00 -04:00
Barret Schloerke
aa3e2a0b64 ctxId's are now upgraded to start with 'ctx' in logging 2018-04-17 09:47:26 -04:00
Barret Schloerke
3df89dd9a3 local logging done with ". " for spacing 2018-04-17 09:46:54 -04:00
Barret Schloerke
6ef751422a first pass at reformatting rlog 2018-04-16 13:37:50 -04:00
Barret Schloerke
05d49ee45e use MessageLogger for node information cache 2018-04-16 09:47:58 -04:00
Barret Schloerke
3e4783c454 remove dot syntax 2018-04-16 09:33:09 -04:00
Barret Schloerke
ce93201843 make the rlog messages a r6 object 2018-04-16 09:28:54 -04:00
Barret Schloerke
f9fc3a46b5 change all nodeId to reactId 2018-04-16 09:17:04 -04:00
Barret Schloerke
0467d6666a merge master -> barret/reactlog 2018-04-13 11:26:34 -04:00
Barret Schloerke
1f26b076a3 first pass gantt chart... brings up future thoughts
could add a gantt chart at bottom of react-graph for the current execution session. Would be interesting to have a full gantt of the current execution 'cycle' with a bar indicating where we are in time to give context to the current graph layout. the gantt coult reset at each 'cycle' as the context is reset as well
2018-04-13 11:22:23 -04:00
Barret Schloerke
7944f21925 break apart the large react-graph.html file 2018-04-13 10:20:08 -04:00
Barret Schloerke
e91eda8eca add npm scripts to build, clean, and watch the js 2018-04-13 10:19:10 -04:00
Barret Schloerke
d8ac84a5da add rLogValueChange (no start/end, just change) 2018-04-13 10:18:11 -04:00
Barret Schloerke
3098a02b72 first pass at making rlog. need javascript to recognize new log format 2018-04-13 10:07:03 -04:00
316 changed files with 13158 additions and 11734 deletions

View File

@@ -20,3 +20,4 @@
^revdep$
^TODO-promises.md$
^manualtests$
^\.github$

40
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,40 @@
---
name : Bug report
about : Report a bug in Shiny.
---
<!--
This issue tracker is for bugs and feature requests in the Shiny package. If you're having trouble with Shiny Server or a related package, please file an issue in the appropriate repository.
If you're having trouble with shinyapps.io, and you have a paid account (Starter, Basic, Standard, or Pro), please file a support ticket via https://support.rstudio.com. If you have a Free account, please post to the RStudio Community with the shinyappsio tag: https://community.rstudio.com/tags/shinyappsio.
Finally, if you are an RStudio customer and are having trouble with one of our Pro products, get in touch with our support team at support@rstudio.com.
Before you file an issue, please upgrade to the latest version of Shiny from CRAN and confirm that the problem persists.
# First, restart R.
# To install latest shiny from CRAN:
install.packages("shiny")
See our guide to writing good bug reports for further guidance: https://github.com/rstudio/shiny/wiki/Writing-Good-Bug-Reports. The better your report is, the likelier we are to be able to reproduce and ultimately solve it.
-->
### System details
Browser Version: <!-- If applicable -->
Output of `sessionInfo()`:
```
# sessionInfo() output goes here
```
### Example application *or* steps to reproduce the problem
<!-- If you're able to create one, a reproducible example is extremely helpful to us. For instructions on how to create one, please see: https://github.com/rstudio/shiny/wiki/Creating-a-Reproducible-Example -->
```R
# Minimal, self-contained example app code goes here
```
### Describe the problem in detail

View File

@@ -0,0 +1,17 @@
---
name : Feature request
about : Request a new feature.
---
<!--
Thanks for taking the time to file a feature request! Please take the time to search for an existing feature request, to avoid creating duplicate requests. If you find an existing feature request, please give it a thumbs-up reaction, as we'll use these reactions to help prioritize the implementation of these features in the future.
If the feature has not yet been filed, then please describe the feature you'd like to see become a part of Shiny. See:
https://github.com/rstudio/shiny/wiki/Writing-Good-Feature-Requests
for a guide on how to write good feature requests.
-->

7
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View File

@@ -0,0 +1,7 @@
---
name : Ask a Question
about : The issue tracker is not for questions -- please ask questions at https://community.rstudio.com/c/shiny.
---
The issue tracker is not for questions. If you have a question, please feel free to ask it on our community site, at https://community.rstudio.com/c/shiny.

1
.gitignore vendored
View File

@@ -9,3 +9,4 @@
shinyapps/
README.html
.*.Rnb.cached
tools/yarn-error.log

View File

@@ -1,12 +1,30 @@
language: r
r:
- oldrel
- release
- devel
matrix:
include:
- name: "Roxygen check"
r: release
r_packages:
- devtools
- roxygen2
script: ./tools/checkDocsCurrent.sh
- name: "Javascript check"
language: node_js
cache: yarn
script: ./tools/checkJSCurrent.sh
node_js:
- "10"
- name: "Old Release Check"
r: oldrel
- name: "Current Release Check"
r: release
- name: "Development Release Check"
r: devel
sudo: false
cache: packages
notifications:
email:
on_success: change
on_failure: change
slack:
secure: QoM0+hliVC4l2HYv126AkljG/uFvgwayW9IpuB5QNqjSukM122MhMDL7ZuMB9a2vWP24juzOTXiNIymgEspfnvvAMnZwYRBNWkuot2m8HIR2B9UjQLiztFnN1EAT+P+thz8Qax9TV2SOfXb2S2ZOeZmRTVkJctxkL8heAZadIC4=
on_pull_requests: false

View File

@@ -1,7 +1,7 @@
Package: shiny
Type: Package
Title: Web Application Framework for R
Version: 1.2.0
Version: 1.3.2.9001
Authors@R: c(
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@rstudio.com"),
person("Joe", "Cheng", role = "aut", email = "joe@rstudio.com"),
@@ -65,28 +65,33 @@ Depends:
Imports:
utils,
grDevices,
httpuv (>= 1.4.4),
httpuv (>= 1.5.1.9002),
mime (>= 0.3),
jsonlite (>= 0.9.16),
xtable,
digest,
htmltools (>= 0.3.5),
htmltools (>= 0.3.6.9004),
R6 (>= 2.0),
sourcetools,
later (>= 0.7.2),
promises (>= 1.0.1),
tools,
crayon,
rlang
rlang (>= 0.4.0),
fastmap (>= 1.0.0)
Suggests:
datasets,
Cairo (>= 1.5-5),
testthat,
testthat (>= 2.1.1),
knitr (>= 1.6),
markdown,
rmarkdown,
ggplot2,
reactlog (>= 1.0.0),
magrittr
Remotes:
rstudio/htmltools,
rstudio/httpuv
URL: http://shiny.rstudio.com
BugReports: https://github.com/rstudio/shiny/issues
Collate:
@@ -94,6 +99,7 @@ Collate:
'bookmark-state-local.R'
'stack.R'
'bookmark-state.R'
'bootstrap-deprecated.R'
'bootstrap-layout.R'
'globals.R'
'conditions.R'
@@ -160,4 +166,6 @@ Collate:
'test-export.R'
'timer.R'
'update-input.R'
RoxygenNote: 6.1.0
RoxygenNote: 6.1.1
Encoding: UTF-8
Roxygen: list(markdown = TRUE)

View File

@@ -25,7 +25,7 @@ these components are included below):
jQuery license and license for included components from jQuery UI
----------------------------------------------------------------------
Copyright jQuery Foundation and other contributors, https://jquery.org/
Copyright JS Foundation and other contributors, https://js.foundation/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@@ -51,7 +51,7 @@ Bootstrap License
----------------------------------------------------------------------
The MIT License (MIT)
Copyright (c) 2011-2014 Twitter, Inc
Copyright (c) 2011-2019 Twitter, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -25,9 +25,10 @@ S3method(as.tags,shiny.render.function)
S3method(format,reactiveExpr)
S3method(format,reactiveVal)
S3method(names,reactivevalues)
S3method(print,key_missing)
S3method(print,reactive)
S3method(print,reactivevalues)
S3method(print,shiny.appobj)
S3method(print,shiny.render.function)
S3method(str,reactivevalues)
export("conditionStackTrace<-")
export(..stacktraceoff..)
@@ -133,12 +134,7 @@ export(isRunning)
export(isTruthy)
export(isolate)
export(key_missing)
export(knit_print.html)
export(knit_print.reactive)
export(knit_print.shiny.appobj)
export(knit_print.shiny.render.function)
export(knit_print.shiny.tag)
export(knit_print.shiny.tag.list)
export(loadSupport)
export(mainPanel)
export(makeReactiveBinding)
export(markRenderFunction)
@@ -189,10 +185,14 @@ export(reactiveUI)
export(reactiveVal)
export(reactiveValues)
export(reactiveValuesToList)
export(reactlog)
export(reactlogReset)
export(reactlogShow)
export(registerInputHandler)
export(removeInputHandler)
export(removeModal)
export(removeNotification)
export(removeResourcePath)
export(removeTab)
export(removeUI)
export(renderCachedPlot)
@@ -205,6 +205,7 @@ export(renderText)
export(renderUI)
export(repeatable)
export(req)
export(resourcePaths)
export(restoreInput)
export(runApp)
export(runExample)
@@ -251,6 +252,8 @@ export(tag)
export(tagAppendAttributes)
export(tagAppendChild)
export(tagAppendChildren)
export(tagGetAttribute)
export(tagHasAttribute)
export(tagList)
export(tagSetChildren)
export(tags)
@@ -298,5 +301,8 @@ import(httpuv)
import(methods)
import(mime)
import(xtable)
importFrom(fastmap,fastmap)
importFrom(fastmap,is.key_missing)
importFrom(fastmap,key_missing)
importFrom(grDevices,dev.cur)
importFrom(grDevices,dev.set)

117
NEWS.md
View File

@@ -1,3 +1,116 @@
shiny 1.3.2.9001
=======
## Changes
* 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))
### Improvements
* 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](https://github.com/rstudio/shiny/pull/2547))
* 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](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))
* 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](https://github.com/rstudio/shiny/issues/2267): Fixed a memory leak with `invalidateLater`. ([#2555](https://github.com/rstudio/shiny/pull/2555))
* 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)
* 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](https://github.com/rstudio/shiny/pull/2459))
* Resolved [#2515](https://github.com/rstudio/shiny/issues/2515): `selectInput()` now deals appropriately with named factors. ([#2524](https://github.com/rstudio/shiny/pull/2524))
* 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 [#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))
### Bug fixes
* 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))
* 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](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](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](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](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](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](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](https://github.com/rstudio/shiny/pull/2424))
* 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.
### Library updates
* Resolved [#2554](https://github.com/rstudio/shiny/issues/2554): Upgraded bootstrap to v3.4.1 and jQuery to v3.4.1. ([#2557](https://github.com/rstudio/shiny/pull/2557))
shiny 1.3.2
===========
### Bug fixes
* Fixed [#2285](https://github.com/rstudio/shiny/issues/2285), [#2288](https://github.com/rstudio/shiny/issues/2288): Static CSS/JS resources in subapps in R Markdown documents did not render properly. ([#2386](https://github.com/rstudio/shiny/pull/2386))
* 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
===========
## Full changelog
### Bug fixes
* 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
===========
## Full changelog
### Breaking changes
### 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](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](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](https://github.com/rstudio/shiny/pull/2319))
### Bug fixes
* Fixed [#2245](https://github.com/rstudio/shiny/issues/2245): `updateSelectizeInput()` did not update labels. ([#2248](https://github.com/rstudio/shiny/pull/2248))
* 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](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](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](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
===========
@@ -43,6 +156,8 @@ This release features plot caching, an important new tool for improving performa
* 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](https://github.com/rstudio/shiny/pull/2166)
* 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](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)
@@ -117,7 +232,7 @@ This is a significant release for Shiny, with a major new feature that was nearl
* 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/master/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))
* 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](https://github.com/rstudio/shiny/issues/1784): `runApp()` will avoid port 6697, which is considered unsafe by Chrome.

197
R/app.R
View File

@@ -3,40 +3,40 @@
#' Create a Shiny app object
#'
#' These functions create Shiny app objects from either an explicit UI/server
#' pair (\code{shinyApp}), or by passing the path of a directory that contains a
#' Shiny app (\code{shinyAppDir}). You generally shouldn't need to use these
#' functions to create/run applications; they are intended for interoperability
#' purposes, such as embedding Shiny apps inside a \pkg{knitr} document.
#' pair (`shinyApp`), or by passing the path of a directory that contains a
#' Shiny app (`shinyAppDir`).
#'
#' Normally when this function is used at the R console, the Shiny app object is
#' automatically passed to the \code{print()} function, which runs the app. If
#' automatically passed to the `print()` function, which runs the app. If
#' this is called in the middle of a function, the value will not be passed to
#' \code{print()} and the app will not be run. To make the app run, pass the app
#' object to \code{print()} or \code{\link{runApp}()}.
#' `print()` and the app will not be run. To make the app run, pass the app
#' object to `print()` or [runApp()].
#'
#' @param ui The UI definition of the app (for example, a call to
#' \code{fluidPage()} with nested controls)
#' @param server A server function
#' `fluidPage()` with nested controls)
#' @param server A function with three parameters: `input`, `output`, and
#' `session`. The function is called once for each session ensuring that each
#' app is independent.
#' @param onStart A function that will be called before the app is actually run.
#' This is only needed for \code{shinyAppObj}, since in the \code{shinyAppDir}
#' case, a \code{global.R} file can be used for this purpose.
#' @param options Named options that should be passed to the \code{runApp} call
#' This is only needed for `shinyAppObj`, since in the `shinyAppDir`
#' case, a `global.R` file can be used for this purpose.
#' @param options Named options that should be passed to the `runApp` call
#' (these can be any of the following: "port", "launch.browser", "host", "quiet",
#' "display.mode" and "test.mode"). You can also specify \code{width} and
#' \code{height} parameters which provide a hint to the embedding environment
#' "display.mode" and "test.mode"). You can also specify `width` and
#' `height` parameters which provide a hint to the embedding environment
#' about the ideal height/width for the app.
#' @param uiPattern A regular expression that will be applied to each \code{GET}
#' request to determine whether the \code{ui} should be used to handle the
#' @param uiPattern A regular expression that will be applied to each `GET`
#' request to determine whether the `ui` should be used to handle the
#' request. Note that the entire request path must match the regular
#' expression in order for the match to be considered successful.
#' @param enableBookmarking Can be one of \code{"url"}, \code{"server"}, or
#' \code{"disable"}. This is equivalent to calling the
#' \code{\link{enableBookmarking}()} function just before calling
#' \code{shinyApp()}. With the default value (\code{NULL}), the app will
#' respect the setting from any previous calls to \code{enableBookmarking()}.
#' See \code{\link{enableBookmarking}} for more information.
#' @param enableBookmarking Can be one of `"url"`, `"server"`, or
#' `"disable"`. This is equivalent to calling the
#' [enableBookmarking()] function just before calling
#' `shinyApp()`. With the default value (`NULL`), the app will
#' respect the setting from any previous calls to `enableBookmarking()`.
#' See [enableBookmarking()] for more information.
#' @return An object that represents the app. Printing the object or passing it
#' to \code{\link{runApp}} will run the app.
#' to [runApp()] will run the app.
#'
#' @examples
#' ## Only run this example in interactive R sessions
@@ -70,10 +70,10 @@
#' runApp(app)
#' }
#' @export
shinyApp <- function(ui=NULL, server=NULL, onStart=NULL, options=list(),
shinyApp <- function(ui, server, onStart=NULL, options=list(),
uiPattern="/", enableBookmarking=NULL) {
if (is.null(server)) {
stop("`server` missing from shinyApp")
if (!is.function(server)) {
stop("`server` must be a function", call. = FALSE)
}
# Ensure that the entire path is a match
@@ -141,10 +141,23 @@ shinyAppFile <- function(appFile, options=list()) {
# This reads in an app dir in the case that there's a server.R (and ui.R/www)
# present, and returns a shiny.appobj.
# appDir must be a normalized (absolute) path, not a relative one
shinyAppDir_serverR <- function(appDir, options=list()) {
# Most of the complexity here comes from needing to hot-reload if the .R files
# change on disk, or are created, or are removed.
# In an upcoming version of shiny, this option will go away and the new behavior will be used.
if (getOption("shiny.autoload.r", FALSE)) {
# new behavior
# 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 {
# old behavior, default
sharedEnv <- globalenv()
}
# uiHandlerSource is a function that returns an HTTP handler for serving up
# ui.R as a webpage. The "cachedFuncWithFile" call makes sure that the closure
# we're creating here only gets executed when ui.R's contents change.
@@ -155,7 +168,7 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
# If not, then take the last expression that's returned from ui.R.
.globals$ui <- NULL
on.exit(.globals$ui <- NULL, add = FALSE)
ui <- sourceUTF8(uiR, envir = new.env(parent = globalenv()))
ui <- sourceUTF8(uiR, envir = new.env(parent = sharedEnv))
if (!is.null(.globals$ui)) {
ui <- .globals$ui[[1]]
}
@@ -170,7 +183,14 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
}
wwwDir <- file.path.ci(appDir, "www")
if (dirExists(wwwDir)) {
staticPaths <- list("/" = staticPath(wwwDir, indexhtml = FALSE, fallthrough = TRUE))
} else {
staticPaths <- list()
}
fallbackWWWDir <- system.file("www-dir", package = "shiny")
serverSource <- cachedFuncWithFile(appDir, "server.R", case.sensitive = FALSE,
function(serverR) {
# If server.R contains a call to shinyServer (which sets .globals$server),
@@ -178,7 +198,7 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
# server.R.
.globals$server <- NULL
on.exit(.globals$server <- NULL, add = TRUE)
result <- sourceUTF8(serverR, envir = new.env(parent = globalenv()))
result <- sourceUTF8(serverR, envir = new.env(parent = sharedEnv))
if (!is.null(.globals$server)) {
result <- .globals$server[[1]]
}
@@ -209,8 +229,13 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
oldwd <<- getwd()
setwd(appDir)
monitorHandle <<- initAutoReloadMonitor(appDir)
if (file.exists(file.path.ci(appDir, "global.R")))
sourceUTF8(file.path.ci(appDir, "global.R"))
# TODO: we should support hot reloading on global.R and R/*.R changes.
if (getOption("shiny.autoload.r", FALSE)) {
loadSupport(appDir, renv=sharedEnv, globalrenv=globalenv())
} else {
if (file.exists(file.path.ci(appDir, "global.R")))
sourceUTF8(file.path.ci(appDir, "global.R"))
}
}
onStop <- function() {
setwd(oldwd)
@@ -220,6 +245,13 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
structure(
list(
staticPaths = staticPaths,
# Even though the wwwDir is handled as a static path, we need to include
# it here to be handled by R as well. This is because the special case
# of index.html: it is specifically not handled as a staticPath for
# reasons explained above, but if someone does want to serve up an
# index.html, we need to handle it, and we do it by using the
# staticHandler in the R code path. (#2380)
httpHandler = joinHandlers(c(uiHandler, wwwDir, fallbackWWWDir)),
serverFuncSource = serverFuncSource,
onStart = onStart,
@@ -276,18 +308,66 @@ initAutoReloadMonitor <- function(dir) {
obs$destroy
}
#' Load an app's supporting R files
#'
#' Loads all of the supporting R files of a Shiny application. Specifically,
#' this function loads any top-level supporting `.R` files in the `R/` directory
#' adjacent to the `app.R`/`server.R`/`ui.R` files.
#'
#' At the moment, this function is "opt-in" and only called if the option
#' `shiny.autoload.r` is set to `TRUE`.
#'
#' @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
#' @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, renv=new.env(parent=globalenv()), globalrenv=globalenv()){
if (!is.null(globalrenv)){
# Evaluate global.R, if it exists.
if (file.exists(file.path.ci(appDir, "global.R"))){
sourceUTF8(file.path.ci(appDir, "global.R"), envir=globalrenv)
}
}
helpersDir <- file.path(appDir, "R")
helpers <- list.files(helpersDir, pattern="\\.[rR]$", recursive=FALSE, full.names=TRUE)
lapply(helpers, sourceUTF8, envir=renv)
invisible(renv)
}
# This reads in an app dir for a single-file application (e.g. app.R), and
# returns a shiny.appobj.
# appDir must be a normalized (absolute) path, not a relative one
shinyAppDir_appR <- function(fileName, appDir, options=list())
{
fullpath <- file.path.ci(appDir, fileName)
# In an upcoming version of shiny, this option will go away and the new behavior will be used.
if (getOption("shiny.autoload.r", FALSE)) {
# new behavior
# 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 {
# old behavior, default
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) {
result <- sourceUTF8(fullpath, envir = new.env(parent = globalenv()))
result <- sourceUTF8(fullpath, envir = new.env(parent = sharedEnv))
if (!is.shiny.appobj(result))
stop("app.R did not return a shiny.appobj object.")
@@ -309,6 +389,20 @@ shinyAppDir_appR <- function(fileName, appDir, options=list())
}
wwwDir <- file.path.ci(appDir, "www")
if (dirExists(wwwDir)) {
# wwwDir is a static path served by httpuv. It does _not_ serve up
# index.html, for two reasons. (1) It's possible that the user's
# www/index.html file is not actually used as the index, but as a template
# that gets processed before being sent; and (2) the index content may be
# modified by the hosting environment (as in SockJSAdapter.R).
#
# The call to staticPath normalizes the path, so that if the working dir
# later changes, it will continue to point to the right place.
staticPaths <- list("/" = staticPath(wwwDir, indexhtml = FALSE, fallthrough = TRUE))
} else {
staticPaths <- list()
}
fallbackWWWDir <- system.file("www-dir", package = "shiny")
oldwd <- NULL
@@ -316,6 +410,10 @@ shinyAppDir_appR <- function(fileName, appDir, options=list())
onStart <- function() {
oldwd <<- getwd()
setwd(appDir)
# TODO: we should support hot reloading on R/*.R changes.
if (getOption("shiny.autoload.r", FALSE)) {
loadSupport(appDir, renv=sharedEnv, globalrenv=NULL)
}
monitorHandle <<- initAutoReloadMonitor(appDir)
if (!is.null(appObj()$onStart)) appObj()$onStart()
}
@@ -327,6 +425,18 @@ shinyAppDir_appR <- function(fileName, appDir, options=list())
structure(
list(
# fallbackWWWDir is _not_ listed in staticPaths, because it needs to
# come after the uiHandler. It also does not need to be fast, since it
# should rarely be hit. The order is wwwDir (in staticPaths), then
# uiHandler, then falbackWWWDir (which is served up by the R
# staticHandler function).
staticPaths = staticPaths,
# Even though the wwwDir is handled as a static path, we need to include
# it here to be handled by R as well. This is because the special case
# of index.html: it is specifically not handled as a staticPath for
# reasons explained above, but if someone does want to serve up an
# index.html, we need to handle it, and we do it by using the
# staticHandler in the R code path. (#2380)
httpHandler = joinHandlers(c(dynHttpHandler, wwwDir, fallbackWWWDir)),
serverFuncSource = dynServerFuncSource,
onStart = onStart,
@@ -338,26 +448,34 @@ shinyAppDir_appR <- function(fileName, appDir, options=list())
}
#' @rdname shinyApp
#' Shiny App object
#'
#' Internal methods for the `shiny.appobj` S3 class.
#'
#' @keywords internal
#' @name shiny.appobj
NULL
#' @rdname shiny.appobj
#' @param x Object to convert to a Shiny app.
#' @export
as.shiny.appobj <- function(x) {
UseMethod("as.shiny.appobj", x)
}
#' @rdname shinyApp
#' @rdname shiny.appobj
#' @export
as.shiny.appobj.shiny.appobj <- function(x) {
x
}
#' @rdname shinyApp
#' @rdname shiny.appobj
#' @export
as.shiny.appobj.list <- function(x) {
shinyApp(ui = x$ui, server = x$server)
}
#' @rdname shinyApp
#' @rdname shiny.appobj
#' @export
as.shiny.appobj.character <- function(x) {
if (identical(tolower(tools::file_ext(x)), "r"))
@@ -366,13 +484,13 @@ as.shiny.appobj.character <- function(x) {
shinyAppDir(x)
}
#' @rdname shinyApp
#' @rdname shiny.appobj
#' @export
is.shiny.appobj <- function(x) {
inherits(x, "shiny.appobj")
}
#' @rdname shinyApp
#' @rdname shiny.appobj
#' @param ... Additional parameters to be passed to print.
#' @export
print.shiny.appobj <- function(x, ...) {
@@ -387,7 +505,7 @@ print.shiny.appobj <- function(x, ...) {
do.call("runApp", args)
}
#' @rdname shinyApp
#' @rdname shiny.appobj
#' @method as.tags shiny.appobj
#' @export
as.tags.shiny.appobj <- function(x, ...) {
@@ -441,7 +559,6 @@ shiny_rmd_warning <- function() {
}
#' @rdname knitr_methods
#' @export
knit_print.shiny.appobj <- function(x, ...) {
opts <- x$options %OR% list()
width <- if (is.null(opts$width)) "100%" else opts$width
@@ -478,7 +595,6 @@ knit_print.shiny.appobj <- function(x, ...) {
# calling output$value <- renderFoo(...) and fooOutput().
#' @rdname knitr_methods
#' @param inline Whether the object is printed inline.
#' @export
knit_print.shiny.render.function <- function(x, ..., inline = FALSE) {
x <- htmltools::as.tags(x, inline = inline)
output <- knitr::knit_print(tagList(x))
@@ -491,7 +607,6 @@ knit_print.shiny.render.function <- function(x, ..., inline = FALSE) {
# Lets us drop reactive expressions directly into a knitr chunk and have the
# value printed out! Nice for teaching if nothing else.
#' @rdname knitr_methods
#' @export
knit_print.reactive <- function(x, ..., inline = FALSE) {
renderFunc <- if (inline) renderText else renderPrint
knitr::knit_print(renderFunc({

View File

@@ -426,7 +426,7 @@ RestoreInputSet <- R6Class("RestoreInputSet",
},
asList = function() {
as.list.environment(private$values)
as.list.environment(private$values, all.names = TRUE)
}
)
)
@@ -479,7 +479,7 @@ getCurrentRestoreContext <- function() {
#' Restore an input value
#'
#' This restores an input value from the current restore context. It should be
#' called early on inside of input functions (like \code{\link{textInput}}).
#' called early on inside of input functions (like [textInput()]).
#'
#' @param id Name of the input value to restore.
#' @param default A default value to use, if there's no value to restore.
@@ -509,23 +509,23 @@ restoreInput <- function(id, default) {
#' It typically is called from an observer. Note that this will not work in
#' Internet Explorer 9 and below.
#'
#' For \code{mode = "push"}, only three updates are currently allowed:
#' For `mode = "push"`, only three updates are currently allowed:
#' \enumerate{
#' \item the query string (format: \code{?param1=val1&param2=val2})
#' \item the hash (format: \code{#hash})
#' \item the query string (format: `?param1=val1&param2=val2`)
#' \item the hash (format: `#hash`)
#' \item both the query string and the hash
#' (format: \code{?param1=val1&param2=val2#hash})
#' (format: `?param1=val1&param2=val2#hash`)
#' }
#'
#' In other words, if \code{mode = "push"}, the \code{queryString} must start
#' with either \code{?} or with \code{#}.
#' In other words, if `mode = "push"`, the `queryString` must start
#' with either `?` or with `#`.
#'
#' A technical curiosity: under the hood, this function is calling the HTML5
#' history API (which is where the names for the \code{mode} argument come from).
#' When \code{mode = "replace"}, the function called is
#' \code{window.history.replaceState(null, null, queryString)}.
#' When \code{mode = "push"}, the function called is
#' \code{window.history.pushState(null, null, queryString)}.
#' history API (which is where the names for the `mode` argument come from).
#' When `mode = "replace"`, the function called is
#' `window.history.replaceState(null, null, queryString)`.
#' When `mode = "push"`, the function called is
#' `window.history.pushState(null, null, queryString)`.
#'
#' @param queryString The new query string to show in the location bar.
#' @param mode When the query string is updated, should the the current history
@@ -534,7 +534,7 @@ restoreInput <- function(id, default) {
#' context. The latter is useful if you want to navigate between states using
#' the browser's back and forward buttons. See Examples.
#' @param session A Shiny session object.
#' @seealso \code{\link{enableBookmarking}}, \code{\link{getQueryString}}
#' @seealso [enableBookmarking()], [getQueryString()]
#' @examples
#' ## Only run these examples in interactive sessions
#' if (interactive()) {
@@ -597,7 +597,7 @@ updateQueryString <- function(queryString, mode = c("replace", "push"),
#' Create a button for bookmarking/sharing
#'
#' A \code{bookmarkButton} is a \code{\link{actionButton}} with a default label
#' A `bookmarkButton` is a [actionButton()] with a default label
#' that consists of a link icon and the text "Bookmark...". It is meant to be
#' used for bookmarking state.
#'
@@ -607,10 +607,10 @@ updateQueryString <- function(queryString, mode = c("replace", "push"),
#' @param id An ID for the bookmark button. The only time it is necessary to set
#' the ID unless you have more than one bookmark button in your application.
#' If you specify an input ID, it should be excluded from bookmarking with
#' \code{\link{setBookmarkExclude}}, and you must create an observer that
#' [setBookmarkExclude()], and you must create an observer that
#' does the bookmarking when the button is pressed. See the examples below.
#'
#' @seealso \code{\link{enableBookmarking}} for more examples.
#' @seealso [enableBookmarking()] for more examples.
#'
#' @examples
#' ## Only run these examples in interactive sessions
@@ -660,10 +660,10 @@ bookmarkButton <- function(label = "Bookmark...",
#' Generate a modal dialog that displays a URL
#'
#' The modal dialog generated by \code{urlModal} will display the URL in a
#' The modal dialog generated by `urlModal` will display the URL in a
#' textarea input, and the URL text will be selected so that it can be easily
#' copied. The result from \code{urlModal} should be passed to the
#' \code{\link{showModal}} function to display it in the browser.
#' copied. The result from `urlModal` should be passed to the
#' [showModal()] function to display it in the browser.
#'
#' @param url A URL to display in the dialog box.
#' @param title A title for the dialog box.
@@ -719,8 +719,8 @@ urlModal <- function(url, title = "Bookmarked application link", subtitle = NULL
#' Display a modal dialog for bookmarking
#'
#' This is a wrapper function for \code{\link{urlModal}} that is automatically
#' called if an application is bookmarked but no other \code{\link{onBookmark}}
#' This is a wrapper function for [urlModal()] that is automatically
#' called if an application is bookmarked but no other [onBookmark()]
#' callback was set. It displays a modal dialog with the bookmark URL, along
#' with a subtitle that is appropriate for the type of bookmarking used ("url"
#' or "server").
@@ -761,8 +761,8 @@ showBookmarkUrlModal <- function(url) {
#' @details
#'
#' For restoring state to work properly, the UI must be a function that takes
#' one argument, \code{request}. In most Shiny applications, the UI is not a
#' function; it might have the form \code{fluidPage(....)}. Converting it to a
#' one argument, `request`. In most Shiny applications, the UI is not a
#' function; it might have the form `fluidPage(....)`. Converting it to a
#' function is as simple as wrapping it in a function, as in
#' \code{function(request) \{ fluidPage(....) \}}.
#'
@@ -771,17 +771,17 @@ showBookmarkUrlModal <- function(url) {
#' but not if the state is encoded in a URL.
#'
#' When bookmarking state, arbitrary values can be stored, by passing a function
#' as the \code{onBookmark} argument. That function will be passed a
#' \code{ShinySaveState} object. The \code{values} field of the object is a list
#' as the `onBookmark` argument. That function will be passed a
#' `ShinySaveState` object. The `values` field of the object is a list
#' which can be manipulated to save extra information. Additionally, if the
#' state is being saved on the server, and the \code{dir} field of that object
#' state is being saved on the server, and the `dir` field of that object
#' can be used to save extra information to files in that directory.
#'
#' For saved-to-server state, this is how the state directory is chosen:
#' \itemize{
#' \item If running in a hosting environment such as Shiny Server or
#' Connect, the hosting environment will choose the directory.
#' \item If running an app in a directory with \code{\link{runApp}()}, the
#' \item If running an app in a directory with [runApp()], the
#' saved states will be saved in a subdirectory of the app called
#' shiny_bookmarks.
#' \item If running a Shiny app object that is generated from code (not run
@@ -789,22 +789,22 @@ showBookmarkUrlModal <- function(url) {
#' the current working directory called shiny_bookmarks.
#' }
#'
#' When used with \code{\link{shinyApp}()}, this function must be called before
#' \code{shinyApp()}, or in the \code{shinyApp()}'s \code{onStart} function. An
#' alternative to calling the \code{enableBookmarking()} function is to use the
#' \code{enableBookmarking} \emph{argument} for \code{shinyApp()}. See examples
#' When used with [shinyApp()], this function must be called before
#' `shinyApp()`, or in the `shinyApp()`'s `onStart` function. An
#' alternative to calling the `enableBookmarking()` function is to use the
#' `enableBookmarking` *argument* for `shinyApp()`. See examples
#' below.
#'
#' @param store Either \code{"url"}, which encodes all of the relevant values in
#' a URL, \code{"server"}, which saves to disk on the server, or
#' \code{"disable"}, which disables any previously-enabled bookmarking.
#' @param store Either `"url"`, which encodes all of the relevant values in
#' a URL, `"server"`, which saves to disk on the server, or
#' `"disable"`, which disables any previously-enabled bookmarking.
#'
#' @seealso \code{\link{onBookmark}}, \code{\link{onBookmarked}},
#' \code{\link{onRestore}}, and \code{\link{onRestored}} for registering
#' @seealso [onBookmark()], [onBookmarked()],
#' [onRestore()], and [onRestored()] for registering
#' callback functions that are invoked when the state is bookmarked or
#' restored.
#'
#' Also see \code{\link{updateQueryString}}.
#' Also see [updateQueryString()].
#'
#' @export
#' @examples
@@ -983,7 +983,7 @@ enableBookmarking <- function(store = c("url", "server", "disable")) {
#' @param names A character vector containing names of inputs to exclude from
#' bookmarking.
#' @param session A shiny session object.
#' @seealso \code{\link{enableBookmarking}} for examples.
#' @seealso [enableBookmarking()] for examples.
#' @export
setBookmarkExclude <- function(names = character(0), session = getDefaultReactiveDomain()) {
session$setBookmarkExclude(names)
@@ -998,17 +998,17 @@ setBookmarkExclude <- function(names = character(0), session = getDefaultReactiv
#' should be called within an application's server function.
#'
#' \itemize{
#' \item \code{onBookmark} registers a function that will be called just
#' \item `onBookmark` registers a function that will be called just
#' before Shiny bookmarks state.
#' \item \code{onBookmarked} registers a function that will be called just
#' \item `onBookmarked` registers a function that will be called just
#' after Shiny bookmarks state.
#' \item \code{onRestore} registers a function that will be called when a
#' \item `onRestore` registers a function that will be called when a
#' session is restored, after the server function executes, but before all
#' other reactives, observers and render functions are run.
#' \item \code{onRestored} registers a function that will be called after a
#' session is restored. This is similar to \code{onRestore}, but it will be
#' \item `onRestored` registers a function that will be called after a
#' session is restored. This is similar to `onRestore`, but it will be
#' called after all reactives, observers, and render functions run, and
#' after results are sent to the client browser. \code{onRestored}
#' after results are sent to the client browser. `onRestored`
#' callbacks can be useful for sending update messages to the client
#' browser.
#' }
@@ -1019,25 +1019,25 @@ setBookmarkExclude <- function(names = character(0), session = getDefaultReactiv
#' arguments to cancel the registration.
#'
#' The callback function that is passed to these functions should take one
#' argument, typically named "state" (for \code{onBookmark}, \code{onRestore},
#' and \code{onRestored}) or "url" (for \code{onBookmarked}).
#' argument, typically named "state" (for `onBookmark`, `onRestore`,
#' and `onRestored`) or "url" (for `onBookmarked`).
#'
#' For \code{onBookmark}, the state object has three relevant fields. The
#' \code{values} field is an environment which can be used to save arbitrary
#' For `onBookmark`, the state object has three relevant fields. The
#' `values` field is an environment which can be used to save arbitrary
#' values (see examples). If the state is being saved to disk (as opposed to
#' being encoded in a URL), the \code{dir} field contains the name of a
#' being encoded in a URL), the `dir` field contains the name of a
#' directory which can be used to store extra files. Finally, the state object
#' has an \code{input} field, which is simply the application's \code{input}
#' has an `input` field, which is simply the application's `input`
#' object. It can be read, but not modified.
#'
#' For \code{onRestore} and \code{onRestored}, the state object is a list. This
#' list contains \code{input}, which is a named list of input values to restore,
#' \code{values}, which is an environment containing arbitrary values that were
#' saved in \code{onBookmark}, and \code{dir}, the name of the directory that
#' For `onRestore` and `onRestored`, the state object is a list. This
#' list contains `input`, which is a named list of input values to restore,
#' `values`, which is an environment containing arbitrary values that were
#' saved in `onBookmark`, and `dir`, the name of the directory that
#' the state is being restored from, and which could have been used to save
#' extra files.
#'
#' For \code{onBookmarked}, the callback function receives a string with the
#' For `onBookmarked`, the callback function receives a string with the
#' bookmark URL. This callback function should be used to display UI in the
#' client browser with the bookmark URL. If no callback function is registered,
#' then Shiny will by default display a modal dialog with the bookmark URL.

91
R/bootstrap-deprecated.R Normal file
View File

@@ -0,0 +1,91 @@
#' Create a page with a sidebar
#'
#' **DEPRECATED**: use [fluidPage()] and [sidebarLayout()] instead.
#'
#' @param headerPanel The [headerPanel] with the application title
#' @param sidebarPanel The [sidebarPanel] containing input controls
#' @param mainPanel The [mainPanel] containing outputs
#' @keywords internal
#' @return A UI defintion that can be passed to the [shinyUI] function
#' @export
pageWithSidebar <- function(headerPanel,
sidebarPanel,
mainPanel) {
bootstrapPage(
# basic application container divs
div(
class="container-fluid",
div(class="row",
headerPanel
),
div(class="row",
sidebarPanel,
mainPanel
)
)
)
}
#' Create a header panel
#'
#' **DEPRECATED**: use [titlePanel()] instead.
#'
#' @param title An application title to display
#' @param windowTitle The title that should be displayed by the browser window.
#' Useful if `title` is not a string.
#' @return A headerPanel that can be passed to [pageWithSidebar]
#' @keywords internal
#' @export
headerPanel <- function(title, windowTitle=title) {
tagList(
tags$head(tags$title(windowTitle)),
div(class="col-sm-12",
h1(title)
)
)
}
#' Create a Bootstrap page
#'
#' **DEPRECATED**: use [fluidPage()] instead.
#'
#' @param ... The contents of the document body.
#' @param title The browser window title (defaults to the host URL of the page)
#' @param responsive This option is deprecated; it is no longer optional with
#' Bootstrap 3.
#' @param theme Alternative Bootstrap stylesheet (normally a css file within the
#' www directory, e.g. `www/bootstrap.css`)
#'
#' @return A UI defintion that can be passed to the [shinyUI] function.
#'
#' @keywords internal
#' @seealso [fluidPage()], [fixedPage()]
#' @export
bootstrapPage <- function(..., title = NULL, responsive = NULL, theme = NULL) {
if (!is.null(responsive)) {
shinyDeprecated("The 'responsive' argument is no longer used with Bootstrap 3.")
}
attachDependencies(
tagList(
if (!is.null(title)) tags$head(tags$title(title)),
if (!is.null(theme)) {
tags$head(tags$link(rel="stylesheet", type="text/css", href = theme))
},
# remainder of tags passed to the function
list(...)
),
bootstrapLib()
)
}
#' @rdname bootstrapPage
#' @export
basicPage <- function(...) {
bootstrapPage(div(class="container-fluid", list(...)))
}

View File

@@ -10,25 +10,25 @@
#'
#' @param ... Elements to include within the page
#' @param title The browser window title (defaults to the host URL of the page).
#' Can also be set as a side effect of the \code{\link{titlePanel}} function.
#' Can also be set as a side effect of the [titlePanel()] function.
#' @param responsive This option is deprecated; it is no longer optional with
#' Bootstrap 3.
#' @param theme Alternative Bootstrap stylesheet (normally a css file within the
#' www directory). For example, to use the theme located at
#' \code{www/bootstrap.css} you would use \code{theme = "bootstrap.css"}.
#' `www/bootstrap.css` you would use `theme = "bootstrap.css"`.
#'
#' @return A UI defintion that can be passed to the \link{shinyUI} function.
#' @return A UI defintion that can be passed to the [shinyUI] function.
#'
#' @details To create a fluid page use the \code{fluidPage} function and include
#' instances of \code{fluidRow} and \code{\link{column}} within it. As an
#' @details To create a fluid page use the `fluidPage` function and include
#' instances of `fluidRow` and [column()] within it. As an
#' alternative to low-level row and column functions you can also use
#' higher-level layout functions like \code{\link{sidebarLayout}}.
#' higher-level layout functions like [sidebarLayout()].
#'
#' @note See the \href{http://shiny.rstudio.com/articles/layout-guide.html}{
#' Shiny-Application-Layout-Guide} for additional details on laying out fluid
#' @note See the [
#' Shiny-Application-Layout-Guide](http://shiny.rstudio.com/articles/layout-guide.html) for additional details on laying out fluid
#' pages.
#'
#' @seealso \code{\link{column}}, \code{\link{sidebarLayout}}
#' @seealso [column()], [sidebarLayout()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -116,21 +116,21 @@ fluidRow <- function(...) {
#' Bootstrap 3.
#' @param theme Alternative Bootstrap stylesheet (normally a css file within the
#' www directory). For example, to use the theme located at
#' \code{www/bootstrap.css} you would use \code{theme = "bootstrap.css"}.
#' `www/bootstrap.css` you would use `theme = "bootstrap.css"`.
#'
#' @return A UI defintion that can be passed to the \link{shinyUI} function.
#' @return A UI defintion that can be passed to the [shinyUI] function.
#'
#' @details To create a fixed page use the \code{fixedPage} function and include
#' instances of \code{fixedRow} and \code{\link{column}} within it. Note that
#' unlike \code{\link{fluidPage}}, fixed pages cannot make use of higher-level
#' layout functions like \code{sidebarLayout}, rather, all layout must be done
#' with \code{fixedRow} and \code{column}.
#' @details To create a fixed page use the `fixedPage` function and include
#' instances of `fixedRow` and [column()] within it. Note that
#' unlike [fluidPage()], fixed pages cannot make use of higher-level
#' layout functions like `sidebarLayout`, rather, all layout must be done
#' with `fixedRow` and `column`.
#'
#' @note See the \href{http://shiny.rstudio.com/articles/layout-guide.html}{
#' Shiny Application Layout Guide} for additional details on laying out fixed
#' @note See the [
#' Shiny Application Layout Guide](http://shiny.rstudio.com/articles/layout-guide.html) for additional details on laying out fixed
#' pages.
#'
#' @seealso \code{\link{column}}
#' @seealso [column()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -169,8 +169,8 @@ fixedRow <- function(...) {
#' Create a column within a UI definition
#'
#' Create a column for use within a \code{\link{fluidRow}} or
#' \code{\link{fixedRow}}
#' Create a column for use within a [fluidRow()] or
#' [fixedRow()]
#'
#' @param width The grid width of the column (must be between 1 and 12)
#' @param ... Elements to include within the column
@@ -178,10 +178,10 @@ fixedRow <- function(...) {
#' previous column.
#'
#' @return A column that can be included within a
#' \code{\link{fluidRow}} or \code{\link{fixedRow}}.
#' [fluidRow()] or [fixedRow()].
#'
#'
#' @seealso \code{\link{fluidRow}}, \code{\link{fixedRow}}.
#' @seealso [fluidRow()], [fixedRow()].
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -240,7 +240,7 @@ column <- function(width, ..., offset = 0) {
#' @param windowTitle The title that should be displayed by the browser window.
#'
#' @details Calling this function has the side effect of including a
#' \code{title} tag within the head. You can also specify a page title
#' `title` tag within the head. You can also specify a page title
#' explicitly using the `title` parameter of the top-level page function.
#'
#'
@@ -263,16 +263,21 @@ titlePanel <- function(title, windowTitle=title) {
#' Layout a sidebar and main area
#'
#' Create a layout with a sidebar and main area. The sidebar is displayed with a
#' distinct background color and typically contains input controls. The main
#' Create a layout (`sidebarLayout()`) with a sidebar (`sidebarPanel()`) and
#' main area (`mainPanel()`). The sidebar is displayed with a distinct
#' background color and typically contains input controls. The main
#' area occupies 2/3 of the horizontal width and typically contains outputs.
#'
#' @param sidebarPanel The \link{sidebarPanel} containing input controls
#' @param mainPanel The \link{mainPanel} containing outputs
#' @param sidebarPanel The `sidebarPanel()` containing input controls.
#' @param mainPanel The `mainPanel()` containing outputs.
#' @param position The position of the sidebar relative to the main area ("left"
#' or "right")
#' @param fluid \code{TRUE} to use fluid layout; \code{FALSE} to use fixed
#' or "right").
#' @param fluid `TRUE` to use fluid layout; `FALSE` to use fixed
#' layout.
#' @param width The width of the sidebar and main panel. By default, the
#' sidebar takes up 1/3 of the width, and the main panel 2/3. The total
#' width must be 12 or less.
#' @param ... Output elements to include in the sidebar/main panel.
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -337,16 +342,34 @@ sidebarLayout <- function(sidebarPanel,
fixedRow(firstPanel, secondPanel)
}
#' @export
#' @rdname sidebarLayout
sidebarPanel <- function(..., width = 4) {
div(class=paste0("col-sm-", width),
tags$form(class="well",
...
)
)
}
#' @export
#' @rdname sidebarLayout
mainPanel <- function(..., width = 8) {
div(class=paste0("col-sm-", width),
...
)
}
#' Lay out UI elements vertically
#'
#' Create a container that includes one or more rows of content (each element
#' passed to the container will appear on it's own line in the UI)
#'
#' @param ... Elements to include within the container
#' @param fluid \code{TRUE} to use fluid layout; \code{FALSE} to use fixed
#' @param fluid `TRUE` to use fluid layout; `FALSE` to use fixed
#' layout.
#'
#' @seealso \code{\link{fluidPage}}, \code{\link{flowLayout}}
#' @seealso [fluidPage()], [flowLayout()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -377,14 +400,14 @@ verticalLayout <- function(..., fluid = TRUE) {
#' Lays out elements in a left-to-right, top-to-bottom arrangement. The elements
#' on a given row will be top-aligned with each other. This layout will not work
#' well with elements that have a percentage-based width (e.g.
#' \code{\link{plotOutput}} at its default setting of \code{width = "100\%"}).
#' [plotOutput()] at its default setting of `width = "100%"`).
#'
#' @param ... Unnamed arguments will become child elements of the layout. Named
#' arguments will become HTML attributes on the outermost tag.
#' @param cellArgs Any additional attributes that should be used for each cell
#' of the layout.
#'
#' @seealso \code{\link{verticalLayout}}
#' @seealso [verticalLayout()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -415,7 +438,7 @@ flowLayout <- function(..., cellArgs = list()) {
#' Input panel
#'
#' A \code{\link{flowLayout}} with a grey border and light grey background,
#' A [flowLayout()] with a grey border and light grey background,
#' suitable for wrapping inputs.
#'
#' @param ... Input controls or other HTML elements.
@@ -435,7 +458,7 @@ inputPanel <- function(...) {
#' arguments will become HTML attributes on the outermost tag.
#' @param cellWidths Character or numeric vector indicating the widths of the
#' individual cells. Recycling will be used if needed. Character values will
#' be interpreted as CSS lengths (see \code{\link{validateCssUnit}}), numeric
#' be interpreted as CSS lengths (see [validateCssUnit()]), numeric
#' values as pixels.
#' @param cellArgs Any additional attributes that should be used for each cell
#' of the layout.
@@ -509,41 +532,41 @@ splitLayout <- function(..., cellWidths = NULL, cellArgs = list()) {
#'
#' Creates row and column layouts with proportionally-sized cells, using the
#' Flex Box layout model of CSS3. These can be nested to create arbitrary
#' proportional-grid layouts. \strong{Warning:} Flex Box is not well supported
#' proportional-grid layouts. **Warning:** Flex Box is not well supported
#' by Internet Explorer, so these functions should only be used where modern
#' browsers can be assumed.
#'
#' @details If you try to use \code{fillRow} and \code{fillCol} inside of other
#' Shiny containers, such as \code{\link{sidebarLayout}},
#' \code{\link{navbarPage}}, or even \code{tags$div}, you will probably find
#' that they will not appear. This is due to \code{fillRow} and \code{fillCol}
#' defaulting to \code{height="100\%"}, which will only work inside of
#' @details If you try to use `fillRow` and `fillCol` inside of other
#' Shiny containers, such as [sidebarLayout()],
#' [navbarPage()], or even `tags$div`, you will probably find
#' that they will not appear. This is due to `fillRow` and `fillCol`
#' defaulting to `height="100%"`, which will only work inside of
#' containers that have determined their own size (rather than shrinking to
#' the size of their contents, as is usually the case in HTML).
#'
#' To avoid this problem, you have two options:
#' \itemize{
#' \item only use \code{fillRow}/\code{fillCol} inside of \code{fillPage},
#' \code{fillRow}, or \code{fillCol}
#' \item provide an explicit \code{height} argument to
#' \code{fillRow}/\code{fillCol}
#' \item only use `fillRow`/`fillCol` inside of `fillPage`,
#' `fillRow`, or `fillCol`
#' \item provide an explicit `height` argument to
#' `fillRow`/`fillCol`
#' }
#'
#' @param ... UI objects to put in each row/column cell; each argument will
#' occupy a single cell. (To put multiple items in a single cell, you can use
#' \code{\link{tagList}} or \code{\link{div}} to combine them.) Named
#' arguments will be used as attributes on the \code{div} element that
#' [tagList()] or [div()] to combine them.) Named
#' arguments will be used as attributes on the `div` element that
#' encapsulates the row/column.
#' @param flex Determines how space should be distributed to the cells. Can be a
#' single value like \code{1} or \code{2} to evenly distribute the available
#' single value like `1` or `2` to evenly distribute the available
#' space; or use a vector of numbers to specify the proportions. For example,
#' \code{flex = c(2, 3)} would cause the space to be split 40\%/60\% between
#' `flex = c(2, 3)` would cause the space to be split 40\%/60\% between
#' two cells. NA values will cause the corresponding cell to be sized
#' according to its contents (without growing or shrinking).
#' @param width,height The total amount of width and height to use for the
#' entire row/column. For the default height of \code{"100\%"} to be
#' effective, the parent must be \code{fillPage}, another
#' \code{fillRow}/\code{fillCol}, or some other HTML element whose height is
#' entire row/column. For the default height of `"100%"` to be
#' effective, the parent must be `fillPage`, another
#' `fillRow`/`fillCol`, or some other HTML element whose height is
#' not determined by the height of its contents.
#'
#' @examples

View File

@@ -1,66 +1,21 @@
#' @include utils.R
NULL
#' Create a Bootstrap page
#'
#' Create a Shiny UI page that loads the CSS and JavaScript for
#' \href{http://getbootstrap.com/}{Bootstrap}, and has no content in the page
#' body (other than what you provide).
#'
#' This function is primarily intended for users who are proficient in HTML/CSS,
#' and know how to lay out pages in Bootstrap. Most applications should use
#' \code{\link{fluidPage}} along with layout functions like
#' \code{\link{fluidRow}} and \code{\link{sidebarLayout}}.
#'
#' @param ... The contents of the document body.
#' @param title The browser window title (defaults to the host URL of the page)
#' @param responsive This option is deprecated; it is no longer optional with
#' Bootstrap 3.
#' @param theme Alternative Bootstrap stylesheet (normally a css file within the
#' www directory, e.g. \code{www/bootstrap.css})
#'
#' @return A UI defintion that can be passed to the \link{shinyUI} function.
#'
#' @note The \code{basicPage} function is deprecated, you should use the
#' \code{\link{fluidPage}} function instead.
#'
#' @seealso \code{\link{fluidPage}}, \code{\link{fixedPage}}
#' @export
bootstrapPage <- function(..., title = NULL, responsive = NULL, theme = NULL) {
if (!is.null(responsive)) {
shinyDeprecated("The 'responsive' argument is no longer used with Bootstrap 3.")
}
attachDependencies(
tagList(
if (!is.null(title)) tags$head(tags$title(title)),
if (!is.null(theme)) {
tags$head(tags$link(rel="stylesheet", type="text/css", href = theme))
},
# remainder of tags passed to the function
list(...)
),
bootstrapLib()
)
}
#' Bootstrap libraries
#'
#' This function returns a set of web dependencies necessary for using Bootstrap
#' components in a web page.
#'
#' It isn't necessary to call this function if you use
#' \code{\link{bootstrapPage}} or others which use \code{bootstrapPage}, such
#' \code{\link{basicPage}}, \code{\link{fluidPage}}, \code{\link{fillPage}},
#' \code{\link{pageWithSidebar}}, and \code{\link{navbarPage}}, because they
#' [bootstrapPage()] or others which use `bootstrapPage`, such
#' [basicPage()], [fluidPage()], [fillPage()],
#' [pageWithSidebar()], and [navbarPage()], because they
#' already include the Bootstrap web dependencies.
#'
#' @inheritParams bootstrapPage
#' @export
bootstrapLib <- function(theme = NULL) {
htmlDependency("bootstrap", "3.3.7",
htmlDependency("bootstrap", "3.4.1",
c(
href = "shared/bootstrap",
file = system.file("www/shared/bootstrap", package = "shiny")
@@ -76,43 +31,37 @@ bootstrapLib <- function(theme = NULL) {
)
}
#' @rdname bootstrapPage
#' @export
basicPage <- function(...) {
bootstrapPage(div(class="container-fluid", list(...)))
}
#' Create a page that fills the window
#'
#' \code{fillPage} creates a page whose height and width always fill the
#' `fillPage` creates a page whose height and width always fill the
#' available area of the browser window.
#'
#' The \code{\link{fluidPage}} and \code{\link{fixedPage}} functions are used
#' The [fluidPage()] and [fixedPage()] functions are used
#' for creating web pages that are laid out from the top down, leaving
#' whitespace at the bottom if the page content's height is smaller than the
#' browser window, and scrolling if the content is larger than the window.
#'
#' \code{fillPage} is designed to latch the document body's size to the size of
#' `fillPage` is designed to latch the document body's size to the size of
#' the window. This makes it possible to fill it with content that also scales
#' to the size of the window.
#'
#' For example, \code{fluidPage(plotOutput("plot", height = "100\%"))} will not
#' work as expected; the plot element's effective height will be \code{0},
#' because the plot's containing elements (\code{<div>} and \code{<body>}) have
#' \emph{automatic} height; that is, they determine their own height based on
#' For example, `fluidPage(plotOutput("plot", height = "100%"))` will not
#' work as expected; the plot element's effective height will be `0`,
#' because the plot's containing elements (`<div>` and `<body>`) have
#' *automatic* height; that is, they determine their own height based on
#' the height of their contained elements. However,
#' \code{fillPage(plotOutput("plot", height = "100\%"))} will work because
#' \code{fillPage} fixes the \code{<body>} height at 100\% of the window height.
#' `fillPage(plotOutput("plot", height = "100%"))` will work because
#' `fillPage` fixes the `<body>` height at 100\% of the window height.
#'
#' Note that \code{fillPage(plotOutput("plot"))} will not cause the plot to fill
#' the page. Like most Shiny output widgets, \code{plotOutput}'s default height
#' is a fixed number of pixels. You must explicitly set \code{height = "100\%"}
#' Note that `fillPage(plotOutput("plot"))` will not cause the plot to fill
#' the page. Like most Shiny output widgets, `plotOutput`'s default height
#' is a fixed number of pixels. You must explicitly set `height = "100%"`
#' if you want a plot (or htmlwidget, say) to fill its container.
#'
#' One must be careful what layouts/panels/elements come between the
#' \code{fillPage} and the plots/widgets. Any container that has an automatic
#' height will cause children with \code{height = "100\%"} to misbehave. Stick
#' `fillPage` and the plots/widgets. Any container that has an automatic
#' height will cause children with `height = "100%"` to misbehave. Stick
#' to functions that are designed for fill layouts, such as the ones in this
#' package.
#'
@@ -127,7 +76,7 @@ basicPage <- function(...) {
#' be interpreted as top, right, bottom, and left respectively.
#' @param title The title to use for the browser window/tab (it will not be
#' shown in the document).
#' @param bootstrap If \code{TRUE}, load the Bootstrap CSS library.
#' @param bootstrap If `TRUE`, load the Bootstrap CSS library.
#' @param theme URL to alternative Bootstrap stylesheet.
#'
#' @examples
@@ -178,115 +127,60 @@ collapseSizes <- function(padding) {
collapse = " ")
}
#' Create a page with a sidebar
#'
#' Create a Shiny UI that contains a header with the application title, a
#' sidebar for input controls, and a main area for output.
#'
#' @param headerPanel The \link{headerPanel} with the application title
#' @param sidebarPanel The \link{sidebarPanel} containing input controls
#' @param mainPanel The \link{mainPanel} containing outputs
#' @return A UI defintion that can be passed to the \link{shinyUI} function
#'
#' @note This function is deprecated. You should use \code{\link{fluidPage}}
#' along with \code{\link{sidebarLayout}} to implement a page with a sidebar.
#'
#' @examples
#' # Define UI
#' pageWithSidebar(
#'
#' # Application title
#' headerPanel("Hello Shiny!"),
#'
#' # Sidebar with a slider input
#' sidebarPanel(
#' sliderInput("obs",
#' "Number of observations:",
#' min = 0,
#' max = 1000,
#' value = 500)
#' ),
#'
#' # Show a plot of the generated distribution
#' mainPanel(
#' plotOutput("distPlot")
#' )
#' )
#' @export
pageWithSidebar <- function(headerPanel,
sidebarPanel,
mainPanel) {
bootstrapPage(
# basic application container divs
div(
class="container-fluid",
div(class="row",
headerPanel
),
div(class="row",
sidebarPanel,
mainPanel
)
)
)
}
#' Create a page with a top level navigation bar
#'
#' Create a page that contains a top level navigation bar that can be used to
#' toggle a set of \code{\link{tabPanel}} elements.
#' toggle a set of [tabPanel()] elements.
#'
#' @param title The title to display in the navbar
#' @param ... \code{\link{tabPanel}} elements to include in the page. The
#' \code{navbarMenu} function also accepts strings, which will be used as menu
#' section headers. If the string is a set of dashes like \code{"----"} a
#' @param ... [tabPanel()] elements to include in the page. The
#' `navbarMenu` function also accepts strings, which will be used as menu
#' section headers. If the string is a set of dashes like `"----"` a
#' horizontal separator will be displayed in the menu.
#' @param id If provided, you can use \code{input$}\emph{\code{id}} in your
#' @param id If provided, you can use `input$`*`id`* in your
#' server logic to determine which of the current tabs is active. The value
#' will correspond to the \code{value} argument that is passed to
#' \code{\link{tabPanel}}.
#' @param selected The \code{value} (or, if none was supplied, the \code{title})
#' of the tab that should be selected by default. If \code{NULL}, the first
#' will correspond to the `value` argument that is passed to
#' [tabPanel()].
#' @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 position Determines whether the navbar should be displayed at the top
#' of the page with normal scrolling behavior (\code{"static-top"}), pinned at
#' the top (\code{"fixed-top"}), or pinned at the bottom
#' (\code{"fixed-bottom"}). Note that using \code{"fixed-top"} or
#' \code{"fixed-bottom"} will cause the navbar to overlay your body content,
#' of the page with normal scrolling behavior (`"static-top"`), pinned at
#' the top (`"fixed-top"`), or pinned at the bottom
#' (`"fixed-bottom"`). Note that using `"fixed-top"` or
#' `"fixed-bottom"` will cause the navbar to overlay your body content,
#' unless you add padding, e.g.: \code{tags$style(type="text/css", "body
#' {padding-top: 70px;}")}
#' @param header Tag or list of tags to display as a common header above all
#' tabPanels.
#' @param footer Tag or list of tags to display as a common footer below all
#' tabPanels
#' @param inverse \code{TRUE} to use a dark background and light text for the
#' @param inverse `TRUE` to use a dark background and light text for the
#' navigation bar
#' @param collapsible \code{TRUE} to automatically collapse the navigation
#' @param collapsible `TRUE` to automatically collapse the navigation
#' elements into a menu when the width of the browser is less than 940 pixels
#' (useful for viewing on smaller touchscreen device)
#' @param collapsable Deprecated; use \code{collapsible} instead.
#' @param fluid \code{TRUE} to use a fluid layout. \code{FALSE} to use a fixed
#' @param collapsable Deprecated; use `collapsible` instead.
#' @param fluid `TRUE` to use a fluid layout. `FALSE` to use a fixed
#' layout.
#' @param responsive This option is deprecated; it is no longer optional with
#' Bootstrap 3.
#' @param theme Alternative Bootstrap stylesheet (normally a css file within the
#' www directory). For example, to use the theme located at
#' \code{www/bootstrap.css} you would use \code{theme = "bootstrap.css"}.
#' `www/bootstrap.css` you would use `theme = "bootstrap.css"`.
#' @param windowTitle The title that should be displayed by the browser window.
#' Useful if \code{title} is not a string.
#' @param icon Optional icon to appear on a \code{navbarMenu} tab.
#' Useful if `title` is not a string.
#' @param icon Optional icon to appear on a `navbarMenu` tab.
#'
#' @return A UI defintion that can be passed to the \link{shinyUI} function.
#' @return A UI defintion that can be passed to the [shinyUI] function.
#'
#' @details The \code{navbarMenu} function can be used to create an embedded
#' @details The `navbarMenu` function can be used to create an embedded
#' menu within the navbar that in turns includes additional tabPanels (see
#' example below).
#'
#' @seealso \code{\link{tabPanel}}, \code{\link{tabsetPanel}},
#' \code{\link{updateNavbarPage}}, \code{\link{insertTab}},
#' \code{\link{showTab}}
#' @seealso [tabPanel()], [tabsetPanel()],
#' [updateNavbarPage()], [insertTab()],
#' [showTab()]
#'
#' @examples
#' navbarPage("App Title",
@@ -394,9 +288,9 @@ navbarPage <- function(title,
)
}
#' @param menuName A name that identifies this \code{navbarMenu}. This
#' @param menuName A name that identifies this `navbarMenu`. This
#' is needed if you want to insert/remove or show/hide an entire
#' \code{navbarMenu}.
#' `navbarMenu`.
#'
#' @rdname navbarPage
#' @export
@@ -408,31 +302,10 @@ navbarMenu <- function(title, ..., menuName = title, icon = NULL) {
class = "shiny.navbarmenu")
}
#' Create a header panel
#'
#' Create a header panel containing an application title.
#'
#' @param title An application title to display
#' @param windowTitle The title that should be displayed by the browser window.
#' Useful if \code{title} is not a string.
#' @return A headerPanel that can be passed to \link{pageWithSidebar}
#'
#' @examples
#' headerPanel("Hello Shiny!")
#' @export
headerPanel <- function(title, windowTitle=title) {
tagList(
tags$head(tags$title(windowTitle)),
div(class="col-sm-12",
h1(title)
)
)
}
#' Create a well panel
#'
#' Creates a panel with a slightly inset border and grey background. Equivalent
#' to Bootstrap's \code{well} CSS class.
#' to Bootstrap's `well` CSS class.
#'
#' @param ... UI elements to include inside the panel.
#' @return The newly created panel.
@@ -441,81 +314,28 @@ wellPanel <- function(...) {
div(class="well", ...)
}
#' Create a sidebar panel
#'
#' Create a sidebar panel containing input controls that can in turn be passed
#' to \code{\link{sidebarLayout}}.
#'
#' @param ... UI elements to include on the sidebar
#' @param width The width of the sidebar. For fluid layouts this is out of 12
#' total units; for fixed layouts it is out of whatever the width of the
#' sidebar's parent column is.
#' @return A sidebar that can be passed to \code{\link{sidebarLayout}}
#'
#' @examples
#' # Sidebar with controls to select a dataset and specify
#' # the number of observations to view
#' sidebarPanel(
#' selectInput("dataset", "Choose a dataset:",
#' choices = c("rock", "pressure", "cars")),
#'
#' numericInput("obs", "Observations:", 10)
#' )
#' @export
sidebarPanel <- function(..., width = 4) {
div(class=paste0("col-sm-", width),
tags$form(class="well",
...
)
)
}
#' Create a main panel
#'
#' Create a main panel containing output elements that can in turn be passed to
#' \code{\link{sidebarLayout}}.
#'
#' @param ... Output elements to include in the main panel
#' @param width The width of the main panel. For fluid layouts this is out of 12
#' total units; for fixed layouts it is out of whatever the width of the main
#' panel's parent column is.
#' @return A main panel that can be passed to \code{\link{sidebarLayout}}.
#'
#' @examples
#' # Show the caption and plot of the requested variable against mpg
#' mainPanel(
#' h3(textOutput("caption")),
#' plotOutput("mpgPlot")
#' )
#' @export
mainPanel <- function(..., width = 8) {
div(class=paste0("col-sm-", width),
...
)
}
#' Conditional Panel
#'
#' Creates a panel that is visible or not, depending on the value of a
#' JavaScript expression. The JS expression is evaluated once at startup and
#' whenever Shiny detects a relevant change in input/output.
#'
#' In the JS expression, you can refer to \code{input} and \code{output}
#' In the JS expression, you can refer to `input` and `output`
#' JavaScript objects that contain the current values of input and output. For
#' example, if you have an input with an id of \code{foo}, then you can use
#' \code{input.foo} to read its value. (Be sure not to modify the input/output
#' example, if you have an input with an id of `foo`, then you can use
#' `input.foo` to read its value. (Be sure not to modify the input/output
#' objects, as this may cause unpredictable behavior.)
#'
#' @param condition A JavaScript expression that will be evaluated repeatedly to
#' determine whether the panel should be displayed.
#' @param ns The \code{\link[=NS]{namespace}} object of the current module, if
#' @param ns The [`namespace()`][NS] object of the current module, if
#' any.
#' @param ... Elements to include in the panel.
#'
#' @note You are not recommended to use special JavaScript characters such as a
#' period \code{.} in the input id's, but if you do use them anyway, for
#' example, \code{inputId = "foo.bar"}, you will have to use
#' \code{input["foo.bar"]} instead of \code{input.foo.bar} to read the input
#' period `.` in the input id's, but if you do use them anyway, for
#' example, `inputId = "foo.bar"`, you will have to use
#' `input["foo.bar"]` instead of `input.foo.bar` to read the input
#' value.
#' @examples
#' ## Only run this example in interactive R sessions
@@ -589,18 +409,19 @@ helpText <- function(...) {
#' Create a tab panel
#'
#' Create a tab panel that can be included within a \code{\link{tabsetPanel}}.
#' 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 \code{tabsetPanel} reports
#' that this tab is selected. If omitted and \code{tabsetPanel} has an
#' \code{id}, then the title will be used..
#' @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..
#' @param icon Optional icon to appear on the tab. This attribute is only
#' valid when using a \code{tabPanel} within a \code{\link{navbarPage}}.
#' @return A tab that can be passed to \code{\link{tabsetPanel}}
#' valid when using a `tabPanel` within a [navbarPage()].
#' @return A tab that can be passed to [tabsetPanel()]
#'
#' @seealso \code{\link{tabsetPanel}}
#' @seealso [tabsetPanel()]
#'
#' @examples
#' # Show a tabset that includes a plot, summary, and
@@ -623,25 +444,25 @@ tabPanel <- function(title, ..., value = title, icon = NULL) {
#' Create a tabset panel
#'
#' Create a tabset that contains \code{\link{tabPanel}} elements. Tabsets are
#' Create a tabset that contains [tabPanel()] elements. Tabsets are
#' useful for dividing output into multiple independently viewable sections.
#'
#' @param ... \code{\link{tabPanel}} elements to include in the tabset
#' @param id If provided, you can use \code{input$}\emph{\code{id}} in your
#' @param ... [tabPanel()] elements to include in the tabset
#' @param id If provided, you can use `input$`*`id`* in your
#' server logic to determine which of the current tabs is active. The value
#' will correspond to the \code{value} argument that is passed to
#' \code{\link{tabPanel}}.
#' @param selected The \code{value} (or, if none was supplied, the \code{title})
#' of the tab that should be selected by default. If \code{NULL}, the first
#' will correspond to the `value` argument that is passed to
#' [tabPanel()].
#' @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 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 \code{\link{mainPanel}}
#' @return A tabset that can be passed to [mainPanel()]
#'
#' @seealso \code{\link{tabPanel}}, \code{\link{updateTabsetPanel}},
#' \code{\link{insertTab}}, \code{\link{showTab}}
#' @seealso [tabPanel()], [updateTabsetPanel()],
#' [insertTab()], [showTab()]
#'
#' @examples
#' # Show a tabset that includes a plot, summary, and
@@ -687,29 +508,29 @@ tabsetPanel <- function(...,
#' Create a navigation list panel that provides a list of links on the left
#' which navigate to a set of tabPanels displayed to the right.
#'
#' @param ... \code{\link{tabPanel}} elements to include in the navlist
#' @param id If provided, you can use \code{input$}\emph{\code{id}} in your
#' @param ... [tabPanel()] elements to include in the navlist
#' @param id If provided, you can use `input$`*`id`* in your
#' server logic to determine which of the current navlist items is active. The
#' value will correspond to the \code{value} argument that is passed to
#' \code{\link{tabPanel}}.
#' @param selected The \code{value} (or, if none was supplied, the \code{title})
#' of the navigation item that should be selected by default. If \code{NULL},
#' value will correspond to the `value` argument that is passed to
#' [tabPanel()].
#' @param selected The `value` (or, if none was supplied, the `title`)
#' of the navigation item that should be selected by default. If `NULL`,
#' the first navigation will be selected.
#' @param well \code{TRUE} to place a well (gray rounded rectangle) around the
#' @param well `TRUE` to place a well (gray rounded rectangle) around the
#' navigation list.
#' @param fluid \code{TRUE} to use fluid layout; \code{FALSE} to use fixed
#' @param fluid `TRUE` to use fluid layout; `FALSE` to use fixed
#' layout.
#' @param widths Column withs of the navigation list and tabset content areas
#' respectively.
#'
#' @details You can include headers within the \code{navlistPanel} by including
#' @details You can include headers within the `navlistPanel` by including
#' plain text elements in the list. Versions of Shiny before 0.11 supported
#' separators with "------", but as of 0.11, separators were no longer
#' supported. This is because version 0.11 switched to Bootstrap 3, which
#' doesn't support separators.
#'
#' @seealso \code{\link{tabPanel}}, \code{\link{updateNavlistPanel}},
#' \code{\link{insertTab}}, \code{\link{showTab}}
#' @seealso [tabPanel()], [updateNavlistPanel()],
#' [insertTab()], [showTab()]
#'
#' @examples
#' fluidPage(
@@ -918,14 +739,14 @@ buildTabItem <- function(index, tabsetId, foundSelected, tabs = NULL,
#' Create a text output element
#'
#' Render a reactive output variable as text within an application page. The
#' text will be included within an HTML \code{div} tag by default.
#' text will be included within an HTML `div` tag by default.
#' @param outputId output variable to read the value from
#' @param container a function to generate an HTML element to contain the text
#' @param inline use an inline (\code{span()}) or block container (\code{div()})
#' @param inline use an inline (`span()`) or block container (`div()`)
#' for the output
#' @return A text output element that can be included in a panel
#' @details Text is HTML-escaped prior to rendering. This element is often used
#' to display \link{renderText} output variables.
#' to display [renderText] output variables.
#' @examples
#' h3(textOutput("caption"))
#' @export
@@ -936,14 +757,14 @@ textOutput <- function(outputId, container = if (inline) span else div, inline =
#' Create a verbatim text output element
#'
#' Render a reactive output variable as verbatim text within an
#' application page. The text will be included within an HTML \code{pre} tag.
#' application page. The text will be included within an HTML `pre` tag.
#' @param outputId output variable to read the value from
#' @param placeholder if the output is empty or \code{NULL}, should an empty
#' @param placeholder if the output is empty or `NULL`, should an empty
#' rectangle be displayed to serve as a placeholder? (does not affect
#' behavior when the the output in nonempty)
#' @return A verbatim text output element that can be included in a panel
#' @details Text is HTML-escaped prior to rendering. This element is often used
#' with the \link{renderPrint} function to preserve fixed-width formatting
#' with the [renderPrint] function to preserve fixed-width formatting
#' of printed objects.
#' @examples
#' ## Only run this example in interactive R sessions
@@ -1066,86 +887,85 @@ imageOutput <- function(outputId, width = "100%", height="400px",
#' Create an plot or image output element
#'
#' Render a \code{\link{renderPlot}} or \code{\link{renderImage}} within an
#' Render a [renderPlot()] or [renderImage()] within an
#' application page.
#'
#' @section Interactive plots:
#'
#' Plots and images in Shiny support mouse-based interaction, via clicking,
#' double-clicking, hovering, and brushing. When these interaction events
#' occur, the mouse coordinates will be sent to the server as \code{input$}
#' variables, as specified by \code{click}, \code{dblclick}, \code{hover}, or
#' \code{brush}.
#' occur, the mouse coordinates will be sent to the server as `input$`
#' variables, as specified by `click`, `dblclick`, `hover`, or
#' `brush`.
#'
#' For \code{plotOutput}, the coordinates will be sent scaled to the data
#' For `plotOutput`, the coordinates will be sent scaled to the data
#' space, if possible. (At the moment, plots generated by base graphics and
#' ggplot2 support this scaling, although plots generated by lattice and
#' others do not.) If scaling is not possible, the raw pixel coordinates will
#' be sent. For \code{imageOutput}, the coordinates will be sent in raw pixel
#' be sent. For `imageOutput`, the coordinates will be sent in raw pixel
#' coordinates.
#'
#' With ggplot2 graphics, the code in \code{renderPlot} should return a ggplot
#' With ggplot2 graphics, the code in `renderPlot` should return a ggplot
#' object; if instead the code prints the ggplot2 object with something like
#' \code{print(p)}, then the coordinates for interactive graphics will not be
#' `print(p)`, then the coordinates for interactive graphics will not be
#' properly scaled to the data space.
#'
#' @param outputId output variable to read the plot/image from.
#' @param width,height Image width/height. Must be a valid CSS unit (like
#' \code{"100\%"}, \code{"400px"}, \code{"auto"}) or a number, which will be
#' coerced to a string and have \code{"px"} appended. These two arguments are
#' ignored when \code{inline = TRUE}, in which case the width/height of a plot
#' must be specified in \code{renderPlot()}. Note that, for height, using
#' \code{"auto"} or \code{"100\%"} generally will not work as expected,
#' `"100%"`, `"400px"`, `"auto"`) or a number, which will be
#' coerced to a string and have `"px"` appended. These two arguments are
#' ignored when `inline = TRUE`, in which case the width/height of a plot
#' must be specified in `renderPlot()`. Note that, for height, using
#' `"auto"` or `"100%"` generally will not work as expected,
#' because of how height is computed with HTML/CSS.
#' @param click This can be \code{NULL} (the default), a string, or an object
#' created by the \code{\link{clickOpts}} function. If you use a value like
#' \code{"plot_click"} (or equivalently, \code{clickOpts(id="plot_click")}),
#' @param click This can be `NULL` (the default), a string, or an object
#' created by the [clickOpts()] function. If you use a value like
#' `"plot_click"` (or equivalently, `clickOpts(id="plot_click")`),
#' the plot will send coordinates to the server whenever it is clicked, and
#' the value will be accessible via \code{input$plot_click}. The value will be
#' a named list with \code{x} and \code{y} elements indicating the mouse
#' the value will be accessible via `input$plot_click`. The value will be
#' a named list with `x` and `y` elements indicating the mouse
#' position.
#' @param dblclick This is just like the \code{click} argument, but for
#' @param dblclick This is just like the `click` argument, but for
#' double-click events.
#' @param hover Similar to the \code{click} argument, this can be \code{NULL}
#' @param hover Similar to the `click` argument, this can be `NULL`
#' (the default), a string, or an object created by the
#' \code{\link{hoverOpts}} function. If you use a value like
#' \code{"plot_hover"} (or equivalently, \code{hoverOpts(id="plot_hover")}),
#' [hoverOpts()] function. If you use a value like
#' `"plot_hover"` (or equivalently, `hoverOpts(id="plot_hover")`),
#' the plot will send coordinates to the server pauses on the plot, and the
#' value will be accessible via \code{input$plot_hover}. The value will be a
#' named list with \code{x} and \code{y} elements indicating the mouse
#' value will be accessible via `input$plot_hover`. The value will be a
#' named list with `x` and `y` elements indicating the mouse
#' position. To control the hover time or hover delay type, you must use
#' \code{\link{hoverOpts}}.
#' @param clickId Deprecated; use \code{click} instead. Also see the
#' \code{\link{clickOpts}} function.
#' @param hoverId Deprecated; use \code{hover} instead. Also see the
#' \code{\link{hoverOpts}} function.
#' @param hoverDelay Deprecated; use \code{hover} instead. Also see the
#' \code{\link{hoverOpts}} function.
#' @param hoverDelayType Deprecated; use \code{hover} instead. Also see the
#' \code{\link{hoverOpts}} function.
#' @param brush Similar to the \code{click} argument, this can be \code{NULL}
#' [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
#' \code{\link{brushOpts}} function. If you use a value like
#' \code{"plot_brush"} (or equivalently, \code{brushOpts(id="plot_brush")}),
#' [brushOpts()] function. If you use a value like
#' `"plot_brush"` (or equivalently, `brushOpts(id="plot_brush")`),
#' the plot will allow the user to "brush" in the plotting area, and will send
#' information about the brushed area to the server, and the value will be
#' accessible via \code{input$plot_brush}. Brushing means that the user will
#' accessible via `input$plot_brush`. Brushing means that the user will
#' be able to draw a rectangle in the plotting area and drag it around. The
#' value will be a named list with \code{xmin}, \code{xmax}, \code{ymin}, and
#' \code{ymax} elements indicating the brush area. To control the brush
#' behavior, use \code{\link{brushOpts}}. Multiple
#' \code{imageOutput}/\code{plotOutput} calls may share the same \code{id}
#' value will be a named list with `xmin`, `xmax`, `ymin`, and
#' `ymax` elements indicating the brush area. To control the brush
#' behavior, use [brushOpts()]. Multiple
#' `imageOutput`/`plotOutput` calls may share the same `id`
#' value; brushing one image or plot will cause any other brushes with the
#' same \code{id} to disappear.
#' same `id` to disappear.
#' @inheritParams textOutput
#' @note The arguments \code{clickId} and \code{hoverId} only work for R base
#' graphics (see the \pkg{\link[graphics:graphics-package]{graphics}} package). They do not work for
#' \pkg{\link[grid:grid-package]{grid}}-based graphics, such as \pkg{ggplot2},
#' \pkg{lattice}, and so on.
#'
#' @note The arguments `clickId` and `hoverId` only work for R base graphics
#' (see the \pkg{\link[graphics:graphics-package]{graphics}} package). They do
#' not work for \pkg{\link[grid:grid-package]{grid}}-based graphics, such as
#' \pkg{ggplot2}, \pkg{lattice}, and so on.
#' @return A plot or image output element that can be included in a panel.
#' @seealso For the corresponding server-side functions, see
#' \code{\link{renderPlot}} and \code{\link{renderImage}}.
#' @seealso For the corresponding server-side functions, see [renderPlot()] and
#' [renderImage()].
#'
#' @examples
#' # Only run these examples in interactive R sessions
@@ -1324,15 +1144,15 @@ plotOutput <- function(outputId, width = "100%", height="400px",
#' Create a table output element
#'
#' Render a \code{\link{renderTable}} or \code{\link{renderDataTable}} within an
#' application page. \code{renderTable} uses a standard HTML table, while
#' \code{renderDataTable} uses the DataTables Javascript library to create an
#' Render a [renderTable()] or [renderDataTable()] within an
#' application page. `renderTable` uses a standard HTML table, while
#' `renderDataTable` uses the DataTables Javascript library to create an
#' interactive table with more features.
#'
#' @param outputId output variable to read the table from
#' @return A table output element that can be included in a panel
#'
#' @seealso \code{\link{renderTable}}, \code{\link{renderDataTable}}.
#' @seealso [renderTable()], [renderDataTable()].
#' @examples
#' ## Only run this example in interactive R sessions
#' if (interactive()) {
@@ -1394,11 +1214,11 @@ dataTableOutput <- function(outputId) {
#' Create an HTML output element
#'
#' Render a reactive output variable as HTML within an application page. The
#' text will be included within an HTML \code{div} tag, and is presumed to
#' text will be included within an HTML `div` tag, and is presumed to
#' contain HTML content which should not be escaped.
#'
#' \code{uiOutput} is intended to be used with \code{renderUI} on the server
#' side. It is currently just an alias for \code{htmlOutput}.
#' `uiOutput` is intended to be used with `renderUI` on the server
#' side. It is currently just an alias for `htmlOutput`.
#'
#' @param outputId output variable to read the value from
#' @param ... Other arguments to pass to the container tag function. This is
@@ -1430,10 +1250,10 @@ uiOutput <- htmlOutput
#'
#' Use these functions to create a download button or link; when clicked, it
#' will initiate a browser download. The filename and contents are specified by
#' the corresponding \code{\link{downloadHandler}} defined in the server
#' the corresponding [downloadHandler()] defined in the server
#' function.
#'
#' @param outputId The name of the output slot that the \code{downloadHandler}
#' @param outputId The name of the output slot that the `downloadHandler`
#' 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.
@@ -1456,7 +1276,7 @@ uiOutput <- htmlOutput
#' }
#'
#' @aliases downloadLink
#' @seealso \code{\link{downloadHandler}}
#' @seealso [downloadHandler()]
#' @export
downloadButton <- function(outputId,
label="Download",
@@ -1485,26 +1305,26 @@ downloadLink <- function(outputId, label="Download", class=NULL, ...) {
#' Create an icon
#'
#' Create an icon for use within a page. Icons can appear on their own, inside
#' of a button, or as an icon for a \code{\link{tabPanel}} within a
#' \code{\link{navbarPage}}.
#' of a button, or as an icon for a [tabPanel()] within a
#' [navbarPage()].
#'
#' @param name Name of icon. Icons are drawn from the
#' \href{https://fontawesome.com/}{Font Awesome Free} (currently icons from
#' [Font Awesome Free](https://fontawesome.com/) (currently icons from
#' the v5.3.1 set are supported with the v4 naming convention) and
#' \href{http://getbootstrap.com/components/#glyphicons}{Glyphicons}
#' [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
#' "calendar")
#' @param class Additional classes to customize the style of the icon (see the
#' \href{http://fontawesome.io/examples/}{usage examples} for details on
#' [usage examples](http://fontawesome.io/examples/) for details on
#' supported styles).
#' @param lib Icon library to use ("font-awesome" or "glyphicon")
#'
#' @return An icon element
#'
#' @seealso For lists of available icons, see
#' \href{http://fontawesome.io/icons/}{http://fontawesome.io/icons/} and
#' \href{http://getbootstrap.com/components/#glyphicons}{http://getbootstrap.com/components/#glyphicons}.
#' [http://fontawesome.io/icons/](http://fontawesome.io/icons/) and
#' [http://getbootstrap.com/components/#glyphicons](http://getbootstrap.com/components/#glyphicons).
#'
#'
#' @examples

View File

@@ -1,44 +1,44 @@
#' Create a disk cache object
#'
#' A disk cache object is a key-value store that saves the values as files in a
#' directory on disk. Objects can be stored and retrieved using the \code{get()}
#' and \code{set()} methods. Objects are automatically pruned from the cache
#' according to the parameters \code{max_size}, \code{max_age}, \code{max_n},
#' and \code{evict}.
#' directory on disk. Objects can be stored and retrieved using the `get()`
#' and `set()` methods. Objects are automatically pruned from the cache
#' according to the parameters `max_size`, `max_age`, `max_n`,
#' and `evict`.
#'
#'
#' @section Missing Keys:
#'
#' The \code{missing} and \code{exec_missing} parameters controls what happens
#' when \code{get()} is called with a key that is not in the cache (a cache
#' miss). The default behavior is to return a \code{\link{key_missing}}
#' object. This is a \emph{sentinel value} that indicates that the key was not
#' The `missing` and `exec_missing` parameters controls what happens
#' when `get()` is called with a key that is not in the cache (a cache
#' miss). The default behavior is to return a [key_missing()]
#' object. This is a *sentinel value* that indicates that the key was not
#' present in the cache. You can test if the returned value represents a
#' missing key by using the \code{\link{is.key_missing}} function. You can
#' also have \code{get()} return a different sentinel value, like \code{NULL}.
#' missing key by using the [is.key_missing()] function. You can
#' also have `get()` return a different sentinel value, like `NULL`.
#' If you want to throw an error on a cache miss, you can do so by providing a
#' function for \code{missing} that takes one argument, the key, and also use
#' \code{exec_missing=TRUE}.
#' function for `missing` that takes one argument, the key, and also use
#' `exec_missing=TRUE`.
#'
#' When the cache is created, you can supply a value for \code{missing}, which
#' When the cache is created, you can supply a value for `missing`, which
#' sets the default value to be returned for missing values. It can also be
#' overridden when \code{get()} is called, by supplying a \code{missing}
#' argument. For example, if you use \code{cache$get("mykey", missing =
#' NULL)}, it will return \code{NULL} if the key is not in the cache.
#' overridden when `get()` is called, by supplying a `missing`
#' argument. For example, if you use `cache$get("mykey", missing =
#' NULL)`, it will return `NULL` if the key is not in the cache.
#'
#' If your cache is configured so that \code{get()} returns a sentinel value
#' to represent a cache miss, then \code{set} will also not allow you to store
#' If your cache is configured so that `get()` returns a sentinel value
#' to represent a cache miss, then `set` will also not allow you to store
#' the sentinel value in the cache. It will throw an error if you attempt to
#' do so.
#'
#' Instead of returning the same sentinel value each time there is cache miss,
#' the cache can execute a function each time \code{get()} encounters missing
#' key. If the function returns a value, then \code{get()} will in turn return
#' the cache can execute a function each time `get()` encounters missing
#' key. If the function returns a value, then `get()` will in turn return
#' that value. However, a more common use is for the function to throw an
#' error. If an error is thrown, then \code{get()} will not return a value.
#' error. If an error is thrown, then `get()` will not return a value.
#'
#' To do this, pass a one-argument function to \code{missing}, and use
#' \code{exec_missing=TRUE}. For example, if you want to throw an error that
#' To do this, pass a one-argument function to `missing`, and use
#' `exec_missing=TRUE`. For example, if you want to throw an error that
#' prints the missing key, you could do this:
#'
#' \preformatted{
@@ -50,58 +50,58 @@
#' )
#' }
#'
#' If you use this, the code that calls \code{get()} should be wrapped with
#' \code{\link{tryCatch}()} to gracefully handle missing keys.
#' If you use this, the code that calls `get()` should be wrapped with
#' [tryCatch()] to gracefully handle missing keys.
#'
#' @section Cache pruning:
#'
#' Cache pruning occurs when \code{set()} is called, or it can be invoked
#' manually by calling \code{prune()}.
#' Cache pruning occurs when `set()` is called, or it can be invoked
#' manually by calling `prune()`.
#'
#' The disk cache will throttle the pruning so that it does not happen on
#' every call to \code{set()}, because the filesystem operations for checking
#' every call to `set()`, because the filesystem operations for checking
#' the status of files can be slow. Instead, it will prune once in every 20
#' calls to \code{set()}, or if at least 5 seconds have elapsed since the last
#' calls to `set()`, or if at least 5 seconds have elapsed since the last
#' prune occurred, whichever is first. These parameters are currently not
#' customizable, but may be in the future.
#'
#' When a pruning occurs, if there are any objects that are older than
#' \code{max_age}, they will be removed.
#' `max_age`, they will be removed.
#'
#' The \code{max_size} and \code{max_n} parameters are applied to the cache as
#' a whole, in contrast to \code{max_age}, which is applied to each object
#' The `max_size` and `max_n` parameters are applied to the cache as
#' a whole, in contrast to `max_age`, which is applied to each object
#' individually.
#'
#' If the number of objects in the cache exceeds \code{max_n}, then objects
#' If the number of objects in the cache exceeds `max_n`, then objects
#' will be removed from the cache according to the eviction policy, which is
#' set with the \code{evict} parameter. Objects will be removed so that the
#' number of items is \code{max_n}.
#' set with the `evict` parameter. Objects will be removed so that the
#' number of items is `max_n`.
#'
#' If the size of the objects in the cache exceeds \code{max_size}, then
#' If the size of the objects in the cache exceeds `max_size`, then
#' objects will be removed from the cache. Objects will be removed from the
#' cache so that the total size remains under \code{max_size}. Note that the
#' cache so that the total size remains under `max_size`. Note that the
#' size is calculated using the size of the files, not the size of disk space
#' used by the files -- these two values can differ because of files are
#' used by the files --- these two values can differ because of files are
#' stored in blocks on disk. For example, if the block size is 4096 bytes,
#' then a file that is one byte in size will take 4096 bytes on disk.
#'
#' Another time that objects can be removed from the cache is when
#' \code{get()} is called. If the target object is older than \code{max_age},
#' `get()` is called. If the target object is older than `max_age`,
#' it will be removed and the cache will report it as a missing value.
#'
#' @section Eviction policies:
#'
#' If \code{max_n} or \code{max_size} are used, then objects will be removed
#' If `max_n` or `max_size` are used, then objects will be removed
#' from the cache according to an eviction policy. The available eviction
#' policies are:
#'
#' \describe{
#' \item{\code{"lru"}}{
#' \item{`"lru"`}{
#' Least Recently Used. The least recently used objects will be removed.
#' This uses the filesystem's mtime property. When "lru" is used, each
#' \code{get()} is called, it will update the file's mtime.
#' `get()` is called, it will update the file's mtime.
#' }
#' \item{\code{"fifo"}}{
#' \item{`"fifo"`}{
#' First-in-first-out. The oldest objects will be removed.
#' }
#' }
@@ -126,17 +126,17 @@
#' Redis, SQLite, mySQL, or similar.
#'
#' When multiple processes share a cache directory, there are some potential
#' race conditions. For example, if your code calls \code{exists(key)} to check
#' if an object is in the cache, and then call \code{get(key)}, the object may
#' be removed from the cache in between those two calls, and \code{get(key)}
#' race conditions. For example, if your code calls `exists(key)` to check
#' 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 \code{get(key)}, and use \code{tryCatch()} to handle the error
#' 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
#' another processes is trying to prune objects. If this happens, you may see
#' a warning from \code{file.remove()} failing to remove a file that has
#' a warning from `file.remove()` failing to remove a file that has
#' already been deleted.
#'
#'
@@ -145,72 +145,72 @@
#' A disk cache object has the following methods:
#'
#' \describe{
#' \item{\code{get(key, missing, exec_missing)}}{
#' Returns the value associated with \code{key}. If the key is not in the
#' cache, then it returns the value specified by \code{missing} or,
#' \code{missing} is a function and \code{exec_missing=TRUE}, then
#' executes \code{missing}. The function can throw an error or return the
#' \item{`get(key, missing, exec_missing)`}{
#' Returns the value associated with `key`. If the key is not in the
#' cache, then it returns the value specified by `missing` or,
#' `missing` is a function and `exec_missing=TRUE`, then
#' executes `missing`. The function can throw an error or return the
#' value. If either of these parameters are specified here, then they
#' will override the defaults that were set when the DiskCache object was
#' created. See section Missing Keys for more information.
#' }
#' \item{\code{set(key, value)}}{
#' Stores the \code{key}-\code{value} pair in the cache.
#' \item{`set(key, value)`}{
#' Stores the `key`-`value` pair in the cache.
#' }
#' \item{\code{exists(key)}}{
#' Returns \code{TRUE} if the cache contains the key, otherwise
#' \code{FALSE}.
#' \item{`exists(key)`}{
#' Returns `TRUE` if the cache contains the key, otherwise
#' `FALSE`.
#' }
#' \item{\code{size()}}{
#' \item{`size()`}{
#' Returns the number of items currently in the cache.
#' }
#' \item{\code{keys()}}{
#' \item{`keys()`}{
#' Returns a character vector of all keys currently in the cache.
#' }
#' \item{\code{reset()}}{
#' \item{`reset()`}{
#' Clears all objects from the cache.
#' }
#' \item{\code{destroy()}}{
#' \item{`destroy()`}{
#' Clears all objects in the cache, and removes the cache directory from
#' disk.
#' }
#' \item{\code{prune()}}{
#' Prunes the cache, using the parameters specified by \code{max_size},
#' \code{max_age}, \code{max_n}, and \code{evict}.
#' \item{`prune()`}{
#' Prunes the cache, using the parameters specified by `max_size`,
#' `max_age`, `max_n`, and `evict`.
#' }
#' }
#'
#' @param dir Directory to store files for the cache. If \code{NULL} (the
#' @param dir Directory to store files for the cache. If `NULL` (the
#' default) it will create and use a temporary directory.
#' @param max_age Maximum age of files in cache before they are evicted, in
#' seconds. Use \code{Inf} for no age limit.
#' seconds. Use `Inf` for no age limit.
#' @param max_size Maximum size of the cache, in bytes. If the cache exceeds
#' this size, cached objects will be removed according to the value of the
#' \code{evict}. Use \code{Inf} for no size limit.
#' `evict`. Use `Inf` for no size limit.
#' @param max_n Maximum number of objects in the cache. If the number of objects
#' exceeds this value, then cached objects will be removed according to the
#' value of \code{evict}. Use \code{Inf} for no limit of number of items.
#' value of `evict`. Use `Inf` for no limit of number of items.
#' @param evict The eviction policy to use to decide which objects are removed
#' when a cache pruning occurs. Currently, \code{"lru"} and \code{"fifo"} are
#' when a cache pruning occurs. Currently, `"lru"` and `"fifo"` are
#' supported.
#' @param destroy_on_finalize If \code{TRUE}, then when the DiskCache object is
#' @param destroy_on_finalize If `TRUE`, then when the DiskCache object is
#' garbage collected, the cache directory and all objects inside of it will be
#' deleted from disk. If \code{FALSE} (the default), it will do nothing when
#' deleted from disk. If `FALSE` (the default), it will do nothing when
#' finalized.
#' @param missing A value to return or a function to execute when
#' \code{get(key)} is called but the key is not present in the cache. The
#' default is a \code{\link{key_missing}} object. If it is a function to
#' `get(key)` is called but the key is not present in the cache. The
#' default is a [key_missing()] object. If it is a function to
#' execute, the function must take one argument (the key), and you must also
#' use \code{exec_missing = TRUE}. If it is a function, it is useful in most
#' use `exec_missing = TRUE`. If it is a function, it is useful in most
#' cases for it to throw an error, although another option is to return a
#' value. If a value is returned, that value will in turn be returned by
#' \code{get()}. See section Missing keys for more information.
#' @param exec_missing If \code{FALSE} (the default), then treat \code{missing}
#' as a value to return when \code{get()} results in a cache miss. If
#' \code{TRUE}, treat \code{missing} as a function to execute when
#' \code{get()} results in a cache miss.
#' `get()`. See section Missing keys for more information.
#' @param exec_missing If `FALSE` (the default), then treat `missing`
#' as a value to return when `get()` results in a cache miss. If
#' `TRUE`, treat `missing` as a function to execute when
#' `get()` results in a cache miss.
#' @param logfile An optional filename or connection object to where logging
#' information will be written. To log to the console, use \code{stdout()}.
#' information will be written. To log to the console, use `stdout()`.
#'
#' @export
diskCache <- function(

View File

@@ -1,50 +1,50 @@
#' Create a memory cache object
#'
#' A memory cache object is a key-value store that saves the values in an
#' environment. Objects can be stored and retrieved using the \code{get()} and
#' \code{set()} methods. Objects are automatically pruned from the cache
#' according to the parameters \code{max_size}, \code{max_age}, \code{max_n},
#' and \code{evict}.
#' environment. Objects can be stored and retrieved using the `get()` and
#' `set()` methods. Objects are automatically pruned from the cache
#' according to the parameters `max_size`, `max_age`, `max_n`,
#' and `evict`.
#'
#' In a \code{MemoryCache}, R objects are stored directly in the cache; they are
#' not \emph{not} serialized before being stored in the cache. This contrasts
#' with other cache types, like \code{\link{diskCache}}, where objects are
#' In a `MemoryCache`, R objects are stored directly in the cache; they are
#' not *not* serialized before being stored in the cache. This contrasts
#' with other cache types, like [diskCache()], where objects are
#' serialized, and the serialized object is cached. This can result in some
#' differences of behavior. For example, as long as an object is stored in a
#' MemoryCache, it will not be garbage collected.
#'
#'
#' @section Missing keys:
#' The \code{missing} and \code{exec_missing} parameters controls what happens
#' when \code{get()} is called with a key that is not in the cache (a cache
#' miss). The default behavior is to return a \code{\link{key_missing}}
#' object. This is a \emph{sentinel value} that indicates that the key was not
#' The `missing` and `exec_missing` parameters controls what happens
#' when `get()` is called with a key that is not in the cache (a cache
#' miss). The default behavior is to return a [key_missing()]
#' object. This is a *sentinel value* that indicates that the key was not
#' present in the cache. You can test if the returned value represents a
#' missing key by using the \code{\link{is.key_missing}} function. You can
#' also have \code{get()} return a different sentinel value, like \code{NULL}.
#' missing key by using the [is.key_missing()] function. You can
#' also have `get()` return a different sentinel value, like `NULL`.
#' If you want to throw an error on a cache miss, you can do so by providing a
#' function for \code{missing} that takes one argument, the key, and also use
#' \code{exec_missing=TRUE}.
#' function for `missing` that takes one argument, the key, and also use
#' `exec_missing=TRUE`.
#'
#' When the cache is created, you can supply a value for \code{missing}, which
#' When the cache is created, you can supply a value for `missing`, which
#' sets the default value to be returned for missing values. It can also be
#' overridden when \code{get()} is called, by supplying a \code{missing}
#' argument. For example, if you use \code{cache$get("mykey", missing =
#' NULL)}, it will return \code{NULL} if the key is not in the cache.
#' overridden when `get()` is called, by supplying a `missing`
#' argument. For example, if you use `cache$get("mykey", missing =
#' NULL)`, it will return `NULL` if the key is not in the cache.
#'
#' If your cache is configured so that \code{get()} returns a sentinel value
#' to represent a cache miss, then \code{set} will also not allow you to store
#' If your cache is configured so that `get()` returns a sentinel value
#' to represent a cache miss, then `set` will also not allow you to store
#' the sentinel value in the cache. It will throw an error if you attempt to
#' do so.
#'
#' Instead of returning the same sentinel value each time there is cache miss,
#' the cache can execute a function each time \code{get()} encounters missing
#' key. If the function returns a value, then \code{get()} will in turn return
#' the cache can execute a function each time `get()` encounters missing
#' key. If the function returns a value, then `get()` will in turn return
#' that value. However, a more common use is for the function to throw an
#' error. If an error is thrown, then \code{get()} will not return a value.
#' error. If an error is thrown, then `get()` will not return a value.
#'
#' To do this, pass a one-argument function to \code{missing}, and use
#' \code{exec_missing=TRUE}. For example, if you want to throw an error that
#' To do this, pass a one-argument function to `missing`, and use
#' `exec_missing=TRUE`. For example, if you want to throw an error that
#' prints the missing key, you could do this:
#'
#' \preformatted{
@@ -56,53 +56,53 @@
#' )
#' }
#'
#' If you use this, the code that calls \code{get()} should be wrapped with
#' \code{\link{tryCatch}()} to gracefully handle missing keys.
#' If you use this, the code that calls `get()` should be wrapped with
#' [tryCatch()] to gracefully handle missing keys.
#'
#' @section Cache pruning:
#'
#' Cache pruning occurs when \code{set()} is called, or it can be invoked
#' manually by calling \code{prune()}.
#' Cache pruning occurs when `set()` is called, or it can be invoked
#' manually by calling `prune()`.
#'
#' When a pruning occurs, if there are any objects that are older than
#' \code{max_age}, they will be removed.
#' `max_age`, they will be removed.
#'
#' The \code{max_size} and \code{max_n} parameters are applied to the cache as
#' a whole, in contrast to \code{max_age}, which is applied to each object
#' The `max_size` and `max_n` parameters are applied to the cache as
#' a whole, in contrast to `max_age`, which is applied to each object
#' individually.
#'
#' If the number of objects in the cache exceeds \code{max_n}, then objects
#' If the number of objects in the cache exceeds `max_n`, then objects
#' will be removed from the cache according to the eviction policy, which is
#' set with the \code{evict} parameter. Objects will be removed so that the
#' number of items is \code{max_n}.
#' set with the `evict` parameter. Objects will be removed so that the
#' number of items is `max_n`.
#'
#' If the size of the objects in the cache exceeds \code{max_size}, then
#' If the size of the objects in the cache exceeds `max_size`, then
#' objects will be removed from the cache. Objects will be removed from the
#' cache so that the total size remains under \code{max_size}. Note that the
#' cache so that the total size remains under `max_size`. Note that the
#' size is calculated using the size of the files, not the size of disk space
#' used by the files -- these two values can differ because of files are
#' used by the files --- these two values can differ because of files are
#' stored in blocks on disk. For example, if the block size is 4096 bytes,
#' then a file that is one byte in size will take 4096 bytes on disk.
#'
#' Another time that objects can be removed from the cache is when
#' \code{get()} is called. If the target object is older than \code{max_age},
#' `get()` is called. If the target object is older than `max_age`,
#' it will be removed and the cache will report it as a missing value.
#'
#' @section Eviction policies:
#'
#' If \code{max_n} or \code{max_size} are used, then objects will be removed
#' If `max_n` or `max_size` are used, then objects will be removed
#' from the cache according to an eviction policy. The available eviction
#' policies are:
#'
#' \describe{
#' \item{\code{"lru"}}{
#' \item{`"lru"`}{
#' Least Recently Used. The least recently used objects will be removed.
#' This uses the filesystem's atime property. Some filesystems do not
#' support atime, or have a very low atime resolution. The DiskCache will
#' check for atime support, and if the filesystem does not support atime,
#' a warning will be issued and the "fifo" policy will be used instead.
#' }
#' \item{\code{"fifo"}}{
#' \item{`"fifo"`}{
#' First-in-first-out. The oldest objects will be removed.
#' }
#' }
@@ -112,38 +112,38 @@
#' A disk cache object has the following methods:
#'
#' \describe{
#' \item{\code{get(key, missing, exec_missing)}}{
#' Returns the value associated with \code{key}. If the key is not in the
#' cache, then it returns the value specified by \code{missing} or,
#' \code{missing} is a function and \code{exec_missing=TRUE}, then
#' executes \code{missing}. The function can throw an error or return the
#' \item{`get(key, missing, exec_missing)`}{
#' Returns the value associated with `key`. If the key is not in the
#' cache, then it returns the value specified by `missing` or,
#' `missing` is a function and `exec_missing=TRUE`, then
#' executes `missing`. The function can throw an error or return the
#' value. If either of these parameters are specified here, then they
#' will override the defaults that were set when the DiskCache object was
#' created. See section Missing Keys for more information.
#' }
#' \item{\code{set(key, value)}}{
#' Stores the \code{key}-\code{value} pair in the cache.
#' \item{`set(key, value)`}{
#' Stores the `key`-`value` pair in the cache.
#' }
#' \item{\code{exists(key)}}{
#' Returns \code{TRUE} if the cache contains the key, otherwise
#' \code{FALSE}.
#' \item{`exists(key)`}{
#' Returns `TRUE` if the cache contains the key, otherwise
#' `FALSE`.
#' }
#' \item{\code{size()}}{
#' \item{`size()`}{
#' Returns the number of items currently in the cache.
#' }
#' \item{\code{keys()}}{
#' \item{`keys()`}{
#' Returns a character vector of all keys currently in the cache.
#' }
#' \item{\code{reset()}}{
#' \item{`reset()`}{
#' Clears all objects from the cache.
#' }
#' \item{\code{destroy()}}{
#' \item{`destroy()`}{
#' Clears all objects in the cache, and removes the cache directory from
#' disk.
#' }
#' \item{\code{prune()}}{
#' Prunes the cache, using the parameters specified by \code{max_size},
#' \code{max_age}, \code{max_n}, and \code{evict}.
#' \item{`prune()`}{
#' Prunes the cache, using the parameters specified by `max_size`,
#' `max_age`, `max_n`, and `evict`.
#' }
#' }
#'
@@ -179,7 +179,7 @@ MemoryCache <- R6Class("MemoryCache",
if (!is.numeric(max_size)) stop("max_size must be a number. Use `Inf` for no limit.")
if (!is.numeric(max_age)) stop("max_age must be a number. Use `Inf` for no limit.")
if (!is.numeric(max_n)) stop("max_n must be a number. Use `Inf` for no limit.")
private$cache <- new.env(parent = emptyenv())
private$cache <- fastmap()
private$max_size <- max_size
private$max_age <- max_age
private$max_n <- max_n
@@ -208,7 +208,7 @@ MemoryCache <- R6Class("MemoryCache",
}
private$log(paste0('get: key "', key, '" found'))
value <- private$cache[[key]]$value
value <- private$cache$get(key)$value
value
},
@@ -226,37 +226,36 @@ MemoryCache <- R6Class("MemoryCache",
size <- NULL
}
private$cache[[key]] <- list(
private$cache$set(key, list(
key = key,
value = value,
size = size,
mtime = time,
atime = time
)
))
self$prune()
invisible(self)
},
exists = function(key) {
validate_key(key)
# Faster than `exists(key, envir = private$cache, inherits = FALSE)
!is.null(private$cache[[key]])
private$cache$has(key)
},
keys = function() {
ls(private$cache, sorted = FALSE) # Faster with sorted=FALSE
private$cache$keys()
},
remove = function(key) {
private$log(paste0('remove: key "', key, '"'))
validate_key(key)
rm(list = key, envir = private$cache)
private$cache$remove(key)
invisible(self)
},
reset = function() {
private$log(paste0('reset'))
rm(list = self$keys(), envir = private$cache)
private$cache$reset()
invisible(self)
},
@@ -271,7 +270,7 @@ MemoryCache <- R6Class("MemoryCache",
rm_idx <- timediff > private$max_age
if (any(rm_idx)) {
private$log(paste0("prune max_age: Removing ", paste(info$key[rm_idx], collapse = ", ")))
rm(list = info$key[rm_idx], envir = private$cache)
private$cache$remove(info$key[rm_idx])
info <- info[!rm_idx, ]
}
}
@@ -298,7 +297,7 @@ MemoryCache <- R6Class("MemoryCache",
ensure_info_is_sorted()
rm_idx <- seq_len(nrow(info)) > private$max_n
private$log(paste0("prune max_n: Removing ", paste(info$key[rm_idx], collapse = ", ")))
rm(list = info$key[rm_idx], envir = private$cache)
private$cache$remove(info$key[rm_idx])
info <- info[!rm_idx, ]
}
@@ -308,7 +307,7 @@ MemoryCache <- R6Class("MemoryCache",
cum_size <- cumsum(info$size)
rm_idx <- cum_size > private$max_size
private$log(paste0("prune max_size: Removing ", paste(info$key[rm_idx], collapse = ", ")))
rm(list = info$key[rm_idx], envir = private$cache)
private$cache$remove(info$key[rm_idx])
info <- info[!rm_idx, ]
}
@@ -335,23 +334,23 @@ MemoryCache <- R6Class("MemoryCache",
maybe_prune_single = function(key) {
if (!is.finite(private$max_age)) return()
obj <- private$cache[[key]]
obj <- private$cache$get(key)
if (is.null(obj)) return()
timediff <- as.numeric(Sys.time()) - obj$mtime
if (timediff > private$max_age) {
private$log(paste0("pruning single object exceeding max_age: Removing ", key))
rm(list = key, envir = private$cache)
private$cache$remove(key)
}
},
object_info = function() {
keys <- ls(private$cache, sorted = FALSE)
keys <- private$cache$keys()
data.frame(
key = keys,
size = vapply(keys, function(key) private$cache[[key]]$size, 0),
mtime = vapply(keys, function(key) private$cache[[key]]$mtime, 0),
atime = vapply(keys, function(key) private$cache[[key]]$atime, 0),
size = vapply(keys, function(key) private$cache$get(key)$size, 0),
mtime = vapply(keys, function(key) private$cache$get(key)$mtime, 0),
atime = vapply(keys, function(key) private$cache$get(key)$atime, 0),
stringsAsFactors = FALSE
)
},

View File

@@ -1,26 +1,10 @@
#' A Key Missing object
#'
#' A \code{key_missing} object represents a cache miss.
#'
#' @param x An object to test.
#'
#' @seealso \code{\link{diskCache}}, \code{\link{memoryCache}}.
#'
#' @importFrom fastmap key_missing
#' @export
key_missing <- function() {
structure(list(), class = "key_missing")
}
fastmap::key_missing
#' @rdname key_missing
#' @importFrom fastmap is.key_missing
#' @export
is.key_missing <- function(x) {
inherits(x, "key_missing")
}
#' @export
print.key_missing <- function(x, ...) {
cat("<Key Missing>\n")
}
fastmap::is.key_missing
validate_key <- function(key) {
@@ -31,3 +15,4 @@ validate_key <- function(key) {
stop("Invalid key: ", key, ". Only lowercase letters and numbers are allowed.")
}
}

View File

@@ -3,9 +3,9 @@
#' Advanced (borderline internal) functions for capturing, printing, and
#' manipulating stack traces.
#'
#' @return \code{printError} and \code{printStackTrace} return
#' \code{invisible()}. The other functions pass through the results of
#' \code{expr}.
#' @return `printError` and `printStackTrace` return
#' `invisible()`. The other functions pass through the results of
#' `expr`.
#'
#' @examples
#' # Keeps tryCatch and withVisible related calls off the
@@ -106,17 +106,17 @@ getCallCategories <- function(calls) {
}, character(1))
}
#' @details \code{captureStackTraces} runs the given \code{expr} and if any
#' \emph{uncaught} errors occur, annotates them with stack trace info for use
#' by \code{printError} and \code{printStackTrace}. It is not necessary to use
#' \code{captureStackTraces} around the same expression as
#' \code{withLogErrors}, as the latter includes a call to the former. Note
#' that if \code{expr} contains calls (either directly or indirectly) to
#' \code{try}, or \code{tryCatch} with an error handler, stack traces therein
#' cannot be captured unless another \code{captureStackTraces} call is
#' inserted in the interior of the \code{try} or \code{tryCatch}. This is
#' @details `captureStackTraces` runs the given `expr` and if any
#' *uncaught* errors occur, annotates them with stack trace info for use
#' by `printError` and `printStackTrace`. It is not necessary to use
#' `captureStackTraces` around the same expression as
#' `withLogErrors`, as the latter includes a call to the former. Note
#' that if `expr` contains calls (either directly or indirectly) to
#' `try`, or `tryCatch` with an error handler, stack traces therein
#' cannot be captured unless another `captureStackTraces` call is
#' inserted in the interior of the `try` or `tryCatch`. This is
#' because these calls catch the error and prevent it from traveling up to the
#' condition handler installed by \code{captureStackTraces}.
#' condition handler installed by `captureStackTraces`.
#'
#' @param expr The expression to wrap.
#' @rdname stacktrace
@@ -209,11 +209,11 @@ doCaptureStack <- function(e) {
stop(e)
}
#' @details \code{withLogErrors} captures stack traces and logs errors that
#' occur in \code{expr}, but does allow errors to propagate beyond this point
#' @details `withLogErrors` captures stack traces and logs errors that
#' occur in `expr`, but does allow errors to propagate beyond this point
#' (i.e. it doesn't catch the error). The same caveats that apply to
#' \code{captureStackTraces} with regard to \code{try}/\code{tryCatch} apply
#' to \code{withLogErrors}.
#' `captureStackTraces` with regard to `try`/`tryCatch` apply
#' to `withLogErrors`.
#' @rdname stacktrace
#' @export
withLogErrors <- function(expr,
@@ -247,19 +247,19 @@ withLogErrors <- function(expr,
)
}
#' @details \code{printError} prints the error and stack trace (if any) using
#' \code{warning(immediate.=TRUE)}. \code{printStackTrace} prints the stack
#' @details `printError` prints the error and stack trace (if any) using
#' `warning(immediate.=TRUE)`. `printStackTrace` prints the stack
#' trace only.
#'
#' @param cond An condition object (generally, an error).
#' @param full If \code{TRUE}, then every element of \code{sys.calls()} will be
#' included in the stack trace. By default (\code{FALSE}), calls that Shiny
#' @param full If `TRUE`, then every element of `sys.calls()` will be
#' included in the stack trace. By default (`FALSE`), calls that Shiny
#' deems uninteresting will be hidden.
#' @param offset If \code{TRUE} (the default), srcrefs will be reassigned from
#' @param offset If `TRUE` (the default), srcrefs will be reassigned from
#' the calls they originated from, to the destinations of those calls. If
#' you're used to stack traces from other languages, this feels more
#' intuitive, as the definition of the function indicated in the call and the
#' location specified by the srcref match up. If \code{FALSE}, srcrefs will be
#' location specified by the srcref match up. If `FALSE`, srcrefs will be
#' left alone (traditional R treatment where the srcref is of the callsite).
#' @rdname stacktrace
#' @export
@@ -361,10 +361,10 @@ printStackTrace <- function(cond,
invisible()
}
#' @details \code{extractStackTrace} takes a list of calls (e.g. as returned
#' from \code{conditionStackTrace(cond)}) and returns a data frame with one
#' row for each stack frame and the columns \code{num} (stack frame number),
#' \code{call} (a function name or similar), and \code{loc} (source file path
#' @details `extractStackTrace` takes a list of calls (e.g. as returned
#' from `conditionStackTrace(cond)`) and returns a data frame with one
#' row for each stack frame and the columns `num` (stack frame number),
#' `call` (a function name or similar), and `loc` (source file path
#' and line number, if available). It was deprecated after shiny 1.0.5 because
#' it doesn't support deep stack traces.
#' @rdname stacktrace
@@ -537,7 +537,7 @@ offsetSrcrefs <- function(calls, offset = TRUE) {
calls
}
#' @details \code{formatStackTrace} is similar to \code{extractStackTrace}, but
#' @details `formatStackTrace` is similar to `extractStackTrace`, but
#' it returns a preformatted character vector instead of a data frame. It was
#' deprecated after shiny 1.0.5 because it doesn't support deep stack traces.
#' @param indent A string to prefix every line of the stack trace.
@@ -588,11 +588,11 @@ stripStackTrace <- function(cond) {
conditionStackTrace(cond) <- NULL
}
#' @details \code{conditionStackTrace} and \code{conditionStackTrace<-} are
#' @details `conditionStackTrace` and `conditionStackTrace<-` are
#' accessor functions for getting/setting stack traces on conditions.
#'
#' @param cond A condition that may have previously been annotated by
#' \code{captureStackTraces} (or \code{withLogErrors}).
#' `captureStackTraces` (or `withLogErrors`).
#' @rdname stacktrace
#' @export
conditionStackTrace <- function(cond) {
@@ -607,8 +607,8 @@ conditionStackTrace <- function(cond) {
invisible(cond)
}
#' @details The two functions \code{..stacktraceon..} and
#' \code{..stacktraceoff..} have no runtime behavior during normal execution;
#' @details The two functions `..stacktraceon..` and
#' `..stacktraceoff..` have no runtime behavior during normal execution;
#' they exist only to create artifacts on the stack trace (sys.call()) that
#' instruct the stack trace pretty printer what parts of the stack trace are
#' interesting or not. The initial state is 1 and we walk from the outermost
@@ -624,4 +624,4 @@ conditionStackTrace <- function(cond) {
#' @export
..stacktraceoff.. <- function(expr) expr
..stacktracefloor.. <- function(expr) expr
..stacktracefloor.. <- function(expr) expr

View File

@@ -1,11 +1,41 @@
# A scope where we can put mutable global state
.globals <- new.env(parent = emptyenv())
register_s3_method <- function(pkg, generic, class, fun = NULL) {
stopifnot(is.character(pkg), length(pkg) == 1)
stopifnot(is.character(generic), length(generic) == 1)
stopifnot(is.character(class), length(class) == 1)
if (is.null(fun)) {
fun <- get(paste0(generic, ".", class), envir = parent.frame())
} else {
stopifnot(is.function(fun))
}
if (pkg %in% loadedNamespaces()) {
registerS3method(generic, class, fun, envir = asNamespace(pkg))
}
# Always register hook in case package is later unloaded & reloaded
setHook(
packageEvent(pkg, "onLoad"),
function(...) {
registerS3method(generic, class, fun, envir = asNamespace(pkg))
}
)
}
.onLoad <- function(libname, pkgname) {
# R's lazy-loading package scheme causes the private seed to be cached in the
# package itself, making our PRNG completely deterministic. This line resets
# the private seed during load.
withPrivateSeed(set.seed(NULL))
# Make sure these methods are available to knitr if shiny is loaded but not
# attached.
register_s3_method("knitr", "knit_print", "reactive")
register_s3_method("knitr", "knit_print", "shiny.appobj")
register_s3_method("knitr", "knit_print", "shiny.render.function")
}
.onAttach <- function(libname, pkgname) {

594
R/graph.R
View File

@@ -1,21 +1,66 @@
writeReactLog <- function(file=stdout(), sessionToken = NULL) {
log <- .graphStack$as_list()
if (!is.null(sessionToken)) {
log <- Filter(function(x) {
is.null(x$session) || identical(x$session, sessionToken)
}, log)
}
cat(toJSON(log, pretty=TRUE), file=file)
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, location) {
if (is_installed(package, version)) {
return()
}
missing_location <- missing(location)
msg <- paste0(
sQuote(package),
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() && missing_location) {
message(msg, "\nWould you like to install it?")
if (utils::menu(c("Yes", "No")) == 1) {
return(utils::install.packages(package))
}
}
stop(msg, call. = FALSE)
}
# domain is like session
# used to help define truly global react id's.
# should work across session and in global namespace
.globals$reactIdCounter <- 0L
nextGlobalReactId <- function() {
.globals$reactIdCounter <- .globals$reactIdCounter + 1L
reactIdStr(.globals$reactIdCounter)
}
reactIdStr <- function(num) {
paste0("r", num)
}
#' Reactive Log Visualizer
#'
#' Provides an interactive browser-based tool for visualizing reactive
#' dependencies and execution in your application.
#'
#' To use the reactive log visualizer, start with a fresh R session and
#' run the command \code{options(shiny.reactlog=TRUE)}; then launch your
#' application in the usual way (e.g. using \code{\link{runApp}}). At
#' run the command `options(shiny.reactlog=TRUE)`; then launch your
#' application in the usual way (e.g. using [runApp()]). At
#' any time you can hit Ctrl+F3 (or for Mac users, Command+F3) in your
#' web browser to launch the reactive log visualization.
#'
@@ -30,88 +75,499 @@ writeReactLog <- function(file=stdout(), sessionToken = NULL) {
#'
#' As an alternative to pressing Ctrl/Command+F3--for example, if you
#' are using reactives outside of the context of a Shiny
#' application--you can run the \code{showReactLog} function, which will
#' application--you can run the `reactlogShow` function, which will
#' generate the reactive log visualization as a static HTML file and
#' launch it in your default browser. In this case, refreshing your
#' browser will not load new activity into the report; you will need to
#' call \code{showReactLog()} explicitly.
#' call `reactlogShow()` explicitly.
#'
#' For security and performance reasons, do not enable
#' \code{shiny.reactlog} in production environments. When the option is
#' `shiny.reactlog` in production environments. When the option is
#' enabled, it's possible for any user of your app to see at least some
#' of the source code of your reactive expressions and observers.
#'
#' @param time A boolean that specifies whether or not to display the
#' time that each reactive.
#' @name reactlog
NULL
#' @describeIn reactlog Return a list of reactive information. Can be used in conjunction with
#' [reactlog::reactlog_show] to later display the reactlog graph.
#' @export
reactlog <- function() {
rLog$asList()
}
#' @describeIn reactlog Display a full reactlog graph for all sessions.
#' @inheritParams reactlog::reactlog_show
#' @export
reactlogShow <- function(time = TRUE) {
check_reactlog()
reactlog::reactlog_show(reactlog(), time = time)
}
#' @describeIn reactlog This function is deprecated. You should use [reactlogShow()]
#' @export
# legacy purposes
showReactLog <- function(time = TRUE) {
utils::browseURL(renderReactLog(time = as.logical(time)))
shinyDeprecated(new = "`reactlogShow`", version = "1.2.0")
reactlogShow(time = time)
}
#' @describeIn reactlog Resets the entire reactlog stack. Useful for debugging and removing all prior reactive history.
#' @export
reactlogReset <- function() {
rLog$reset()
}
renderReactLog <- function(sessionToken = NULL, time = TRUE) {
templateFile <- system.file('www/reactive-graph.html', package='shiny')
html <- paste(readLines(templateFile, warn=FALSE), collapse='\r\n')
tc <- textConnection(NULL, 'w')
on.exit(close(tc))
writeReactLog(tc, sessionToken)
cat('\n', file=tc)
flush(tc)
html <- sub('__DATA__', paste(textConnectionValue(tc), collapse='\r\n'), html, fixed=TRUE)
html <- sub('__TIME__', paste0('"', time, '"'), html, fixed=TRUE)
file <- tempfile(fileext = '.html')
writeLines(html, file)
return(file)
# called in "/reactlog" middleware
renderReactlog <- function(sessionToken = NULL, time = TRUE) {
check_reactlog()
reactlog::reactlog_render(
reactlog(),
session_token = sessionToken,
time = time
)
}
check_reactlog <- function() {
check_suggested("reactlog", reactlog_version())
}
# read reactlog version from description file
# prevents version mismatch in code and description file
reactlog_version <- function() {
desc <- read.dcf(system.file("DESCRIPTION", package = "shiny", mustWork = TRUE))
suggests <- desc[1,"Suggests"][[1]]
suggests_pkgs <- strsplit(suggests, "\n")[[1]]
.graphAppend <- function(logEntry, domain = getDefaultReactiveDomain()) {
if (isTRUE(getOption('shiny.reactlog'))) {
sessionToken <- if (is.null(domain)) NULL else domain$token
.graphStack$push(c(logEntry, list(
session = sessionToken,
time = as.numeric(Sys.time())
)))
reactlog_info <- suggests_pkgs[grepl("reactlog", suggests_pkgs)]
if (length(reactlog_info) == 0) {
stop("reactlog can not be found in shiny DESCRIPTION file")
}
if (!is.null(domain)) {
domain$reactlog(logEntry)
}
reactlog_info <- sub("^[^\\(]*\\(", "", reactlog_info)
reactlog_info <- sub("\\)[^\\)]*$", "", reactlog_info)
reactlog_info <- sub("^[>= ]*", "", reactlog_info)
package_version(reactlog_info)
}
.graphDependsOn <- function(id, label) {
.graphAppend(list(action='dep', id=id, dependsOn=label))
}
.graphDependsOnId <- function(id, dependee) {
.graphAppend(list(action='depId', id=id, dependsOn=dependee))
}
RLog <- R6Class(
"RLog",
portable = FALSE,
private = list(
option = "shiny.reactlog",
msgOption = "shiny.reactlog.console",
.graphCreateContext <- function(id, label, type, prevId, domain) {
.graphAppend(list(
action='ctx', id=id, label=paste(label, collapse='\n'),
srcref=as.vector(attr(label, "srcref")), srcfile=attr(label, "srcfile"),
type=type, prevId=prevId
), domain = domain)
}
appendEntry = function(domain, logEntry) {
if (self$isLogging()) {
sessionToken <- if (is.null(domain)) NULL else domain$token
logStack$push(c(logEntry, list(
session = sessionToken,
time = as.numeric(Sys.time())
)))
}
if (!is.null(domain)) domain$reactlog(logEntry)
}
),
public = list(
msg = "<MessageLogger>",
logStack = "<Stack>",
.graphEnterContext <- function(id) {
.graphAppend(list(action='enter', id=id))
}
noReactIdLabel = "NoCtxReactId",
noReactId = reactIdStr("NoCtxReactId"),
dummyReactIdLabel = "DummyReactId",
dummyReactId = reactIdStr("DummyReactId"),
.graphExitContext <- function(id, domain) {
.graphAppend(list(action='exit', id=id), domain = domain)
}
asList = function() {
ret <- self$logStack$as_list()
attr(ret, "version") <- "1"
ret
},
.graphValueChange <- function(label, value) {
.graphAppend(list(
action = 'valueChange',
id = label,
value = paste(utils::capture.output(utils::str(value)), collapse='\n')
))
}
ctxIdStr = function(ctxId) {
if (is.null(ctxId) || identical(ctxId, "")) return(NULL)
paste0("ctx", ctxId)
},
namesIdStr = function(reactId) {
paste0("names(", reactId, ")")
},
asListIdStr = function(reactId) {
paste0("as.list(", reactId, ")")
},
asListAllIdStr = function(reactId) {
paste0("as.list(", reactId, ", all.names = TRUE)")
},
keyIdStr = function(reactId, key) {
paste0(reactId, "$", key)
},
.graphInvalidate <- function(id, domain) {
.graphAppend(list(action='invalidate', id=id), domain)
}
valueStr = function(value, n = 200) {
if (!self$isLogging()) {
# return a placeholder string to avoid calling str
return("<reactlog is turned off>")
}
output <- try(silent = TRUE, {
# only capture the first level of the object
utils::capture.output(utils::str(value, max.level = 1))
})
outputTxt <- paste0(output, collapse="\n")
msg$shortenString(outputTxt, n = n)
},
initialize = function(rlogOption = "shiny.reactlog", msgOption = "shiny.reactlog.console") {
private$option <- rlogOption
private$msgOption <- msgOption
self$reset()
},
reset = function() {
.globals$reactIdCounter <- 0L
self$logStack <- Stack$new()
self$msg <- MessageLogger$new(option = private$msgOption)
# setup dummy and missing react information
self$msg$setReact(force = TRUE, list(reactId = self$noReactId, label = self$noReactIdLabel))
self$msg$setReact(force = TRUE, list(reactId = self$dummyReactId, label = self$dummyReactIdLabel))
},
isLogging = function() {
isTRUE(getOption(private$option, FALSE))
},
define = function(reactId, value, label, type, domain) {
valueStr <- self$valueStr(value)
if (msg$hasReact(reactId)) {
stop("react definition for id: ", reactId, " already found!!", "Label: ", label, "Type: ", type)
}
msg$setReact(list(reactId = reactId, label = label))
msg$log("define:", msg$reactStr(reactId), msg$typeStr(type = type), msg$valueStr(valueStr))
private$appendEntry(domain, list(
action = "define",
reactId = reactId,
label = msg$shortenString(label),
type = type,
value = valueStr
))
},
defineNames = function(reactId, value, label, domain) {
self$define(self$namesIdStr(reactId), value, self$namesIdStr(label), "reactiveValuesNames", domain)
},
defineAsList = function(reactId, value, label, domain) {
self$define(self$asListIdStr(reactId), value, self$asListIdStr(label), "reactiveValuesAsList", domain)
},
defineAsListAll = function(reactId, value, label, domain) {
self$define(self$asListAllIdStr(reactId), value, self$asListAllIdStr(label), "reactiveValuesAsListAll", domain)
},
defineKey = function(reactId, value, key, label, domain) {
self$define(self$keyIdStr(reactId, key), value, self$keyIdStr(label, key), "reactiveValuesKey", domain)
},
defineObserver = function(reactId, label, domain) {
self$define(reactId, value = NULL, label, "observer", domain)
},
dependsOn = function(reactId, depOnReactId, ctxId, domain) {
if (is.null(reactId)) return()
ctxId <- ctxIdStr(ctxId)
msg$log("dependsOn:", msg$reactStr(reactId), " on", msg$reactStr(depOnReactId), msg$ctxStr(ctxId))
private$appendEntry(domain, list(
action = "dependsOn",
reactId = reactId,
depOnReactId = depOnReactId,
ctxId = ctxId
))
},
dependsOnKey = function(reactId, depOnReactId, key, ctxId, domain) {
self$dependsOn(reactId, self$keyIdStr(depOnReactId, key), ctxId, domain)
},
dependsOnRemove = function(reactId, depOnReactId, ctxId, domain) {
ctxId <- self$ctxIdStr(ctxId)
msg$log("dependsOnRemove:", msg$reactStr(reactId), " on", msg$reactStr(depOnReactId), msg$ctxStr(ctxId))
private$appendEntry(domain, list(
action = "dependsOnRemove",
reactId = reactId,
depOnReactId = depOnReactId,
ctxId = ctxId
))
},
dependsOnKeyRemove = function(reactId, depOnReactId, key, ctxId, domain) {
self$dependsOnRemove(reactId, self$keyIdStr(depOnReactId, key), ctxId, domain)
},
createContext = function(ctxId, label, type, prevCtxId, domain) {
ctxId <- self$ctxIdStr(ctxId)
prevCtxId <- self$ctxIdStr(prevCtxId)
msg$log("createContext:", msg$ctxPrevCtxStr(preCtxIdTxt = " ", ctxId, prevCtxId, type))
private$appendEntry(domain, list(
action = "createContext",
ctxId = ctxId,
label = msg$shortenString(label),
type = type,
prevCtxId = prevCtxId,
srcref = as.vector(attr(label, "srcref")), srcfile=attr(label, "srcfile")
))
},
enter = function(reactId, ctxId, type, domain) {
ctxId <- self$ctxIdStr(ctxId)
if (identical(type, "isolate")) {
msg$log("isolateEnter:", msg$reactStr(reactId), msg$ctxStr(ctxId))
msg$depthIncrement()
private$appendEntry(domain, list(
action = "isolateEnter",
reactId = reactId,
ctxId = ctxId
))
} else {
msg$log("enter:", msg$reactStr(reactId), msg$ctxStr(ctxId, type))
msg$depthIncrement()
private$appendEntry(domain, list(
action = "enter",
reactId = reactId,
ctxId = ctxId,
type = type
))
}
},
exit = function(reactId, ctxId, type, domain) {
ctxId <- self$ctxIdStr(ctxId)
if (identical(type, "isolate")) {
msg$depthDecrement()
msg$log("isolateExit:", msg$reactStr(reactId), msg$ctxStr(ctxId))
private$appendEntry(domain, list(
action = "isolateExit",
reactId = reactId,
ctxId = ctxId
))
} else {
msg$depthDecrement()
msg$log("exit:", msg$reactStr(reactId), msg$ctxStr(ctxId, type))
private$appendEntry(domain, list(
action = "exit",
reactId = reactId,
ctxId = ctxId,
type = type
))
}
},
valueChange = function(reactId, value, domain) {
valueStr <- self$valueStr(value)
msg$log("valueChange:", msg$reactStr(reactId), msg$valueStr(valueStr))
private$appendEntry(domain, list(
action = "valueChange",
reactId = reactId,
value = valueStr
))
},
valueChangeNames = function(reactId, nameValues, domain) {
self$valueChange(self$namesIdStr(reactId), nameValues, domain)
},
valueChangeAsList = function(reactId, listValue, domain) {
self$valueChange(self$asListIdStr(reactId), listValue, domain)
},
valueChangeAsListAll = function(reactId, listValue, domain) {
self$valueChange(self$asListAllIdStr(reactId), listValue, domain)
},
valueChangeKey = function(reactId, key, value, domain) {
self$valueChange(self$keyIdStr(reactId, key), value, domain)
},
invalidateStart = function(reactId, ctxId, type, domain) {
ctxId <- self$ctxIdStr(ctxId)
if (identical(type, "isolate")) {
msg$log("isolateInvalidateStart:", msg$reactStr(reactId), msg$ctxStr(ctxId))
msg$depthIncrement()
private$appendEntry(domain, list(
action = "isolateInvalidateStart",
reactId = reactId,
ctxId = ctxId
))
} else {
msg$log("invalidateStart:", msg$reactStr(reactId), msg$ctxStr(ctxId, type))
msg$depthIncrement()
private$appendEntry(domain, list(
action = "invalidateStart",
reactId = reactId,
ctxId = ctxId,
type = type
))
}
},
invalidateEnd = function(reactId, ctxId, type, domain) {
ctxId <- self$ctxIdStr(ctxId)
if (identical(type, "isolate")) {
msg$depthDecrement()
msg$log("isolateInvalidateEnd:", msg$reactStr(reactId), msg$ctxStr(ctxId))
private$appendEntry(domain, list(
action = "isolateInvalidateEnd",
reactId = reactId,
ctxId = ctxId
))
} else {
msg$depthDecrement()
msg$log("invalidateEnd:", msg$reactStr(reactId), msg$ctxStr(ctxId, type))
private$appendEntry(domain, list(
action = "invalidateEnd",
reactId = reactId,
ctxId = ctxId,
type = type
))
}
},
invalidateLater = function(reactId, runningCtx, millis, domain) {
msg$log("invalidateLater: ", millis, "ms", msg$reactStr(reactId), msg$ctxStr(runningCtx))
private$appendEntry(domain, list(
action = "invalidateLater",
reactId = reactId,
ctxId = runningCtx,
millis = millis
))
},
idle = function(domain = NULL) {
msg$log("idle")
private$appendEntry(domain, list(
action = "idle"
))
},
asyncStart = function(domain = NULL) {
msg$log("asyncStart")
private$appendEntry(domain, list(
action = "asyncStart"
))
},
asyncStop = function(domain = NULL) {
msg$log("asyncStop")
private$appendEntry(domain, list(
action = "asyncStop"
))
},
freezeReactiveVal = function(reactId, domain) {
msg$log("freeze:", msg$reactStr(reactId))
private$appendEntry(domain, list(
action = "freeze",
reactId = reactId
))
},
freezeReactiveKey = function(reactId, key, domain) {
self$freezeReactiveVal(self$keyIdStr(reactId, key), domain)
},
thawReactiveVal = function(reactId, domain) {
msg$log("thaw:", msg$reactStr(reactId))
private$appendEntry(domain, list(
action = "thaw",
reactId = reactId
))
},
thawReactiveKey = function(reactId, key, domain) {
self$thawReactiveVal(self$keyIdStr(reactId, key), domain)
},
userMark = function(domain = NULL) {
msg$log("userMark")
private$appendEntry(domain, list(
action = "userMark"
))
}
)
)
MessageLogger = R6Class(
"MessageLogger",
portable = FALSE,
public = list(
depth = 0L,
reactCache = list(),
option = "shiny.reactlog.console",
initialize = function(option = "shiny.reactlog.console", depth = 0L) {
if (!missing(depth)) self$depth <- depth
if (!missing(option)) self$option <- option
},
isLogging = function() {
isTRUE(getOption(self$option))
},
isNotLogging = function() {
! isTRUE(getOption(self$option))
},
depthIncrement = function() {
if (self$isNotLogging()) return(NULL)
self$depth <- self$depth + 1L
},
depthDecrement = function() {
if (self$isNotLogging()) return(NULL)
self$depth <- self$depth - 1L
},
hasReact = function(reactId) {
if (self$isNotLogging()) return(FALSE)
!is.null(self$getReact(reactId))
},
getReact = function(reactId, force = FALSE) {
if (identical(force, FALSE) && self$isNotLogging()) return(NULL)
self$reactCache[[reactId]]
},
setReact = function(reactObj, force = FALSE) {
if (identical(force, FALSE) && self$isNotLogging()) return(NULL)
self$reactCache[[reactObj$reactId]] <- reactObj
},
shortenString = function(txt, n = 250) {
if (is.null(txt) || isTRUE(is.na(txt))) {
return("")
}
if (nchar(txt) > n) {
return(
paste0(substr(txt, 1, n - 3), "...")
)
}
return(txt)
},
singleLine = function(txt) {
gsub("[^\\]\\n", "\\\\n", txt)
},
valueStr = function(valueStr) {
paste0(
" '", self$shortenString(self$singleLine(valueStr)), "'"
)
},
reactStr = function(reactId) {
if (self$isNotLogging()) return(NULL)
reactInfo <- self$getReact(reactId)
if (is.null(reactInfo)) return(" <UNKNOWN_REACTID>")
paste0(
" ", reactInfo$reactId, ":'", self$shortenString(self$singleLine(reactInfo$label)), "'"
)
},
typeStr = function(type = NULL) {
self$ctxStr(ctxId = NULL, type = type)
},
ctxStr = function(ctxId = NULL, type = NULL) {
if (self$isNotLogging()) return(NULL)
self$ctxPrevCtxStr(ctxId = ctxId, prevCtxId = NULL, type = type)
},
ctxPrevCtxStr = function(ctxId = NULL, prevCtxId = NULL, type = NULL, preCtxIdTxt = " in ") {
if (self$isNotLogging()) return(NULL)
paste0(
if (!is.null(ctxId)) paste0(preCtxIdTxt, ctxId),
if (!is.null(prevCtxId)) paste0(" from ", prevCtxId),
if (!is.null(type) && !identical(type, "other")) paste0(" - ", type)
)
},
log = function(...) {
if (self$isNotLogging()) return(NULL)
msg <- paste0(
paste0(rep("= ", depth), collapse = ""), "- ", paste0(..., collapse = ""),
collapse = ""
)
message(msg)
}
)
)
#' @include stack.R
.graphStack <- Stack$new()
rLog <- RLog$new("shiny.reactlog", "shiny.reactlog.console")

View File

@@ -15,21 +15,21 @@ NULL
#' the conditional on an input or a calculated reactive, you can base it on the
#' query string). However, note that, if you're changing the query string / hash
#' programatically from within the server code, you must use
#' \code{updateQueryString(_yourNewQueryString_, mode = "push")}. The default
#' \code{mode} for \code{updateQueryString} is \code{"replace"}, which doesn't
#' `updateQueryString(_yourNewQueryString_, mode = "push")`. The default
#' `mode` for `updateQueryString` is `"replace"`, which doesn't
#' raise any events, so any observers or reactives that depend on it will
#' \emph{not} get triggered. However, if you're changing the query string / hash
#' *not* get triggered. However, if you're changing the query string / hash
#' directly by typing directly in the browser and hitting enter, you don't have
#' to worry about this.
#'
#' @param session A Shiny session object.
#'
#' @return For \code{getQueryString}, a named list. For example, the query
#' string \code{?param1=value1&param2=value2} becomes \code{list(param1 =
#' value1, param2 = value2)}. For \code{getUrlHash}, a character vector with
#' the hash (including the leading \code{#} symbol).
#' @return For `getQueryString`, a named list. For example, the query
#' string `?param1=value1&param2=value2` becomes `list(param1 =
#' value1, param2 = value2)`. For `getUrlHash`, a character vector with
#' the hash (including the leading `#` symbol).
#'
#' @seealso \code{\link{updateQueryString}}
#' @seealso [updateQueryString()]
#'
#' @examples
#' ## Only run this example in interactive R sessions

View File

@@ -2,20 +2,20 @@
#'
#' Ensure that a file-based HTML dependency (from the htmltools package) can be
#' served over Shiny's HTTP server. This function works by using
#' \code{\link{addResourcePath}} to map the HTML dependency's directory to a
#' [addResourcePath()] to map the HTML dependency's directory to a
#' URL.
#'
#' @param dependency A single HTML dependency object, created using
#' \code{\link[htmltools]{htmlDependency}}. If the \code{src} value is named,
#' then \code{href} and/or \code{file} names must be present.
#' @param scrubFile If TRUE (the default), remove \code{src$file} for the
#' [htmltools::htmlDependency()]. If the `src` value is named,
#' then `href` and/or `file` names must be present.
#' @param scrubFile If TRUE (the default), remove `src$file` for the
#' dependency. This prevents the local file path from being sent to the client
#' when dynamic web dependencies are used. If FALSE, don't remove
#' \code{src$file}. Setting it to FALSE should be needed only in very unusual
#' `src$file`. Setting it to FALSE should be needed only in very unusual
#' cases.
#'
#' @return A single HTML dependency object that has an \code{href}-named element
#' in its \code{src}.
#' @return A single HTML dependency object that has an `href`-named element
#' in its `src`.
#' @export
createWebDependency <- function(dependency, scrubFile = TRUE) {
if (is.null(dependency))

View File

@@ -1,8 +1,11 @@
#' @export a br code div em h1 h2 h3 h4 h5 h6 hr HTML img p pre span strong
#' @export includeCSS includeHTML includeMarkdown includeScript includeText
#' @export is.singleton singleton
#' @export tag tagAppendAttributes tagAppendChild tagAppendChildren tagList tags tagSetChildren withTags
#' @import htmltools
#' @export tags p h1 h2 h3 h4 h5 h6 a br div span pre code img strong em hr
#' @export tag tagList tagAppendAttributes tagHasAttribute tagGetAttribute tagAppendChild tagAppendChildren tagSetChildren
#' @export HTML
#' @export includeHTML includeText includeMarkdown includeCSS includeScript
#' @export singleton is.singleton
#' @export validateCssUnit
#' @export knit_print.html knit_print.shiny.tag knit_print.shiny.tag.list
#' @export htmlTemplate suppressDependencies
#' @export htmlTemplate
#' @export suppressDependencies
#' @export withTags
NULL

View File

@@ -1,11 +1,11 @@
#' Create an object representing click options
#'
#' This generates an object representing click options, to be passed as the
#' \code{click} argument of \code{\link{imageOutput}} or
#' \code{\link{plotOutput}}.
#' `click` argument of [imageOutput()] or
#' [plotOutput()].
#'
#' @param id Input value name. For example, if the value is \code{"plot_click"},
#' then the click coordinates will be available as \code{input$plot_click}.
#' @param id Input value name. For example, if the value is `"plot_click"`,
#' then the click coordinates will be available as `input$plot_click`.
#' @param clip Should the click area be clipped to the plotting area? If FALSE,
#' 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.
@@ -24,12 +24,12 @@ clickOpts <- function(id = NULL, clip = TRUE) {
#' Create an object representing double-click options
#'
#' This generates an object representing dobule-click options, to be passed as
#' the \code{dblclick} argument of \code{\link{imageOutput}} or
#' \code{\link{plotOutput}}.
#' the `dblclick` argument of [imageOutput()] or
#' [plotOutput()].
#'
#' @param id Input value name. For example, if the value is
#' \code{"plot_dblclick"}, then the click coordinates will be available as
#' \code{input$plot_dblclick}.
#' `"plot_dblclick"`, then the click coordinates will be available as
#' `input$plot_dblclick`.
#' @param clip Should the click area be clipped to the plotting area? If FALSE,
#' then the server will receive double-click events even when the mouse is
#' outside the plotting area, as long as it is still inside the image.
@@ -50,23 +50,23 @@ dblclickOpts <- function(id = NULL, clip = TRUE, delay = 400) {
#' Create an object representing hover options
#'
#' This generates an object representing hovering options, to be passed as the
#' \code{hover} argument of \code{\link{imageOutput}} or
#' \code{\link{plotOutput}}.
#' `hover` argument of [imageOutput()] or
#' [plotOutput()].
#'
#' @param id Input value name. For example, if the value is \code{"plot_hover"},
#' then the hover coordinates will be available as \code{input$plot_hover}.
#' @param id Input value name. For example, if the value is `"plot_hover"`,
#' then the hover coordinates will be available as `input$plot_hover`.
#' @param delay How long to delay (in milliseconds) when debouncing or
#' throttling, before sending the mouse location to the server.
#' @param delayType The type of algorithm for limiting the number of hover
#' events. Use \code{"throttle"} to limit the number of hover events to one
#' every \code{delay} milliseconds. Use \code{"debounce"} to suspend events
#' events. Use `"throttle"` to limit the number of hover events to one
#' every `delay` milliseconds. Use `"debounce"` to suspend events
#' while the cursor is moving, and wait until the cursor has been at rest for
#' \code{delay} milliseconds before sending an event.
#' `delay` milliseconds before sending an event.
#' @param clip Should the hover area be clipped to the plotting area? If FALSE,
#' then the server will receive hover events even when the mouse is outside
#' the plotting area, as long as it is still inside the image.
#' @param nullOutside If \code{TRUE} (the default), the value will be set to
#' \code{NULL} when the mouse exits the plotting area. If \code{FALSE}, the
#' @param nullOutside If `TRUE` (the default), the value will be set to
#' `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 = NULL, delay = 300,
@@ -87,34 +87,34 @@ hoverOpts <- function(id = NULL, delay = 300,
#' Create an object representing brushing options
#'
#' This generates an object representing brushing options, to be passed as the
#' \code{brush} argument of \code{\link{imageOutput}} or
#' \code{\link{plotOutput}}.
#' `brush` argument of [imageOutput()] or
#' [plotOutput()].
#'
#' @param id Input value name. For example, if the value is \code{"plot_brush"},
#' then the coordinates will be available as \code{input$plot_brush}. Multiple
#' \code{imageOutput}/\code{plotOutput} calls may share the same \code{id}
#' @param id Input value name. For example, if the value is `"plot_brush"`,
#' then the coordinates will be available as `input$plot_brush`. Multiple
#' `imageOutput`/`plotOutput` calls may share the same `id`
#' value; brushing one image or plot will cause any other brushes with the
#' same \code{id} to disappear.
#' same `id` to disappear.
#' @param fill Fill color of the brush.
#' @param stroke Outline color of the brush.
#' @param opacity Opacity of the brush
#' @param delay How long to delay (in milliseconds) when debouncing or
#' throttling, before sending the brush data to the server.
#' @param delayType The type of algorithm for limiting the number of brush
#' events. Use \code{"throttle"} to limit the number of brush events to one
#' every \code{delay} milliseconds. Use \code{"debounce"} to suspend events
#' events. Use `"throttle"` to limit the number of brush events to one
#' every `delay` milliseconds. Use `"debounce"` to suspend events
#' while the cursor is moving, and wait until the cursor has been at rest for
#' \code{delay} milliseconds before sending an event.
#' `delay` milliseconds before sending an event.
#' @param clip Should the brush area be clipped to the plotting area? If FALSE,
#' then the user will be able to brush outside the plotting area, as long as
#' it is still inside the image.
#' @param direction The direction for brushing. If \code{"xy"}, the brush can be
#' drawn and moved in both x and y directions. If \code{"x"}, or \code{"y"},
#' @param direction The direction for brushing. If `"xy"`, the brush can be
#' drawn and moved in both x and y directions. If `"x"`, or `"y"`,
#' the brush wil work horizontally or vertically.
#' @param resetOnNew When a new image is sent to the browser (via
#' \code{\link{renderImage}}), should the brush be reset? The default,
#' \code{FALSE}, is useful if you want to update the plot while keeping the
#' brush. Using \code{TRUE} is useful if you want to clear the brush whenever
#' [renderImage()]), should the brush be reset? The default,
#' `FALSE`, is useful if you want to update the plot while keeping the
#' brush. Using `TRUE` is useful if you want to clear the brush whenever
#' the plot is updated.
#' @export
brushOpts <- function(id = NULL, fill = "#9cf", stroke = "#036",

View File

@@ -1,28 +1,28 @@
#' Find rows of data that are selected by a brush
#'
#' This function returns rows from a data frame which are under a brush used
#' with \code{\link{plotOutput}}.
#' with [plotOutput()].
#'
#' 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.
#' 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.
#'
#' The \code{xvar}, \code{yvar}, \code{panelvar1}, and \code{panelvar2}
#' 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
#' \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")}.
#' `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 \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
#' 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.
@@ -33,26 +33,26 @@
#' 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
#' `brushOpts(direction = "x")`, then this function will filter out points
#' using just the x or y variable, whichever is appropriate.
#'
#' @param brush The data from a brush, such as \code{input$plot_brush}.
#' @param brush The data from a brush, such as `input$plot_brush`.
#' @param df A data frame from which to select rows.
#' @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 \code{df}. If absent, then this
#' 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
#' \code{cyl}, then you can use \code{"cyl"} here. However, specifying the
#' `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 \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 inside the
#' brush (\code{TRUE}) or outside the brush (\code{FALSE}).
#' @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
#' brush (`TRUE`) or outside the brush (`FALSE`).
#'
#' @seealso \code{\link{plotOutput}} for example usage.
#' @seealso [plotOutput()] for example usage.
#' @export
brushedPoints <- function(df, brush, xvar = NULL, yvar = NULL,
panelvar1 = NULL, panelvar2 = NULL,
@@ -88,17 +88,14 @@ brushedPoints <- function(df, brush, xvar = NULL, yvar = NULL,
stop("brushedPoints: not able to automatically infer `xvar` from brush")
if (!(xvar %in% names(df)))
stop("brushedPoints: `xvar` ('", xvar ,"') not in names of input")
# Extract data values from the data frame
x <- asNumber(df[[xvar]])
keep_rows <- keep_rows & (x >= brush$xmin & x <= brush$xmax)
keep_rows <- keep_rows & within_brush(df[[xvar]], brush, "x")
}
if (use_y) {
if (is.null(yvar))
stop("brushedPoints: not able to automatically infer `yvar` from brush")
if (!(yvar %in% names(df)))
stop("brushedPoints: `yvar` ('", yvar ,"') not in names of input")
y <- asNumber(df[[yvar]])
keep_rows <- keep_rows & (y >= brush$ymin & y <= brush$ymax)
keep_rows <- keep_rows & within_brush(df[[yvar]], brush, "y")
}
# Find which rows are matches for the panel vars (if present)
@@ -197,39 +194,39 @@ brushedPoints <- function(df, brush, xvar = NULL, yvar = NULL,
#'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 \code{\link{plotOutput}}. The rows will be sorted
#'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 \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. If this is used, the resulting data frame will not
#'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 \code{xvar}, \code{yvar}, \code{panelvar1}, and \code{panelvar2} arguments
#'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
#'\code{plot(x=cars$speed, y=cars$dist)}, and your click variable is named
#'\code{"cars_click"}, then you would use \code{nearPoints(cars,
#'input$cars_brush, "speed", "dist")}.
#'`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 \code{input$plot_click}.
#'@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 \code{threshold} will be
#' 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 \code{dist_} that contains the
#'@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 \code{dist_} will be \code{NA}.
#'@param 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 inside the
#' selected by the mouse event (\code{TRUE}) or not (\code{FALSE}).
#' 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 \code{\link{plotOutput}} for more examples.
#'@seealso [plotOutput()] for more examples.
#'
#' @examples
#' \dontrun{
@@ -281,8 +278,8 @@ nearPoints <- function(df, coordinfo, xvar = NULL, yvar = NULL,
stop("nearPoints: `yvar` ('", yvar ,"') not in names of input")
# Extract data values from the data frame
x <- asNumber(df[[xvar]])
y <- asNumber(df[[yvar]])
x <- asNumber(df[[xvar]], coordinfo$domain$discrete_limits$x)
y <- asNumber(df[[yvar]], coordinfo$domain$discrete_limits$y)
# Get the coordinates of the point (in img pixel coordinates)
point_img <- coordinfo$coords_img
@@ -402,11 +399,27 @@ nearPoints <- function(df, coordinfo, xvar = NULL, yvar = NULL,
# ..$ y: NULL
# $ .nonce : num 0.603
# Helper to determine if data values are within the limits of
# an input brush
within_brush <- function(vals, brush, var = "x") {
var <- match.arg(var, c("x", "y"))
vals <- asNumber(vals, brush$domain$discrete_limits[[var]])
# It's possible for a non-missing data values to not
# map to the axis limits, for example:
# https://github.com/rstudio/shiny/pull/2410#issuecomment-488100881
!is.na(vals) &
vals >= brush[[paste0(var, "min")]] &
vals <= brush[[paste0(var, "max")]]
}
# Coerce various types of variables to numbers. This works for Date, POSIXt,
# characters, and factors. Used because the mouse coords are numeric.
asNumber <- function(x) {
# The `levels` argument should be used when mapping this variable to
# a known set of discrete levels, which is needed for ggplot2 since
# it allows you to control ordering and possible values of a discrete
# positional scale (#2410)
asNumber <- function(x, levels = NULL) {
if (length(levels)) return(match(x, levels))
if (is.character(x)) x <- as.factor(x)
if (is.factor(x)) x <- as.integer(x)
as.numeric(x)

View File

@@ -31,29 +31,29 @@ startPNG <- function(filename, width, height, res, ...) {
#' Run a plotting function and save the output as a PNG
#'
#' This function returns the name of the PNG file that it generates. In
#' essence, it calls \code{png()}, then \code{func()}, then \code{dev.off()}.
#' So \code{func} must be a function that will generate a plot when used this
#' essence, it calls `png()`, then `func()`, then `dev.off()`.
#' So `func` must be a function that will generate a plot when used this
#' way.
#'
#' For output, it will try to use the following devices, in this order:
#' quartz (via \code{\link[grDevices]{png}}), then \code{\link[Cairo]{CairoPNG}},
#' and finally \code{\link[grDevices]{png}}. This is in order of quality of
#' output. Notably, plain \code{png} output on Linux and Windows may not
#' quartz (via [grDevices::png()]), then [Cairo::CairoPNG()],
#' and finally [grDevices::png()]. This is in order of quality of
#' output. Notably, plain `png` output on Linux and Windows may not
#' antialias some point shapes, resulting in poor quality output.
#'
#' In some cases, \code{Cairo()} provides output that looks worse than
#' \code{png()}. To disable Cairo output for an app, use
#' \code{options(shiny.usecairo=FALSE)}.
#' In some cases, `Cairo()` provides output that looks worse than
#' `png()`. To disable Cairo output for an app, use
#' `options(shiny.usecairo=FALSE)`.
#'
#' @param func A function that generates a plot.
#' @param filename The name of the output file. Defaults to a temp file with
#' extension \code{.png}.
#' extension `.png`.
#' @param width Width in pixels.
#' @param height Height in pixels.
#' @param res Resolution in pixels per inch. This value is passed to
#' \code{\link[grDevices]{png}}. Note that this affects the resolution of PNG rendering in
#' [grDevices::png()]. Note that this affects the resolution of PNG rendering in
#' R; it won't change the actual ppi of the browser.
#' @param ... Arguments to be passed through to \code{\link[grDevices]{png}}.
#' @param ... Arguments to be passed through to [grDevices::png()].
#' These can be used to set the width, height, background color, etc.
#' @export
plotPNG <- function(func, filename=tempfile(fileext='.png'),

View File

@@ -6,7 +6,7 @@
#' @inheritParams textInput
#' @param label The contents of the button or link--usually a text label, but
#' you could also use any other HTML, like an image.
#' @param icon An optional \code{\link{icon}} to appear on the button.
#' @param icon An optional [icon()] to appear on the button.
#' @param ... Named attributes to be applied to the button or link.
#'
#' @family input elements
@@ -36,7 +36,7 @@
#'
#' }
#'
#' @seealso \code{\link{observeEvent}} and \code{\link{eventReactive}}
#' @seealso [observeEvent()] and [eventReactive()]
#' @export
actionButton <- function(inputId, label, icon = NULL, width = NULL, ...) {

View File

@@ -3,11 +3,11 @@
#' Create a checkbox that can be used to specify logical values.
#'
#' @inheritParams textInput
#' @param value Initial value (\code{TRUE} or \code{FALSE}).
#' @param value Initial value (`TRUE` or `FALSE`).
#' @return A checkbox control that can be added to a UI definition.
#'
#' @family input elements
#' @seealso \code{\link{checkboxGroupInput}}, \code{\link{updateCheckboxInput}}
#' @seealso [checkboxGroupInput()], [updateCheckboxInput()]
#'
#' @examples
#' ## Only run examples in interactive R sessions

View File

@@ -7,25 +7,25 @@
#' @inheritParams textInput
#' @param choices List of values to show checkboxes for. If elements of the list
#' are named then that name rather than the value is displayed to the user. If
#' this argument is provided, then \code{choiceNames} and \code{choiceValues}
#' this argument is provided, then `choiceNames` and `choiceValues`
#' must not be provided, and vice-versa. The values should be strings; other
#' types (such as logicals and numbers) will be coerced to strings.
#' @param selected The values that should be initially selected, if any.
#' @param inline If \code{TRUE}, render the choices inline (i.e. horizontally)
#' @param inline If `TRUE`, render the choices inline (i.e. horizontally)
#' @param choiceNames,choiceValues List of names and values, respectively,
#' that are displayed to the user in the app and correspond to the each
#' choice (for this reason, \code{choiceNames} and \code{choiceValues}
#' choice (for this reason, `choiceNames` and `choiceValues`
#' must have the same length). If either of these arguments is
#' provided, then the other \emph{must} be provided and \code{choices}
#' \emph{must not} be provided. The advantage of using both of these over
#' a named list for \code{choices} is that \code{choiceNames} allows any
#' provided, then the other *must* be provided and `choices`
#' *must not* be provided. The advantage of using both of these over
#' a named list for `choices` is that `choiceNames` allows any
#' type of UI object to be passed through (tag objects, icons, HTML code,
#' ...), instead of just simple text. See Examples.
#'
#' @return A list of HTML elements that can be added to a UI definition.
#'
#' @family input elements
#' @seealso \code{\link{checkboxInput}}, \code{\link{updateCheckboxGroupInput}}
#' @seealso [checkboxInput()], [updateCheckboxGroupInput()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -94,7 +94,7 @@ checkboxGroupInput <- function(inputId, label, choices = NULL, selected = NULL,
tags$div(id = inputId,
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
class = divClass,
controlLabel(inputId, label),
shinyInputLabel(inputId, label),
options
)
}

View File

@@ -3,32 +3,32 @@
#' Creates a text input which, when clicked on, brings up a calendar that
#' the user can click on to select dates.
#'
#' The date \code{format} string specifies how the date will be displayed in
#' The date `format` string specifies how the date will be displayed in
#' the browser. It allows the following values:
#'
#' \itemize{
#' \item \code{yy} Year without century (12)
#' \item \code{yyyy} Year with century (2012)
#' \item \code{mm} Month number, with leading zero (01-12)
#' \item \code{m} Month number, without leading zero (1-12)
#' \item \code{M} Abbreviated month name
#' \item \code{MM} Full month name
#' \item \code{dd} Day of month with leading zero
#' \item \code{d} Day of month without leading zero
#' \item \code{D} Abbreviated weekday name
#' \item \code{DD} Full weekday name
#' \item `yy` Year without century (12)
#' \item `yyyy` Year with century (2012)
#' \item `mm` Month number, with leading zero (01-12)
#' \item `m` Month number, without leading zero (1-12)
#' \item `M` Abbreviated month name
#' \item `MM` Full month name
#' \item `dd` Day of month with leading zero
#' \item `d` Day of month without leading zero
#' \item `D` Abbreviated weekday name
#' \item `DD` Full weekday name
#' }
#'
#' @inheritParams textInput
#' @param value The starting date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format. If NULL (the default), will use the current date
#' `yyyy-mm-dd` format. If NULL (the default), will use the current date
#' in the client's time zone.
#' @param min The minimum allowed date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#' `yyyy-mm-dd` format.
#' @param max The maximum allowed date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#' `yyyy-mm-dd` format.
#' @param format The format of the date to display in the browser. Defaults to
#' \code{"yyyy-mm-dd"}.
#' `"yyyy-mm-dd"`.
#' @param startview The date range shown when the input object is first clicked.
#' Can be "month" (the default), "year", or "decade".
#' @param weekstart Which day is the start of the week. Should be an integer
@@ -44,12 +44,12 @@
#' @param autoclose Whether or not to close the datepicker immediately when a
#' date is selected.
#' @param datesdisabled Which dates should be disabled. Either a Date object,
#' or a string in \code{yyyy-mm-dd} format.
#' or a string in `yyyy-mm-dd` format.
#' @param daysofweekdisabled Days of the week that should be disabled. Should be
#' a integer vector with values from 0 (Sunday) to 6 (Saturday).
#'
#' @family input elements
#' @seealso \code{\link{dateRangeInput}}, \code{\link{updateDateInput}}
#' @seealso [dateRangeInput()], [updateDateInput()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -78,7 +78,7 @@
#'
#' # Disable Mondays and Tuesdays.
#' dateInput("date7", "Date:", daysofweekdisabled = c(1,2)),
#'
#'
#' # Disable specific dates.
#' dateInput("date8", "Date:", value = "2012-02-29",
#' datesdisabled = c("2012-03-01", "2012-03-02"))
@@ -92,14 +92,10 @@ dateInput <- function(inputId, label, value = NULL, min = NULL, max = NULL,
language = "en", width = NULL, autoclose = TRUE,
datesdisabled = NULL, daysofweekdisabled = NULL) {
# If value is a date object, convert it to a string with yyyy-mm-dd format
# Same for min and max
if (inherits(value, "Date")) value <- format(value, "%Y-%m-%d")
if (inherits(min, "Date")) min <- format(min, "%Y-%m-%d")
if (inherits(max, "Date")) max <- format(max, "%Y-%m-%d")
if (inherits(datesdisabled, "Date")) {
datesdisabled <- format(datesdisabled, "%Y-%m-%d")
}
value <- dateYMD(value, "value")
min <- dateYMD(min, "min")
max <- dateYMD(max, "max")
datesdisabled <- dateYMD(datesdisabled, "datesdisabled")
value <- restoreInput(id = inputId, default = value)
@@ -107,7 +103,7 @@ dateInput <- function(inputId, label, value = NULL, min = NULL, max = NULL,
class = "shiny-date-input form-group shiny-input-container",
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
controlLabel(inputId, label),
shinyInputLabel(inputId, label),
tags$input(type = "text",
class = "form-control",
`data-date-language` = language,

View File

@@ -3,33 +3,33 @@
#' Creates a pair of text inputs which, when clicked on, bring up calendars that
#' the user can click on to select dates.
#'
#' The date \code{format} string specifies how the date will be displayed in
#' The date `format` string specifies how the date will be displayed in
#' the browser. It allows the following values:
#'
#' \itemize{
#' \item \code{yy} Year without century (12)
#' \item \code{yyyy} Year with century (2012)
#' \item \code{mm} Month number, with leading zero (01-12)
#' \item \code{m} Month number, without leading zero (1-12)
#' \item \code{M} Abbreviated month name
#' \item \code{MM} Full month name
#' \item \code{dd} Day of month with leading zero
#' \item \code{d} Day of month without leading zero
#' \item \code{D} Abbreviated weekday name
#' \item \code{DD} Full weekday name
#' \item `yy` Year without century (12)
#' \item `yyyy` Year with century (2012)
#' \item `mm` Month number, with leading zero (01-12)
#' \item `m` Month number, without leading zero (1-12)
#' \item `M` Abbreviated month name
#' \item `MM` Full month name
#' \item `dd` Day of month with leading zero
#' \item `d` Day of month without leading zero
#' \item `D` Abbreviated weekday name
#' \item `DD` Full weekday name
#' }
#'
#' @inheritParams dateInput
#' @param start The initial start date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format. If NULL (the default), will use the current
#' `yyyy-mm-dd` format. If NULL (the default), will use the current
#' date in the client's time zone.
#' @param end The initial end date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format. If NULL (the default), will use the current
#' `yyyy-mm-dd` format. If NULL (the default), will use the current
#' date in the client's time zone.
#' @param separator String to display between the start and end input boxes.
#'
#' @family input elements
#' @seealso \code{\link{dateInput}}, \code{\link{updateDateRangeInput}}
#' @seealso [dateInput()], [updateDateRangeInput()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -76,12 +76,10 @@ dateRangeInput <- function(inputId, label, start = NULL, end = NULL,
weekstart = 0, language = "en", separator = " to ", width = NULL,
autoclose = TRUE) {
# If start and end are date objects, convert to a string with yyyy-mm-dd format
# Same for min and max
if (inherits(start, "Date")) start <- format(start, "%Y-%m-%d")
if (inherits(end, "Date")) end <- format(end, "%Y-%m-%d")
if (inherits(min, "Date")) min <- format(min, "%Y-%m-%d")
if (inherits(max, "Date")) max <- format(max, "%Y-%m-%d")
start <- dateYMD(start, "start")
end <- dateYMD(end, "end")
min <- dateYMD(min, "min")
max <- dateYMD(max, "max")
restored <- restoreInput(id = inputId, default = list(start, end))
start <- restored[[1]]
@@ -92,7 +90,7 @@ dateRangeInput <- function(inputId, label, start = NULL, end = NULL,
class = "shiny-date-range-input form-group shiny-input-container",
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
controlLabel(inputId, label),
shinyInputLabel(inputId, label),
# input-daterange class is needed for dropdown behavior
div(class = "input-daterange input-group",
tags$input(

View File

@@ -6,15 +6,15 @@
#' to a dataframe. This dataframe contains one row for each selected file, and
#' the following columns:
#' \describe{
#' \item{\code{name}}{The filename provided by the web browser. This is
#' \strong{not} the path to read to get at the actual data that was uploaded
#' \item{`name`}{The filename provided by the web browser. This is
#' **not** the path to read to get at the actual data that was uploaded
#' (see
#' \code{datapath} column).}
#' \item{\code{size}}{The size of the uploaded data, in
#' `datapath` column).}
#' \item{`size`}{The size of the uploaded data, in
#' bytes.}
#' \item{\code{type}}{The MIME type reported by the browser (for example,
#' \code{text/plain}), or empty string if the browser didn't know.}
#' \item{\code{datapath}}{The path to a temp file that contains the data that was
#' \item{`type`}{The MIME type reported by the browser (for example,
#' `text/plain`), or empty string if the browser didn't know.}
#' \item{`datapath`}{The path to a temp file that contains the data that was
#' uploaded. This file may be deleted if the user performs another upload
#' operation.}
#' }
@@ -23,8 +23,8 @@
#'
#' @inheritParams textInput
#' @param multiple Whether the user should be allowed to select and upload
#' multiple files at once. \bold{Does not work on older browsers, including
#' Internet Explorer 9 and earlier.}
#' multiple files at once. **Does not work on older browsers, including
#' Internet Explorer 9 and earlier.**
#' @param accept A character vector of MIME types; gives the browser a hint of
#' what kind of files the server is expecting.
#' @param buttonLabel The label used on the button. Can be text or an HTML tag
@@ -103,7 +103,7 @@ fileInput <- function(inputId, label, multiple = FALSE, accept = NULL,
div(class = "form-group shiny-input-container",
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
label %AND% tags$label(label),
shinyInputLabel(inputId, label),
div(class = "input-group",
tags$label(class = "input-group-btn",

View File

@@ -9,7 +9,7 @@
#' @return A numeric input control that can be added to a UI definition.
#'
#' @family input elements
#' @seealso \code{\link{updateNumericInput}}
#' @seealso [updateNumericInput()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -42,7 +42,7 @@ numericInput <- function(inputId, label, value, min = NA, max = NA, step = NA,
div(class = "form-group shiny-input-container",
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
label %AND% tags$label(label, `for` = inputId),
shinyInputLabel(inputId, label),
inputTag
)
}

View File

@@ -6,7 +6,7 @@
#' @return A text input control that can be added to a UI definition.
#'
#' @family input elements
#' @seealso \code{\link{updateTextInput}}
#' @seealso [updateTextInput()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -30,7 +30,7 @@ passwordInput <- function(inputId, label, value = "", width = NULL,
placeholder = NULL) {
div(class = "form-group shiny-input-container",
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
label %AND% tags$label(label, `for` = inputId),
shinyInputLabel(inputId, label),
tags$input(id = inputId, type="password", class="form-control", value=value,
placeholder = placeholder)
)

View File

@@ -3,33 +3,33 @@
#' Create a set of radio buttons used to select an item from a list.
#'
#' If you need to represent a "None selected" state, it's possible to default
#' the radio buttons to have no options selected by using \code{selected =
#' character(0)}. However, this is not recommended, as it gives the user no way
#' the radio buttons to have no options selected by using `selected =
#' character(0)`. However, this is not recommended, as it gives the user no way
#' to return to that state once they've made a selection. Instead, consider
#' having the first of your choices be \code{c("None selected" = "")}.
#' having the first of your choices be `c("None selected" = "")`.
#'
#' @inheritParams textInput
#' @param choices List of values to select from (if elements of the list are
#' named then that name rather than the value is displayed to the user). If
#' this argument is provided, then \code{choiceNames} and \code{choiceValues}
#' this argument is provided, then `choiceNames` and `choiceValues`
#' must not be provided, and vice-versa. The values should be strings; other
#' types (such as logicals and numbers) will be coerced to strings.
#' @param selected The initially selected value (if not specified then defaults
#' to the first value)
#' @param inline If \code{TRUE}, render the choices inline (i.e. horizontally)
#' @param inline If `TRUE`, render the choices inline (i.e. horizontally)
#' @return A set of radio buttons that can be added to a UI definition.
#' @param choiceNames,choiceValues List of names and values, respectively, that
#' are displayed to the user in the app and correspond to the each choice (for
#' this reason, \code{choiceNames} and \code{choiceValues} must have the same
#' this reason, `choiceNames` and `choiceValues` must have the same
#' length). If either of these arguments is provided, then the other
#' \emph{must} be provided and \code{choices} \emph{must not} be provided. The
#' advantage of using both of these over a named list for \code{choices} is
#' that \code{choiceNames} allows any type of UI object to be passed through
#' *must* be provided and `choices` *must not* be provided. The
#' advantage of using both of these over a named list for `choices` is
#' that `choiceNames` allows any type of UI object to be passed through
#' (tag objects, icons, HTML code, ...), instead of just simple text. See
#' Examples.
#'
#' @family input elements
#' @seealso \code{\link{updateRadioButtons}}
#' @seealso [updateRadioButtons()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -102,7 +102,7 @@ radioButtons <- function(inputId, label, choices = NULL, selected = NULL,
tags$div(id = inputId,
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
class = divClass,
controlLabel(inputId, label),
shinyInputLabel(inputId, label),
options
)
}

View File

@@ -3,37 +3,37 @@
#' Create a select list that can be used to choose a single or multiple items
#' from a list of values.
#'
#' By default, \code{selectInput()} and \code{selectizeInput()} use the
#' By default, `selectInput()` and `selectizeInput()` use the
#' JavaScript library \pkg{selectize.js}
#' (\url{https://github.com/selectize/selectize.js}) to instead of the basic
#' (<https://github.com/selectize/selectize.js>) instead of the basic
#' select input element. To use the standard HTML select input element, use
#' \code{selectInput()} with \code{selectize=FALSE}.
#' `selectInput()` with `selectize=FALSE`.
#'
#' In selectize mode, if the first element in \code{choices} has a value of
#' \code{""}, its name will be treated as a placeholder prompt. For example:
#' \code{selectInput("letter", "Letter", c("Choose one" = "", LETTERS))}
#' In selectize mode, if the first element in `choices` has a value of
#' `""`, its name will be treated as a placeholder prompt. For example:
#' `selectInput("letter", "Letter", c("Choose one" = "", LETTERS))`
#'
#' @inheritParams textInput
#' @param choices List of values to select from. If elements of the list are
#' named, then that name rather than the value is displayed to the user.
#' This can also be a named list whose elements are (either named or
#' unnamed) lists or vectors. If this is the case, the outermost names
#' will be used as the "optgroup" label for the elements in the respective
#' sublist. This allows you to group and label similar choices. See the
#' named, then that name --- rather than the value --- is displayed to the
#' user. It's also possible to group related inputs by providing a named list
#' whose elements are (either named or unnamed) lists, vectors, or factors. In this
#' case, the outermost names will be used as the group labels (leveraging the
#' `<optgroup>` HTML tag) for the elements in the respective sublist. See the
#' example section for a small demo of this feature.
#' @param selected The initially selected value (or multiple values if
#' \code{multiple = TRUE}). If not specified then defaults to the first value
#' `multiple = TRUE`). If not specified then defaults to the first value
#' for single-select lists and no values for multiple select lists.
#' @param multiple Is selection of multiple items allowed?
#' @param selectize Whether to use \pkg{selectize.js} or not.
#' @param size Number of items to show in the selection box; a larger number
#' will result in a taller box. Not compatible with \code{selectize=TRUE}.
#' Normally, when \code{multiple=FALSE}, a select input will be a drop-down
#' list, but when \code{size} is set, it will be a box instead.
#' will result in a taller box. Not compatible with `selectize=TRUE`.
#' Normally, when `multiple=FALSE`, a select input will be a drop-down
#' list, but when `size` is set, it will be a box instead.
#' @return A select list control that can be added to a UI definition.
#'
#' @family input elements
#' @seealso \code{\link{updateSelectInput}} \code{\link{varSelectInput}}
#' @seealso [updateSelectInput()] [varSelectInput()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -55,7 +55,7 @@
#' }
#' )
#'
#' # demoing optgroup support in the `choices` arg
#' # demoing group support in the `choices` arg
#' shinyApp(
#' ui = fluidPage(
#' selectInput("state", "Choose a state:",
@@ -105,7 +105,7 @@ selectInput <- function(inputId, label, choices, selected = NULL,
res <- div(
class = "form-group shiny-input-container",
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
controlLabel(inputId, label),
shinyInputLabel(inputId, label),
div(selectTag)
)
@@ -153,21 +153,21 @@ needOptgroup <- function(choices) {
}
#' @rdname selectInput
#' @param ... Arguments passed to \code{selectInput()}.
#' @param ... Arguments passed to `selectInput()`.
#' @param options A list of options. See the documentation of \pkg{selectize.js}
#' for possible options (character option values inside \code{\link[base]{I}()} will
#' be treated as literal JavaScript code; see \code{\link{renderDataTable}()}
#' for possible options (character option values inside [base::I()] will
#' be treated as literal JavaScript code; see [renderDataTable()]
#' for details).
#' @param width The width of the input, e.g. \code{'400px'}, or \code{'100\%'};
#' see \code{\link{validateCssUnit}}.
#' @note The selectize input created from \code{selectizeInput()} allows
#' @param width The width of the input, e.g. `'400px'`, or `'100%'`;
#' see [validateCssUnit()].
#' @note The selectize input created from `selectizeInput()` allows
#' deletion of the selected option even in a single select input, which will
#' return an empty string as its value. This is the default behavior of
#' \pkg{selectize.js}. However, the selectize input created from
#' \code{selectInput(..., selectize = TRUE)} will ignore the empty string
#' `selectInput(..., selectize = TRUE)` will ignore the empty string
#' value when it is a single choice input and the empty string is not in the
#' \code{choices} argument. This is to keep compatibility with
#' \code{selectInput(..., selectize = FALSE)}.
#' `choices` argument. This is to keep compatibility with
#' `selectInput(..., selectize = FALSE)`.
#' @export
selectizeInput <- function(inputId, ..., options = NULL, width = NULL) {
selectizeIt(
@@ -225,30 +225,30 @@ selectizeIt <- function(inputId, select, options, nonempty = FALSE) {
#' Create a select list that can be used to choose a single or multiple items
#' from the column names of a data frame.
#'
#' The resulting server \code{input} value will be returned as:
#' The resulting server `input` value will be returned as:
#' \itemize{
#' \item a symbol if \code{multiple = FALSE}. The \code{input} value should be
#' used with rlang's \code{\link[rlang]{!!}}. For example,
#' \code{ggplot2::aes(!!input$variable)}.
#' \item a list of symbols if \code{multiple = TRUE}. The \code{input} value
#' should be used with rlang's \code{\link[rlang]{!!!}} to expand
#' \item a symbol if `multiple = FALSE`. The `input` value should be
#' used with rlang's [rlang::!!()]. For example,
#' `ggplot2::aes(!!input$variable)`.
#' \item a list of symbols if `multiple = TRUE`. The `input` value
#' should be used with rlang's [rlang::!!!()] to expand
#' the symbol list as individual arguments. For example,
#' \code{dplyr::select(mtcars, !!!input$variabls)} which is
#' equivalent to \code{dplyr::select(mtcars, !!input$variabls[[1]], !!input$variabls[[2]], ..., !!input$variabls[[length(input$variabls)]])}.
#' `dplyr::select(mtcars, !!!input$variabls)` which is
#' equivalent to `dplyr::select(mtcars, !!input$variabls[[1]], !!input$variabls[[2]], ..., !!input$variabls[[length(input$variabls)]])`.
#' }
#'
#' By default, \code{varSelectInput()} and \code{selectizeInput()} use the
#' By default, `varSelectInput()` and `selectizeInput()` use the
#' JavaScript library \pkg{selectize.js}
#' (\url{https://github.com/selectize/selectize.js}) to instead of the basic
#' (<https://github.com/selectize/selectize.js>) to instead of the basic
#' select input element. To use the standard HTML select input element, use
#' \code{selectInput()} with \code{selectize=FALSE}.
#' `selectInput()` with `selectize=FALSE`.
#'
#' @inheritParams selectInput
#' @param data A data frame. Used to retrieve the column names as choices for a \code{\link{selectInput}}
#' @param data A data frame. Used to retrieve the column names as choices for a [selectInput()]
#' @return A variable select list control that can be added to a UI definition.
#'
#' @family input elements
#' @seealso \code{\link{updateSelectInput}}
#' @seealso [updateSelectInput()]
#' @examples
#'
#' ## Only run examples in interactive R sessions
@@ -321,21 +321,21 @@ varSelectInput <- function(
#' @rdname varSelectInput
#' @param ... Arguments passed to \code{varSelectInput()}.
#' @param ... Arguments passed to `varSelectInput()`.
#' @param options A list of options. See the documentation of \pkg{selectize.js}
#' for possible options (character option values inside \code{\link[base]{I}()} will
#' be treated as literal JavaScript code; see \code{\link{renderDataTable}()}
#' for possible options (character option values inside [base::I()] will
#' be treated as literal JavaScript code; see [renderDataTable()]
#' for details).
#' @param width The width of the input, e.g. \code{'400px'}, or \code{'100\%'};
#' see \code{\link{validateCssUnit}}.
#' @note The variable selectize input created from \code{varSelectizeInput()} allows
#' @param width The width of the input, e.g. `'400px'`, or `'100%'`;
#' see [validateCssUnit()].
#' @note The variable selectize input created from `varSelectizeInput()` allows
#' deletion of the selected option even in a single select input, which will
#' return an empty string as its value. This is the default behavior of
#' \pkg{selectize.js}. However, the selectize input created from
#' \code{selectInput(..., selectize = TRUE)} will ignore the empty string
#' `selectInput(..., selectize = TRUE)` will ignore the empty string
#' value when it is a single choice input and the empty string is not in the
#' \code{choices} argument. This is to keep compatibility with
#' \code{selectInput(..., selectize = FALSE)}.
#' `choices` argument. This is to keep compatibility with
#' `selectInput(..., selectize = FALSE)`.
#' @export
varSelectizeInput <- function(inputId, ..., options = NULL, width = NULL) {
selectizeIt(

View File

@@ -8,45 +8,45 @@
#' @param value The initial value of the slider. A numeric vector of length one
#' will create a regular slider; a numeric vector of length two will create a
#' double-ended range slider. A warning will be issued if the value doesn't
#' fit between \code{min} and \code{max}.
#' fit between `min` and `max`.
#' @param step Specifies the interval between each selectable value on the
#' slider (if \code{NULL}, a heuristic is used to determine the step size). If
#' the values are dates, \code{step} is in days; if the values are times
#' (POSIXt), \code{step} is in seconds.
#' @param round \code{TRUE} to round all values to the nearest integer;
#' \code{FALSE} if no rounding is desired; or an integer to round to that
#' slider (if `NULL`, a heuristic is used to determine the step size). If
#' the values are dates, `step` is in days; if the values are times
#' (POSIXt), `step` is in seconds.
#' @param round `TRUE` to round all values to the nearest integer;
#' `FALSE` if no rounding is desired; or an integer to round to that
#' number of digits (for example, 1 will round to the nearest 10, and -2 will
#' round to the nearest .01). Any rounding will be applied after snapping to
#' the nearest step.
#' @param format Deprecated.
#' @param locale Deprecated.
#' @param ticks \code{FALSE} to hide tick marks, \code{TRUE} to show them
#' @param ticks `FALSE` to hide tick marks, `TRUE` to show them
#' according to some simple heuristics.
#' @param animate \code{TRUE} to show simple animation controls with default
#' settings; \code{FALSE} not to; or a custom settings list, such as those
#' created using \code{\link{animationOptions}}.
#' @param animate `TRUE` to show simple animation controls with default
#' settings; `FALSE` not to; or a custom settings list, such as those
#' created using [animationOptions()].
#' @param sep Separator between thousands places in numbers.
#' @param pre A prefix string to put in front of the value.
#' @param post A suffix string to put after the value.
#' @param dragRange This option is used only if it is a range slider (with two
#' values). If \code{TRUE} (the default), the range can be dragged. In other
#' words, the min and max can be dragged together. If \code{FALSE}, the range
#' values). If `TRUE` (the default), the range can be dragged. In other
#' words, the min and max can be dragged together. If `FALSE`, the range
#' cannot be dragged.
#' @param timeFormat Only used if the values are Date or POSIXt objects. A time
#' format string, to be passed to the Javascript strftime library. See
#' \url{https://github.com/samsonjs/strftime} for more details. The allowed
#' <https://github.com/samsonjs/strftime> for more details. The allowed
#' format specifications are very similar, but not identical, to those for R's
#' \code{\link[base]{strftime}} function. For Dates, the default is \code{"\%F"}
#' (like \code{"2015-07-01"}), and for POSIXt, the default is \code{"\%F \%T"}
#' (like \code{"2015-07-01 15:32:10"}).
#' [base::strftime()] function. For Dates, the default is `"%F"`
#' (like `"2015-07-01"`), and for POSIXt, the default is `"%F %T"`
#' (like `"2015-07-01 15:32:10"`).
#' @param timezone Only used if the values are POSIXt objects. A string
#' specifying the time zone offset for the displayed times, in the format
#' \code{"+HHMM"} or \code{"-HHMM"}. If \code{NULL} (the default), times will
#' be displayed in the browser's time zone. The value \code{"+0000"} will
#' `"+HHMM"` or `"-HHMM"`. If `NULL` (the default), times will
#' be displayed in the browser's time zone. The value `"+0000"` will
#' result in UTC time.
#' @inheritParams selectizeInput
#' @family input elements
#' @seealso \code{\link{updateSliderInput}}
#' @seealso [updateSliderInput()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -172,7 +172,7 @@ sliderInput <- function(inputId, label, min, max, value, step = NULL,
sliderTag <- div(class = "form-group shiny-input-container",
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
if (!is.null(label)) controlLabel(inputId, label),
shinyInputLabel(inputId, label),
do.call(tags$input, sliderProps)
)
@@ -253,13 +253,13 @@ findStepSize <- function(min, max, step) {
#' @rdname sliderInput
#'
#' @param interval The interval, in milliseconds, between each animation step.
#' @param loop \code{TRUE} to automatically restart the animation when it
#' @param loop `TRUE` to automatically restart the animation when it
#' reaches the end.
#' @param playButton Specifies the appearance of the play button. Valid values
#' are a one-element character vector (for a simple text label), an HTML tag
#' or list of tags (using \code{\link{tag}} and friends), or raw HTML (using
#' \code{\link{HTML}}).
#' @param pauseButton Similar to \code{playButton}, but for the pause button.
#' or list of tags (using [tag()] and friends), or raw HTML (using
#' [HTML()]).
#' @param pauseButton Similar to `playButton`, but for the pause button.
#' @export
animationOptions <- function(interval=1000,
loop=FALSE,

View File

@@ -3,30 +3,30 @@
#' Create a submit button for an app. Apps that include a submit
#' button do not automatically update their outputs when inputs change,
#' rather they wait until the user explicitly clicks the submit button.
#' The use of \code{submitButton} is generally discouraged in favor of
#' the more versatile \code{\link{actionButton}} (see details below).
#' The use of `submitButton` is generally discouraged in favor of
#' the more versatile [actionButton()] (see details below).
#'
#' Submit buttons are unusual Shiny inputs, and we recommend using
#' \code{\link{actionButton}} instead of \code{submitButton} when you
#' [actionButton()] instead of `submitButton` when you
#' want to delay a reaction.
#' See \href{http://shiny.rstudio.com/articles/action-buttons.html}{this
#' article} for more information (including a demo of how to "translate"
#' code using a \code{submitButton} to code using an \code{actionButton}).
#' See [this
#' article](http://shiny.rstudio.com/articles/action-buttons.html) for more information (including a demo of how to "translate"
#' code using a `submitButton` to code using an `actionButton`).
#'
#' In essence, the presence of a submit button stops all inputs from
#' sending their values automatically to the server. This means, for
#' instance, that if there are \emph{two} submit buttons in the same app,
#' instance, that if there are *two* submit buttons in the same app,
#' clicking either one will cause all inputs in the app to send their
#' values to the server. This is probably not what you'd want, which is
#' why submit button are unwieldy for all but the simplest apps. There
#' are other problems with submit buttons: for example, dynamically
#' created submit buttons (for example, with \code{\link{renderUI}}
#' or \code{\link{insertUI}}) will not work.
#' created submit buttons (for example, with [renderUI()]
#' or [insertUI()]) will not work.
#'
#' @param text Button caption
#' @param icon Optional \code{\link{icon}} to appear on the button
#' @param width The width of the button, e.g. \code{'400px'}, or \code{'100\%'};
#' see \code{\link{validateCssUnit}}.
#' @param icon Optional [icon()] to appear on the button
#' @param width The width of the button, e.g. `'400px'`, or `'100%'`;
#' see [validateCssUnit()].
#' @return A submit button that can be added to a UI definition.
#'
#' @family input elements

View File

@@ -2,18 +2,18 @@
#'
#' Create an input control for entry of unstructured text values
#'
#' @param inputId The \code{input} slot that will be used to access the value.
#' @param label Display label for the control, or \code{NULL} for no label.
#' @param inputId The `input` slot that will be used to access the value.
#' @param label Display label for the control, or `NULL` for no label.
#' @param value Initial value.
#' @param width The width of the input, e.g. \code{'400px'}, or \code{'100\%'};
#' see \code{\link{validateCssUnit}}.
#' @param width The width of the input, e.g. `'400px'`, or `'100%'`;
#' see [validateCssUnit()].
#' @param placeholder A character string giving the user a hint as to what can
#' be entered into the control. Internet Explorer 8 and 9 do not support this
#' option.
#' @return A text input control that can be added to a UI definition.
#'
#' @family input elements
#' @seealso \code{\link{updateTextInput}}
#' @seealso [updateTextInput()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -36,7 +36,7 @@ textInput <- function(inputId, label, value = "", width = NULL,
div(class = "form-group shiny-input-container",
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
label %AND% tags$label(label, `for` = inputId),
shinyInputLabel(inputId, label),
tags$input(id = inputId, type="text", class="form-control", value=value,
placeholder = placeholder)
)

View File

@@ -3,22 +3,23 @@
#' Create a textarea input control for entry of unstructured text values.
#'
#' @inheritParams textInput
#' @param height The height of the input, e.g. \code{'400px'}, or
#' \code{'100\%'}; see \code{\link{validateCssUnit}}.
#' @param cols Value of the visible character columns of the input, e.g.
#' \code{80}. If used with \code{width}, \code{width} will take precedence in
#' the browser's rendering.
#' @param rows The value of the visible character rows of the input, e.g.
#' \code{6}. If used with \code{height}, \code{height} will take precedence in
#' the browser's rendering.
#' @param height The height of the input, e.g. `'400px'`, or `'100%'`; see
#' [validateCssUnit()].
#' @param cols Value of the visible character columns of the input, e.g. `80`.
#' This argument will only take effect if there is not a CSS `width` rule
#' defined for this element; such a rule could come from the `width` argument
#' of this function or from a containing page layout such as
#' [fluidPage()].
#' @param rows The value of the visible character rows of the input, e.g. `6`.
#' If the `height` argument is specified, `height` will take precedence in the
#' browser's rendering.
#' @param resize Which directions the textarea box can be resized. Can be one of
#' \code{"both"}, \code{"none"}, \code{"vertical"}, and \code{"horizontal"}.
#' The default, \code{NULL}, will use the client browser's default setting for
#' resizing textareas.
#' `"both"`, `"none"`, `"vertical"`, and `"horizontal"`. The default, `NULL`,
#' will use the client browser's default setting for resizing textareas.
#' @return A textarea input control that can be added to a UI definition.
#'
#' @family input elements
#' @seealso \code{\link{updateTextAreaInput}}
#' @seealso [updateTextAreaInput()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -55,7 +56,7 @@ textAreaInput <- function(inputId, label, value = "", width = NULL, height = NUL
if (length(style) == 0) style <- NULL
div(class = "form-group shiny-input-container",
label %AND% tags$label(label, `for` = inputId),
shinyInputLabel(inputId, label),
tags$textarea(
id = inputId,
class = "form-control",

View File

@@ -1,5 +1,10 @@
controlLabel <- function(controlName, label) {
label %AND% tags$label(class = "control-label", `for` = controlName, label)
shinyInputLabel <- function(inputId, label = NULL) {
tags$label(
label,
class = "control-label",
class = if (is.null(label)) "shiny-label-null",
`for` = inputId
)
}
# This function takes in either a list or vector for `choices` (and
@@ -85,45 +90,76 @@ generateOptions <- function(inputId, selected, inline, type = 'checkbox',
div(class = "shiny-options-group", options)
}
# True when a choice list item represents a group of related inputs.
isGroup <- function(choice) {
length(choice) > 1 || !is.null(names(choice))
}
# Takes a vector or list, and adds names (same as the value) to any entries
# True when choices is a list and contains at least one group of related inputs.
hasGroups <- function(choices) {
is.list(choices) && any(vapply(choices, isGroup, logical(1)))
}
# Assigns empty names to x if it's unnamed, and then fills any empty names with
# the corresponding value coerced to a character(1).
setDefaultNames <- function(x) {
x <- asNamed(x)
emptyNames <- names(x) == ""
names(x)[emptyNames] <- as.character(x)[emptyNames]
x
}
# Makes a character vector out of x in a way that preserves names.
asCharacter <- function(x) {
stats::setNames(as.character(x), names(x))
}
# Processes a "flat" set of choices, or a collection of choices not containing
# any named groups. choices should be a list without any list children, or an
# atomic vector. choices may be named or unnamed. Any empty names are replaced
# with the corresponding value coerced to a character.
processFlatChoices <- function(choices) {
choices <- setDefaultNames(asCharacter(choices))
as.list(choices)
}
# Processes a "nested" set of choices, or a collection of choices that contains
# one or more named groups of related choices and zero or more "flat" choices.
# choices should be a named list, and any choice group must have a non-empty
# name. Empty names of remaining "flat" choices are replaced with that choice's
# value coerced to a character.
processGroupedChoices <- function(choices) {
# We assert choices is a list, since only a list may contain a group.
stopifnot(is.list(choices))
choices <- mapply(function(name, choice) {
choiceIsGroup <- isGroup(choice)
if (choiceIsGroup && name == "") {
# If the choice is a group, and if its name is empty, produce an error. We
# error here because the composite nature of the choice prevents us from
# meaningfully automatically naming it. Note that while not documented,
# groups are not necessarily lists (aka generic vectors) but can also be
# any named atomic vector, or any atomic vector of length > 1.
stop('All sub-lists in "choices" must be named.')
} else if (choiceIsGroup) {
# The choice is a group, but it is named. Process it using the same
# function we use for "top level" choices.
processFlatChoices(choice)
} else {
# The choice was not named and is not a group; it is a "leaf".
as.character(choice)
}
}, names(choices), choices, SIMPLIFY = FALSE)
# By this point, any leaves in the choices list might still have empty names,
# so we're sure to automatically name them.
setDefaultNames(choices)
}
# Takes a vector/list/factor, and adds names (same as the value) to any entries
# without names. Coerces all leaf nodes to `character`.
choicesWithNames <- function(choices) {
# Take a vector or list, and convert to list. Also, if any children are
# vectors with length > 1, convert those to list. If the list is unnamed,
# convert it to a named list with blank names.
listify <- function(obj) {
# If a list/vector is unnamed, give it blank names
makeNamed <- function(x) {
if (is.null(names(x))) names(x) <- character(length(x))
x
}
res <- lapply(obj, function(val) {
if (is.list(val))
listify(val)
else if (length(val) == 1 && is.null(names(val)))
as.character(val)
else
makeNamed(as.list(val))
})
makeNamed(res)
if (hasGroups(choices)) {
processGroupedChoices(choices)
} else {
processFlatChoices(choices)
}
choices <- listify(choices)
if (length(choices) == 0) return(choices)
# Recurse into any subgroups
choices <- mapply(choices, names(choices), FUN = function(choice, name) {
if (!is.list(choice)) return(choice)
if (name == "") stop('All sub-lists in "choices" must be named.')
choicesWithNames(choice)
}, SIMPLIFY = FALSE)
# default missing names to choice values
missing <- names(choices) == ""
names(choices)[missing] <- as.character(choices)[missing]
choices
}

View File

@@ -1,43 +1,43 @@
#' Dynamically insert/remove a tabPanel
#'
#' Dynamically insert or remove a \code{\link{tabPanel}} (or a
#' \code{\link{navbarMenu}}) from an existing \code{\link{tabsetPanel}},
#' \code{\link{navlistPanel}} or \code{\link{navbarPage}}.
#' Dynamically insert or remove a [tabPanel()] (or a
#' [navbarMenu()]) from an existing [tabsetPanel()],
#' [navlistPanel()] or [navbarPage()].
#'
#' When you want to insert a new tab before or after an existing tab, you
#' should use \code{insertTab}. When you want to prepend a tab (i.e. add a
#' tab to the beginning of the \code{tabsetPanel}), use \code{prependTab}.
#' should use `insertTab`. When you want to prepend a tab (i.e. add a
#' tab to the beginning of the `tabsetPanel`), use `prependTab`.
#' When you want to append a tab (i.e. add a tab to the end of the
#' \code{tabsetPanel}), use \code{appendTab}.
#' `tabsetPanel`), use `appendTab`.
#'
#' For \code{navbarPage}, you can insert/remove conventional
#' \code{tabPanel}s (whether at the top level or nested inside a
#' \code{navbarMenu}), as well as an entire \code{\link{navbarMenu}}.
#' For the latter case, \code{target} should be the \code{menuName} that
#' you gave your \code{navbarMenu} when you first created it (by default,
#' this is equal to the value of the \code{title} argument).
#' For `navbarPage`, you can insert/remove conventional
#' `tabPanel`s (whether at the top level or nested inside a
#' `navbarMenu`), as well as an entire [navbarMenu()].
#' For the latter case, `target` should be the `menuName` that
#' you gave your `navbarMenu` when you first created it (by default,
#' this is equal to the value of the `title` argument).
#'
#' @param inputId The \code{id} of the \code{tabsetPanel} (or
#' \code{navlistPanel} or \code{navbarPage}) into which \code{tab} will
#' @param inputId The `id` of the `tabsetPanel` (or
#' `navlistPanel` or `navbarPage`) into which `tab` will
#' be inserted/removed.
#'
#' @param tab The item to be added (must be created with \code{tabPanel},
#' or with \code{navbarMenu}).
#' @param tab The item to be added (must be created with `tabPanel`,
#' or with `navbarMenu`).
#'
#' @param target If inserting: the \code{value} of an existing
#' \code{tabPanel}, next to which \code{tab} will be added.
#' If removing: the \code{value} of the \code{tabPanel} that
#' @param target If inserting: the `value` of an existing
#' `tabPanel`, next to which `tab` will be added.
#' If removing: the `value` of the `tabPanel` that
#' you want to remove. See Details if you want to insert next to/remove
#' an entire \code{navbarMenu} instead.
#' an entire `navbarMenu` instead.
#'
#' @param position Should \code{tab} be added before or after the
#' \code{target} tab?
#' @param position Should `tab` be added before or after the
#' `target` tab?
#'
#' @param select Should \code{tab} be selected upon being inserted?
#' @param select Should `tab` be selected upon being inserted?
#'
#' @param session The shiny session within which to call this function.
#'
#' @seealso \code{\link{showTab}}
#' @seealso [showTab()]
#'
#' @examples
#' ## Only run this example in interactive R sessions
@@ -144,16 +144,16 @@ insertTab <- function(inputId, tab, target,
}
#' @param menuName This argument should only be used when you want to
#' prepend (or append) \code{tab} to the beginning (or end) of an
#' existing \code{\link{navbarMenu}} (which must itself be part of
#' an existing \code{\link{navbarPage}}). In this case, this argument
#' should be the \code{menuName} that you gave your \code{navbarMenu}
#' prepend (or append) `tab` to the beginning (or end) of an
#' existing [navbarMenu()] (which must itself be part of
#' an existing [navbarPage()]). In this case, this argument
#' should be the `menuName` that you gave your `navbarMenu`
#' when you first created it (by default, this is equal to the value
#' of the \code{title} argument). Note that you still need to set the
#' \code{inputId} argument to whatever the \code{id} of the parent
#' \code{navbarPage} is. If \code{menuName} is left as \code{NULL},
#' \code{tab} will be prepended (or appended) to whatever
#' \code{inputId} is.
#' of the `title` argument). Note that you still need to set the
#' `inputId` argument to whatever the `id` of the parent
#' `navbarPage` is. If `menuName` is left as `NULL`,
#' `tab` will be prepended (or appended) to whatever
#' `inputId` is.
#'
#' @rdname insertTab
#' @export
@@ -221,30 +221,30 @@ removeTab <- function(inputId, target,
#' Dynamically hide/show a tabPanel
#'
#' Dynamically hide or show a \code{\link{tabPanel}} (or a
#' \code{\link{navbarMenu}})from an existing \code{\link{tabsetPanel}},
#' \code{\link{navlistPanel}} or \code{\link{navbarPage}}.
#' Dynamically hide or show a [tabPanel()] (or a
#' [navbarMenu()])from an existing [tabsetPanel()],
#' [navlistPanel()] or [navbarPage()].
#'
#' For \code{navbarPage}, you can hide/show conventional
#' \code{tabPanel}s (whether at the top level or nested inside a
#' \code{navbarMenu}), as well as an entire \code{\link{navbarMenu}}.
#' For the latter case, \code{target} should be the \code{menuName} that
#' you gave your \code{navbarMenu} when you first created it (by default,
#' this is equal to the value of the \code{title} argument).
#' For `navbarPage`, you can hide/show conventional
#' `tabPanel`s (whether at the top level or nested inside a
#' `navbarMenu`), as well as an entire [navbarMenu()].
#' For the latter case, `target` should be the `menuName` that
#' you gave your `navbarMenu` when you first created it (by default,
#' this is equal to the value of the `title` argument).
#'
#' @param inputId The \code{id} of the \code{tabsetPanel} (or
#' \code{navlistPanel} or \code{navbarPage}) in which to find
#' \code{target}.
#' @param inputId The `id` of the `tabsetPanel` (or
#' `navlistPanel` or `navbarPage`) in which to find
#' `target`.
#'
#' @param target The \code{value} of the \code{tabPanel} to be
#' @param target The `value` of the `tabPanel` to be
#' hidden/shown. See Details if you want to hide/show an entire
#' \code{navbarMenu} instead.
#' `navbarMenu` instead.
#'
#' @param select Should \code{target} be selected upon being shown?
#' @param select Should `target` be selected upon being shown?
#'
#' @param session The shiny session within which to call this function.
#'
#' @seealso \code{\link{insertTab}}
#' @seealso [insertTab()]
#'
#' @examples
#' ## Only run this example in interactive R sessions

View File

@@ -4,51 +4,51 @@
#'
#' This function allows you to dynamically add an arbitrarily large UI
#' object into your app, whenever you want, as many times as you want.
#' Unlike \code{\link{renderUI}}, the UI generated with \code{insertUI}
#' Unlike [renderUI()], the UI generated with `insertUI`
#' is not updatable as a whole: once it's created, it stays there. Each
#' new call to \code{insertUI} creates more UI objects, in addition to
#' new call to `insertUI` creates more UI objects, in addition to
#' the ones already there (all independent from one another). To
#' update a part of the UI (ex: an input object), you must use the
#' appropriate \code{render} function or a customized \code{reactive}
#' function. To remove any part of your UI, use \code{\link{removeUI}}.
#' appropriate `render` function or a customized `reactive`
#' function. To remove any part of your UI, use [removeUI()].
#'
#' @param selector A string that is accepted by jQuery's selector (i.e. the
#' string \code{s} to be placed in a \code{$(s)} jQuery call). This selector
#' string `s` to be placed in a `$(s)` jQuery call). This selector
#' will determine the element(s) relative to which you want to insert your
#' UI object.
#'
#' @param where Where your UI object should go relative to the selector:
#' \describe{
#' \item{\code{beforeBegin}}{Before the selector element itself}
#' \item{\code{afterBegin}}{Just inside the selector element, before its
#' \item{`beforeBegin`}{Before the selector element itself}
#' \item{`afterBegin`}{Just inside the selector element, before its
#' first child}
#' \item{\code{beforeEnd}}{Just inside the selector element, after its
#' \item{`beforeEnd`}{Just inside the selector element, after its
#' last child (default)}
#' \item{\code{afterEnd}}{After the selector element itself}
#' \item{`afterEnd`}{After the selector element itself}
#' }
#' Adapted from
#' \href{https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML}{here}.
#' [here](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML).
#'
#' @param ui The UI object you want to insert. This can be anything that
#' you usually put inside your apps's \code{ui} function. If you're inserting
#' you usually put inside your apps's `ui` function. If you're inserting
#' multiple elements in one call, make sure to wrap them in either a
#' \code{tagList()} or a \code{tags$div()} (the latter option has the
#' advantage that you can give it an \code{id} to make it easier to
#' `tagList()` or a `tags$div()` (the latter option has the
#' advantage that you can give it an `id` to make it easier to
#' reference or remove it later on). If you want to insert raw html, use
#' \code{ui = HTML()}.
#' `ui = HTML()`.
#'
#' @param multiple In case your selector matches more than one element,
#' \code{multiple} determines whether Shiny should insert the UI object
#' `multiple` determines whether Shiny should insert the UI object
#' relative to all matched elements or just relative to the first
#' matched element (default).
#'
#' @param immediate Whether the UI object should be immediately inserted into
#' the app when you call \code{insertUI}, or whether Shiny should wait until
#' the app when you call `insertUI`, or whether Shiny should wait until
#' all outputs have been updated and all observers have been run (default).
#'
#' @param session The shiny session within which to call \code{insertUI}.
#' @param session The shiny session within which to call `insertUI`.
#'
#' @seealso \code{\link{removeUI}}
#' @seealso [removeUI()]
#'
#' @examples
#' ## Only run this example in interactive R sessions
@@ -104,34 +104,34 @@ insertUI <- function(selector,
#'
#' Remove a UI object from the app.
#'
#' This function allows you to remove any part of your UI. Once \code{removeUI}
#' This function allows you to remove any part of your UI. Once `removeUI`
#' is executed on some element, it is gone forever.
#'
#' While it may be a particularly useful pattern to pair this with
#' \code{\link{insertUI}} (to remove some UI you had previously inserted),
#' there is no restriction on what you can use \code{removeUI} on. Any
#' [insertUI()] (to remove some UI you had previously inserted),
#' there is no restriction on what you can use `removeUI` on. Any
#' element that can be selected through a jQuery selector can be removed
#' through this function.
#'
#' @param selector A string that is accepted by jQuery's selector (i.e. the
#' string \code{s} to be placed in a \code{$(s)} jQuery call). This selector
#' string `s` to be placed in a `$(s)` jQuery call). This selector
#' will determine the element(s) to be removed. If you want to remove a
#' Shiny input or output, note that many of these are wrapped in \code{div}s,
#' so you may need to use a somewhat complex selector -- see the Examples below.
#' Shiny input or output, note that many of these are wrapped in `div`s,
#' so you may need to use a somewhat complex selector --- see the Examples below.
#' (Alternatively, you could also wrap the inputs/outputs that you want to be
#' able to remove easily in a \code{div} with an id.)
#' able to remove easily in a `div` with an id.)
#'
#' @param multiple In case your selector matches more than one element,
#' \code{multiple} determines whether Shiny should remove all the matched
#' `multiple` determines whether Shiny should remove all the matched
#' elements or just the first matched element (default).
#'
#' @param immediate Whether the element(s) should be immediately removed from
#' the app when you call \code{removeUI}, or whether Shiny should wait until
#' the app when you call `removeUI`, or whether Shiny should wait until
#' all outputs have been updated and all observers have been run (default).
#'
#' @param session The shiny session within which to call \code{removeUI}.
#' @param session The shiny session within which to call `removeUI`.
#'
#' @seealso \code{\link{insertUI}}
#' @seealso [insertUI()]
#'
#' @examples
#' ## Only run this example in interactive R sessions

View File

@@ -2,32 +2,32 @@
#'
#' Creates a panel whose contents are absolutely positioned.
#'
#' The \code{absolutePanel} function creates a \code{<div>} tag whose CSS
#' position is set to \code{absolute} (or fixed if \code{fixed = TRUE}). The way
#' The `absolutePanel` function creates a `<div>` tag whose CSS
#' position is set to `absolute` (or fixed if `fixed = TRUE`). The way
#' absolute positioning works in HTML is that absolute coordinates are specified
#' relative to its nearest parent element whose position is not set to
#' \code{static} (which is the default), and if no such parent is found, then
#' `static` (which is the default), and if no such parent is found, then
#' relative to the page borders. If you're not sure what that means, just keep
#' in mind that you may get strange results if you use \code{absolutePanel} from
#' in mind that you may get strange results if you use `absolutePanel` from
#' inside of certain types of panels.
#'
#' The \code{fixedPanel} function is the same as \code{absolutePanel} with
#' \code{fixed = TRUE}.
#' The `fixedPanel` function is the same as `absolutePanel` with
#' `fixed = TRUE`.
#'
#' The position (\code{top}, \code{left}, \code{right}, \code{bottom}) and size
#' (\code{width}, \code{height}) parameters are all optional, but you should
#' specify exactly two of \code{top}, \code{bottom}, and \code{height} and
#' exactly two of \code{left}, \code{right}, and \code{width} for predictable
#' The position (`top`, `left`, `right`, `bottom`) and size
#' (`width`, `height`) parameters are all optional, but you should
#' specify exactly two of `top`, `bottom`, and `height` and
#' exactly two of `left`, `right`, and `width` for predictable
#' results.
#'
#' Like most other distance parameters in Shiny, the position and size
#' parameters take a number (interpreted as pixels) or a valid CSS size string,
#' such as \code{"100px"} (100 pixels) or \code{"25\%"}.
#' such as `"100px"` (100 pixels) or `"25%"`.
#'
#' For arcane HTML reasons, to have the panel fill the page or parent you should
#' specify \code{0} for \code{top}, \code{left}, \code{right}, and \code{bottom}
#' rather than the more obvious \code{width = "100\%"} and \code{height =
#' "100\%"}.
#' specify `0` for `top`, `left`, `right`, and `bottom`
#' rather than the more obvious `width = "100%"` and `height =
#' "100%"`.
#'
#' @param ... Attributes (named arguments) or children (unnamed arguments) that
#' should be included in the panel.
@@ -42,16 +42,16 @@
#' page or parent container.
#' @param width Width of the panel.
#' @param height Height of the panel.
#' @param draggable If \code{TRUE}, allows the user to move the panel by
#' @param draggable If `TRUE`, allows the user to move the panel by
#' clicking and dragging.
#' @param fixed Positions the panel relative to the browser window and prevents
#' it from being scrolled with the rest of the page.
#' @param cursor The type of cursor that should appear when the user mouses over
#' the panel. Use \code{"move"} for a north-east-south-west icon,
#' \code{"default"} for the usual cursor arrow, or \code{"inherit"} for the
#' the panel. Use `"move"` for a north-east-south-west icon,
#' `"default"` for the usual cursor arrow, or `"inherit"` for the
#' usual cursor behavior (including changing to an I-beam when the cursor is
#' over text). The default is \code{"auto"}, which is equivalent to
#' \code{ifelse(draggable, "move", "inherit")}.
#' over text). The default is `"auto"`, which is equivalent to
#' `ifelse(draggable, "move", "inherit")`.
#' @return An HTML element or list of elements.
#' @export
absolutePanel <- function(...,

47
R/map.R
View File

@@ -9,63 +9,58 @@
# Remove of unknown key does nothing
# Setting a key twice always results in last-one-wins
# /TESTS
# Note that Map objects can't be saved in one R session and restored in
# another, because they are based on fastmap, which uses an external pointer,
# and external pointers can't be saved and restored in another session.
#' @importFrom fastmap fastmap
Map <- R6Class(
'Map',
portable = FALSE,
public = list(
initialize = function() {
private$env <- new.env(parent=emptyenv())
private$map <<- fastmap()
},
get = function(key) {
env[[key]]
map$get(key)
},
set = function(key, value) {
env[[key]] <- value
map$set(key, value)
value
},
mget = function(keys) {
base::mget(keys, env)
map$mget(keys)
},
mset = function(...) {
args <- list(...)
if (length(args) == 0)
return()
arg_names <- names(args)
if (is.null(arg_names) || any(!nzchar(arg_names)))
stop("All elements must be named")
list2env(args, envir = env)
map$mset(...)
},
remove = function(key) {
if (!self$containsKey(key))
if (!map$has(key))
return(NULL)
result <- env[[key]]
rm(list=key, envir=env, inherits=FALSE)
result <- map$get(key)
map$remove(key)
result
},
containsKey = function(key) {
exists(key, envir=env, inherits=FALSE)
map$has(key)
},
keys = function() {
# Sadly, this is much faster than ls(), because it doesn't sort the keys.
names(as.list(env, all.names=TRUE))
keys = function(sort = FALSE) {
map$keys(sort = sort)
},
values = function() {
as.list(env, all.names=TRUE)
values = function(sort = FALSE) {
map$as_list(sort = sort)
},
clear = function() {
private$env <- new.env(parent=emptyenv())
invisible(NULL)
map$reset()
},
size = function() {
length(env)
map$size()
}
),
private = list(
env = 'environment'
map = NULL
)
)

View File

@@ -2,19 +2,43 @@
NULL
reactLogHandler <- function(req) {
if (!identical(req$PATH_INFO, '/reactlog'))
return(NULL)
if (!isTRUE(getOption('shiny.reactlog'))) {
if (! rLog$isLogging()) {
return(NULL)
}
sessionToken <- parseQueryString(req$QUERY_STRING)$s
if (identical(req$PATH_INFO, "/reactlog/mark")) {
sessionToken <- parseQueryString(req$QUERY_STRING)$s
shinysession <- appsByToken$get(sessionToken)
return(httpResponse(
status=200,
content=list(file=renderReactLog(sessionToken), owned=TRUE)
))
# log time
withReactiveDomain(shinysession, {
rLog$userMark(getDefaultReactiveDomain())
})
return(httpResponse(
status = 200,
content = "marked",
content_type = "text/plain"
))
} else if (identical(req$PATH_INFO, "/reactlog")){
sessionToken <- parseQueryString(req$QUERY_STRING)$s
# `renderReactLog` will check/throw if reactlog doesn't exist
reactlogFile <- renderReactlog(sessionToken)
return(httpResponse(
status = 200,
content = list(
file = reactlogFile,
owned = TRUE
)
))
} else {
return(NULL)
}
}
sessionHandler <- function(req) {

View File

@@ -199,6 +199,9 @@ staticHandler <- function(root) {
if (path == '/')
path <- '/index.html'
if (grepl('\\', path, fixed = TRUE))
return(NULL)
abs.path <- resolve(root, path)
if (is.null(abs.path))
return(NULL)
@@ -321,21 +324,20 @@ HandlerManager <- R6Class("HandlerManager",
}
)
},
getOption('shiny.sharedSecret')
loadSharedSecret()
),
onWSOpen = function(ws) {
return(wsHandlers$invoke(ws))
}
)
},
.httpServer = function(handler, sharedSecret) {
.httpServer = function(handler, checkSharedSecret) {
filter <- getOption('shiny.http.response.filter')
if (is.null(filter))
filter <- function(req, response) response
function(req) {
if (!is.null(sharedSecret)
&& !identical(sharedSecret, req$HTTP_SHINY_SHARED_SECRET)) {
if (!checkSharedSecret(req$HTTP_SHINY_SHARED_SECRET)) {
return(list(status=403,
body='<h1>403 Forbidden</h1><p>Shared secret mismatch</p>',
headers=list('Content-Type' = 'text/html')))

View File

@@ -1,13 +1,13 @@
#' Show or remove a modal dialog
#'
#' This causes a modal dialog to be displayed in the client browser, and is
#' typically used with \code{\link{modalDialog}}.
#' typically used with [modalDialog()].
#'
#' @param ui UI content to show in the modal.
#' @param session The \code{session} object passed to function given to
#' \code{shinyServer}.
#' @param session The `session` object passed to function given to
#' `shinyServer`.
#'
#' @seealso \code{\link{modalDialog}} for examples.
#' @seealso [modalDialog()] for examples.
#' @export
showModal <- function(ui, session = getDefaultReactiveDomain()) {
res <- processDeps(ui, session)
@@ -35,15 +35,15 @@ removeModal <- function(session = getDefaultReactiveDomain()) {
#'
#' @param ... UI elements for the body of the modal dialog box.
#' @param title An optional title for the dialog.
#' @param footer UI for footer. Use \code{NULL} for no footer.
#' @param size One of \code{"s"} for small, \code{"m"} (the default) for medium,
#' or \code{"l"} for large.
#' @param easyClose If \code{TRUE}, the modal dialog can be dismissed by
#' @param footer UI for footer. Use `NULL` for no footer.
#' @param size One of `"s"` for small, `"m"` (the default) for medium,
#' or `"l"` for large.
#' @param easyClose If `TRUE`, the modal dialog can be dismissed by
#' clicking outside the dialog box, or be pressing the Escape key. If
#' \code{FALSE} (the default), the modal dialog can't be dismissed in those
#' `FALSE` (the default), the modal dialog can't be dismissed in those
#' ways; instead it must be dismissed by clicking on the dismiss button, or
#' from a call to \code{\link{removeModal}} on the server.
#' @param fade If \code{FALSE}, the modal dialog will have no fade-in animation
#' from a call to [removeModal()] on the server.
#' @param fade If `FALSE`, the modal dialog will have no fade-in animation
#' (it will simply appear rather than fade in to view).
#'
#' @examples
@@ -171,10 +171,10 @@ modalDialog <- function(..., title = NULL, footer = modalButton("Dismiss"),
#' Create a button for a modal dialog
#'
#' When clicked, a \code{modalButton} will dismiss the modal dialog.
#' When clicked, a `modalButton` will dismiss the modal dialog.
#'
#' @inheritParams actionButton
#' @seealso \code{\link{modalDialog}} for examples.
#' @seealso [modalDialog()] for examples.
#' @export
modalButton <- function(label, icon = NULL) {
tags$button(type = "button", class = "btn btn-default",

View File

@@ -42,7 +42,7 @@ createSessionProxy <- function(parentSession, ...) {
#' Shiny's module feature lets you break complicated UI and server logic into
#' smaller, self-contained pieces. Compared to large monolithic Shiny apps,
#' modules are easier to reuse and easier to reason about. See the article at
#' \url{http://shiny.rstudio.com/articles/modules.html} to learn more.
#' <http://shiny.rstudio.com/articles/modules.html> to learn more.
#'
#' @param module A Shiny module server function
#' @param id An ID string that corresponds with the ID used to call the module's
@@ -52,7 +52,7 @@ createSessionProxy <- function(parentSession, ...) {
#' almost always be used)
#'
#' @return The return value, if any, from executing the module server function
#' @seealso \url{http://shiny.rstudio.com/articles/modules.html}
#' @seealso <http://shiny.rstudio.com/articles/modules.html>
#' @export
callModule <- function(module, id, ..., session = getDefaultReactiveDomain()) {
childScope <- session$makeScope(id)

View File

@@ -4,17 +4,17 @@
#'
#' @param ui Content of message.
#' @param action Message content that represents an action. For example, this
#' could be a link that the user can click on. This is separate from \code{ui}
#' could be a link that the user can click on. This is separate from `ui`
#' so customized layouts can handle the main notification content separately
#' from action content.
#' @param duration Number of seconds to display the message before it
#' disappears. Use \code{NULL} to make the message not automatically
#' disappears. Use `NULL` to make the message not automatically
#' disappear.
#' @param closeButton If \code{TRUE}, display a button which will make the
#' notification disappear when clicked. If \code{FALSE} do not display.
#' @param closeButton If `TRUE`, display a button which will make the
#' notification disappear when clicked. If `FALSE` do not display.
#' @param id An ID string. This can be used to change the contents of an
#' existing message with \code{showNotification}, or to remove it with
#' \code{removeNotification}. If not provided, one will be generated
#' existing message with `showNotification`, or to remove it with
#' `removeNotification`. If not provided, one will be generated
#' automatically. If an ID is provided and there does not currently exist a
#' notification with that ID, a new notification will be created with that ID.
#' @param type A string which controls the color of the notification. One of

View File

@@ -3,66 +3,66 @@
#' Reports progress to the user during long-running operations.
#'
#' This package exposes two distinct programming APIs for working with
#' progress. \code{\link{withProgress}} and \code{\link{setProgress}}
#' progress. [withProgress()] and [setProgress()]
#' together provide a simple function-based interface, while the
#' \code{Progress} reference class provides an object-oriented API.
#' `Progress` reference class provides an object-oriented API.
#'
#' Instantiating a \code{Progress} object causes a progress panel to be
#' created, and it will be displayed the first time the \code{set}
#' method is called. Calling \code{close} will cause the progress panel
#' Instantiating a `Progress` object causes a progress panel to be
#' created, and it will be displayed the first time the `set`
#' method is called. Calling `close` will cause the progress panel
#' to be removed.
#'
#' As of version 0.14, the progress indicators use Shiny's new notification API.
#' If you want to use the old styling (for example, you may have used customized
#' CSS), you can use \code{style="old"} each time you call
#' \code{Progress$new()}. If you don't want to set the style each time
#' \code{Progress$new} is called, you can instead call
#' \code{\link{shinyOptions}(progress.style="old")} just once, inside the server
#' CSS), you can use `style="old"` each time you call
#' `Progress$new()`. If you don't want to set the style each time
#' `Progress$new` is called, you can instead call
#' [`shinyOptions(progress.style="old")`][shinyOptions] just once, inside the server
#' function.
#'
#' \strong{Methods}
#' **Methods**
#' \describe{
#' \item{\code{initialize(session, min = 0, max = 1)}}{
#' \item{`initialize(session, min = 0, max = 1)`}{
#' Creates a new progress panel (but does not display it).
#' }
#' \item{\code{set(value = NULL, message = NULL, detail = NULL)}}{
#' \item{`set(value = NULL, message = NULL, detail = NULL)`}{
#' Updates the progress panel. When called the first time, the
#' progress panel is displayed.
#' }
#' \item{\code{inc(amount = 0.1, message = NULL, detail = NULL)}}{
#' Like \code{set}, this updates the progress panel. The difference is
#' that \code{inc} increases the progress bar by \code{amount}, instead
#' \item{`inc(amount = 0.1, message = NULL, detail = NULL)`}{
#' Like `set`, this updates the progress panel. The difference is
#' that `inc` increases the progress bar by `amount`, instead
#' of setting it to a specific value.
#' }
#' \item{\code{close()}}{
#' Removes the progress panel. Future calls to \code{set} and
#' \code{close} will be ignored.
#' \item{`close()`}{
#' Removes the progress panel. Future calls to `set` and
#' `close` will be ignored.
#' }
#' }
#'
#' @param session The Shiny session object, as provided by
#' \code{shinyServer} to the server function.
#' `shinyServer` to the server function.
#' @param min The value that represents the starting point of the
#' progress bar. Must be less tham \code{max}.
#' progress bar. Must be less than `max`.
#' @param max The value that represents the end of the progress bar.
#' Must be greater than \code{min}.
#' Must be greater than `min`.
#' @param message A single-element character vector; the message to be
#' displayed to the user, or \code{NULL} to hide the current message
#' displayed to the user, or `NULL` to hide the current message
#' (if any).
#' @param detail A single-element character vector; the detail message
#' to be displayed to the user, or \code{NULL} to hide the current
#' to be displayed to the user, or `NULL` to hide the current
#' detail message (if any). The detail message will be shown with a
#' de-emphasized appearance relative to \code{message}.
#' de-emphasized appearance relative to `message`.
#' @param value A numeric value at which to set
#' the progress bar, relative to \code{min} and \code{max}.
#' @param style Progress display style. If \code{"notification"} (the default),
#' the progress bar, relative to `min` and `max`.
#' @param style Progress display style. If `"notification"` (the default),
#' the progress indicator will show using Shiny's notification API. If
#' \code{"old"}, use the same HTML and CSS used in Shiny 0.13.2 and below
#' `"old"`, use the same HTML and CSS used in Shiny 0.13.2 and below
#' (this is for backward-compatibility).
#' @param amount Single-element numeric vector; the value at which to set
#' the progress bar, relative to \code{min} and \code{max}.
#' \code{NULL} hides the progress bar, if it is currently visible.
#' @param amount For the \code{inc()} method, a numeric value to increment the
#' the progress bar, relative to `min` and `max`.
#' `NULL` hides the progress bar, if it is currently visible.
#' @param amount For the `inc()` method, a numeric value to increment the
#' progress bar.
#'
#' @examples
@@ -91,7 +91,7 @@
#'
#' shinyApp(ui, server)
#' }
#' @seealso \code{\link{withProgress}}
#' @seealso [withProgress()]
#' @format NULL
#' @usage NULL
#' @export
@@ -186,58 +186,58 @@ Progress <- R6Class(
#' Reports progress to the user during long-running operations.
#'
#' This package exposes two distinct programming APIs for working with progress.
#' Using \code{withProgress} with \code{incProgress} or \code{setProgress}
#' provide a simple function-based interface, while the \code{\link{Progress}}
#' Using `withProgress` with `incProgress` or `setProgress`
#' provide a simple function-based interface, while the [Progress()]
#' reference class provides an object-oriented API.
#'
#' Use \code{withProgress} to wrap the scope of your work; doing so will cause a
#' Use `withProgress` to wrap the scope of your work; doing so will cause a
#' new progress panel to be created, and it will be displayed the first time
#' \code{incProgress} or \code{setProgress} are called. When \code{withProgress}
#' `incProgress` or `setProgress` are called. When `withProgress`
#' exits, the corresponding progress panel will be removed.
#'
#' The \code{incProgress} function increments the status bar by a specified
#' amount, whereas the \code{setProgress} function sets it to a specific value,
#' The `incProgress` function increments the status bar by a specified
#' amount, whereas the `setProgress` function sets it to a specific value,
#' and can also set the text displayed.
#'
#' Generally, \code{withProgress}/\code{incProgress}/\code{setProgress} should
#' Generally, `withProgress`/`incProgress`/`setProgress` should
#' be sufficient; the exception is if the work to be done is asynchronous (this
#' is not common) or otherwise cannot be encapsulated by a single scope. In that
#' case, you can use the \code{Progress} reference class.
#' case, you can use the `Progress` reference class.
#'
#' As of version 0.14, the progress indicators use Shiny's new notification API.
#' If you want to use the old styling (for example, you may have used customized
#' CSS), you can use \code{style="old"} each time you call
#' \code{withProgress()}. If you don't want to set the style each time
#' \code{withProgress} is called, you can instead call
#' \code{\link{shinyOptions}(progress.style="old")} just once, inside the server
#' CSS), you can use `style="old"` each time you call
#' `withProgress()`. If you don't want to set the style each time
#' `withProgress` is called, you can instead call
#' [`shinyOptions(progress.style="old")`][shinyOptions] just once, inside the server
#' function.
#'
#' @param session The Shiny session object, as provided by \code{shinyServer} to
#' @param session The Shiny session object, as provided by `shinyServer` to
#' the server function. The default is to automatically find the session by
#' using the current reactive domain.
#' @param expr The work to be done. This expression should contain calls to
#' \code{setProgress}.
#' `setProgress`.
#' @param min The value that represents the starting point of the progress bar.
#' Must be less tham \code{max}. Default is 0.
#' Must be less tham `max`. Default is 0.
#' @param max The value that represents the end of the progress bar. Must be
#' greater than \code{min}. Default is 1.
#' @param amount For \code{incProgress}, the amount to increment the status bar.
#' greater than `min`. Default is 1.
#' @param amount For `incProgress`, the amount to increment the status bar.
#' Default is 0.1.
#' @param env The environment in which \code{expr} should be evaluated.
#' @param quoted Whether \code{expr} is a quoted expression (this is not
#' @param env The environment in which `expr` should be evaluated.
#' @param quoted Whether `expr` is a quoted expression (this is not
#' common).
#' @param message A single-element character vector; the message to be displayed
#' to the user, or \code{NULL} to hide the current message (if any).
#' to the user, or `NULL` to hide the current message (if any).
#' @param detail A single-element character vector; the detail message to be
#' displayed to the user, or \code{NULL} to hide the current detail message
#' displayed to the user, or `NULL` to hide the current detail message
#' (if any). The detail message will be shown with a de-emphasized appearance
#' relative to \code{message}.
#' @param style Progress display style. If \code{"notification"} (the default),
#' relative to `message`.
#' @param style Progress display style. If `"notification"` (the default),
#' the progress indicator will show using Shiny's notification API. If
#' \code{"old"}, use the same HTML and CSS used in Shiny 0.13.2 and below
#' `"old"`, use the same HTML and CSS used in Shiny 0.13.2 and below
#' (this is for backward-compatibility).
#' @param value Single-element numeric vector; the value at which to set the
#' progress bar, relative to \code{min} and \code{max}.
#' progress bar, relative to `min` and `max`.
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -263,7 +263,7 @@ Progress <- R6Class(
#'
#' shinyApp(ui, server)
#' }
#' @seealso \code{\link{Progress}}
#' @seealso [Progress()]
#' @rdname withProgress
#' @export
withProgress <- function(expr, min = 0, max = 1,

View File

@@ -16,25 +16,37 @@ processId <- local({
}
})
#' @include graph.R
Context <- R6Class(
'Context',
portable = FALSE,
class = FALSE,
public = list(
id = character(0),
.reactId = character(0),
.reactType = "other",
.label = character(0), # For debug purposes
.invalidated = FALSE,
.invalidateCallbacks = list(),
.flushCallbacks = list(),
.domain = NULL,
.pid = NULL,
.weak = NULL,
initialize = function(domain, label='', type='other', prevId='') {
id <<- .getReactiveEnvironment()$nextId()
initialize = function(
domain, label='', type='other', prevId='',
reactId = rLog$noReactId,
id = .getReactiveEnvironment()$nextId(), # For dummy context
weak = FALSE
) {
id <<- id
.label <<- label
.domain <<- domain
.pid <<- processId()
.graphCreateContext(id, label, type, prevId, domain)
.reactId <<- reactId
.reactType <<- type
.weak <<- weak
rLog$createContext(id, label, type, prevId, domain)
},
run = function(func) {
"Run the provided function under this context."
@@ -42,10 +54,8 @@ Context <- R6Class(
promises::with_promise_domain(reactivePromiseDomain(), {
withReactiveDomain(.domain, {
env <- .getReactiveEnvironment()
.graphEnterContext(id)
on.exit({
.graphExitContext(id, domain = .domain)
}, add = TRUE)
rLog$enter(.reactId, id, .reactType, .domain)
on.exit(rLog$exit(.reactId, id, .reactType, .domain), add = TRUE)
env$runWith(self, func)
})
})
@@ -62,7 +72,9 @@ Context <- R6Class(
return()
.invalidated <<- TRUE
.graphInvalidate(id, .domain)
rLog$invalidateStart(.reactId, id, .reactType, .domain)
on.exit(rLog$invalidateEnd(.reactId, id, .reactType, .domain), add = TRUE)
lapply(.invalidateCallbacks, function(func) {
func()
})
@@ -99,6 +111,9 @@ Context <- R6Class(
lapply(.flushCallbacks, function(flushCallback) {
flushCallback()
})
},
isWeak = function() {
.weak
}
)
)
@@ -151,7 +166,10 @@ ReactiveEnvironment <- R6Class(
# If already in a flush, don't start another one
if (.inFlush) return(invisible(FALSE))
.inFlush <<- TRUE
on.exit(.inFlush <<- FALSE)
on.exit({
.inFlush <<- FALSE
rLog$idle(domain = NULL)
})
while (hasPendingFlush()) {
ctx <- .pendingFlush$dequeue()
@@ -183,18 +201,16 @@ flushReact <- function() {
getCurrentContext <- function() {
.getReactiveEnvironment()$currentContext()
}
hasCurrentContext <- function() {
!is.null(.getReactiveEnvironment()$.currentContext)
}
getDummyContext <- function() {}
local({
dummyContext <- NULL
getDummyContext <<- function() {
if (is.null(dummyContext)) {
dummyContext <<- Context$new(getDefaultReactiveDomain(), '[none]',
type='isolate')
}
return(dummyContext)
}
})
getDummyContext <- function() {
Context$new(
getDefaultReactiveDomain(), '[none]', type = 'isolate',
id = "Dummy", reactId = rLog$dummyReactId
)
}
wrapForContext <- function(func, ctx) {
force(func)

View File

@@ -168,31 +168,31 @@ onReactiveDomainEnded <- function(domain, callback, failIfNull = FALSE) {
#' them ends) and error handling.
#'
#' At any given time, there can be either a single "default" reactive domain
#' object, or none (i.e. the reactive domain object is \code{NULL}). You can
#' object, or none (i.e. the reactive domain object is `NULL`). You can
#' access the current default reactive domain by calling
#' \code{getDefaultReactiveDomain}.
#' `getDefaultReactiveDomain`.
#'
#' Unless you specify otherwise, newly created observers and reactive
#' expressions will be assigned to the current default domain (if any). You can
#' override this assignment by providing an explicit \code{domain} argument to
#' \code{\link{reactive}} or \code{\link{observe}}.
#' override this assignment by providing an explicit `domain` argument to
#' [reactive()] or [observe()].
#'
#' For advanced usage, it's possible to override the default domain using
#' \code{withReactiveDomain}. The \code{domain} argument will be made the
#' default domain while \code{expr} is evaluated.
#' `withReactiveDomain`. The `domain` argument will be made the
#' default domain while `expr` is evaluated.
#'
#' Implementers of new reactive primitives can use \code{onReactiveDomainEnded}
#' Implementers of new reactive primitives can use `onReactiveDomainEnded`
#' as a convenience function for registering callbacks. If the reactive domain
#' is \code{NULL} and \code{failIfNull} is \code{FALSE}, then the callback will
#' is `NULL` and `failIfNull` is `FALSE`, then the callback will
#' never be invoked.
#'
#' @name domains
#' @param domain A valid domain object (for example, a Shiny session), or
#' \code{NULL}
#' @param expr An expression to evaluate under \code{domain}
#' `NULL`
#' @param expr An expression to evaluate under `domain`
#' @param callback A callback function to be invoked
#' @param failIfNull If \code{TRUE} then an error is given if the \code{domain}
#' is \code{NULL}
#' @param failIfNull If `TRUE` then an error is given if the `domain`
#' is `NULL`
NULL
#

File diff suppressed because it is too large Load Diff

View File

@@ -2,29 +2,29 @@
#'
#' Renders a reactive plot, with plot images cached to disk.
#'
#' \code{expr} is an expression that generates a plot, similar to that in
#' \code{renderPlot}. Unlike with \code{renderPlot}, this expression does not
#' `expr` is an expression that generates a plot, similar to that in
#' `renderPlot`. Unlike with `renderPlot`, this expression does not
#' take reactive dependencies. It is re-executed only when the cache key
#' changes.
#'
#' \code{cacheKeyExpr} is an expression which, when evaluated, returns an object
#' which will be serialized and hashed using the \code{\link[digest]{digest}}
#' `cacheKeyExpr` is an expression which, when evaluated, returns an object
#' which will be serialized and hashed using the [digest::digest()]
#' function to generate a string that will be used as a cache key. This key is
#' used to identify the contents of the plot: if the cache key is the same as a
#' previous time, it assumes that the plot is the same and can be retrieved from
#' the cache.
#'
#' This \code{cacheKeyExpr} is reactive, and so it will be re-evaluated when any
#' This `cacheKeyExpr` is reactive, and so it will be re-evaluated when any
#' upstream reactives are invalidated. This will also trigger re-execution of
#' the plotting expression, \code{expr}.
#' the plotting expression, `expr`.
#'
#' The key should consist of "normal" R objects, like vectors and lists. Lists
#' should in turn contain other normal R objects. If the key contains
#' environments, external pointers, or reference objects -- or even if it has
#' such objects attached as attributes -- then it is possible that it will
#' environments, external pointers, or reference objects --- or even if it has
#' such objects attached as attributes --- then it is possible that it will
#' change unpredictably even when you do not expect it to. Additionally, because
#' the entire key is serialized and hashed, if it contains a very large object
#' -- a large data set, for example -- there may be a noticeable performance
#' --- a large data set, for example --- there may be a noticeable performance
#' penalty.
#'
#' If you face these issues with the cache key, you can work around them by
@@ -32,12 +32,12 @@
#' to normal R objects before returning them. Your expression could even
#' serialize and hash that information in an efficient way and return a string,
#' which will in turn be hashed (very quickly) by the
#' \code{\link[digest]{digest}} function.
#' [digest::digest()] function.
#'
#' Internally, the result from \code{cacheKeyExpr} is combined with the name of
#' the output (if you assign it to \code{output$plot1}, it will be combined
#' with \code{"plot1"}) to form the actual key that is used. As a result, even
#' if there are multiple plots that have the same \code{cacheKeyExpr}, they
#' Internally, the result from `cacheKeyExpr` is combined with the name of
#' the output (if you assign it to `output$plot1`, it will be combined
#' with `"plot1"`) to form the actual key that is used. As a result, even
#' if there are multiple plots that have the same `cacheKeyExpr`, they
#' will not have cache key collisions.
#'
#' @section Cache scoping:
@@ -49,28 +49,28 @@
#' cache that persists even after the application is shut down and started
#' again.
#'
#' To control the scope of the cache, use the \code{cache} parameter. There
#' To control the scope of the cache, use the `cache` parameter. There
#' are two ways of having Shiny automatically create and clean up the disk
#' cache.
#'
#' \describe{
#' \item{1}{To scope the cache to one run of a Shiny application (shared
#' among possibly multiple user sessions), use \code{cache="app"}. This
#' among possibly multiple user sessions), use `cache="app"`. This
#' is the default. The cache will be shared across multiple sessions, so
#' there is potentially a large performance benefit if there are many users
#' of the application. When the application stops running, the cache will
#' be deleted. If plots cannot be safely shared across users, this should
#' not be used.}
#' \item{2}{To scope the cache to one session, use \code{cache="session"}.
#' When a new user session starts -- in other words, when a web browser
#' visits the Shiny application -- a new cache will be created on disk
#' \item{2}{To scope the cache to one session, use `cache="session"`.
#' When a new user session starts --- in other words, when a web browser
#' visits the Shiny application --- a new cache will be created on disk
#' for that session. When the session ends, the cache will be deleted.
#' The cache will not be shared across multiple sessions.}
#' }
#'
#' If either \code{"app"} or \code{"session"} is used, the cache will be 10 MB
#' If either `"app"` or `"session"` is used, the cache will be 10 MB
#' in size, and will be stored stored in memory, using a
#' \code{\link{memoryCache}} object. Note that the cache space will be shared
#' [memoryCache()] object. Note that the cache space will be shared
#' among all cached plots within a single application or session.
#'
#' In some cases, you may want more control over the caching behavior. For
@@ -79,7 +79,7 @@
#' multiple runs of an application, or even across multiple R processes.
#'
#' To use different settings for an application-scoped cache, you can call
#' \code{\link{shinyOptions}()} at the top of your app.R, server.R, or
#' [shinyOptions()] at the top of your app.R, server.R, or
#' global.R. For example, this will create a cache with 20 MB of space
#' instead of the default 10 MB:
#' \preformatted{
@@ -87,9 +87,9 @@
#' }
#'
#' To use different settings for a session-scoped cache, you can call
#' \code{\link{shinyOptions}()} at the top of your server function. To use
#' the session-scoped cache, you must also call \code{renderCachedPlot} with
#' \code{cache="session"}. This will create a 20 MB cache for the session:
#' [shinyOptions()] at the top of your server function. To use
#' the session-scoped cache, you must also call `renderCachedPlot` with
#' `cache="session"`. This will create a 20 MB cache for the session:
#' \preformatted{
#' function(input, output, session) {
#' shinyOptions(cache = memoryCache(size = 20e6))
@@ -102,7 +102,7 @@
#' }
#'
#' If you want to create a cache that is shared across multiple concurrent
#' R processes, you can use a \code{\link{diskCache}}. You can create an
#' R processes, you can use a [diskCache()]. You can create an
#' application-level shared cache by putting this at the top of your app.R,
#' server.R, or global.R:
#' \preformatted{
@@ -110,7 +110,7 @@
#' }
#'
#' This will create a subdirectory in your system temp directory named
#' \code{myapp-cache} (replace \code{myapp-cache} with a unique name of
#' `myapp-cache` (replace `myapp-cache` with a unique name of
#' your choosing). On most platforms, this directory will be removed when
#' your system reboots. This cache will persist across multiple starts and
#' stops of the R process, as long as you do not reboot.
@@ -126,34 +126,34 @@
#' the directory.
#'
#' You can also scope a cache to just one plot, or selected plots. To do that,
#' create a \code{\link{memoryCache}} or \code{\link{diskCache}}, and pass it
#' as the \code{cache} argument of \code{renderCachedPlot}.
#' create a [memoryCache()] or [diskCache()], and pass it
#' as the `cache` argument of `renderCachedPlot`.
#'
#' @section Interactive plots:
#'
#' \code{renderCachedPlot} can be used to create interactive plots. See
#' \code{\link{plotOutput}} for more information and examples.
#' `renderCachedPlot` can be used to create interactive plots. See
#' [plotOutput()] for more information and examples.
#'
#'
#' @inheritParams renderPlot
#' @param cacheKeyExpr An expression that returns a cache key. This key should
#' be a unique identifier for a plot: the assumption is that if the cache key
#' is the same, then the plot will be the same.
#' @param sizePolicy A function that takes two arguments, \code{width} and
#' \code{height}, and returns a list with \code{width} and \code{height}. The
#' @param sizePolicy A function that takes two arguments, `width` and
#' `height`, and returns a list with `width` and `height`. The
#' purpose is to round the actual pixel dimensions from the browser to some
#' other dimensions, so that this will not generate and cache images of every
#' possible pixel dimension. See \code{\link{sizeGrowthRatio}} for more
#' possible pixel dimension. See [sizeGrowthRatio()] for more
#' information on the default sizing policy.
#' @param res The resolution of the PNG, in pixels per inch.
#' @param cache The scope of the cache, or a cache object. This can be
#' \code{"app"} (the default), \code{"session"}, or a cache object like
#' a \code{\link{diskCache}}. See the Cache Scoping section for more
#' `"app"` (the default), `"session"`, or a cache object like
#' a [diskCache()]. See the Cache Scoping section for more
#' information.
#'
#' @seealso See \code{\link{renderPlot}} for the regular, non-cached version of
#' @seealso See [renderPlot()] for the regular, non-cached version of
#' this function. For more about configuring caches, see
#' \code{\link{memoryCache}} and \code{\link{diskCache}}.
#' [memoryCache()] and [diskCache()].
#'
#'
#' @examples
@@ -379,7 +379,7 @@ renderCachedPlot <- function(expr,
hybrid_chain(
# Depend on the user cache key, even though we don't use the value. When
# it changes, it can cause the drawReactive to re-execute. (Though
# drawReactive will not necessarily re-execute -- it must be called from
# drawReactive will not necessarily re-execute --- it must be called from
# renderFunc, which happens only if there's a cache miss.)
userCacheKey(),
function(userCacheKeyValue) {
@@ -476,62 +476,64 @@ renderCachedPlot <- function(expr,
}
)
},
function(result) {
width <- result$width
height <- result$height
pixelratio <- result$pixelratio
function(possiblyAsyncResult) {
hybrid_chain(possiblyAsyncResult, function(result) {
width <- result$width
height <- result$height
pixelratio <- result$pixelratio
# Three possibilities when we get here:
# 1. There was a cache hit. No need to set a value in the cache.
# 2. There was a cache miss, and the plotObj is already the correct
# size (because drawReactive re-executed). In this case, we need
# to cache it.
# 3. There was a cache miss, and the plotObj was not the corect size.
# In this case, we need to replay the display list, and then cache
# the result.
if (!result$cacheHit) {
# If the image is already the correct size, this just returns the
# object unchanged.
result$plotObj <- do.call("resizeSavedPlot", c(
list(
name,
shinysession,
result$plotObj,
width,
height,
pixelratio,
res
),
args
))
# Three possibilities when we get here:
# 1. There was a cache hit. No need to set a value in the cache.
# 2. There was a cache miss, and the plotObj is already the correct
# size (because drawReactive re-executed). In this case, we need
# to cache it.
# 3. There was a cache miss, and the plotObj was not the corect size.
# In this case, we need to replay the display list, and then cache
# the result.
if (!result$cacheHit) {
# If the image is already the correct size, this just returns the
# object unchanged.
result$plotObj <- do.call("resizeSavedPlot", c(
list(
name,
shinysession,
result$plotObj,
width,
height,
pixelratio,
res
),
args
))
# Save a cached copy of the plotObj. The recorded displaylist for
# the plot can't be serialized and restored properly within the same
# R session, so we NULL it out before saving. (The image data and
# other metadata be saved and restored just fine.) Displaylists can
# also be very large (~1.5MB for a basic ggplot), and they would not
# be commonly used. Note that displaylist serialization was fixed in
# revision 74506 (2e6c669), and should be in R 3.6. A MemoryCache
# doesn't need to serialize objects, so it could actually save a
# display list, but for the reasons listed previously, it's
# generally not worth it.
# The plotResult is not the same as the recordedPlot (it is used to
# retrieve coordmap information for ggplot2 objects) but it is only
# used in conjunction with the recordedPlot, and we'll remove it
# because it can be quite large.
result$plotObj$plotResult <- NULL
result$plotObj$recordedPlot <- NULL
cache$set(result$key, result$plotObj)
}
# Save a cached copy of the plotObj. The recorded displaylist for
# the plot can't be serialized and restored properly within the same
# R session, so we NULL it out before saving. (The image data and
# other metadata be saved and restored just fine.) Displaylists can
# also be very large (~1.5MB for a basic ggplot), and they would not
# be commonly used. Note that displaylist serialization was fixed in
# revision 74506 (2e6c669), and should be in R 3.6. A MemoryCache
# doesn't need to serialize objects, so it could actually save a
# display list, but for the reasons listed previously, it's
# generally not worth it.
# The plotResult is not the same as the recordedPlot (it is used to
# retrieve coordmap information for ggplot2 objects) but it is only
# used in conjunction with the recordedPlot, and we'll remove it
# because it can be quite large.
result$plotObj$plotResult <- NULL
result$plotObj$recordedPlot <- NULL
cache$set(result$key, result$plotObj)
}
img <- result$plotObj$img
# Replace exact pixel dimensions; instead, the max-height and
# max-width will be set to 100% from CSS.
img$class <- "shiny-scalable"
img$width <- NULL
img$height <- NULL
img <- result$plotObj$img
# Replace exact pixel dimensions; instead, the max-height and
# max-width will be set to 100% from CSS.
img$class <- "shiny-scalable"
img$width <- NULL
img$height <- NULL
img
img
})
}
)
}
@@ -560,7 +562,7 @@ renderCachedPlot <- function(expr,
#' @param width,height Base width and height.
#' @param growthRate Growth rate multiplier.
#'
#' @seealso This is to be used with \code{\link{renderCachedPlot}}.
#' @seealso This is to be used with [renderCachedPlot()].
#'
#' @examples
#' f <- sizeGrowthRatio(500, 500, 1.25)

View File

@@ -1,48 +1,54 @@
#' Plot Output
#'
#' Renders a reactive plot that is suitable for assigning to an \code{output}
#' Renders a reactive plot that is suitable for assigning to an `output`
#' slot.
#'
#' The corresponding HTML output tag should be \code{div} or \code{img} and have
#' the CSS class name \code{shiny-plot-output}.
#' The corresponding HTML output tag should be `div` or `img` and have
#' the CSS class name `shiny-plot-output`.
#'
#' @section Interactive plots:
#'
#' With ggplot2 graphics, the code in \code{renderPlot} should return a ggplot
#' With ggplot2 graphics, the code in `renderPlot` should return a ggplot
#' object; if instead the code prints the ggplot2 object with something like
#' \code{print(p)}, then the coordinates for interactive graphics will not be
#' `print(p)`, then the coordinates for interactive graphics will not be
#' properly scaled to the data space.
#'
#' See \code{\link{plotOutput}} for more information about interactive plots.
#' See [plotOutput()] for more information about interactive plots.
#'
#' @seealso For the corresponding client-side output function, and example
#' usage, see \code{\link{plotOutput}}. For more details on how the plots are
#' generated, and how to control the output, see \code{\link{plotPNG}}.
#' usage, see [plotOutput()]. For more details on how the plots are
#' generated, and how to control the output, see [plotPNG()].
#' [renderCachedPlot()] offers a way to cache generated plots to
#' expedite the rendering of identical plots.
#'
#' @param expr An expression that generates a plot.
#' @param width,height The width/height of the rendered plot, in pixels; or
#' \code{'auto'} to use the \code{offsetWidth}/\code{offsetHeight} of the HTML
#' element that is bound to this plot. You can also pass in a function that
#' returns the width/height in pixels or \code{'auto'}; in the body of the
#' function you may reference reactive values and functions. When rendering an
#' inline plot, you must provide numeric values (in pixels) to both
#' \code{width} and \code{height}.
#' @param width,height Height and width can be specified in three ways:
#' * `"auto"`, the default, uses the size specified by [plotOutput()]
#' (i.e. the `offsetWidth`/`offsetHeight`` of the HTML element bound to
#' this plot.)
#' * An integer, defining the width/height in pixels.
#' * A function that returns the width/height in pixels (or `"auto"`).
#' The function is executed in a reactive context so that you can refer to
#' reactive values and expression to make the width/height reactive.
#'
#' When rendering an inline plot, you must provide numeric values (in pixels)
#' to both \code{width} and \code{height}.
#' @param res Resolution of resulting plot, in pixels per inch. This value is
#' passed to \code{\link[grDevices]{png}}. Note that this affects the resolution of PNG
#' 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 ... Arguments to be passed through to \code{\link[grDevices]{png}}.
#' @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 \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' @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 execOnResize If \code{FALSE} (the default), then when a plot is
#' resized, Shiny will \emph{replay} the plot drawing commands with
#' \code{\link[grDevices]{replayPlot}()} instead of re-executing \code{expr}.
#' @param execOnResize If `FALSE` (the default), then when a plot is
#' resized, Shiny will *replay* the plot drawing commands with
#' [grDevices::replayPlot()] instead of re-executing `expr`.
#' This can result in faster plot redrawing, but there may be rare cases where
#' it is undesirable. If you encounter problems when resizing a plot, you can
#' have Shiny re-execute the code on resize by setting this to \code{TRUE}.
#' have Shiny re-execute the code on resize by setting this to `TRUE`.
#' @param outputArgs A list of arguments to be passed through to the implicit
#' call to \code{\link{plotOutput}} when \code{renderPlot} is used in an
#' call to [plotOutput()] when `renderPlot` is used in an
#' interactive R Markdown document.
#' @export
renderPlot <- function(expr, width='auto', height='auto', res=72, ...,
@@ -353,62 +359,88 @@ custom_print.ggplot <- function(x) {
# With a faceted ggplot2 plot, the outer list contains two objects, each of
# which represents one panel. In this example, there is one panelvar, but there
# can be up to two of them.
# mtc <- mtcars
# mtc$am <- factor(mtc$am)
# p <- print(ggplot(mtc, aes(wt, mpg)) + geom_point() + facet_wrap(~ am))
# str(getGgplotCoordmap(p, 400, 300, 72))
# p <- print(ggplot(mpg) + geom_point(aes(fl, cty), alpha = 0.2) + facet_wrap(~drv, scales = "free_x"))
# str(getGgplotCoordmap(p, 500, 400, 72))
# List of 2
# $ panels:List of 2
# $ panels:List of 3
# ..$ :List of 8
# .. ..$ panel : num 1
# .. ..$ row : int 1
# .. ..$ col : int 1
# .. ..$ panel_vars:List of 1
# .. .. ..$ panelvar1: Factor w/ 2 levels "0","1": 1
# .. .. ..$ panelvar1: chr "4"
# .. ..$ log :List of 2
# .. .. ..$ x: NULL
# .. .. ..$ y: NULL
# .. ..$ domain :List of 4
# .. .. ..$ left : num 1.32
# .. .. ..$ right : num 5.62
# .. .. ..$ bottom: num 9.22
# .. .. ..$ top : num 35.1
# .. ..$ domain :List of 5
# .. .. ..$ left : num 0.4
# .. .. ..$ right : num 4.6
# .. .. ..$ bottom : num 7.7
# .. .. ..$ top : num 36.3
# .. .. ..$ discrete_limits:List of 1
# .. .. .. ..$ x: chr [1:4] "d" "e" "p" "r"
# .. ..$ mapping :List of 3
# .. .. ..$ x : chr "wt"
# .. .. ..$ y : chr "mpg"
# .. .. ..$ panelvar1: chr "am"
# .. .. ..$ x : chr "fl"
# .. .. ..$ y : chr "cty"
# .. .. ..$ panelvar1: chr "drv"
# .. ..$ range :List of 4
# .. .. ..$ left : num 33.3
# .. .. ..$ right : num 191
# .. .. ..$ bottom: num 328
# .. .. ..$ right : num 177
# .. .. ..$ bottom: num 448
# .. .. ..$ top : num 23.1
# ..$ :List of 8
# .. ..$ panel : num 2
# .. ..$ row : int 1
# .. ..$ col : int 2
# .. ..$ panel_vars:List of 1
# .. .. ..$ panelvar1: Factor w/ 2 levels "0","1": 2
# .. .. ..$ panelvar1: chr "f"
# .. ..$ log :List of 2
# .. .. ..$ x: NULL
# .. .. ..$ y: NULL
# .. ..$ domain :List of 4
# .. .. ..$ left : num 1.32
# .. .. ..$ right : num 5.62
# .. .. ..$ bottom: num 9.22
# .. .. ..$ top : num 35.1
# .. ..$ domain :List of 5
# .. .. ..$ left : num 0.4
# .. .. ..$ right : num 5.6
# .. .. ..$ bottom : num 7.7
# .. .. ..$ top : num 36.3
# .. .. ..$ discrete_limits:List of 1
# .. .. .. ..$ x: chr [1:5] "c" "d" "e" "p" ...
# .. ..$ mapping :List of 3
# .. .. ..$ x : chr "wt"
# .. .. ..$ y : chr "mpg"
# .. .. ..$ panelvar1: chr "am"
# .. .. ..$ x : chr "fl"
# .. .. ..$ y : chr "cty"
# .. .. ..$ panelvar1: chr "drv"
# .. ..$ range :List of 4
# .. .. ..$ left : num 197
# .. .. ..$ right : num 355
# .. .. ..$ bottom: num 328
# .. .. ..$ left : num 182
# .. .. ..$ right : num 326
# .. .. ..$ bottom: num 448
# .. .. ..$ top : num 23.1
# ..$ :List of 8
# .. ..$ panel : num 3
# .. ..$ row : int 1
# .. ..$ col : int 3
# .. ..$ panel_vars:List of 1
# .. .. ..$ panelvar1: chr "r"
# .. ..$ log :List of 2
# .. .. ..$ x: NULL
# .. .. ..$ y: NULL
# .. ..$ domain :List of 5
# .. .. ..$ left : num 0.4
# .. .. ..$ right : num 3.6
# .. .. ..$ bottom : num 7.7
# .. .. ..$ top : num 36.3
# .. .. ..$ discrete_limits:List of 1
# .. .. .. ..$ x: chr [1:3] "e" "p" "r"
# .. ..$ mapping :List of 3
# .. .. ..$ x : chr "fl"
# .. .. ..$ y : chr "cty"
# .. .. ..$ panelvar1: chr "drv"
# .. ..$ range :List of 4
# .. .. ..$ left : num 331
# .. .. ..$ right : num 475
# .. .. ..$ bottom: num 448
# .. .. ..$ top : num 23.1
# $ dims :List of 2
# ..$ width : num 400
# ..$ height: num 300
# ..$ width : num 500
# ..$ height: num 400
getCoordmap <- function(x, width, height, res) {
if (inherits(x, "ggplot_build_gtable")) {
@@ -570,6 +602,9 @@ find_panel_info_api <- function(b) {
domain$bottom <- -domain$bottom
}
domain <- add_discrete_limits(domain, xscale, "x")
domain <- add_discrete_limits(domain, yscale, "y")
domain
}
@@ -689,6 +724,9 @@ find_panel_info_non_api <- function(b, ggplot_format) {
domain$bottom <- -domain$bottom
}
domain <- add_discrete_limits(domain, xscale, "x")
domain <- add_discrete_limits(domain, yscale, "y")
domain
}
@@ -995,3 +1033,23 @@ find_panel_ranges <- function(g, res) {
)
})
}
# Remember the x/y limits of discrete axes. This info is
# necessary to properly inverse map the numeric (i.e., trained)
# positions back to the data scale, for example:
# https://github.com/rstudio/shiny/pull/2410#issuecomment-487783828
# https://github.com/rstudio/shiny/pull/2410#issuecomment-488100881
#
# Eventually, we may want to consider storing the entire ggplot2
# object server-side and querying information from that object
# as we need it...that's the only way we'll ever be able to
# faithfully brush examples like this:
# https://github.com/rstudio/shiny/issues/2411
add_discrete_limits <- function(domain, scale, var = "x") {
var <- match.arg(var, c("x", "y"))
if (!is.function(scale$is_discrete) || !is.function(scale$get_limits)) return(domain)
if (scale$is_discrete()) {
domain$discrete_limits[[var]] <- scale$get_limits()
}
domain
}

View File

@@ -1,50 +1,50 @@
#' Table Output
#'
#' Creates a reactive table that is suitable for assigning to an \code{output}
#' Creates a reactive table that is suitable for assigning to an `output`
#' slot.
#'
#' The corresponding HTML output tag should be \code{div} and have the CSS
#' class name \code{shiny-html-output}.
#' The corresponding HTML output tag should be `div` and have the CSS
#' class name `shiny-html-output`.
#'
#' @param expr An expression that returns an R object that can be used with
#' \code{\link[xtable]{xtable}}.
#' @param striped,hover,bordered Logicals: if \code{TRUE}, apply the
#' [xtable::xtable()].
#' @param striped,hover,bordered Logicals: if `TRUE`, apply the
#' corresponding Bootstrap table format to the output table.
#' @param spacing The spacing between the rows of the table (\code{xs}
#' stands for "extra small", \code{s} for "small", \code{m} for "medium"
#' and \code{l} for "large").
#' @param spacing The spacing between the rows of the table (`xs`
#' stands for "extra small", `s` for "small", `m` for "medium"
#' and `l` for "large").
#' @param width Table width. Must be a valid CSS unit (like "100%", "400px",
#' "auto") or a number, which will be coerced to a string and
#' have "px" appended.
#' @param align A string that specifies the column alignment. If equal to
#' \code{'l'}, \code{'c'} or \code{'r'}, then all columns will be,
#' respectively, left-, center- or right-aligned. Otherwise, \code{align}
#' `'l'`, `'c'` or `'r'`, then all columns will be,
#' respectively, left-, center- or right-aligned. Otherwise, `align`
#' must have the same number of characters as the resulting table (if
#' \code{rownames = TRUE}, this will be equal to \code{ncol()+1}), with
#' the \emph{i}-th character specifying the alignment for the
#' \emph{i}-th column (besides \code{'l'}, \code{'c'} and
#' \code{'r'}, \code{'?'} is also permitted - \code{'?'} is a placeholder
#' `rownames = TRUE`, this will be equal to `ncol()+1`), with
#' the *i*-th character specifying the alignment for the
#' *i*-th column (besides `'l'`, `'c'` and
#' `'r'`, `'?'` is also permitted - `'?'` is a placeholder
#' for that particular column, indicating that it should keep its default
#' alignment). If \code{NULL}, then all numeric/integer columns (including
#' alignment). If `NULL`, then all numeric/integer columns (including
#' the row names, if they are numbers) will be right-aligned and
#' everything else will be left-aligned (\code{align = '?'} produces the
#' everything else will be left-aligned (`align = '?'` produces the
#' same result).
#' @param rownames,colnames Logicals: include rownames? include colnames
#' (column headers)?
#' @param digits An integer specifying the number of decimal places for
#' the numeric columns (this will not apply to columns with an integer
#' class). If \code{digits} is set to a negative value, then the numeric
#' class). If `digits` is set to a negative value, then the numeric
#' columns will be displayed in scientific format with a precision of
#' \code{abs(digits)} digits.
#' `abs(digits)` digits.
#' @param na The string to use in the table cells whose values are missing
#' (i.e. they either evaluate to \code{NA} or \code{NaN}).
#' @param ... Arguments to be passed through to \code{\link[xtable]{xtable}}
#' and \code{\link[xtable]{print.xtable}}.
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})?
#' (i.e. they either evaluate to `NA` or `NaN`).
#' @param ... Arguments to be passed through to [xtable::xtable()]
#' and [xtable::print.xtable()].
#' @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 \code{\link{tableOutput}} when \code{renderTable} is
#' implicit call to [tableOutput()] when `renderTable` is
#' used in an interactive R Markdown document.
#' @export
renderTable <- function(expr, striped = FALSE, hover = FALSE,

View File

@@ -1,24 +1,24 @@
#' Run a Shiny application from a URL
#'
#' \code{runUrl()} downloads and launches a Shiny application that is hosted at
#' `runUrl()` downloads and launches a Shiny application that is hosted at
#' a downloadable URL. The Shiny application must be saved in a .zip, .tar, or
#' .tar.gz file. The Shiny application files must be contained in the root
#' directory or a subdirectory in the archive. For example, the files might be
#' \code{myapp/server.r} and \code{myapp/ui.r}. The functions \code{runGitHub()}
#' and \code{runGist()} are based on \code{runUrl()}, using URL's from GitHub
#' (\url{https://github.com}) and GitHub gists (\url{https://gist.github.com}),
#' `myapp/server.r` and `myapp/ui.r`. The functions `runGitHub()`
#' and `runGist()` are based on `runUrl()`, using URL's from GitHub
#' (<https://github.com>) and GitHub gists (<https://gist.github.com>),
#' respectively.
#' @param url URL of the application.
#' @param filetype The file type (\code{".zip"}, \code{".tar"}, or
#' \code{".tar.gz"}. Defaults to the file extension taken from the url.
#' @param filetype The file type (`".zip"`, `".tar"`, or
#' `".tar.gz"`. Defaults to the file extension taken from the url.
#' @param subdir A subdirectory in the repository that contains the app. By
#' default, this function will run an app from the top level of the repo, but
#' you can use a path such as `\code{"inst/shinyapp"}.
#' @param destdir Directory to store the downloaded application files. If \code{NULL}
#' you can use a path such as `"inst/shinyapp"`.
#' @param destdir Directory to store the downloaded application files. If `NULL`
#' (the default), the application files will be stored in a temporary directory
#' and removed when the app exits
#' @param ... Other arguments to be passed to \code{\link{runApp}()}, such as
#' \code{port} and \code{launch.browser}.
#' @param ... Other arguments to be passed to [runApp()], such as
#' `port` and `launch.browser`.
#' @export
#' @examples
#' ## Only run this example in interactive R sessions
@@ -88,8 +88,8 @@ runUrl <- function(url, filetype = NULL, subdir = NULL, destdir = NULL, ...) {
#' @rdname runUrl
#' @param gist The identifier of the gist. For example, if the gist is
#' https://gist.github.com/jcheng5/3239667, then \code{3239667},
#' \code{'3239667'}, and \code{'https://gist.github.com/jcheng5/3239667'} are
#' https://gist.github.com/jcheng5/3239667, then `3239667`,
#' `'3239667'`, and `'https://gist.github.com/jcheng5/3239667'` are
#' all valid values.
#' @export
#' @examples
@@ -118,10 +118,10 @@ runGist <- function(gist, destdir = NULL, ...) {
#' @rdname runUrl
#' @param repo Name of the repository.
#' @param username GitHub username. If \code{repo} is of the form
#' \code{"username/repo"}, \code{username} will be taken from \code{repo}.
#' @param username GitHub username. If `repo` is of the form
#' `"username/repo"`, `username` will be taken from `repo`.
#' @param ref Desired git reference. Could be a commit, tag, or branch name.
#' Defaults to \code{"master"}.
#' Defaults to `"master"`.
#' @export
#' @examples
#' ## Only run this example in interactive R sessions

View File

@@ -5,34 +5,34 @@ 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 \code{input}
#' variable of the \code{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 \code{type} used
#' (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".
#'
#' Currently Shiny registers the following handlers: \code{shiny.matrix},
#' \code{shiny.number}, and \code{shiny.date}.
#' Currently Shiny registers the following handlers: `shiny.matrix`,
#' `shiny.number`, and `shiny.date`.
#'
#' The \code{type} of a custom Shiny Input widget will be deduced using the
#' \code{getType()} JavaScript function on the registered Shiny inputBinding.
#' @param type The type for which the handler should be added -- should be a
#' 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.
#' @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
#' \code{input} variable. The function will be called with the following three
#' `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 \code{shinysession} in which the input exists.}
#' \item{The `shinysession` in which the input exists.}
#' \item{The name of the input.}
#' }
#' @param force If \code{TRUE}, will overwrite any existing handler without
#' warning. If \code{FALSE}, will throw an error if this class already has
#' @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{
@@ -48,7 +48,7 @@ inputHandlers <- Map$new()
#' }
#'
#' }
#' @seealso \code{\link{removeInputHandler}}
#' @seealso [removeInputHandler()]
#' @export
registerInputHandler <- function(type, fun, force=FALSE){
if (inputHandlers$containsKey(type) && !force){
@@ -63,9 +63,9 @@ registerInputHandler <- function(type, fun, force=FALSE){
#' for data of this type, the default jsonlite serialization will be used.
#'
#' @param type The type for which handlers should be removed.
#' @return The handler previously associated with this \code{type}, if one
#' existed. Otherwise, \code{NULL}.
#' @seealso \code{\link{registerInputHandler}}
#' @return The handler previously associated with this `type`, if one
#' existed. Otherwise, `NULL`.
#' @seealso [registerInputHandler()]
#' @export
removeInputHandler <- function(type){
inputHandlers$remove(type)
@@ -103,8 +103,8 @@ applyInputHandler <- function(name, val, shinysession) {
#' values.
#'
#' The raw input values should be in a named list. Some values may have names
#' like \code{"x:shiny.date"}. This function would apply the \code{"shiny.date"}
#' input handler to the value, and then rename the result to \code{"x"}, in the
#' like `"x:shiny.date"`. This function would apply the `"shiny.date"`
#' input handler to the value, and then rename the result to `"x"`, in the
#' output.
#'
#' @param inputs A named list of input values.

View File

@@ -22,6 +22,7 @@ registerClient <- function(client) {
}
.globals$resourcePaths <- list()
.globals$resources <- list()
.globals$showcaseDefault <- 0
@@ -30,29 +31,46 @@ registerClient <- function(client) {
#' Resource Publishing
#'
#' Adds a directory of static resources to Shiny's web server, with the given
#' path prefix. Primarily intended for package authors to make supporting
#' JavaScript/CSS files available to their components.
#' Add, remove, or list directory of static resources to Shiny's web server,
#' with the given path prefix. Primarily intended for package authors to make
#' supporting JavaScript/CSS files available to their components.
#'
#' Shiny provides two ways of serving static files (i.e., resources):
#'
#' 1. Static files under the `www/` directory are automatically made available
#' under a request path that begins with `/`.
#' 2. `addResourcePath()` makes static files in a `directoryPath` available
#' under a request path that begins with `prefix`.
#'
#' The second approach is primarily intended for package authors to make
#' supporting JavaScript/CSS files available to their components.
#'
#' Tools for managing static resources published by Shiny's web server:
#' * `addResourcePath()` adds a directory of static resources.
#' * `resourcePaths()` lists the currently active resource mappings.
#' * `removeResourcePath()` removes a directory of static resources.
#'
#' @param prefix The URL prefix (without slashes). Valid characters are a-z,
#' A-Z, 0-9, hyphen, period, and underscore.
#' For example, a value of 'foo' means that any request paths that begin with
#' '/foo' will be mapped to the given directory.
#' A-Z, 0-9, hyphen, period, and underscore. For example, a value of 'foo'
#' means that any request paths that begin with '/foo' will be mapped to the
#' given directory.
#' @param directoryPath The directory that contains the static resources to be
#' served.
#'
#' @details You can call \code{addResourcePath} multiple times for a given
#' \code{prefix}; only the most recent value will be retained. If the
#' normalized \code{directoryPath} is different than the directory that's
#' currently mapped to the \code{prefix}, a warning will be issued.
#'
#' @seealso \code{\link{singleton}}
#' @rdname resourcePaths
#' @seealso [singleton()]
#'
#' @examples
#' addResourcePath('datasets', system.file('data', package='datasets'))
#' resourcePaths()
#' removeResourcePath('datasets')
#' resourcePaths()
#'
#' # make sure all resources are removed
#' lapply(names(resourcePaths()), removeResourcePath)
#' @export
addResourcePath <- function(prefix, directoryPath) {
prefix <- prefix[1]
if (length(prefix) != 1) stop("prefix must be of length 1")
if (!grepl('^[a-z0-9\\-_][a-z0-9\\-_.]*$', prefix, ignore.case = TRUE, perl = TRUE)) {
stop("addResourcePath called with invalid prefix; please see documentation")
}
@@ -66,30 +84,112 @@ addResourcePath <- function(prefix, directoryPath) {
"`prefix` = '", prefix, "'; `directoryPath` = '" , directoryPath, "'")
}
)
# # Often times overwriting a resource path is "what you want",
# # but sometimes it can lead to difficult to diagnose issues
# # (e.g. an implict dependency might set a resource path that
# # conflicts with what you, the app author, are trying to register)
# # Note that previous versions of shiny used to warn about this case,
# # but it was eventually removed since it caused confusion (#567).
# # It seems a good compromise is to throw a more information message.
# if (getOption("shiny.resourcePathChanges", FALSE) &&
# prefix %in% names(.globals$resourcePaths)) {
# existingPath <- .globals$resourcePaths[[prefix]]$path
# if (normalizedPath != existingPath) {
# message(
# "The resource path '", prefix, "' used to point to ",
# existingPath, ", but it now points to ", normalizedPath, ". ",
# "If your app doesn't work as expected, you may want to ",
# "choose a different prefix name."
# )
# }
# }
# If a shiny app is currently running, dynamically register this path with
# the corresponding httpuv server object.
if (!is.null(getShinyOption("server")))
{
getShinyOption("server")$setStaticPath(.list = stats::setNames(normalizedPath, prefix))
}
# .globals$resourcePaths and .globals$resources persist across runs of applications.
.globals$resourcePaths[[prefix]] <- staticPath(normalizedPath)
# This is necessary because resourcePaths is only for serving assets out of C++;
# to support subapps, we also need assets to be served out of R, because those
# URLs are rewritten by R code (i.e. routeHandler) before they can be matched to
# a resource path.
.globals$resources[[prefix]] <- list(
directoryPath = normalizedPath,
func = staticHandler(normalizedPath)
)
}
#' @rdname resourcePaths
#' @export
resourcePaths <- function() {
urls <- names(.globals$resourcePaths)
paths <- vapply(.globals$resourcePaths, function(x) x$path, character(1))
stats::setNames(paths, urls)
}
hasResourcePath <- function(prefix) {
prefix %in% names(resourcePaths())
}
#' @rdname resourcePaths
#' @export
removeResourcePath <- function(prefix) {
if (length(prefix) > 1) stop("`prefix` must be of length 1.")
if (!hasResourcePath(prefix)) {
warning("Resource ", prefix, " not found.")
return(invisible(FALSE))
}
.globals$resourcePaths[[prefix]] <- NULL
.globals$resources[[prefix]] <- NULL
invisible(TRUE)
}
# This function handles any GET request with two or more path elements where the
# first path element matches a prefix that was previously added using
# addResourcePath().
#
# For example, if `addResourcePath("foo", "~/bar")` was called, then a GET
# request for /foo/one/two.html would rewrite the PATH_INFO as /one/two.html and
# send it to the resource path function for "foo". As of this writing, that
# function will always be a staticHandler, which serves up a file if it exists
# and NULL if it does not.
#
# Since Shiny 1.3.x, assets registered via addResourcePath should mostly be
# served out of httpuv's native static file serving features. However, in the
# specific case of subapps, the R code path must be used, because subapps insert
# a giant random ID into the beginning of the URL that must be stripped off by
# an R route handler (see addSubApp()).
resourcePathHandler <- function(req) {
if (!identical(req$REQUEST_METHOD, 'GET'))
return(NULL)
# e.g. "/foo/one/two.html"
path <- req$PATH_INFO
match <- regexpr('^/([^/]+)/', path, perl=TRUE)
if (match == -1)
return(NULL)
len <- attr(match, 'capture.length')
# e.g. "foo"
prefix <- substr(path, 2, 2 + len - 1)
resInfo <- .globals$resources[[prefix]]
if (is.null(resInfo))
return(NULL)
# e.g. "/one/two.html"
suffix <- substr(path, 2 + len, nchar(path))
# Create a new request that's a clone of the current request, but adjust
# PATH_INFO and SCRIPT_NAME to reflect that we have already matched the first
# path element (e.g. "/foo"). See routeHandler() for more info.
subreq <- as.environment(as.list(req, all.names=TRUE))
subreq$PATH_INFO <- suffix
subreq$SCRIPT_NAME <- paste(subreq$SCRIPT_NAME, substr(path, 1, 2 + len), sep='')
@@ -101,23 +201,23 @@ resourcePathHandler <- function(req) {
#'
#' Defines the server-side logic of the Shiny application. This generally
#' involves creating functions that map user inputs to various kinds of output.
#' In older versions of Shiny, it was necessary to call \code{shinyServer()} in
#' the \code{server.R} file, but this is no longer required as of Shiny 0.10.
#' Now the \code{server.R} file may simply return the appropriate server
#' In older versions of Shiny, it was necessary to call `shinyServer()` in
#' the `server.R` file, but this is no longer required as of Shiny 0.10.
#' Now the `server.R` file may simply return the appropriate server
#' function (as the last expression in the code), without calling
#' \code{shinyServer()}.
#' `shinyServer()`.
#'
#' Call \code{shinyServer} from your application's \code{server.R}
#' Call `shinyServer` from your application's `server.R`
#' file, passing in a "server function" that provides the server-side logic of
#' your application.
#'
#' The server function will be called when each client (web browser) first loads
#' the Shiny application's page. It must take an \code{input} and an
#' \code{output} parameter. Any return value will be ignored. It also takes an
#' optional \code{session} parameter, which is used when greater control is
#' the Shiny application's page. It must take an `input` and an
#' `output` parameter. Any return value will be ignored. It also takes an
#' optional `session` parameter, which is used when greater control is
#' needed.
#'
#' See the \href{http://rstudio.github.com/shiny/tutorial/}{tutorial} for more
#' See the [tutorial](http://rstudio.github.com/shiny/tutorial/) for more
#' on how to write a server function.
#'
#' @param func The server function for this application. See the details section
@@ -144,6 +244,7 @@ resourcePathHandler <- function(req) {
#' }
#' }
#' @export
#' @keywords internal
shinyServer <- function(func) {
.globals$server <- list(func)
invisible(func)
@@ -187,7 +288,7 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
# This value, if non-NULL, must be present on all HTTP and WebSocket
# requests as the Shiny-Shared-Secret header or else access will be
# denied (403 response for HTTP, and instant close for websocket).
sharedSecret <- getOption('shiny.sharedSecret')
checkSharedSecret <- loadSharedSecret()
appHandlers <- list(
http = joinHandlers(c(
@@ -195,10 +296,10 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
httpHandlers,
sys.www.root,
resourcePathHandler,
reactLogHandler)),
reactLogHandler
)),
ws = function(ws) {
if (!is.null(sharedSecret)
&& !identical(sharedSecret, ws$request$HTTP_SHINY_SHARED_SECRET)) {
if (!checkSharedSecret(ws$request$HTTP_SHINY_SHARED_SECRET)) {
ws$close()
return(TRUE)
}
@@ -417,6 +518,70 @@ startApp <- function(appObj, port, host, quiet) {
handlerManager$addHandler(appHandlers$http, "/", tail = TRUE)
handlerManager$addWSHandler(appHandlers$ws, "/", tail = TRUE)
httpuvApp <- handlerManager$createHttpuvApp()
httpuvApp$staticPaths <- c(
appObj$staticPaths,
list(
# Always handle /session URLs dynamically, even if / is a static path.
"session" = excludeStaticPath(),
"shared" = system.file(package = "shiny", "www", "shared")
),
.globals$resourcePaths
)
# throw an informative warning if a subdirectory of the
# app's www dir conflicts with another resource prefix
wwwDir <- httpuvApp$staticPaths[["/"]]$path
if (length(wwwDir)) {
# although httpuv allows for resource prefixes like 'foo/bar',
# we won't worry about conflicts in sub-sub directories since
# addResourcePath() currently doesn't allow it
wwwSubDirs <- list.dirs(wwwDir, recursive = FALSE, full.names = FALSE)
resourceConflicts <- intersect(wwwSubDirs, names(httpuvApp$staticPaths))
if (length(resourceConflicts)) {
warning(
"Found subdirectories of your app's www/ directory that ",
"conflict with other resource URL prefixes. ",
"Consider renaming these directories: '",
paste0("www/", resourceConflicts, collapse = "', '"), "'",
call. = FALSE
)
}
}
# check for conflicts in each pairwise combinations of resource mappings
checkResourceConflict <- function(paths) {
if (length(paths) < 2) return(NULL)
# ensure paths is a named character vector: c(resource_path = local_path)
paths <- vapply(paths, function(x) if (inherits(x, "staticPath")) x$path else x, character(1))
# get all possible pairwise combinations of paths
pair_indices <- utils::combn(length(paths), 2, simplify = FALSE)
lapply(pair_indices, function(x) {
p1 <- paths[x[1]]
p2 <- paths[x[2]]
if (identical(names(p1), names(p2)) && (p1 != p2)) {
warning(
"Found multiple local file paths pointing the same resource prefix: ", names(p1), ". ",
"If you run into resource-related issues (e.g. 404 requests), consider ",
"using `addResourcePath()` and/or `removeResourcePath()` to manage resource mappings.",
call. = FALSE
)
}
})
}
checkResourceConflict(httpuvApp$staticPaths)
httpuvApp$staticPathOptions <- httpuv::staticPathOptions(
html_charset = "utf-8",
headers = list("X-UA-Compatible" = "IE=edge,chrome=1"),
validation =
if (!is.null(getOption("shiny.sharedSecret"))) {
sprintf('"Shiny-Shared-Secret" == "%s"', getOption("shiny.sharedSecret"))
} else {
character(0)
}
)
if (is.numeric(port) || is.integer(port)) {
if (!quiet) {
hostString <- host
@@ -424,7 +589,7 @@ startApp <- function(appObj, port, host, quiet) {
hostString <- paste0("[", hostString, "]")
message('\n', 'Listening on http://', hostString, ':', port)
}
return(startServer(host, port, handlerManager$createHttpuvApp()))
return(startServer(host, port, httpuvApp))
} else if (is.character(port)) {
if (!quiet) {
message('\n', 'Listening on domain socket ', port)
@@ -436,7 +601,7 @@ startApp <- function(appObj, port, host, quiet) {
"configuration (and not domain sockets), then `port` must ",
"be numeric, not a string.")
}
return(startPipeServer(port, mask, handlerManager$createHttpuvApp()))
return(startPipeServer(port, mask, httpuvApp))
}
}
@@ -468,8 +633,8 @@ serviceApp <- function() {
#'
#' This function tests whether a Shiny application is currently running.
#'
#' @return \code{TRUE} if a Shiny application is currently running. Otherwise,
#' \code{FALSE}.
#' @return `TRUE` if a Shiny application is currently running. Otherwise,
#' `FALSE`.
#' @export
isRunning <- function() {
.globals$running
@@ -481,44 +646,44 @@ isRunning <- function() {
#' to stop the application (usually by pressing Ctrl+C or Esc).
#'
#' The host parameter was introduced in Shiny 0.9.0. Its default value of
#' \code{"127.0.0.1"} means that, contrary to previous versions of Shiny, only
#' `"127.0.0.1"` means that, contrary to previous versions of Shiny, only
#' the current machine can access locally hosted Shiny apps. To allow other
#' clients to connect, use the value \code{"0.0.0.0"} instead (which was the
#' clients to connect, use the value `"0.0.0.0"` instead (which was the
#' value that was hard-coded into Shiny in 0.8.0 and earlier).
#'
#' @param appDir The application to run. Should be one of the following:
#' \itemize{
#' \item A directory containing \code{server.R}, plus, either \code{ui.R} or
#' a \code{www} directory that contains the file \code{index.html}.
#' \item A directory containing \code{app.R}.
#' \item An \code{.R} file containing a Shiny application, ending with an
#' \item A directory containing `server.R`, plus, either `ui.R` or
#' a `www` directory that contains the file `index.html`.
#' \item A directory containing `app.R`.
#' \item An `.R` file containing a Shiny application, ending with an
#' expression that produces a Shiny app object.
#' \item A list with \code{ui} and \code{server} components.
#' \item A Shiny app object created by \code{\link{shinyApp}}.
#' \item A list with `ui` and `server` components.
#' \item A Shiny app object created by [shinyApp()].
#' }
#' @param port The TCP port that the application should listen on. If the
#' \code{port} is not specified, and the \code{shiny.port} option is set (with
#' \code{options(shiny.port = XX)}), then that port will be used. Otherwise,
#' `port` is not specified, and the `shiny.port` option is set (with
#' `options(shiny.port = XX)`), then that port will be used. Otherwise,
#' use 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. This value of this parameter can also be a
#' function to call with the application's URL.
#' @param host The IPv4 address that the application should listen on. Defaults
#' to the \code{shiny.host} option, if set, or \code{"127.0.0.1"} if not. See
#' to the `shiny.host` option, if set, or `"127.0.0.1"` if not. See
#' Details.
#' @param workerId Can generally be ignored. Exists to help some editions of
#' Shiny Server Pro route requests to the correct process.
#' @param quiet Should Shiny status messages be shown? Defaults to FALSE.
#' @param display.mode The mode in which to display the application. If set to
#' the value \code{"showcase"}, shows application code and metadata from a
#' \code{DESCRIPTION} file in the application directory alongside the
#' application. If set to \code{"normal"}, displays the application normally.
#' Defaults to \code{"auto"}, which displays the application in the mode given
#' in its \code{DESCRIPTION} file, if any.
#' the value `"showcase"`, shows application code and metadata from a
#' `DESCRIPTION` file in the application directory alongside the
#' application. If set to `"normal"`, displays the application normally.
#' Defaults to `"auto"`, which displays the application in the mode given
#' in its `DESCRIPTION` file, if any.
#' @param test.mode Should the application be launched in test mode? This is
#' only used for recording or running automated tests. Defaults to the
#' \code{shiny.testmode} option, or FALSE if the option is not set.
#' `shiny.testmode` option, or FALSE if the option is not set.
#'
#' @examples
#' \dontrun{
@@ -777,6 +942,10 @@ runApp <- function(appDir=getwd(),
server <- startApp(appParts, port, host, quiet)
# Make the httpuv server object accessible. Needed for calling
# addResourcePath while app is running.
shinyOptions(server = server)
on.exit({
stopServer(server)
}, add = TRUE)
@@ -818,7 +987,6 @@ runApp <- function(appDir=getwd(),
captureStackTraces({
while (!.globals$stopped) {
..stacktracefloor..(serviceApp())
Sys.sleep(0.001)
}
})
)
@@ -835,10 +1003,10 @@ runApp <- function(appDir=getwd(),
#' Stop the currently running Shiny app
#'
#' Stops the currently running Shiny app, returning control to the caller of
#' \code{\link{runApp}}.
#' [runApp()].
#'
#' @param returnValue The value that should be returned from
#' \code{\link{runApp}}.
#' [runApp()].
#' @export
stopApp <- function(returnValue = invisible()) {
# reterror will indicate whether retval is an error (i.e. it should be passed
@@ -867,7 +1035,7 @@ stopApp <- function(returnValue = invisible()) {
#'
#' Launch Shiny example applications, and optionally, your system's web browser.
#'
#' @param example The name of the example to run, or \code{NA} (the default) to
#' @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.
@@ -875,9 +1043,9 @@ stopApp <- function(returnValue = invisible()) {
#' launched automatically after the app is started. Defaults to true in
#' interactive sessions only.
#' @param host The IPv4 address that the application should listen on. Defaults
#' to the \code{shiny.host} option, if set, or \code{"127.0.0.1"} if not.
#' to the `shiny.host` option, if set, or `"127.0.0.1"` if not.
#' @param display.mode The mode in which to display the example. Defaults to
#' \code{showcase}, but may be set to \code{normal} to see the example without
#' `showcase`, but may be set to `normal` to see the example without
#' code or commentary.
#'
#' @examples
@@ -924,21 +1092,21 @@ runExample <- function(example=NA,
#' Run a gadget
#'
#' Similar to \code{runApp}, but handles \code{input$cancel} automatically, and
#' Similar to `runApp`, but handles `input$cancel` automatically, and
#' if running in RStudio, defaults to viewing the app in the Viewer pane.
#'
#' @param app Either a Shiny app object as created by
#' \code{\link[=shiny]{shinyApp}} et al, or, a UI object.
#' @param server Ignored if \code{app} is a Shiny app object; otherwise, passed
#' along to \code{shinyApp} (i.e. \code{shinyApp(ui = app, server = server)}).
#' @param port See \code{\link[=shiny]{runApp}}.
#' [`shinyApp()`][shiny] et al, or, a UI object.
#' @param server Ignored if `app` is a Shiny app object; otherwise, passed
#' along to `shinyApp` (i.e. `shinyApp(ui = app, server = server)`).
#' @param port See [`runApp()`][shiny].
#' @param viewer Specify where the gadget should be displayed--viewer pane,
#' dialog window, or external browser--by passing in a call to one of the
#' \code{\link{viewer}} functions.
#' @param stopOnCancel If \code{TRUE} (the default), then an \code{observeEvent}
#' is automatically created that handles \code{input$cancel} by calling
#' \code{stopApp()} with an error. Pass \code{FALSE} if you want to handle
#' \code{input$cancel} yourself.
#' [viewer()] functions.
#' @param stopOnCancel If `TRUE` (the default), then an `observeEvent`
#' is automatically created that handles `input$cancel` by calling
#' `stopApp()` with an error. Pass `FALSE` if you want to handle
#' `input$cancel` yourself.
#' @return The value returned by the gadget.
#'
#' @examples
@@ -1003,10 +1171,10 @@ decorateServerFunc <- function(appobj, serverFunc) {
#' other R environments that emulate RStudio's viewer pane/dialog APIs). If
#' viewer APIs are not available in the current R environment, then the gadget
#' will be displayed in the system's default web browser (see
#' \code{\link[utils]{browseURL}}).
#' [utils::browseURL()]).
#'
#' @return A function that takes a single \code{url} parameter, suitable for
#' passing as the \code{viewer} argument of \code{\link{runGadget}}.
#' @return A function that takes a single `url` parameter, suitable for
#' passing as the `viewer` argument of [runGadget()].
#'
#' @rdname viewer
#' @name viewer
@@ -1014,8 +1182,8 @@ NULL
#' @param minHeight The minimum height (in pixels) desired to show the gadget in
#' the viewer pane. If a positive number, resize the pane if necessary to show
#' at least that many pixels. If \code{NULL}, use the existing viewer pane
#' size. If \code{"maximize"}, use the maximum available vertical space.
#' at least that many pixels. If `NULL`, use the existing viewer pane
#' size. If `"maximize"`, use the maximum available vertical space.
#' @rdname viewer
#' @export
paneViewer <- function(minHeight = NULL) {
@@ -1044,7 +1212,7 @@ dialogViewer <- function(dialogName, width = 600, height = 600) {
}
}
#' @param browser See \code{\link[utils]{browseURL}}.
#' @param browser See [utils::browseURL()].
#' @rdname viewer
#' @export
browserViewer <- function(browser = getOption("browser")) {

View File

@@ -16,20 +16,102 @@ getShinyOption <- function(name, default = NULL) {
#' Get or set Shiny options
#'
#' \code{getShinyOption} retrieves the value of a Shiny option.
#' \code{shinyOptions} sets the value of Shiny options; it can also be used to
#' return a list of all currently-set Shiny options.
#' `getShinyOption()` retrieves the value of a Shiny option. `shinyOptions()`
#' sets the value of Shiny options; it can also be used to return a list of all
#' currently-set Shiny options.
#'
#' There is a global option set, which is available by default. When a Shiny
#' application is run with \code{\link{runApp}}, that option set is duplicated
#' and the new option set is available for getting or setting values. If options
#' are set from global.R, app.R, ui.R, or server.R, or if they are set from
#' inside the server function, then the options will be scoped to the
#' @section Scope:
#' There is a global option set which is available by default. When a Shiny
#' application is run with [runApp()], that option set is duplicated and the
#' new option set is available for getting or setting values. If options
#' are set from `global.R`, `app.R`, `ui.R`, or `server.R`, or if they are set
#' from inside the server function, then the options will be scoped to the
#' application. When the application exits, the new option set is discarded and
#' the global option set is restored.
#'
#' @param ... Options to set, with the form \code{name = value}.
#' @section Options:
#' There are a number of global options that affect Shiny's behavior. These can
#' be set globally with `options()` or locally (for a single app) with
#' `shinyOptions()`.
#'
#' \describe{
#' \item{shiny.autoreload}{If `TRUE` when a Shiny app is launched, the
#' app directory will be continually monitored for changes to files that
#' have the extensions: r, htm, html, js, css, png, jpg, jpeg, gif. If any
#' changes are detected, all connected Shiny sessions are reloaded. This
#' allows for fast feedback loops when tweaking Shiny UI.
#'
#' Since monitoring for changes is expensive (we simply poll for last
#' modified times), this feature is intended only for development.
#'
#' You can customize the file patterns Shiny will monitor by setting the
#' shiny.autoreload.pattern option. For example, to monitor only ui.R:
#' `options(shiny.autoreload.pattern = glob2rx("ui.R"))`
#'
#' The default polling interval is 500 milliseconds. You can change this
#' by setting e.g. `options(shiny.autoreload.interval = 2000)` (every
#' two seconds).}
#' \item{shiny.deprecation.messages}{This controls whether messages for
#' deprecated functions in Shiny will be printed. See
#' [shinyDeprecated()] for more information.}
#' \item{shiny.error}{This can be a function which is called when an error
#' occurs. For example, `options(shiny.error=recover)` will result a
#' the debugger prompt when an error occurs.}
#' \item{shiny.fullstacktrace}{Controls whether "pretty" or full stack traces
#' are dumped to the console when errors occur during Shiny app execution.
#' The default is `FALSE` (pretty stack traces).}
#' \item{shiny.host}{The IP address that Shiny should listen on. See
#' [runApp()] for more information.}
#' \item{shiny.json.digits}{The number of digits to use when converting
#' numbers to JSON format to send to the client web browser.}
#' \item{shiny.launch.browser}{A boolean which controls the default behavior
#' when an app is run. See [runApp()] for more information.}
#' \item{shiny.maxRequestSize}{This is a number which specifies the maximum
#' web request size, which serves as a size limit for file uploads. If
#' unset, the maximum request size defaults to 5MB.}
#' \item{shiny.minified}{If this is `TRUE` or unset (the default), then
#' Shiny will use minified JavaScript (`shiny.min.js`). If
#' `FALSE`, then Shiny will use the un-minified JavaScript
#' (`shiny.js`); this can be useful during development.}
#' \item{shiny.port}{A port number that Shiny will listen on. See
#' [runApp()] for more information.}
#' \item{shiny.reactlog}{If `TRUE`, enable logging of reactive events,
#' which can be viewed later with the [reactlogShow()] function.
#' This incurs a substantial performance penalty and should not be used in
#' production.}
#' \item{shiny.sanitize.errors}{If `TRUE`, then normal errors (i.e.
#' errors not wrapped in `safeError`) won't show up in the app; a simple
#' generic error message is printed instead (the error and strack trace printed
#' to the console remain unchanged). The default is `FALSE` (unsanitized
#' errors).If you want to sanitize errors in general, but you DO want a
#' particular error `e` to get displayed to the user, then set this option
#' to `TRUE` and use `stop(safeError(e))` for errors you want the
#' user to see.}
#' \item{shiny.stacktraceoffset}{If `TRUE`, then Shiny's printed stack
#' traces will display srcrefs one line above their usual location. This is
#' an arguably more intuitive arrangement for casual R users, as the name
#' of a function appears next to the srcref where it is defined, rather than
#' where it is currently being called from.}
#' \item{shiny.suppressMissingContextError}{Normally, invoking a reactive
#' outside of a reactive context (or [isolate()]) results in
#' an error. If this is `TRUE`, don't error in these cases. This
#' should only be used for debugging or demonstrations of reactivity at the
#' console.}
#' \item{shiny.table.class}{CSS class names to use for tables.}
#' \item{shiny.testmode}{If `TRUE`, then enable features for testing Shiny
#' applications. If `FALSE` (the default), do not enable those features.}
#' \item{shiny.trace}{Print messages sent between the R server and the web
#' browser client to the R console. This is useful for debugging. Possible
#' values are `"send"` (only print messages sent to the client),
#' `"recv"` (only print messages received by the server), `TRUE`
#' (print all messages), or `FALSE` (default; don't print any of these
#' messages).}
#' \item{shiny.usecairo}{This is used to disable graphical rendering by the
#' Cairo package, if it is installed. See [plotPNG()] for more
#' information.}
#' }
#' @param ... Options to set, with the form `name = value`.
#' @aliases shiny-options
#' @examples
#' \dontrun{
#' shinyOptions(myOption = 10)

283
R/shiny.R
View File

@@ -8,11 +8,11 @@ NULL
#' prebuilt widgets make it possible to build beautiful, responsive, and
#' powerful applications with minimal effort.
#'
#' The Shiny tutorial at \url{http://shiny.rstudio.com/tutorial/} explains
#' The Shiny tutorial at <http://shiny.rstudio.com/tutorial/> explains
#' the framework in depth, walks you through building a simple application, and
#' includes extensive annotated examples.
#'
#' @seealso \link{shiny-options} for documentation about global options.
#' @seealso [shiny-options] for documentation about global options.
#'
#' @name shiny-package
#' @aliases shiny
@@ -28,91 +28,6 @@ NULL
#' @import methods
NULL
#' Global options for Shiny
#'
#' There are a number of global options that affect Shiny's behavior. These can
#' be set with (for example) \code{options(shiny.trace=TRUE)}.
#'
#' \describe{
#' \item{shiny.launch.browser}{A boolean which controls the default behavior
#' when an app is run. See \code{\link{runApp}} for more information.}
#' \item{shiny.port}{A port number that Shiny will listen on. See
#' \code{\link{runApp}} for more information.}
#' \item{shiny.trace}{Print messages sent between the R server and the web
#' browser client to the R console. This is useful for debugging. Possible
#' values are \code{"send"} (only print messages sent to the client),
#' \code{"recv"} (only print messages received by the server), \code{TRUE}
#' (print all messages), or \code{FALSE} (default; don't print any of these
#' messages).}
#' \item{shiny.autoreload}{If \code{TRUE} when a Shiny app is launched, the
#' app directory will be continually monitored for changes to files that
#' have the extensions: r, htm, html, js, css, png, jpg, jpeg, gif. If any
#' changes are detected, all connected Shiny sessions are reloaded. This
#' allows for fast feedback loops when tweaking Shiny UI.
#'
#' Since monitoring for changes is expensive (we simply poll for last
#' modified times), this feature is intended only for development.
#'
#' You can customize the file patterns Shiny will monitor by setting the
#' shiny.autoreload.pattern option. For example, to monitor only ui.R:
#' \code{options(shiny.autoreload.pattern = glob2rx("ui.R"))}
#'
#' The default polling interval is 500 milliseconds. You can change this
#' by setting e.g. \code{options(shiny.autoreload.interval = 2000)} (every
#' two seconds).}
#' \item{shiny.reactlog}{If \code{TRUE}, enable logging of reactive events,
#' which can be viewed later with the \code{\link{showReactLog}} function.
#' This incurs a substantial performance penalty and should not be used in
#' production.}
#' \item{shiny.usecairo}{This is used to disable graphical rendering by the
#' Cairo package, if it is installed. See \code{\link{plotPNG}} for more
#' information.}
#' \item{shiny.maxRequestSize}{This is a number which specifies the maximum
#' web request size, which serves as a size limit for file uploads. If
#' unset, the maximum request size defaults to 5MB.}
#' \item{shiny.suppressMissingContextError}{Normally, invoking a reactive
#' outside of a reactive context (or \code{\link{isolate}()}) results in
#' an error. If this is \code{TRUE}, don't error in these cases. This
#' should only be used for debugging or demonstrations of reactivity at the
#' console.}
#' \item{shiny.host}{The IP address that Shiny should listen on. See
#' \code{\link{runApp}} for more information.}
#' \item{shiny.json.digits}{The number of digits to use when converting
#' numbers to JSON format to send to the client web browser.}
#' \item{shiny.minified}{If this is \code{TRUE} or unset (the default), then
#' Shiny will use minified JavaScript (\code{shiny.min.js}). If
#' \code{FALSE}, then Shiny will use the un-minified JavaScript
#' (\code{shiny.js}); this can be useful during development.}
#' \item{shiny.error}{This can be a function which is called when an error
#' occurs. For example, \code{options(shiny.error=recover)} will result a
#' the debugger prompt when an error occurs.}
#' \item{shiny.table.class}{CSS class names to use for tables.}
#' \item{shiny.deprecation.messages}{This controls whether messages for
#' deprecated functions in Shiny will be printed. See
#' \code{\link{shinyDeprecated}} for more information.}
#' \item{shiny.fullstacktrace}{Controls whether "pretty" or full stack traces
#' are dumped to the console when errors occur during Shiny app execution.
#' The default is \code{FALSE} (pretty stack traces).}
#' \item{shiny.stacktraceoffset}{If \code{TRUE}, then Shiny's printed stack
#' traces will display srcrefs one line above their usual location. This is
#' an arguably more intuitive arrangement for casual R users, as the name
#' of a function appears next to the srcref where it is defined, rather than
#' where it is currently being called from.}
#' \item{shiny.sanitize.errors}{If \code{TRUE}, then normal errors (i.e.
#' errors not wrapped in \code{safeError}) won't show up in the app; a simple
#' generic error message is printed instead (the error and strack trace printed
#' to the console remain unchanged). The default is \code{FALSE} (unsanitized
#' errors).If you want to sanitize errors in general, but you DO want a
#' particular error \code{e} to get displayed to the user, then set this option
#' to \code{TRUE} and use \code{stop(safeError(e))} for errors you want the
#' user to see.}
#' \item{shiny.testmode}{If \code{TRUE}, then enable features for testing Shiny
#' applications. If \code{FALSE} (the default), do not enable those features.
#' }
#' }
#' @name shiny-options
NULL
createUniqueId <- function(bytes, prefix = "", suffix = "") {
withPrivateSeed({
paste(
@@ -179,78 +94,78 @@ workerId <- local({
#' Session object
#'
#' Shiny server functions can optionally include \code{session} as a parameter
#' (e.g. \code{function(input, output, session)}). The session object is an
#' Shiny server functions can optionally include `session` as a parameter
#' (e.g. `function(input, output, session)`). The session object is an
#' environment that can be used to access information and functionality
#' relating to the session. The following list describes the items available
#' in the environment; they can be accessed using the \code{$} operator (for
#' example, \code{session$clientData$url_search}).
#' in the environment; they can be accessed using the `$` operator (for
#' example, `session$clientData$url_search`).
#'
#' @return
#' \item{allowReconnect(value)}{
#' If \code{value} is \code{TRUE} and run in a hosting environment (Shiny
#' If `value` is `TRUE` and run in a hosting environment (Shiny
#' Server or Connect) with reconnections enabled, then when the session ends
#' due to the network connection closing, the client will attempt to
#' reconnect to the server. If a reconnection is successful, the browser will
#' send all the current input values to the new session on the server, and
#' the server will recalculate any outputs and send them back to the client.
#' If \code{value} is \code{FALSE}, reconnections will be disabled (this is
#' the default state). If \code{"force"}, then the client browser will always
#' attempt to reconnect. The only reason to use \code{"force"} is for testing
#' If `value` is `FALSE`, reconnections will be disabled (this is
#' the default state). If `"force"`, then the client browser will always
#' attempt to reconnect. The only reason to use `"force"` is for testing
#' on a local connection (without Shiny Server or Connect).
#' }
#' \item{clientData}{
#' A \code{\link{reactiveValues}} object that contains information about the client.
#' A [reactiveValues()] object that contains information about the client.
#' \itemize{
#' \item{\code{allowDataUriScheme} is a logical value that indicates whether
#' the browser is able to handle URIs that use the \code{data:} scheme.
#' \item{`allowDataUriScheme` is a logical value that indicates whether
#' the browser is able to handle URIs that use the `data:` scheme.
#' }
#' \item{\code{pixelratio} reports the "device pixel ratio" from the web browser,
#' \item{`pixelratio` reports the "device pixel ratio" from the web browser,
#' or 1 if none is reported. The value is 2 for Apple Retina displays.
#' }
#' \item{\code{singletons} - for internal use}
#' \item{\code{url_protocol}, \code{url_hostname}, \code{url_port},
#' \code{url_pathname}, \code{url_search}, \code{url_hash_initial}
#' and \code{url_hash} can be used to get the components of the URL
#' \item{`singletons` - for internal use}
#' \item{`url_protocol`, `url_hostname`, `url_port`,
#' `url_pathname`, `url_search`, `url_hash_initial`
#' and `url_hash` can be used to get the components of the URL
#' that was requested by the browser to load the Shiny app page.
#' These values are from the browser's perspective, so neither HTTP
#' proxies nor Shiny Server will affect these values. The
#' \code{url_search} value may be used with \code{\link{parseQueryString}}
#' `url_search` value may be used with [parseQueryString()]
#' to access query string parameters.
#' }
#' }
#' \code{clientData} also contains information about each output.
#' `clientData` also contains information about each output.
#' \code{output_\var{outputId}_width} and \code{output_\var{outputId}_height}
#' give the dimensions (using \code{offsetWidth} and \code{offsetHeight}) of
#' give the dimensions (using `offsetWidth` and `offsetHeight`) of
#' the DOM element that is bound to \code{\var{outputId}}, and
#' \code{output_\var{outputId}_hidden} is a logical that indicates whether
#' the element is hidden. These values may be \code{NULL} if the output is
#' the element is hidden. These values may be `NULL` if the output is
#' not bound.
#' }
#' \item{input}{
#' The session's \code{input} object (the same as is passed into the Shiny
#' The session's `input` object (the same as is passed into the Shiny
#' server function as an argument).
#' }
#' \item{isClosed()}{A function that returns \code{TRUE} if the client has
#' \item{isClosed()}{A function that returns `TRUE` if the client has
#' disconnected.
#' }
#' \item{ns(id)}{
#' Server-side version of \code{ns <- \link{NS}(id)}. If bare IDs need to be
#' explicitly namespaced for the current module, \code{session$ns("name")}
#' Server-side version of [`ns <- NS(id)`][NS]. If bare IDs need to be
#' explicitly namespaced for the current module, `session$ns("name")`
#' will return the fully-qualified ID.
#' }
#' \item{onEnded(callback)}{
#' Synonym for \code{onSessionEnded}.
#' Synonym for `onSessionEnded`.
#' }
#' \item{onFlush(func, once=TRUE)}{
#' Registers a function to be called before the next time (if \code{once=TRUE})
#' or every time (if \code{once=FALSE}) Shiny flushes the reactive system.
#' Registers a function to be called before the next time (if `once=TRUE`)
#' or every time (if `once=FALSE`) Shiny flushes the reactive system.
#' Returns a function that can be called with no arguments to cancel the
#' registration.
#' }
#' \item{onFlushed(func, once=TRUE)}{
#' Registers a function to be called after the next time (if \code{once=TRUE})
#' or every time (if \code{once=FALSE}) Shiny flushes the reactive system.
#' Registers a function to be called after the next time (if `once=TRUE`)
#' or every time (if `once=FALSE`) Shiny flushes the reactive system.
#' Returns a function that can be called with no arguments to cancel the
#' registration.
#' }
@@ -260,7 +175,7 @@ workerId <- local({
#' registration.
#' }
#' \item{output}{
#' The session's \code{output} object (the same as is passed into the Shiny
#' The session's `output` object (the same as is passed into the Shiny
#' server function as an argument).
#' }
#' \item{reactlog}{
@@ -268,13 +183,13 @@ workerId <- local({
#' }
#' \item{registerDataObj(name, data, filterFunc)}{
#' Publishes any R object as a URL endpoint that is unique to this session.
#' \code{name} must be a single element character vector; it will be used
#' to form part of the URL. \code{filterFunc} must be a function that takes
#' two arguments: \code{data} (the value that was passed into
#' \code{registerDataObj}) and \code{req} (an environment that implements
#' the Rook specification for HTTP requests). \code{filterFunc} will be
#' `name` must be a single element character vector; it will be used
#' to form part of the URL. `filterFunc` must be a function that takes
#' two arguments: `data` (the value that was passed into
#' `registerDataObj`) and `req` (an environment that implements
#' the Rook specification for HTTP requests). `filterFunc` will be
#' called with these values whenever an HTTP request is made to the URL
#' endpoint. The return value of \code{filterFunc} should be a Rook-style
#' endpoint. The return value of `filterFunc` should be a Rook-style
#' response.
#' }
#' \item{reload()}{
@@ -291,35 +206,35 @@ workerId <- local({
#' session-specific data they want.
#' }
#' \item{resetBrush(brushId)}{
#' Resets/clears the brush with the given \code{brushId}, if it exists on
#' any \code{imageOutput} or \code{plotOutput} in the app.
#' Resets/clears the brush with the given `brushId`, if it exists on
#' any `imageOutput` or `plotOutput` in the app.
#' }
#' \item{sendCustomMessage(type, message)}{
#' Sends a custom message to the web page. \code{type} must be a
#' Sends a custom message to the web page. `type` must be a
#' single-element character vector giving the type of message, while
#' \code{message} can be any jsonlite-encodable value. Custom messages
#' `message` can be any jsonlite-encodable value. Custom messages
#' have no meaning to Shiny itself; they are used soley to convey information
#' to custom JavaScript logic in the browser. You can do this by adding
#' JavaScript code to the browser that calls
#' \code{Shiny.addCustomMessageHandler(type, function(message){...})}
#' as the page loads; the function you provide to
#' \code{addCustomMessageHandler} will be invoked each time
#' \code{sendCustomMessage} is called on the server.
#' `addCustomMessageHandler` will be invoked each time
#' `sendCustomMessage` is called on the server.
#' }
#' \item{sendBinaryMessage(type, message)}{
#' Similar to \code{sendCustomMessage}, but the message must be a raw vector
#' Similar to `sendCustomMessage`, but the message must be a raw vector
#' and the registration method on the client is
#' \code{Shiny.addBinaryMessageHandler(type, function(message){...})}. The
#' message argument on the client will be a
#' \href{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView}{DataView}.
#' [DataView](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView).
#' }
#' \item{sendInputMessage(inputId, message)}{
#' Sends a message to an input on the session's client web page; if the input
#' is present and bound on the page at the time the message is received, then
#' the input binding object's \code{receiveMessage(el, message)} method will
#' be called. \code{sendInputMessage} should generally not be called directly
#' the input binding object's `receiveMessage(el, message)` method will
#' be called. `sendInputMessage` should generally not be called directly
#' from Shiny apps, but through friendlier wrapper functions like
#' \code{\link{updateTextInput}}.
#' [updateTextInput()].
#' }
#' \item{setBookmarkExclude(names)}{
#' Set input names to be excluded from bookmarking.
@@ -351,10 +266,10 @@ workerId <- local({
#' \item{getTestSnapshotUrl(input=TRUE, output=TRUE, export=TRUE,
#' format="json")}{
#' Returns a URL for the test snapshots. Only has an effect when the
#' \code{shiny.testmode} option is set to TRUE. For the input, output, and
#' `shiny.testmode` option is set to TRUE. For the input, output, and
#' export arguments, TRUE means to return all of these values. It is also
#' possible to specify by name which values to return by providing a
#' character vector, as in \code{input=c("x", "y")}. The format can be
#' character vector, as in `input=c("x", "y")`. The format can be
#' "rds" or "json".
#' }
#'
@@ -363,26 +278,26 @@ NULL
#' Namespaced IDs for inputs/outputs
#'
#' The \code{NS} function creates namespaced IDs out of bare IDs, by joining
#' them using \code{ns.sep} as the delimiter. It is intended for use in Shiny
#' modules. See \url{http://shiny.rstudio.com/articles/modules.html}.
#' The `NS` function creates namespaced IDs out of bare IDs, by joining
#' them using `ns.sep` as the delimiter. It is intended for use in Shiny
#' modules. See <http://shiny.rstudio.com/articles/modules.html>.
#'
#' Shiny applications use IDs to identify inputs and outputs. These IDs must be
#' unique within an application, as accidentally using the same input/output ID
#' more than once will result in unexpected behavior. The traditional solution
#' for preventing name collisions is \emph{namespaces}; a namespace is to an ID
#' as a directory is to a file. Use the \code{NS} function to turn a bare ID
#' into a namespaced one, by combining them with \code{ns.sep} in between.
#' for preventing name collisions is *namespaces*; a namespace is to an ID
#' as a directory is to a file. Use the `NS` function to turn a bare ID
#' into a namespaced one, by combining them with `ns.sep` in between.
#'
#' @param namespace The character vector to use for the namespace. This can have
#' any length, though a single element is most common. Length 0 will cause the
#' \code{id} to be returned without a namespace, and length 2 will be
#' `id` to be returned without a namespace, and length 2 will be
#' interpreted as multiple namespaces, in increasing order of specificity
#' (i.e. starting with the top-level namespace).
#' @param id The id string to be namespaced (optional).
#' @return If \code{id} is missing, returns a function that expects an id string
#' @return If `id` is missing, returns a function that expects an id string
#' as its only argument and returns that id with the namespace prepended.
#' @seealso \url{http://shiny.rstudio.com/articles/modules.html}
#' @seealso <http://shiny.rstudio.com/articles/modules.html>
#' @export
NS <- function(namespace, id = NULL) {
if (length(namespace) == 0)
@@ -513,8 +428,7 @@ ShinySession <- R6Class(
# in the web page; in these cases, there's no output_foo_hidden flag,
# and hidden should be TRUE. In other words, NULL and TRUE should map to
# TRUE, FALSE should map to FALSE.
hidden <- private$.clientData$.values[[paste("output_", name, "_hidden",
sep="")]]
hidden <- private$.clientData$.values$get(paste0("output_", name, "_hidden"))
if (is.null(hidden)) hidden <- TRUE
return(hidden && private$getOutputOption(name, 'suspendWhenHidden', TRUE))
@@ -576,7 +490,7 @@ ShinySession <- R6Class(
# Apply preprocessor functions for inputs that have them.
values$input <- lapply(
setNames(names(values$input), names(values$input)),
stats::setNames(names(values$input), names(values$input)),
function(name) {
preprocess <- private$getSnapshotPreprocessInput(name)
preprocess(values$input[[name]])
@@ -604,7 +518,7 @@ ShinySession <- R6Class(
# Apply snapshotPreprocess functions for outputs that have them.
values$output <- lapply(
setNames(names(values$output), names(values$output)),
stats::setNames(names(values$output), names(values$output)),
function(name) {
preprocess <- private$getSnapshotPreprocessOutput(name)
preprocess(values$output[[name]])
@@ -640,7 +554,7 @@ ShinySession <- R6Class(
# that the resulting object is represented as an object in JSON
# instead of an array, and so that the RDS data structure is of a
# consistent type.
values <- lapply(values, asNamedVector)
values <- lapply(values, asNamed)
if (length(values) == 0) {
return(httpResponse(400, "text/plain",
@@ -750,8 +664,8 @@ ShinySession <- R6Class(
private$flushCallbacks <- Callbacks$new()
private$flushedCallbacks <- Callbacks$new()
private$inputReceivedCallbacks <- Callbacks$new()
private$.input <- ReactiveValues$new(dedupe = FALSE)
private$.clientData <- ReactiveValues$new(dedupe = TRUE)
private$.input <- ReactiveValues$new(dedupe = FALSE, label = "input")
private$.clientData <- ReactiveValues$new(dedupe = TRUE, label = "clientData")
private$timingRecorder <- ShinyServerTimingRecorder$new()
self$progressStack <- Stack$new()
self$files <- Map$new()
@@ -759,9 +673,7 @@ ShinySession <- R6Class(
self$userData <- new.env(parent = emptyenv())
self$input <- .createReactiveValues(private$.input, readonly=TRUE)
.setLabel(self$input, 'input')
self$clientData <- .createReactiveValues(private$.clientData, readonly=TRUE)
.setLabel(self$clientData, 'clientData')
self$output <- .createOutputWriter(self)
@@ -1090,6 +1002,9 @@ ShinySession <- R6Class(
# will be attached to the observer after it's created.
outputAttrs <- attr(func, "outputAttrs", TRUE)
# Save this for getOutput purposes
outputAttrs$renderFunc <- func
funcFormals <- formals(func)
# ..stacktraceon matches with the top-level ..stacktraceoff.., because
# the observer we set up below has ..stacktraceon=FALSE
@@ -1197,6 +1112,9 @@ ShinySession <- R6Class(
stop(paste("Unexpected", class(func), "output for", name))
}
},
getOutput = function(name) {
attr(private$.outputs[[name]], "renderFunc", exact = TRUE)
},
flushOutput = function() {
if (private$busyCount > 0)
return()
@@ -1206,6 +1124,11 @@ ShinySession <- R6Class(
if (self$isClosed())
return()
# This is the only place in the session where the restoreContext is
# flushed.
if (!is.null(self$restoreContext))
self$restoreContext$flushPending()
# Return TRUE if there's any stuff to send to the client.
hasPendingUpdates <- function() {
# Even though progressKeys isn't sent to the client, we use it in this
@@ -1621,8 +1544,14 @@ ShinySession <- R6Class(
reactlog = function(logEntry) {
# Use sendCustomMessage instead of sendMessage, because the handler in
# shiny-showcase.js only has access to public API of the Shiny object.
if (private$showcase)
self$sendCustomMessage("reactlog", logEntry)
if (private$showcase) {
srcref <- logEntry$srcref
srcfile <- logEntry$srcfile
if (!is.null(srcref) && !is.null(srcfile)) {
# only send needed information, not all of reactlog info.
self$sendCustomMessage("showcase-src", list(srcref = srcref, srcfile = srcfile))
}
}
},
reload = function() {
private$sendMessage(reload = TRUE)
@@ -1914,7 +1843,7 @@ ShinySession <- R6Class(
fileData <- readBin(file, 'raw', n=bytes)
if (isTRUE(private$.clientData$.values$allowDataUriScheme)) {
if (isTRUE(private$.clientData$.values$get("allowDataUriScheme"))) {
b64 <- rawToBase64(fileData)
return(paste('data:', contentType, ';base64,', b64, sep=''))
} else {
@@ -2025,6 +1954,7 @@ ShinySession <- R6Class(
},
incrementBusyCount = function() {
if (private$busyCount == 0L) {
rLog$asyncStart(domain = self)
private$sendMessage(busy = "busy")
}
private$busyCount <- private$busyCount + 1L
@@ -2032,6 +1962,7 @@ ShinySession <- R6Class(
decrementBusyCount = function() {
private$busyCount <- private$busyCount - 1L
if (private$busyCount == 0L) {
rLog$asyncStop(domain = self)
private$sendMessage(busy = "idle")
self$requestFlush()
# We defer the call to startCycle() using later(), to defend against
@@ -2089,7 +2020,13 @@ ShinySession <- R6Class(
#' @export
`$.shinyoutput` <- function(x, name) {
stop("Reading objects from shinyoutput object not allowed.")
name <- .subset2(x, 'ns')(name)
if (getOption("shiny.allowoutputreads", FALSE)) {
.subset2(x, 'impl')$getOutput(name)
} else {
stop("Reading from shinyoutput object is not allowed.")
}
}
#' @export
@@ -2109,9 +2046,9 @@ ShinySession <- R6Class(
#'
#' These are the available options for an output object:
#' \itemize{
#' \item suspendWhenHidden. When \code{TRUE} (the default), the output object
#' \item suspendWhenHidden. When `TRUE` (the default), the output object
#' will be suspended (not execute) when it is hidden on the web page. When
#' \code{FALSE}, the output object will not suspend when hidden, and if it
#' `FALSE`, the output object will not suspend when hidden, and if it
#' was already hidden and suspended, then it will resume immediately.
#' \item priority. The priority level of the output object. Queued outputs
#' with higher priority values will execute before those with lower values.
@@ -2132,7 +2069,7 @@ ShinySession <- R6Class(
#' outputOptions(output, "myplot")
#' }
#'
#' @param x A shinyoutput object (typically \code{output}).
#' @param x A shinyoutput object (typically `output`).
#' @param name The name of an output observer in the shinyoutput object.
#' @param ... Options to set for the output observer.
#' @export
@@ -2163,9 +2100,9 @@ getCurrentOutputInfo <- function(session = getDefaultReactiveDomain()) {
#' Add callbacks for Shiny session events
#'
#' These functions are for registering callbacks on Shiny session events.
#' \code{onFlush} registers a function that will be called before Shiny flushes
#' the reactive system. \code{onFlushed} registers a function that will be
#' called after Shiny flushes the reactive system. \code{onSessionEnded}
#' `onFlush` registers a function that will be called before Shiny flushes
#' the reactive system. `onFlushed` registers a function that will be
#' called after Shiny flushes the reactive system. `onSessionEnded`
#' registers a function to be called after the client has disconnected.
#'
#' These functions should be called within the application's server function.
@@ -2175,8 +2112,8 @@ getCurrentOutputInfo <- function(session = getDefaultReactiveDomain()) {
#'
#' @param fun A callback function.
#' @param once Should the function be run once, and then cleared, or should it
#' re-run each time the event occurs. (Only for \code{onFlush} and
#' \code{onFlushed}.)
#' re-run each time the event occurs. (Only for `onFlush` and
#' `onFlushed`.)
#' @param session A shiny session object.
#'
#' @export
@@ -2192,7 +2129,7 @@ onFlushed <- function(fun, once = TRUE, session = getDefaultReactiveDomain()) {
#' @rdname onFlush
#'
#' @seealso \code{\link{onStop}()} for registering callbacks that will be
#' @seealso [onStop()] for registering callbacks that will be
#' invoked when the application exits, or when a session ends.
#' @export
onSessionEnded <- function(fun, session = getDefaultReactiveDomain()) {
@@ -2220,20 +2157,20 @@ flushPendingSessions <- function() {
#' Run code after an application or session ends
#'
#' This function registers callback functions that are invoked when the
#' application exits (when \code{\link{runApp}} exits), or after each user
#' application exits (when [runApp()] exits), or after each user
#' session ends (when a client disconnects).
#'
#' @param fun A function that will be called after the app has finished running.
#' @param session A scope for when the callback will run. If \code{onStop} is
#' @param session A scope for when the callback will run. If `onStop` is
#' called from within the server function, this will default to the current
#' session, and the callback will be invoked when the current session ends. If
#' \code{onStop} is called outside a server function, then the callback will
#' be invoked with the application exits. If \code{NULL}, it is the same as
#' calling \code{onStop} outside of the server function, and the callback will
#' `onStop` is called outside a server function, then the callback will
#' be invoked with the application exits. If `NULL`, it is the same as
#' calling `onStop` outside of the server function, and the callback will
#' be invoked when the application exits.
#'
#'
#' @seealso \code{\link{onSessionEnded}()} for the same functionality, but at
#' @seealso [onSessionEnded()] for the same functionality, but at
#' the session level only.
#'
#' @return A function which, if invoked, will cancel the callback.

View File

@@ -4,9 +4,9 @@ NULL
#' Load the MathJax library and typeset math expressions
#'
#' This function adds MathJax to the page and typeset the math expressions (if
#' found) in the content \code{...}. It only needs to be called once in an app
#' unless the content is rendered \emph{after} the page is loaded, e.g. via
#' \code{\link{renderUI}}, in which case we have to call it explicitly every
#' found) in the content `...`. It only needs to be called once in an app
#' unless the content is rendered *after* the page is loaded, e.g. via
#' [renderUI()], in which case we have to call it explicitly every
#' time we write math expressions to the output.
#' @param ... any HTML elements to apply MathJax to
#' @export
@@ -44,7 +44,7 @@ renderPage <- function(ui, connection, showcase=0, testMode=FALSE) {
shiny_deps <- list(
htmlDependency("json2", "2014.02.04", c(href="shared"), script = "json2-min.js"),
htmlDependency("jquery", "1.12.4", c(href="shared"), script = "jquery.min.js"),
htmlDependency("jquery", "3.4.1", c(href="shared"), script = "jquery.min.js"),
htmlDependency("shiny", utils::packageVersion("shiny"), c(href="shared"),
script = if (getOption("shiny.minified", TRUE)) "shiny.min.js" else "shiny.js",
stylesheet = "shiny.css")
@@ -71,6 +71,7 @@ renderPage <- function(ui, connection, showcase=0, testMode=FALSE) {
#'
#' @param ui A user interace definition
#' @return The user interface definition, without modifications or side effects.
#' @keywords internal
#' @export
shinyUI <- function(ui) {
.globals$ui <- list(ui)

View File

@@ -2,7 +2,7 @@ utils::globalVariables('func')
#' Mark a function as a render function
#'
#' Should be called by implementers of \code{renderXXX} functions in order to
#' Should be called by implementers of `renderXXX` functions in order to
#' mark their return values as Shiny render functions, and to provide a hint to
#' Shiny regarding what UI function is most commonly used with this type of
#' render function. This can be used in R Markdown documents to create complete
@@ -12,13 +12,13 @@ utils::globalVariables('func')
#' an output ID.
#' @param renderFunc A function that is suitable for assigning to a Shiny output
#' slot.
#' @param outputArgs A list of arguments to pass to the \code{uiFunc}. Render
#' functions should include \code{outputArgs = list()} in their own parameter
#' list, and pass through the value to \code{markRenderFunction}, to allow
#' @param outputArgs A list of arguments to pass to the `uiFunc`. Render
#' functions should include `outputArgs = list()` in their own parameter
#' list, and pass through the value to `markRenderFunction`, to allow
#' app authors to customize outputs. (Currently, this is only supported for
#' dynamically generated UIs, such as those created by Shiny code snippets
#' embedded in R Markdown documents).
#' @return The \code{renderFunc} function, with annotations.
#' @return The `renderFunc` function, with annotations.
#' @export
markRenderFunction <- function(uiFunc, renderFunc, outputArgs = list()) {
# a mutable object that keeps track of whether `useRenderFunction` has been
@@ -52,27 +52,32 @@ markRenderFunction <- function(uiFunc, renderFunc, outputArgs = list()) {
hasExecuted = hasExecuted)
}
#' @export
print.shiny.render.function <- function(x, ...) {
cat_line("<shiny.render.function>")
}
#' Implement render functions
#'
#' @param func A function without parameters, that returns user data. If the
#' returned value is a promise, then the render function will proceed in async
#' mode.
#' @param transform A function that takes four arguments: \code{value},
#' \code{session}, \code{name}, and \code{...} (for future-proofing). This
#' function will be invoked each time a value is returned from \code{func},
#' @param transform A function that takes four arguments: `value`,
#' `session`, `name`, and `...` (for future-proofing). This
#' function will be invoked each time a value is returned from `func`,
#' and is responsible for changing the value into a JSON-ready value to be
#' JSON-encoded and sent to the browser.
#' @param outputFunc The UI function that is used (or most commonly used) with
#' this render function. This can be used in R Markdown documents to create
#' complete output widgets out of just the render function.
#' @param outputArgs A list of arguments to pass to the \code{outputFunc}.
#' Render functions should include \code{outputArgs = list()} in their own
#' @param outputArgs A list of arguments to pass to the `outputFunc`.
#' Render functions should include `outputArgs = list()` in their own
#' parameter list, and pass through the value as this argument, to allow app
#' authors to customize outputs. (Currently, this is only supported for
#' dynamically generated UIs, such as those created by Shiny code snippets
#' embedded in R Markdown documents).
#' @return An annotated render function, ready to be assigned to an
#' \code{output} slot.
#' `output` slot.
#'
#' @export
createRenderFunction <- function(
@@ -165,37 +170,37 @@ markOutputAttrs <- function(renderFunc, snapshotExclude = NULL,
#' Image file output
#'
#' Renders a reactive image that is suitable for assigning to an \code{output}
#' Renders a reactive image that is suitable for assigning to an `output`
#' slot.
#'
#' The expression \code{expr} must return a list containing the attributes for
#' the \code{img} object on the client web page. For the image to display,
#' properly, the list must have at least one entry, \code{src}, which is the
#' path to the image file. It may also useful to have a \code{contentType}
#' The expression `expr` must return a list containing the attributes for
#' the `img` object on the client web page. For the image to display,
#' properly, the list must have at least one entry, `src`, which is the
#' path to the image file. It may also useful to have a `contentType`
#' entry specifying the MIME type of the image. If one is not provided,
#' \code{renderImage} will try to autodetect the type, based on the file
#' `renderImage` will try to autodetect the type, based on the file
#' extension.
#'
#' Other elements such as \code{width}, \code{height}, \code{class}, and
#' \code{alt}, can also be added to the list, and they will be used as
#' attributes in the \code{img} object.
#' Other elements such as `width`, `height`, `class`, and
#' `alt`, can also be added to the list, and they will be used as
#' attributes in the `img` object.
#'
#' The corresponding HTML output tag should be \code{div} or \code{img} and have
#' the CSS class name \code{shiny-image-output}.
#' The corresponding HTML output tag should be `div` or `img` and have
#' the CSS class name `shiny-image-output`.
#'
#' @seealso For more details on how the images are generated, and how to control
#' the output, see \code{\link{plotPNG}}.
#' the output, see [plotPNG()].
#'
#' @param expr An expression that returns a list.
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' @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 deleteFile Should the file in \code{func()$src} be deleted after
#' @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 \code{func}, then this should be \code{TRUE};
#' if the image is not a temp file, this should be \code{FALSE}.
#' temp file generated within `func`, then this should be `TRUE`;
#' 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 \code{\link{imageOutput}} when \code{renderImage} is used in an
#' call to [imageOutput()] when `renderImage` is used in an
#' interactive R Markdown document.
#' @export
#'
@@ -295,30 +300,30 @@ renderImage <- function(expr, env=parent.frame(), quoted=FALSE,
#'
#' Makes a reactive version of the given function that captures any printed
#' output, and also captures its printable result (unless
#' \code{\link[base]{invisible}}), into a string. The resulting function is suitable
#' for assigning to an \code{output} slot.
#' [base::invisible()]), into a string. The resulting function is suitable
#' for assigning to an `output` slot.
#'
#' The corresponding HTML output tag can be anything (though \code{pre} is
#' 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 \code{shiny-text-output}.
#' have the CSS class name `shiny-text-output`.
#'
#' The result of executing \code{func} will be printed inside a
#' \code{\link[utils]{capture.output}} call.
#' The result of executing `func` will be printed inside a
#' [utils::capture.output()] call.
#'
#' Note that unlike most other Shiny output functions, if the given function
#' returns \code{NULL} then \code{NULL} will actually be visible in the output.
#' To display nothing, make your function return \code{\link[base]{invisible}()}.
#' 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 \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' @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 The value for \code{\link[base]{options}('width')}.
#' @param width The value for `[options][base::options]('width')`.
#' @param outputArgs A list of arguments to be passed through to the implicit
#' call to \code{\link{verbatimTextOutput}} when \code{renderPrint} is used
#' call to [verbatimTextOutput()] when `renderPrint` is used
#' in an interactive R Markdown document.
#' @seealso \code{\link{renderText}} for displaying the value returned from a
#' @seealso [renderText()] for displaying the value returned from a
#' function, instead of the printed output.
#'
#' @example res/text-example.R
@@ -398,38 +403,40 @@ createRenderPrintPromiseDomain <- function(width) {
#' Text Output
#'
#' Makes a reactive version of the given function that also uses
#' \code{\link[base]{cat}} to turn its result into a single-element character
#' [base::cat()] to turn its result into a single-element character
#' vector.
#'
#' The corresponding HTML output tag can be anything (though \code{pre} is
#' 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 \code{shiny-text-output}.
#' have the CSS class name `shiny-text-output`.
#'
#' The result of executing \code{func} will passed to \code{cat}, inside a
#' \code{\link[utils]{capture.output}} call.
#' 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 \code{cat}.
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' 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 \code{\link{textOutput}} when \code{renderText} is used in an
#' 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 \code{\link{renderPrint}} for capturing the print output of a
#' @seealso [renderPrint()] for capturing the print output of a
#' function, rather than the returned text value.
#'
#' @example res/text-example.R
#' @export
renderText <- function(expr, env=parent.frame(), quoted=FALSE,
outputArgs=list()) {
outputArgs=list(), sep=" ") {
installExprFunction(expr, "func", env, quoted)
createRenderFunction(
func,
function(value, session, name, ...) {
paste(utils::capture.output(cat(value)), collapse="\n")
paste(utils::capture.output(cat(value, sep=sep)), collapse="\n")
},
textOutput, outputArgs
)
@@ -439,19 +446,19 @@ renderText <- function(expr, env=parent.frame(), quoted=FALSE,
#'
#' Renders reactive HTML using the Shiny UI library.
#'
#' The corresponding HTML output tag should be \code{div} and have the CSS class
#' name \code{shiny-html-output} (or use \code{\link{uiOutput}}).
#' The corresponding HTML output tag should be `div` and have the CSS class
#' name `shiny-html-output` (or use [uiOutput()]).
#'
#' @param expr An expression that returns a Shiny tag object, \code{\link{HTML}},
#' @param expr An expression that returns a Shiny tag object, [HTML()],
#' or a list of such objects.
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' @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 \code{\link{uiOutput}} when \code{renderUI} is used in an
#' call to [uiOutput()] when `renderUI` is used in an
#' interactive R Markdown document.
#'
#' @seealso \code{\link{uiOutput}}
#' @seealso [uiOutput()]
#' @export
#' @examples
#' ## Only run examples in interactive R sessions
@@ -494,25 +501,25 @@ renderUI <- function(expr, env=parent.frame(), quoted=FALSE,
#' file downloads (for example, downloading the currently visible data as a CSV
#' file). Both filename and contents can be calculated dynamically at the time
#' the user initiates the download. Assign the return value to a slot on
#' \code{output} in your server function, and in the UI use
#' \code{\link{downloadButton}} or \code{\link{downloadLink}} to make the
#' `output` in your server function, and in the UI use
#' [downloadButton()] or [downloadLink()] to make the
#' download available.
#'
#' @param filename A string of the filename, including extension, that the
#' user's web browser should default to when downloading the file; or a
#' function that returns such a string. (Reactive values and functions may be
#' used from this function.)
#' @param content A function that takes a single argument \code{file} that is a
#' @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.)
#' @param contentType A string of the download's
#' \href{http://en.wikipedia.org/wiki/Internet_media_type}{content type}, for
#' example \code{"text/csv"} or \code{"image/png"}. If \code{NULL} or
#' \code{NA}, the content type will be guessed based on the filename
#' extension, or \code{application/octet-stream} if the extension is unknown.
#' [content type](http://en.wikipedia.org/wiki/Internet_media_type), for
#' example `"text/csv"` or `"image/png"`. If `NULL` or
#' `NA`, the content type will be guessed based on the filename
#' extension, or `application/octet-stream` if the extension is unknown.
#' @param outputArgs A list of arguments to be passed through to the implicit
#' call to \code{\link{downloadButton}} when \code{downloadHandler} is used
#' call to [downloadButton()] when `downloadHandler` is used
#' in an interactive R Markdown document.
#'
#' @examples
@@ -556,12 +563,12 @@ downloadHandler <- function(filename, content, contentType=NA, outputArgs=list()
#' searching, filtering, and sorting can be done on the R side using Shiny as
#' the server infrastructure.
#'
#' For the \code{options} argument, the character elements that have the class
#' \code{"AsIs"} (usually returned from \code{\link[base]{I}()}) will be evaluated in
#' For the `options` argument, the character elements that have the class
#' `"AsIs"` (usually returned from [base::I()]) will be evaluated in
#' JavaScript. This is useful when the type of the option value is not supported
#' in JSON, e.g., a JavaScript function, which can be obtained by evaluating a
#' character string. Note this only applies to the root-level elements of the
#' options list, and the \code{I()} notation does not work for lower-level
#' options list, and the `I()` notation does not work for lower-level
#' elements in the list.
#' @param expr An expression that returns a data frame or a matrix.
#' @param options A list of initialization options to be passed to DataTables,
@@ -570,25 +577,25 @@ downloadHandler <- function(filename, content, contentType=NA, outputArgs=list()
#' frequent search requests).
#' @param callback A JavaScript function to be applied to the DataTable object.
#' This is useful for DataTables plug-ins, which often require the DataTable
#' instance to be available (\url{http://datatables.net/extensions/}).
#' @param escape Whether to escape HTML entities in the table: \code{TRUE} means
#' to escape the whole table, and \code{FALSE} means not to escape it.
#' instance to be available (<http://datatables.net/extensions/>).
#' @param escape Whether to escape HTML entities in the table: `TRUE` means
#' to escape the whole table, and `FALSE` means not to escape it.
#' Alternatively, you can specify numeric column indices or column names to
#' indicate which columns to escape, e.g. \code{1:5} (the first 5 columns),
#' \code{c(1, 3, 4)}, or \code{c(-1, -3)} (all columns except the first and
#' third), or \code{c('Species', 'Sepal.Length')}.
#' indicate which columns to escape, e.g. `1:5` (the first 5 columns),
#' `c(1, 3, 4)`, or `c(-1, -3)` (all columns except the first and
#' third), or `c('Species', 'Sepal.Length')`.
#' @param outputArgs A list of arguments to be passed through to the implicit
#' call to \code{\link{dataTableOutput}} when \code{renderDataTable} is used
#' call to [dataTableOutput()] when `renderDataTable` is used
#' in an interactive R Markdown document.
#'
#' @references \url{http://datatables.net}
#' @references <http://datatables.net>
#' @note This function only provides the server-side version of DataTables
#' (using R to process the data object on the server side). There is a
#' separate package \pkg{DT} (\url{https://github.com/rstudio/DT}) that allows
#' separate package \pkg{DT} (<https://github.com/rstudio/DT>) that allows
#' you to create both server-side and client-side DataTables, and supports
#' additional DataTables features. Consider using \code{DT::renderDataTable()}
#' and \code{DT::dataTableOutput()} (see
#' \url{http://rstudio.github.io/DT/shiny.html} for more information).
#' additional DataTables features. Consider using `DT::renderDataTable()`
#' and `DT::dataTableOutput()` (see
#' <http://rstudio.github.io/DT/shiny.html> for more information).
#' @export
#' @inheritParams renderPlot
#' @examples
@@ -707,13 +714,19 @@ checkDT9 <- function(options) {
# Deprecated functions ------------------------------------------------------
#' Deprecated reactive functions
#' @name deprecatedReactives
#' @keywords internal
NULL
#' Plot output (deprecated)
#'
#' See \code{\link{renderPlot}}.
#' `reactivePlot` has been replaced by [renderPlot()].
#' @param func A function.
#' @param width Width.
#' @param height Height.
#' @param ... Other arguments to pass on.
#' @rdname deprecatedReactives
#' @export
reactivePlot <- function(func, width='auto', height='auto', ...) {
shinyDeprecated(new="renderPlot")
@@ -722,9 +735,8 @@ reactivePlot <- function(func, width='auto', height='auto', ...) {
#' Table output (deprecated)
#'
#' See \code{\link{renderTable}}.
#' @param func A function.
#' @param ... Other arguments to pass on.
#' `reactiveTable` has been replaced by [renderTable()].
#' @rdname deprecatedReactives
#' @export
reactiveTable <- function(func, ...) {
shinyDeprecated(new="renderTable")
@@ -733,8 +745,8 @@ reactiveTable <- function(func, ...) {
#' Print output (deprecated)
#'
#' See \code{\link{renderPrint}}.
#' @param func A function.
#' `reactivePrint` has been replaced by [renderPrint()].
#' @rdname deprecatedReactives
#' @export
reactivePrint <- function(func) {
shinyDeprecated(new="renderPrint")
@@ -743,8 +755,8 @@ reactivePrint <- function(func) {
#' UI output (deprecated)
#'
#' See \code{\link{renderUI}}.
#' @param func A function.
#' `reactiveUI` has been replaced by [renderUI()].
#' @rdname deprecatedReactives
#' @export
reactiveUI <- function(func) {
shinyDeprecated(new="renderUI")
@@ -753,8 +765,8 @@ reactiveUI <- function(func) {
#' Text output (deprecated)
#'
#' See \code{\link{renderText}}.
#' @param func A function.
#' `reactiveText` has been replaced by [renderText()].
#' @rdname deprecatedReactives
#' @export
reactiveText <- function(func) {
shinyDeprecated(new="renderText")

View File

@@ -4,10 +4,10 @@
#' event occurs. These events are triggered by accessing a snapshot URL.
#'
#' This function only has an effect if the app is launched in test mode. This is
#' done by calling \code{runApp()} with \code{test.mode=TRUE}, or by setting the
#' global option \code{shiny.testmode} to \code{TRUE}.
#' done by calling `runApp()` with `test.mode=TRUE`, or by setting the
#' global option `shiny.testmode` to `TRUE`.
#'
#' @param quoted_ Are the expression quoted? Default is \code{FALSE}.
#' @param quoted_ Are the expression quoted? Default is `FALSE`.
#' @param env_ The environment in which the expression should be evaluated.
#' @param session_ A Shiny session object.
#' @param ... Named arguments that are quoted or unquoted expressions that will

View File

@@ -4,7 +4,7 @@
#' @param value The value to set for the input object.
#' @param placeholder The placeholder to set for the input object.
#'
#' @seealso \code{\link{textInput}}
#' @seealso [textInput()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -45,7 +45,7 @@ updateTextInput <- function(session, inputId, label = NULL, value = NULL, placeh
#' @template update-input
#' @inheritParams updateTextInput
#'
#' @seealso \code{\link{textAreaInput}}
#' @seealso [textAreaInput()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -84,7 +84,7 @@ updateTextAreaInput <- updateTextInput
#' @template update-input
#' @param value The value to set for the input object.
#'
#' @seealso \code{\link{checkboxInput}}
#' @seealso [checkboxInput()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -117,9 +117,9 @@ updateCheckboxInput <- function(session, inputId, label = NULL, value = NULL) {
#'
#' @template update-input
#' @param icon The icon to set for the input object. To remove the
#' current icon, use \code{icon=character(0)}.
#' current icon, use `icon=character(0)`.
#'
#' @seealso \code{\link{actionButton}}
#' @seealso [actionButton()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -170,13 +170,13 @@ updateActionButton <- function(session, inputId, label = NULL, icon = NULL) {
#'
#' @template update-input
#' @param value The desired date value. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format. Supply \code{NA} to clear the date.
#' `yyyy-mm-dd` format. Supply `NA` to clear the date.
#' @param min The minimum allowed date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#' `yyyy-mm-dd` format.
#' @param max The maximum allowed date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#' `yyyy-mm-dd` format.
#'
#' @seealso \code{\link{dateInput}}
#' @seealso [dateInput()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -205,18 +205,9 @@ updateActionButton <- function(session, inputId, label = NULL, icon = NULL) {
updateDateInput <- function(session, inputId, label = NULL, value = NULL,
min = NULL, max = NULL) {
# Make sure values are NULL or Date objects. This is so we can ensure that
# they will be formatted correctly. For example, the string "2016-08-9" is not
# correctly formatted, but the conversion to Date and back to string will fix
# it.
formatDate <- function(x) {
if (is.null(x))
return(NULL)
format(as.Date(x), "%Y-%m-%d")
}
value <- formatDate(value)
min <- formatDate(min)
max <- formatDate(max)
value <- dateYMD(value, "value")
min <- dateYMD(min, "min")
max <- dateYMD(max, "max")
message <- dropNulls(list(label=label, value=value, min=min, max=max))
session$sendInputMessage(inputId, message)
@@ -227,15 +218,15 @@ updateDateInput <- function(session, inputId, label = NULL, value = NULL,
#'
#' @template update-input
#' @param start The start date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format. Supplying \code{NA} clears the start date.
#' `yyyy-mm-dd` format. Supplying `NA` clears the start date.
#' @param end The end date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format. Supplying \code{NA} clears the end date.
#' `yyyy-mm-dd` format. Supplying `NA` clears the end date.
#' @param min The minimum allowed date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#' `yyyy-mm-dd` format.
#' @param max The maximum allowed date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#' `yyyy-mm-dd` format.
#'
#' @seealso \code{\link{dateRangeInput}}
#' @seealso [dateRangeInput()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -266,12 +257,11 @@ updateDateInput <- function(session, inputId, label = NULL, value = NULL,
updateDateRangeInput <- function(session, inputId, label = NULL,
start = NULL, end = NULL, min = NULL,
max = NULL) {
# Make sure start and end are strings, not date objects. This is for
# consistency across different locales.
if (inherits(start, "Date")) start <- format(start, '%Y-%m-%d')
if (inherits(end, "Date")) end <- format(end, '%Y-%m-%d')
if (inherits(min, "Date")) min <- format(min, '%Y-%m-%d')
if (inherits(max, "Date")) max <- format(max, '%Y-%m-%d')
start <- dateYMD(start, "start")
end <- dateYMD(end, "end")
min <- dateYMD(min, "min")
max <- dateYMD(max, "max")
message <- dropNulls(list(
label = label,
@@ -285,14 +275,14 @@ updateDateRangeInput <- function(session, inputId, label = NULL,
#' Change the selected tab on the client
#'
#' @param session The \code{session} object passed to function given to
#' \code{shinyServer}.
#' @param inputId The id of the \code{tabsetPanel}, \code{navlistPanel},
#' or \code{navbarPage} object.
#' @param session The `session` object passed to function given to
#' `shinyServer`.
#' @param inputId The id of the `tabsetPanel`, `navlistPanel`,
#' or `navbarPage` object.
#' @param selected The name of the tab to make active.
#'
#' @seealso \code{\link{tabsetPanel}}, \code{\link{navlistPanel}},
#' \code{\link{navbarPage}}
#' @seealso [tabsetPanel()], [navlistPanel()],
#' [navbarPage()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -343,7 +333,7 @@ updateNavlistPanel <- updateTabsetPanel
#' @param max Maximum value.
#' @param step Step size.
#'
#' @seealso \code{\link{numericInput}}
#' @seealso [numericInput()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -395,7 +385,7 @@ updateNumericInput <- function(session, inputId, label = NULL, value = NULL,
#' @param timeFormat Date and POSIXt formatting.
#' @param timezone The timezone offset for POSIXt objects.
#'
#' @seealso \code{\link{sliderInput}}
#' @seealso [sliderInput()]
#'
#' @examples
#' ## Only run this example in interactive R sessions
@@ -428,13 +418,15 @@ updateNumericInput <- function(session, inputId, label = NULL, value = NULL,
updateSliderInput <- function(session, inputId, label = NULL, value = NULL,
min = NULL, max = NULL, step = NULL, timeFormat = NULL, timezone = NULL)
{
# If no min/max/value is provided, we won't know the
# type, and this will return an empty string
dataType <- getSliderType(min, max, value)
if (is.null(timeFormat)) {
timeFormat <- switch(dataType, date = "%F", datetime = "%F %T", number = NULL)
}
if (dataType == "date" || dataType == "datetime") {
if (isTRUE(dataType %in% c("date", "datetime"))) {
to_ms <- function(x) 1000 * as.numeric(as.POSIXct(x))
if (!is.null(min)) min <- to_ms(min)
if (!is.null(max)) max <- to_ms(max)
@@ -481,7 +473,7 @@ updateInputOptions <- function(session, inputId, label = NULL, choices = NULL,
#' @template update-input
#' @inheritParams checkboxGroupInput
#'
#' @seealso \code{\link{checkboxGroupInput}}
#' @seealso [checkboxGroupInput()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -528,7 +520,7 @@ updateCheckboxGroupInput <- function(session, inputId, label = NULL,
#' @template update-input
#' @inheritParams radioButtons
#'
#' @seealso \code{\link{radioButtons}}
#' @seealso [radioButtons()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -576,7 +568,7 @@ updateRadioButtons <- function(session, inputId, label = NULL, choices = NULL,
#' @template update-input
#' @inheritParams selectInput
#'
#' @seealso \code{\link{selectInput}} \code{\link{varSelectInput}}
#' @seealso [selectInput()] [varSelectInput()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -621,9 +613,9 @@ updateSelectInput <- function(session, inputId, label = NULL, choices = NULL,
#' @rdname updateSelectInput
#' @inheritParams selectizeInput
#' @param server whether to store \code{choices} on the server side, and load
#' @param server whether to store `choices` on the server side, and load
#' the select options dynamically on searching, instead of writing all
#' \code{choices} into the page at once (i.e., only use the client-side
#' `choices` into the page at once (i.e., only use the client-side
#' version of \pkg{selectize.js})
#' @export
updateSelectizeInput <- function(session, inputId, label = NULL, choices = NULL,

235
R/utils.R
View File

@@ -14,7 +14,7 @@ NULL
#'
#' @note When called, the returned function attempts to preserve the R session's
#' current seed by snapshotting and restoring
#' \code{\link[base]{.Random.seed}}.
#' [base::.Random.seed()].
#'
#' @examples
#' rnormA <- repeatable(rnorm)
@@ -121,8 +121,8 @@ isWholeNum <- function(x, tol = .Machine$double.eps^0.5) {
}
`%AND%` <- function(x, y) {
if (!is.null(x) && !is.na(x))
if (!is.null(y) && !is.na(y))
if (!is.null(x) && !isTRUE(is.na(x)))
if (!is.null(y) && !isTRUE(is.na(y)))
return(y)
return(NULL)
}
@@ -173,12 +173,12 @@ anyUnnamed <- function(x) {
}
# Given a vector/list, returns a named vector (the labels will be blank).
asNamedVector <- function(x) {
if (!is.null(names(x)))
return(x)
# Given a vector/list, returns a named vector/list (the labels will be blank).
asNamed <- function(x) {
if (is.null(names(x))) {
names(x) <- character(length(x))
}
names(x) <- rep.int("", length(x))
x
}
@@ -468,12 +468,12 @@ exprToFunction <- function(expr, env=parent.frame(), quoted=FALSE) {
#' Installs an expression in the given environment as a function, and registers
#' debug hooks so that breakpoints may be set in the function.
#'
#' This function can replace \code{exprToFunction} as follows: we may use
#' \code{func <- exprToFunction(expr)} if we do not want the debug hooks, or
#' \code{installExprFunction(expr, "func")} if we do. Both approaches create a
#' function named \code{func} in the current environment.
#' This function can replace `exprToFunction` as follows: we may use
#' `func <- exprToFunction(expr)` if we do not want the debug hooks, or
#' `installExprFunction(expr, "func")` if we do. Both approaches create a
#' function named `func` in the current environment.
#'
#' @seealso Wraps \code{\link{exprToFunction}}; see that method's documentation
#' @seealso Wraps [exprToFunction()]; see that method's documentation
#' for more documentation and examples.
#'
#' @param expr A quoted or unquoted expression
@@ -485,7 +485,7 @@ exprToFunction <- function(expr, env=parent.frame(), quoted=FALSE) {
#' @param label A label for the object to be shown in the debugger. Defaults to
#' the name of the calling function.
#' @param wrappedWithLabel,..stacktraceon Advanced use only. For stack manipulation purposes; see
#' \code{\link{stacktrace}}.
#' [stacktrace()].
#' @export
installExprFunction <- function(expr, name, eval.env = parent.frame(2),
quoted = FALSE,
@@ -516,6 +516,7 @@ installExprFunction <- function(expr, name, eval.env = parent.frame(2),
#'
#' Returns a named list of key-value pairs.
#'
#' @noMd
#' @param str The query string. It can have a leading \code{"?"} or not.
#' @param nested Whether to parse the query string of as a nested list when it
#' contains pairs of square brackets \code{[]}. For example, the query
@@ -613,7 +614,7 @@ shinyCallingHandlers <- function(expr) {
#' Print message for deprecated functions in Shiny
#'
#' To disable these messages, use \code{options(shiny.deprecation.messages=FALSE)}.
#' To disable these messages, use `options(shiny.deprecation.messages=FALSE)`.
#'
#' @param new Name of replacement function.
#' @param msg Message to print. If used, this will override the default message.
@@ -949,28 +950,28 @@ columnToRowData <- function(data) {
#'
#' This should be used when you want to let the user see an error
#' message even if the default is to sanitize all errors. If you have an
#' error \code{e} and call \code{stop(safeError(e))}, then Shiny will
#' ignore the value of \code{getOption("shiny.sanitize.errors")} and always
#' error `e` and call `stop(safeError(e))`, then Shiny will
#' ignore the value of `getOption("shiny.sanitize.errors")` and always
#' display the error in the app itself.
#'
#' @param error Either an "error" object or a "character" object (string).
#' In the latter case, the string will become the message of the error
#' returned by \code{safeError}.
#' returned by `safeError`.
#'
#' @return An "error" object
#'
#' @details An error generated by \code{safeError} has priority over all
#' @details An error generated by `safeError` has priority over all
#' other Shiny errors. This can be dangerous. For example, if you have set
#' \code{options(shiny.sanitize.errors = TRUE)}, then by default all error
#' `options(shiny.sanitize.errors = TRUE)`, then by default all error
#' messages are omitted in the app, and replaced by a generic error message.
#' However, this does not apply to \code{safeError}: whatever you pass
#' through \code{error} will be displayed to the user. So, this should only
#' However, this does not apply to `safeError`: whatever you pass
#' through `error` will be displayed to the user. So, this should only
#' be used when you are sure that your error message does not contain any
#' sensitive information. In those situations, \code{safeError} can make
#' sensitive information. In those situations, `safeError` can make
#' your users' lives much easier by giving them a hint as to where the
#' error occurred.
#'
#' @seealso \code{\link{shiny-options}}
#' @seealso [shiny-options()]
#'
#' @examples
#' ## Only run examples in interactive R sessions
@@ -1050,7 +1051,7 @@ safeError <- function(error) {
# #' @examples
# #' ## Note: the breaking of the reactive chain that happens in the app
# #' ## below (when input$txt = 'bad' and input$allowBad = 'FALSE') is
# #' ## easily visualized with `showReactLog()`
# #' ## easily visualized with `reactlogShow()`
# #'
# #' ## Only run examples in interactive R sessions
# #' if (interactive()) {
@@ -1084,58 +1085,58 @@ reactiveStop <- function(message = "", class = NULL) {
#' Validate input values and other conditions
#'
#' For an output rendering function (e.g. \code{\link{renderPlot}()}), you may
#' For an output rendering function (e.g. [renderPlot()]), you may
#' need to check that certain input values are available and valid before you
#' can render the output. \code{validate} gives you a convenient mechanism for
#' can render the output. `validate` gives you a convenient mechanism for
#' doing so.
#'
#' The \code{validate} function takes any number of (unnamed) arguments, each of
#' The `validate` function takes any number of (unnamed) arguments, each of
#' which represents a condition to test. If any of the conditions represent
#' failure, then a special type of error is signaled which stops execution. If
#' this error is not handled by application-specific code, it is displayed to
#' the user by Shiny.
#'
#' An easy way to provide arguments to \code{validate} is to use the \code{need}
#' An easy way to provide arguments to `validate` is to use the `need`
#' function, which takes an expression and a string; if the expression is
#' considered a failure, then the string will be used as the error message. The
#' \code{need} function considers its expression to be a failure if it is any of
#' `need` function considers its expression to be a failure if it is any of
#' the following:
#'
#' \itemize{
#' \item{\code{FALSE}}
#' \item{\code{NULL}}
#' \item{\code{""}}
#' \item{`FALSE`}
#' \item{`NULL`}
#' \item{`""`}
#' \item{An empty atomic vector}
#' \item{An atomic vector that contains only missing values}
#' \item{A logical vector that contains all \code{FALSE} or missing values}
#' \item{An object of class \code{"try-error"}}
#' \item{A value that represents an unclicked \code{\link{actionButton}}}
#' \item{A logical vector that contains all `FALSE` or missing values}
#' \item{An object of class `"try-error"`}
#' \item{A value that represents an unclicked [actionButton()]}
#' }
#'
#' If any of these values happen to be valid, you can explicitly turn them to
#' logical values. For example, if you allow \code{NA} but not \code{NULL}, you
#' can use the condition \code{!is.null(input$foo)}, because \code{!is.null(NA)
#' == TRUE}.
#' logical values. For example, if you allow `NA` but not `NULL`, you
#' can use the condition `!is.null(input$foo)`, because `!is.null(NA)
#' == TRUE`.
#'
#' If you need validation logic that differs significantly from \code{need}, you
#' If you need validation logic that differs significantly from `need`, you
#' can create other validation test functions. A passing test should return
#' \code{NULL}. A failing test should return an error message as a
#' `NULL`. A failing test should return an error message as a
#' single-element character vector, or if the failure should happen silently,
#' \code{FALSE}.
#' `FALSE`.
#'
#' Because validation failure is signaled as an error, you can use
#' \code{validate} in reactive expressions, and validation failures will
#' `validate` in reactive expressions, and validation failures will
#' automatically propagate to outputs that use the reactive expression. In
#' other words, if reactive expression \code{a} needs \code{input$x}, and two
#' outputs use \code{a} (and thus depend indirectly on \code{input$x}), it's
#' not necessary for the outputs to validate \code{input$x} explicitly, as long
#' as \code{a} does validate it.
#' other words, if reactive expression `a` needs `input$x`, and two
#' outputs use `a` (and thus depend indirectly on `input$x`), it's
#' not necessary for the outputs to validate `input$x` explicitly, as long
#' as `a` does validate it.
#'
#' @param ... A list of tests. Each test should equal \code{NULL} for success,
#' \code{FALSE} for silent failure, or a string for failure with an error
#' @param ... A list of tests. Each test should equal `NULL` for success,
#' `FALSE` for silent failure, or a string for failure with an error
#' message.
#' @param errorClass A CSS class to apply. The actual CSS string will have
#' \code{shiny-output-error-} prepended to this value.
#' `shiny-output-error-` prepended to this value.
#' @export
#' @examples
#' ## Only run examples in interactive R sessions
@@ -1187,10 +1188,10 @@ validate <- function(..., errorClass = character(0)) {
#' @param expr An expression to test. The condition will pass if the expression
#' meets the conditions spelled out in Details.
#' @param message A message to convey to the user if the validation condition is
#' not met. If no message is provided, one will be created using \code{label}.
#' To fail with no message, use \code{FALSE} for the message.
#' not met. If no message is provided, one will be created using `label`.
#' To fail with no message, use `FALSE` for the message.
#' @param label A human-readable name for the field that may be missing. This
#' parameter is not needed if \code{message} is provided, but must be provided
#' parameter is not needed if `message` is provided, but must be provided
#' otherwise.
#' @export
#' @rdname validate
@@ -1211,7 +1212,7 @@ need <- function(expr, message = paste(label, "must be provided"), label) {
#' operation is stopped by raising a "silent" exception (not logged by Shiny,
#' nor displayed in the Shiny app's UI).
#'
#' The \code{req} function was designed to be used in one of two ways. The first
#' The `req` function was designed to be used in one of two ways. The first
#' is to call it like a statement (ignoring its return value) before attempting
#' operations using the required values:
#'
@@ -1221,9 +1222,9 @@ need <- function(expr, message = paste(label, "must be provided"), label) {
#' # Code that uses input$a, input$b, and/or rv$state...
#' })}
#'
#' In this example, if \code{r()} is called and any of \code{input$a},
#' \code{input$b}, and \code{rv$state} are \code{NULL}, \code{FALSE}, \code{""},
#' etc., then the \code{req} call will trigger an error that propagates all the
#' In this example, if `r()` is called and any of `input$a`,
#' `input$b`, and `rv$state` are `NULL`, `FALSE`, `""`,
#' etc., then the `req` call will trigger an error that propagates all the
#' way up to whatever render block or observer is executing.
#'
#' The second is to use it to wrap an expression that must be truthy:
@@ -1236,70 +1237,70 @@ need <- function(expr, message = paste(label, "must be provided"), label) {
#' }
#' })}
#'
#' In this example, \code{req(input$plotType)} first checks that
#' \code{input$plotType} is truthy, and if so, returns it. This is a convenient
#' In this example, `req(input$plotType)` first checks that
#' `input$plotType` is truthy, and if so, returns it. This is a convenient
#' way to check for a value "inline" with its first use.
#'
#' \strong{Truthy and falsy values}
#' **Truthy and falsy values**
#'
#' The terms "truthy" and "falsy" generally indicate whether a value, when
#' coerced to a \code{\link[base]{logical}}, is \code{TRUE} or \code{FALSE}. We use
#' coerced to a [base::logical()], is `TRUE` or `FALSE`. We use
#' the term a little loosely here; our usage tries to match the intuitive
#' notions of "Is this value missing or available?", or "Has the user provided
#' an answer?", or in the case of action buttons, "Has the button been
#' clicked?".
#'
#' For example, a \code{textInput} that has not been filled out by the user has
#' a value of \code{""}, so that is considered a falsy value.
#' For example, a `textInput` that has not been filled out by the user has
#' a value of `""`, so that is considered a falsy value.
#'
#' To be precise, \code{req} considers a value truthy \emph{unless} it is one
#' To be precise, `req` considers a value truthy *unless* it is one
#' of:
#'
#' \itemize{
#' \item{\code{FALSE}}
#' \item{\code{NULL}}
#' \item{\code{""}}
#' \item{`FALSE`}
#' \item{`NULL`}
#' \item{`""`}
#' \item{An empty atomic vector}
#' \item{An atomic vector that contains only missing values}
#' \item{A logical vector that contains all \code{FALSE} or missing values}
#' \item{An object of class \code{"try-error"}}
#' \item{A value that represents an unclicked \code{\link{actionButton}}}
#' \item{A logical vector that contains all `FALSE` or missing values}
#' \item{An object of class `"try-error"`}
#' \item{A value that represents an unclicked [actionButton()]}
#' }
#'
#' Note in particular that the value \code{0} is considered truthy, even though
#' \code{as.logical(0)} is \code{FALSE}.
#' Note in particular that the value `0` is considered truthy, even though
#' `as.logical(0)` is `FALSE`.
#'
#' If the built-in rules for truthiness do not match your requirements, you can
#' always work around them. Since \code{FALSE} is falsy, you can simply provide
#' the results of your own checks to \code{req}:
#' always work around them. Since `FALSE` is falsy, you can simply provide
#' the results of your own checks to `req`:
#'
#' \code{req(input$a != 0)}
#' `req(input$a != 0)`
#'
#' \strong{Using \code{req(FALSE)}}
#' **Using `req(FALSE)`**
#'
#' You can use \code{req(FALSE)} (i.e. no condition) if you've already performed
#' You can use `req(FALSE)` (i.e. no condition) if you've already performed
#' all the checks you needed to by that point and just want to stop the reactive
#' chain now. There is no advantange to this, except perhaps ease of readibility
#' if you have a complicated condition to check for (or perhaps if you'd like to
#' divide your condition into nested \code{if} statements).
#' divide your condition into nested `if` statements).
#'
#' \strong{Using \code{cancelOutput = TRUE}}
#' **Using `cancelOutput = TRUE`**
#'
#' When \code{req(..., cancelOutput = TRUE)} is used, the "silent" exception is
#' When `req(..., cancelOutput = TRUE)` is used, the "silent" exception is
#' also raised, but it is treated slightly differently if one or more outputs are
#' currently being evaluated. In those cases, the reactive chain does not proceed
#' or update, but the output(s) are left is whatever state they happen to be in
#' (whatever was their last valid state).
#'
#' Note that this is always going to be the case if
#' this is used inside an output context (e.g. \code{output$txt <- ...}). It may
#' this is used inside an output context (e.g. `output$txt <- ...`). It may
#' or may not be the case if it is used inside a non-output context (e.g.
#' \code{\link{reactive}}, \code{\link{observe}} or \code{\link{observeEvent}})
#' -- depending on whether or not there is an \code{output$...} that is triggered
#' [reactive()], [observe()] or [observeEvent()])
#' --- depending on whether or not there is an `output$...` that is triggered
#' as a result of those calls. See the examples below for concrete scenarios.
#'
#' @param ... Values to check for truthiness.
#' @param cancelOutput If \code{TRUE} and an output is being evaluated, stop
#' @param cancelOutput If `TRUE` and an output is being evaluated, stop
#' processing as usual but instead of clearing the output, leave it in
#' whatever state it happens to be in.
#' @param x An expression whose truthiness value we want to determine
@@ -1438,7 +1439,7 @@ stopWithCondition <- function(class, message) {
#' This function returns the information about the current Shiny Server, such as
#' its version, and whether it is the open source edition or professional
#' edition. If the app is not served through the Shiny Server, this function
#' just returns \code{list(shinyServer = FALSE)}.
#' just returns `list(shinyServer = FALSE)`.
#'
#' This function will only return meaningful data when using Shiny Server
#' version 1.2.2 or later.
@@ -1560,6 +1561,25 @@ URLencode <- function(value, reserved = FALSE) {
if (reserved) encodeURIComponent(value) else encodeURI(value)
}
# Make user-supplied dates are either NULL or can be coerced
# to a yyyy-mm-dd formatted string. If a date is specified, this
# function returns a string for consistency across locales.
# Also, `as.Date()` is used to coerce strings to date objects
# so that strings like "2016-08-9" are expanded to "2016-08-09"
dateYMD <- function(date = NULL, argName = "value") {
if (!length(date)) return(NULL)
if (length(date) > 1) warning("Expected `", argName, "` to be of length 1.")
tryCatch(date <- format(as.Date(date), "%Y-%m-%d"),
error = function(e) {
warning(
"Couldn't coerce the `", argName,
"` argument to a date string with format yyyy-mm-dd",
call. = FALSE
)
}
)
date
}
# This function takes a name and function, and it wraps that function in a new
# function which calls the original function using the specified name. This can
@@ -1730,6 +1750,7 @@ createVarPromiseDomain <- function(env, name, value) {
getSliderType <- function(min, max, value) {
vals <- dropNulls(list(value, min, max))
if (length(vals) == 0) return("")
type <- unique(lapply(vals, function(x) {
if (inherits(x, "Date")) "date"
else if (inherits(x, "POSIXt")) "datetime"
@@ -1740,3 +1761,47 @@ getSliderType <- function(min, max, value) {
}
type[[1]]
}
# Reads the `shiny.sharedSecret` global option, and returns a function that can
# be used to test header values for a match.
loadSharedSecret <- function() {
normalizeToRaw <- function(value, label = "value") {
if (is.null(value)) {
raw()
} else if (is.character(value)) {
charToRaw(paste(value, collapse = "\n"))
} else if (is.raw(value)) {
value
} else {
stop("Wrong type for ", label, "; character or raw expected")
}
}
sharedSecret <- normalizeToRaw(getOption("shiny.sharedSecret"))
if (is.null(sharedSecret)) {
function(x) TRUE
} else {
# We compare the digest of the two values so that their lengths are equalized
function(x) {
x <- normalizeToRaw(x)
# Constant time comparison to avoid timing attacks
constantTimeEquals(sharedSecret, x)
}
}
}
# Compares two raw vectors of equal length for equality, in constant time
constantTimeEquals <- function(raw1, raw2) {
stopifnot(is.raw(raw1))
stopifnot(is.raw(raw2))
if (length(raw1) != length(raw2)) {
return(FALSE)
}
sum(as.integer(xor(raw1, raw2))) == 0
}
cat_line <- function(...) {
cat(paste(..., "\n", collapse = ""))
}

View File

@@ -65,7 +65,4 @@ We welcome contributions to the **shiny** package. Please see our [CONTRIBUTING.
## License
The shiny package is licensed under the GPLv3. See these files in the inst directory for additional details:
- COPYING - shiny package license (GPLv3)
- NOTICE - Copyright notices for additional included software
The shiny package as a whole is licensed under the GPLv3. See the [LICENSE](LICENSE) file for more details.

View File

@@ -9,15 +9,11 @@ sd_section("UI Layout",
"fillRow",
"fixedPage",
"fluidPage",
"headerPanel",
"helpText",
"icon",
"mainPanel",
"navbarPage",
"navlistPanel",
"pageWithSidebar",
"sidebarLayout",
"sidebarPanel",
"tabPanel",
"tabsetPanel",
"titlePanel",
@@ -113,11 +109,6 @@ sd_section("Rendering functions",
"renderTable",
"renderUI",
"downloadHandler",
"reactivePlot",
"reactivePrint",
"reactiveTable",
"reactiveText",
"reactiveUI",
"createRenderFunction"
)
)
@@ -134,7 +125,7 @@ sd_section("Reactive programming",
"isolate",
"invalidateLater",
"debounce",
"showReactLog",
"reactlog",
"makeReactiveBinding",
"reactiveFileReader",
"reactivePoll",
@@ -160,7 +151,8 @@ sd_section("Running",
"runUrl",
"stopApp",
"viewer",
"isRunning"
"isRunning",
"loadSupport"
)
)
sd_section("Bookmarking state",
@@ -177,7 +169,7 @@ sd_section("Extending Shiny",
"Functions that are intended to be called by third-party packages that extend Shiny.",
c(
"createWebDependency",
"addResourcePath",
"resourcePaths",
"registerInputHandler",
"removeInputHandler",
"markRenderFunction"
@@ -209,11 +201,10 @@ sd_section("Utility functions",
"repeatable",
"shinyDeprecated",
"serverInfo",
"shiny-options",
"onStop",
"diskCache",
"memoryCache",
"key_missing"
"reexports"
)
)
sd_section("Plot interaction",

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/*!
* Bootstrap v3.3.7 (http://getbootstrap.com)
* Copyright 2011-2016 Twitter, Inc.
* Bootstrap v3.4.1 (https://getbootstrap.com/)
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
.btn-default,
@@ -9,9 +9,9 @@
.btn-info,
.btn-warning,
.btn-danger {
text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
}
.btn-default:active,
.btn-primary:active,
@@ -25,8 +25,8 @@
.btn-info.active,
.btn-warning.active,
.btn-danger.active {
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
.btn-default.disabled,
.btn-primary.disabled,
@@ -47,7 +47,7 @@ fieldset[disabled] .btn-info,
fieldset[disabled] .btn-warning,
fieldset[disabled] .btn-danger {
-webkit-box-shadow: none;
box-shadow: none;
box-shadow: none;
}
.btn-default .badge,
.btn-primary .badge,
@@ -62,15 +62,15 @@ fieldset[disabled] .btn-danger {
background-image: none;
}
.btn-default {
text-shadow: 0 1px 0 #fff;
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #dbdbdb;
text-shadow: 0 1px 0 #fff;
border-color: #ccc;
}
.btn-default:hover,
@@ -106,9 +106,9 @@ fieldset[disabled] .btn-default.active {
}
.btn-primary {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
@@ -147,9 +147,9 @@ fieldset[disabled] .btn-primary.active {
}
.btn-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
@@ -188,9 +188,9 @@ fieldset[disabled] .btn-success.active {
}
.btn-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
@@ -229,9 +229,9 @@ fieldset[disabled] .btn-info.active {
}
.btn-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
@@ -270,9 +270,9 @@ fieldset[disabled] .btn-warning.active {
}
.btn-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
@@ -311,81 +311,81 @@ fieldset[disabled] .btn-danger.active {
}
.thumbnail,
.img-thumbnail {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
}
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
background-color: #e8e8e8;
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
background-repeat: repeat-x;
background-color: #e8e8e8;
}
.dropdown-menu > .active > a,
.dropdown-menu > .active > a:hover,
.dropdown-menu > .active > a:focus {
background-color: #2e6da4;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
background-color: #2e6da4;
}
.navbar-default {
background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8));
background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#f8f8f8));
background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
}
.navbar-default .navbar-nav > .open > a,
.navbar-default .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
background-repeat: repeat-x;
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
}
.navbar-brand,
.navbar-nav > li > a {
text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);
}
.navbar-inverse {
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
border-radius: 4px;
}
.navbar-inverse .navbar-nav > .open > a,
.navbar-inverse .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
background-repeat: repeat-x;
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
}
.navbar-inverse .navbar-brand,
.navbar-inverse .navbar-nav > li > a {
text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.navbar-static-top,
.navbar-fixed-top,
@@ -398,120 +398,120 @@ fieldset[disabled] .btn-danger.active {
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
color: #fff;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
}
.alert {
text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
}
.alert-success {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
background-repeat: repeat-x;
border-color: #b2dba1;
}
.alert-info {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
background-repeat: repeat-x;
border-color: #9acfea;
}
.alert-warning {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
background-repeat: repeat-x;
border-color: #f5e79e;
}
.alert-danger {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
background-repeat: repeat-x;
border-color: #dca7a7;
}
.progress {
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-striped {
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
}
.list-group {
border-radius: 4px;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
}
.list-group-item.active,
.list-group-item.active:hover,
.list-group-item.active:focus {
text-shadow: 0 -1px 0 #286090;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
background-repeat: repeat-x;
border-color: #2b669a;
@@ -522,66 +522,66 @@ fieldset[disabled] .btn-danger.active {
text-shadow: none;
}
.panel {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.panel-default > .panel-heading {
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
background-repeat: repeat-x;
}
.panel-primary > .panel-heading {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
.panel-success > .panel-heading {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
background-repeat: repeat-x;
}
.panel-info > .panel-heading {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
background-repeat: repeat-x;
}
.panel-warning > .panel-heading {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
background-repeat: repeat-x;
}
.panel-danger > .panel-heading {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
background-repeat: repeat-x;
}
.well {
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
background-repeat: repeat-x;
border-color: #dcdcdc;
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
}
/*# sourceMappingURL=bootstrap-theme.css.map */
/*# sourceMappingURL=bootstrap-theme.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
/*!
* Bootstrap v3.3.7 (http://getbootstrap.com)
* Copyright 2011-2016 Twitter, Inc.
* Bootstrap v3.4.1 (https://getbootstrap.com/)
* Copyright 2011-2019 Twitter, Inc.
* Licensed under the MIT license
*/
@@ -17,10 +17,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: transition.js v3.3.7
* http://getbootstrap.com/javascript/#transitions
* Bootstrap: transition.js v3.4.1
* https://getbootstrap.com/docs/3.4/javascript/#transitions
* ========================================================================
* Copyright 2011-2016 Twitter, Inc.
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -28,7 +28,7 @@ if (typeof jQuery === 'undefined') {
+function ($) {
'use strict';
// CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
// CSS TRANSITION SUPPORT (Shoutout: https://modernizr.com/)
// ============================================================
function transitionEnd() {
@@ -50,7 +50,7 @@ if (typeof jQuery === 'undefined') {
return false // explicit for ie8 ( ._.)
}
// http://blog.alexmaccaw.com/css-transitions
// https://blog.alexmaccaw.com/css-transitions
$.fn.emulateTransitionEnd = function (duration) {
var called = false
var $el = this
@@ -77,10 +77,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: alert.js v3.3.7
* http://getbootstrap.com/javascript/#alerts
* Bootstrap: alert.js v3.4.1
* https://getbootstrap.com/docs/3.4/javascript/#alerts
* ========================================================================
* Copyright 2011-2016 Twitter, Inc.
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -96,7 +96,7 @@ if (typeof jQuery === 'undefined') {
$(el).on('click', dismiss, this.close)
}
Alert.VERSION = '3.3.7'
Alert.VERSION = '3.4.1'
Alert.TRANSITION_DURATION = 150
@@ -109,7 +109,8 @@ if (typeof jQuery === 'undefined') {
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
}
var $parent = $(selector === '#' ? [] : selector)
selector = selector === '#' ? [] : selector
var $parent = $(document).find(selector)
if (e) e.preventDefault()
@@ -172,10 +173,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: button.js v3.3.7
* http://getbootstrap.com/javascript/#buttons
* Bootstrap: button.js v3.4.1
* https://getbootstrap.com/docs/3.4/javascript/#buttons
* ========================================================================
* Copyright 2011-2016 Twitter, Inc.
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -192,7 +193,7 @@ if (typeof jQuery === 'undefined') {
this.isLoading = false
}
Button.VERSION = '3.3.7'
Button.VERSION = '3.4.1'
Button.DEFAULTS = {
loadingText: 'loading...'
@@ -298,10 +299,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: carousel.js v3.3.7
* http://getbootstrap.com/javascript/#carousel
* Bootstrap: carousel.js v3.4.1
* https://getbootstrap.com/docs/3.4/javascript/#carousel
* ========================================================================
* Copyright 2011-2016 Twitter, Inc.
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -329,7 +330,7 @@ if (typeof jQuery === 'undefined') {
.on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
}
Carousel.VERSION = '3.3.7'
Carousel.VERSION = '3.4.1'
Carousel.TRANSITION_DURATION = 600
@@ -443,7 +444,9 @@ if (typeof jQuery === 'undefined') {
var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid"
if ($.support.transition && this.$element.hasClass('slide')) {
$next.addClass(type)
$next[0].offsetWidth // force reflow
if (typeof $next === 'object' && $next.length) {
$next[0].offsetWidth // force reflow
}
$active.addClass(direction)
$next.addClass(direction)
$active
@@ -505,10 +508,17 @@ if (typeof jQuery === 'undefined') {
// =================
var clickHandler = function (e) {
var href
var $this = $(this)
var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
var href = $this.attr('href')
if (href) {
href = href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
}
var target = $this.attr('data-target') || href
var $target = $(document).find(target)
if (!$target.hasClass('carousel')) return
var options = $.extend({}, $target.data(), $this.data())
var slideIndex = $this.attr('data-slide-to')
if (slideIndex) options.interval = false
@@ -536,10 +546,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: collapse.js v3.3.7
* http://getbootstrap.com/javascript/#collapse
* Bootstrap: collapse.js v3.4.1
* https://getbootstrap.com/docs/3.4/javascript/#collapse
* ========================================================================
* Copyright 2011-2016 Twitter, Inc.
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -567,7 +577,7 @@ if (typeof jQuery === 'undefined') {
if (this.options.toggle) this.toggle()
}
Collapse.VERSION = '3.3.7'
Collapse.VERSION = '3.4.1'
Collapse.TRANSITION_DURATION = 350
@@ -674,7 +684,7 @@ if (typeof jQuery === 'undefined') {
}
Collapse.prototype.getParent = function () {
return $(this.options.parent)
return $(document).find(this.options.parent)
.find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
.each($.proxy(function (i, element) {
var $element = $(element)
@@ -697,7 +707,7 @@ if (typeof jQuery === 'undefined') {
var target = $trigger.attr('data-target')
|| (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
return $(target)
return $(document).find(target)
}
@@ -749,10 +759,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: dropdown.js v3.3.7
* http://getbootstrap.com/javascript/#dropdowns
* Bootstrap: dropdown.js v3.4.1
* https://getbootstrap.com/docs/3.4/javascript/#dropdowns
* ========================================================================
* Copyright 2011-2016 Twitter, Inc.
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -769,7 +779,7 @@ if (typeof jQuery === 'undefined') {
$(element).on('click.bs.dropdown', this.toggle)
}
Dropdown.VERSION = '3.3.7'
Dropdown.VERSION = '3.4.1'
function getParent($this) {
var selector = $this.attr('data-target')
@@ -779,7 +789,7 @@ if (typeof jQuery === 'undefined') {
selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
}
var $parent = selector && $(selector)
var $parent = selector !== '#' ? $(document).find(selector) : null
return $parent && $parent.length ? $parent : $this.parent()
}
@@ -915,10 +925,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: modal.js v3.3.7
* http://getbootstrap.com/javascript/#modals
* Bootstrap: modal.js v3.4.1
* https://getbootstrap.com/docs/3.4/javascript/#modals
* ========================================================================
* Copyright 2011-2016 Twitter, Inc.
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -930,15 +940,16 @@ if (typeof jQuery === 'undefined') {
// ======================
var Modal = function (element, options) {
this.options = options
this.$body = $(document.body)
this.$element = $(element)
this.$dialog = this.$element.find('.modal-dialog')
this.$backdrop = null
this.isShown = null
this.originalBodyPad = null
this.scrollbarWidth = 0
this.options = options
this.$body = $(document.body)
this.$element = $(element)
this.$dialog = this.$element.find('.modal-dialog')
this.$backdrop = null
this.isShown = null
this.originalBodyPad = null
this.scrollbarWidth = 0
this.ignoreBackdropClick = false
this.fixedContent = '.navbar-fixed-top, .navbar-fixed-bottom'
if (this.options.remote) {
this.$element
@@ -949,7 +960,7 @@ if (typeof jQuery === 'undefined') {
}
}
Modal.VERSION = '3.3.7'
Modal.VERSION = '3.4.1'
Modal.TRANSITION_DURATION = 300
Modal.BACKDROP_TRANSITION_DURATION = 150
@@ -966,7 +977,7 @@ if (typeof jQuery === 'undefined') {
Modal.prototype.show = function (_relatedTarget) {
var that = this
var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
this.$element.trigger(e)
@@ -1057,8 +1068,8 @@ if (typeof jQuery === 'undefined') {
.off('focusin.bs.modal') // guard against infinite focus loop
.on('focusin.bs.modal', $.proxy(function (e) {
if (document !== e.target &&
this.$element[0] !== e.target &&
!this.$element.has(e.target).length) {
this.$element[0] !== e.target &&
!this.$element.has(e.target).length) {
this.$element.trigger('focus')
}
}, this))
@@ -1160,7 +1171,7 @@ if (typeof jQuery === 'undefined') {
var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
this.$element.css({
paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
})
}
@@ -1185,11 +1196,26 @@ if (typeof jQuery === 'undefined') {
Modal.prototype.setScrollbar = function () {
var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
this.originalBodyPad = document.body.style.paddingRight || ''
if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
var scrollbarWidth = this.scrollbarWidth
if (this.bodyIsOverflowing) {
this.$body.css('padding-right', bodyPad + scrollbarWidth)
$(this.fixedContent).each(function (index, element) {
var actualPadding = element.style.paddingRight
var calculatedPadding = $(element).css('padding-right')
$(element)
.data('padding-right', actualPadding)
.css('padding-right', parseFloat(calculatedPadding) + scrollbarWidth + 'px')
})
}
}
Modal.prototype.resetScrollbar = function () {
this.$body.css('padding-right', this.originalBodyPad)
$(this.fixedContent).each(function (index, element) {
var padding = $(element).data('padding-right')
$(element).removeData('padding-right')
element.style.paddingRight = padding ? padding : ''
})
}
Modal.prototype.measureScrollbar = function () { // thx walsh
@@ -1207,8 +1233,8 @@ if (typeof jQuery === 'undefined') {
function Plugin(option, _relatedTarget) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.modal')
var $this = $(this)
var data = $this.data('bs.modal')
var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
@@ -1219,7 +1245,7 @@ if (typeof jQuery === 'undefined') {
var old = $.fn.modal
$.fn.modal = Plugin
$.fn.modal = Plugin
$.fn.modal.Constructor = Modal
@@ -1236,10 +1262,13 @@ if (typeof jQuery === 'undefined') {
// ==============
$(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
var $this = $(this)
var href = $this.attr('href')
var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
var $this = $(this)
var href = $this.attr('href')
var target = $this.attr('data-target') ||
(href && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
var $target = $(document).find(target)
var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
if ($this.is('a')) e.preventDefault()
@@ -1255,18 +1284,148 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: tooltip.js v3.3.7
* http://getbootstrap.com/javascript/#tooltip
* Bootstrap: tooltip.js v3.4.1
* https://getbootstrap.com/docs/3.4/javascript/#tooltip
* Inspired by the original jQuery.tipsy by Jason Frame
* ========================================================================
* Copyright 2011-2016 Twitter, Inc.
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
var DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']
var uriAttrs = [
'background',
'cite',
'href',
'itemtype',
'longdesc',
'poster',
'src',
'xlink:href'
]
var ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i
var DefaultWhitelist = {
// Global attributes allowed on any supplied element below.
'*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
a: ['target', 'href', 'title', 'rel'],
area: [],
b: [],
br: [],
col: [],
code: [],
div: [],
em: [],
hr: [],
h1: [],
h2: [],
h3: [],
h4: [],
h5: [],
h6: [],
i: [],
img: ['src', 'alt', 'title', 'width', 'height'],
li: [],
ol: [],
p: [],
pre: [],
s: [],
small: [],
span: [],
sub: [],
sup: [],
strong: [],
u: [],
ul: []
}
/**
* A pattern that recognizes a commonly useful subset of URLs that are safe.
*
* Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
*/
var SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi
/**
* A pattern that matches safe data URLs. Only matches image, video and audio types.
*
* Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
*/
var DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i
function allowedAttribute(attr, allowedAttributeList) {
var attrName = attr.nodeName.toLowerCase()
if ($.inArray(attrName, allowedAttributeList) !== -1) {
if ($.inArray(attrName, uriAttrs) !== -1) {
return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN))
}
return true
}
var regExp = $(allowedAttributeList).filter(function (index, value) {
return value instanceof RegExp
})
// Check if a regular expression validates the attribute.
for (var i = 0, l = regExp.length; i < l; i++) {
if (attrName.match(regExp[i])) {
return true
}
}
return false
}
function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) {
if (unsafeHtml.length === 0) {
return unsafeHtml
}
if (sanitizeFn && typeof sanitizeFn === 'function') {
return sanitizeFn(unsafeHtml)
}
// IE 8 and below don't support createHTMLDocument
if (!document.implementation || !document.implementation.createHTMLDocument) {
return unsafeHtml
}
var createdDocument = document.implementation.createHTMLDocument('sanitization')
createdDocument.body.innerHTML = unsafeHtml
var whitelistKeys = $.map(whiteList, function (el, i) { return i })
var elements = $(createdDocument.body).find('*')
for (var i = 0, len = elements.length; i < len; i++) {
var el = elements[i]
var elName = el.nodeName.toLowerCase()
if ($.inArray(elName, whitelistKeys) === -1) {
el.parentNode.removeChild(el)
continue
}
var attributeList = $.map(el.attributes, function (el) { return el })
var whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || [])
for (var j = 0, len2 = attributeList.length; j < len2; j++) {
if (!allowedAttribute(attributeList[j], whitelistedAttributes)) {
el.removeAttribute(attributeList[j].nodeName)
}
}
}
return createdDocument.body.innerHTML
}
// TOOLTIP PUBLIC CLASS DEFINITION
// ===============================
@@ -1282,7 +1441,7 @@ if (typeof jQuery === 'undefined') {
this.init('tooltip', element, options)
}
Tooltip.VERSION = '3.3.7'
Tooltip.VERSION = '3.4.1'
Tooltip.TRANSITION_DURATION = 150
@@ -1299,7 +1458,10 @@ if (typeof jQuery === 'undefined') {
viewport: {
selector: 'body',
padding: 0
}
},
sanitize : true,
sanitizeFn : null,
whiteList : DefaultWhitelist
}
Tooltip.prototype.init = function (type, element, options) {
@@ -1307,7 +1469,7 @@ if (typeof jQuery === 'undefined') {
this.type = type
this.$element = $(element)
this.options = this.getOptions(options)
this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport))
this.$viewport = this.options.viewport && $(document).find($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport))
this.inState = { click: false, hover: false, focus: false }
if (this.$element[0] instanceof document.constructor && !this.options.selector) {
@@ -1340,7 +1502,15 @@ if (typeof jQuery === 'undefined') {
}
Tooltip.prototype.getOptions = function (options) {
options = $.extend({}, this.getDefaults(), this.$element.data(), options)
var dataAttributes = this.$element.data()
for (var dataAttr in dataAttributes) {
if (dataAttributes.hasOwnProperty(dataAttr) && $.inArray(dataAttr, DISALLOWED_ATTRIBUTES) !== -1) {
delete dataAttributes[dataAttr]
}
}
options = $.extend({}, this.getDefaults(), dataAttributes, options)
if (options.delay && typeof options.delay == 'number') {
options.delay = {
@@ -1349,6 +1519,10 @@ if (typeof jQuery === 'undefined') {
}
}
if (options.sanitize) {
options.template = sanitizeHtml(options.template, options.whiteList, options.sanitizeFn)
}
return options
}
@@ -1460,7 +1634,7 @@ if (typeof jQuery === 'undefined') {
.addClass(placement)
.data('bs.' + this.type, this)
this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
this.options.container ? $tip.appendTo($(document).find(this.options.container)) : $tip.insertAfter(this.$element)
this.$element.trigger('inserted.bs.' + this.type)
var pos = this.getPosition()
@@ -1562,7 +1736,16 @@ if (typeof jQuery === 'undefined') {
var $tip = this.tip()
var title = this.getTitle()
$tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
if (this.options.html) {
if (this.options.sanitize) {
title = sanitizeHtml(title, this.options.whiteList, this.options.sanitizeFn)
}
$tip.find('.tooltip-inner').html(title)
} else {
$tip.find('.tooltip-inner').text(title)
}
$tip.removeClass('fade in top bottom left right')
}
@@ -1743,6 +1926,9 @@ if (typeof jQuery === 'undefined') {
})
}
Tooltip.prototype.sanitizeHtml = function (unsafeHtml) {
return sanitizeHtml(unsafeHtml, this.options.whiteList, this.options.sanitizeFn)
}
// TOOLTIP PLUGIN DEFINITION
// =========================
@@ -1776,10 +1962,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: popover.js v3.3.7
* http://getbootstrap.com/javascript/#popovers
* Bootstrap: popover.js v3.4.1
* https://getbootstrap.com/docs/3.4/javascript/#popovers
* ========================================================================
* Copyright 2011-2016 Twitter, Inc.
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -1796,7 +1982,7 @@ if (typeof jQuery === 'undefined') {
if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
Popover.VERSION = '3.3.7'
Popover.VERSION = '3.4.1'
Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
placement: 'right',
@@ -1822,10 +2008,25 @@ if (typeof jQuery === 'undefined') {
var title = this.getTitle()
var content = this.getContent()
$tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
$tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events
this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
](content)
if (this.options.html) {
var typeContent = typeof content
if (this.options.sanitize) {
title = this.sanitizeHtml(title)
if (typeContent === 'string') {
content = this.sanitizeHtml(content)
}
}
$tip.find('.popover-title').html(title)
$tip.find('.popover-content').children().detach().end()[
typeContent === 'string' ? 'html' : 'append'
](content)
} else {
$tip.find('.popover-title').text(title)
$tip.find('.popover-content').children().detach().end().text(content)
}
$tip.removeClass('fade top bottom left right in')
@@ -1844,8 +2045,8 @@ if (typeof jQuery === 'undefined') {
return $e.attr('data-content')
|| (typeof o.content == 'function' ?
o.content.call($e[0]) :
o.content)
o.content.call($e[0]) :
o.content)
}
Popover.prototype.arrow = function () {
@@ -1885,10 +2086,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: scrollspy.js v3.3.7
* http://getbootstrap.com/javascript/#scrollspy
* Bootstrap: scrollspy.js v3.4.1
* https://getbootstrap.com/docs/3.4/javascript/#scrollspy
* ========================================================================
* Copyright 2011-2016 Twitter, Inc.
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -1914,7 +2115,7 @@ if (typeof jQuery === 'undefined') {
this.process()
}
ScrollSpy.VERSION = '3.3.7'
ScrollSpy.VERSION = '3.4.1'
ScrollSpy.DEFAULTS = {
offset: 10
@@ -2058,10 +2259,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: tab.js v3.3.7
* http://getbootstrap.com/javascript/#tabs
* Bootstrap: tab.js v3.4.1
* https://getbootstrap.com/docs/3.4/javascript/#tabs
* ========================================================================
* Copyright 2011-2016 Twitter, Inc.
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -2078,7 +2279,7 @@ if (typeof jQuery === 'undefined') {
// jscs:enable requireDollarBeforejQueryAssignment
}
Tab.VERSION = '3.3.7'
Tab.VERSION = '3.4.1'
Tab.TRANSITION_DURATION = 150
@@ -2107,7 +2308,7 @@ if (typeof jQuery === 'undefined') {
if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
var $target = $(selector)
var $target = $(document).find(selector)
this.activate($this.closest('li'), $ul)
this.activate($target, $target.parent(), function () {
@@ -2132,15 +2333,15 @@ if (typeof jQuery === 'undefined') {
$active
.removeClass('active')
.find('> .dropdown-menu > .active')
.removeClass('active')
.removeClass('active')
.end()
.find('[data-toggle="tab"]')
.attr('aria-expanded', false)
.attr('aria-expanded', false)
element
.addClass('active')
.find('[data-toggle="tab"]')
.attr('aria-expanded', true)
.attr('aria-expanded', true)
if (transition) {
element[0].offsetWidth // reflow for transition
@@ -2152,10 +2353,10 @@ if (typeof jQuery === 'undefined') {
if (element.parent('.dropdown-menu').length) {
element
.closest('li.dropdown')
.addClass('active')
.addClass('active')
.end()
.find('[data-toggle="tab"]')
.attr('aria-expanded', true)
.attr('aria-expanded', true)
}
callback && callback()
@@ -2214,10 +2415,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: affix.js v3.3.7
* http://getbootstrap.com/javascript/#affix
* Bootstrap: affix.js v3.4.1
* https://getbootstrap.com/docs/3.4/javascript/#affix
* ========================================================================
* Copyright 2011-2016 Twitter, Inc.
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -2231,7 +2432,9 @@ if (typeof jQuery === 'undefined') {
var Affix = function (element, options) {
this.options = $.extend({}, Affix.DEFAULTS, options)
this.$target = $(this.options.target)
var target = this.options.target === Affix.DEFAULTS.target ? $(this.options.target) : $(document).find(this.options.target)
this.$target = target
.on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
.on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
@@ -2243,7 +2446,7 @@ if (typeof jQuery === 'undefined') {
this.checkPosition()
}
Affix.VERSION = '3.3.7'
Affix.VERSION = '3.4.1'
Affix.RESET = 'affix affix-top affix-bottom'

File diff suppressed because one or more lines are too long

View File

@@ -671,7 +671,7 @@
visualPadding = 10,
container = $(this.o.container),
windowWidth = container.width(),
scrollTop = this.o.container === 'body' ? $(document).scrollTop() : container.scrollTop(),
scrollTop = this.o.container === 'body:first' ? $(document).scrollTop() : container.scrollTop(),
appendOffset = container.offset();
var parentsZindex = [];
@@ -686,7 +686,7 @@
var left = offset.left - appendOffset.left,
top = offset.top - appendOffset.top;
if (this.o.container !== 'body') {
if (this.o.container !== 'body:first') {
top += scrollTop;
}
@@ -1766,7 +1766,7 @@
enableOnReadonly: true,
showOnFocus: true,
zIndexOffset: 10,
container: 'body',
container: 'body:first',
immediateUpdates: false,
title: '',
templates: {

File diff suppressed because one or more lines are too long

View File

@@ -1723,7 +1723,12 @@
if (this.has_tab_index) {
this.$cache.input.prop("tabindex", -1);
} else {
this.$cache.input.removeProp("tabindex");
try {
this.$cache.input.removeProp("tabindex");
} catch(e) {
// Do nothing (PhantomJS can throw an error with the
// above, #2587)
}
}
this.has_tab_index = !this.has_tab_index;

File diff suppressed because one or more lines are too long

View File

@@ -6,10 +6,10 @@ Michael Geary <mike@geary.com>
Stefan Petre <stefan.petre@gmail.com>
Yehuda Katz <wycats@gmail.com>
Corey Jewett <cj@syntheticplayground.com>
Klaus Hartl <klaus.hartl@googlemail.com>
Klaus Hartl <klaus.hartl@gmail.com>
Franck Marcia <franck.marcia@gmail.com>
Jörn Zaefferer <joern.zaefferer@gmail.com>
Paul Bakaus <paul.bakaus@googlemail.com>
Paul Bakaus <paul.bakaus@gmail.com>
Brandon Aaron <brandon.aaron@gmail.com>
Mike Alsup <malsup@gmail.com>
Dave Methvin <dave.methvin@gmail.com>
@@ -47,7 +47,7 @@ Matt Curry <matt@pseudocoder.com>
Michael Monteleone <michael@michaelmonteleone.net>
Noah Sloan <noah.sloan@gmail.com>
Tom Viner <github@viner.tv>
Douglas Neiner <doug@pixelgraphics.us>
Douglas Neiner <doug@dougneiner.com>
Adam J. Sontag <ajpiano@ajpiano.com>
Dave Reed <dareed@microsoft.com>
Ralph Whitbeck <ralph.whitbeck@gmail.com>
@@ -57,7 +57,7 @@ J. Ryan Stinnett <jryans@gmail.com>
unknown <Igen005@.upcorp.ad.uprr.com>
temp01 <temp01irc@gmail.com>
Heungsub Lee <h@subl.ee>
Colin Snover <colin@alpha.zetafleet.com>
Colin Snover <github.com@zetafleet.com>
Ryan W Tenney <ryan@10e.us>
Pinhook <contact@pinhooklabs.com>
Ron Otten <r.j.g.otten@gmail.com>
@@ -69,7 +69,7 @@ Henri Wiechers <hwiechers@gmail.com>
Russell Holbrook <russell.holbrook@patch.com>
Julian Aubourg <aubourg.julian@gmail.com>
Gianni Alessandro Chiappetta <gianni@runlevel6.org>
Scott Jehl <scott@scottjehl.com>
Scott Jehl <scottjehl@gmail.com>
James Burke <jrburke@gmail.com>
Jonas Pfenniger <jonas@pfenniger.name>
Xavi Ramirez <xavi.rmz@gmail.com>
@@ -77,11 +77,11 @@ Jared Grippe <jared@deadlyicon.com>
Sylvester Keil <sylvester@keil.or.at>
Brandon Sterne <bsterne@mozilla.com>
Mathias Bynens <mathias@qiwi.be>
Timmy Willison <timmywillisn@gmail.com>
Corey Frang <gnarf@gnarf.net>
Timmy Willison <4timmywil@gmail.com>
Corey Frang <gnarf37@gmail.com>
Digitalxero <digitalxero>
Anton Kovalyov <anton@kovalyov.net>
David Murdoch <musicisair@yahoo.com>
David Murdoch <david@davidmurdoch.com>
Josh Varner <josh.varner@gmail.com>
Charles McNulty <cmcnulty@kznf.com>
Jordan Boesch <jboesch26@gmail.com>
@@ -139,7 +139,7 @@ Chris Faulkner <thefaulkner@gmail.com>
Elijah Manor <elijah.manor@gmail.com>
Daniel Chatfield <chatfielddaniel@gmail.com>
Nikita Govorov <nikita.govorov@gmail.com>
Wesley Walser <wwalser@atlassian.com>
Wesley Walser <waw325@gmail.com>
Mike Pennisi <mike@mikepennisi.com>
Markus Staab <markus.staab@redaxo.de>
Dave Riddle <david@joyvuu.com>
@@ -170,6 +170,8 @@ 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>
nanto_vi <nanto@moon.email.ne.jp>
nanto <nanto@moon.email.ne.jp>
Danil Somsikov <danilasomsikov@gmail.com>
Ryunosuke SATO <tricknotes.rs@gmail.com>
@@ -177,62 +179,70 @@ Jean Boussier <jean.boussier@gmail.com>
Adam Coulombe <me@adam.co>
Andrew Plummer <plummer.andrew@gmail.com>
Mark Raddatz <mraddatz@gmail.com>
Dmitry Gusev <dmitry.gusev@gmail.com>
Michał Gołębiowski <m.goleb@gmail.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>
Michał Gołębiowski-Owczarek <m.goleb@gmail.com>
Li Xudong <istonelee@gmail.com>
Steven Benner <admin@stevenbenner.com>
Tom H Fuertes <tomfuertes@gmail.com>
Brandon Johnson <bjohn465+github@gmail.com>
Renato Oliveira dos Santos <ros3@cin.ufpe.br>
ros3cin <ros3@cin.ufpe.br>
Jason Bedard <jason+jquery@jbedard.ca>
Kyle Robinson Young <kyle@dontkry.com>
Renato Oliveira dos Santos <ros3@cin.ufpe.br>
Chris Talkington <chris@talkingtontech.com>
Eddie Monge <eddie@eddiemonge.com>
Terry Jones <terry@jon.es>
Jason Merino <jasonmerino@gmail.com>
Jeremy Dunck <jdunck@gmail.com>
Chris Price <price.c@gmail.com>
Guy Bedford <guybedford@gmail.com>
Amey Sakhadeo <me@ameyms.com>
Mike Sidorov <mikes.ekb@gmail.com>
Anthony Ryan <anthonyryan1@gmail.com>
Dominik D. Geyer <dominik.geyer@gmail.com>
George Kats <katsgeorgeek@gmail.com>
Lihan Li <frankieteardrop@gmail.com>
Ronny Springer <springer.ronny@gmail.com>
Marian Sollmann <marian.sollmann@cargomedia.ch>
Corey Frang <gnarf37@gmail.com>
Chris Antaki <ChrisAntaki@gmail.com>
Noah Hamann <njhamann@gmail.com>
Marian Sollmann <marian.sollmann@cargomedia.ch>
njhamann <njhamann@gmail.com>
Ilya Kantor <iliakan@gmail.com>
David Hong <d.hong@me.com>
Jakob Stoeck <jakob@pokermania.de>
Christopher Jones <christopherjonesqed@gmail.com>
Forbes Lindesay <forbes@lindesay.co.uk>
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>
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>
Liang Peng <poppinlp@gmail.com>
TJ VanToll <tj.vantoll@gmail.com>
Senya Pugach <upisfree@outlook.com>
Aurelio De Rosa <aurelioderosa@gmail.com>
Nazar Mokrynskyi <nazar@mokrynskyi.com>
Amit Merchant <bullredeyes@gmail.com>
Jason Bedard <jason+github@jbedard.ca>
Arthur Verschaeve <contact@arthurverschaeve.be>
Dan Hart <danhart@notonthehighstreet.com>
Scott González <scott.gonzalez@gmail.com>
Zheming Sun <mescodasun@gmail.com>
Bin Xin <rhyzix@gmail.com>
David Corbacho <davidcorbacho@gmail.com>
Veaceslav Grimalschi <grimalschi@yandex.ru>
Daniel Husar <dano.husar@gmail.com>
Jason Bedard <jason+github@jbedard.ca>
Frederic Hemberger <mail@frederic-hemberger.de>
Ben Toews <mastahyeti@gmail.com>
Aditya Raghavan <araghavan3@gmail.com>
Nicolas HENRY <icewil@gmail.com>
Norman Xu <homyu.shinn@gmail.com>
Anne-Gaelle Colom <coloma@westminster.ac.uk>
Victor Homyakov <vkhomyackov@gmail.com>
Shivaji Varma <contact@shivajivarma.com>
Nicolas HENRY <icewil@gmail.com>
Anne-Gaelle Colom <coloma@westminster.ac.uk>
George Mauer <gmauer@gmail.com>
Leonardo Braga <leonardo.braga@gmail.com>
Stephen Edgar <stephen@netweb.com.au>
@@ -246,21 +256,66 @@ 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>
Martin Naumann <martin@geekonaut.de>
Marek Lewandowski <m.lewandowski@cksource.com>
Bruno Pérel <brunoperel@gmail.com>
Reed Loden <reed@reedloden.com>
Daniel Nill <daniellnill@gmail.com>
Yongwoo Jeon <yongwoo.jeon@navercorp.com>
Sean Henderson <seanh.za@gmail.com>
Adrian Olek <adrianolek@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>
Liza Ramo <liza.h.ramo@gmail.com>
Joelle Fleurantin <joasqueeniebee@gmail.com>
Julian Alexander Murillo <julian.alexander.murillo@gmail.com>
Joelle Fleurantin <joasqueeniebee@gmail.com>
Jae Sung Park <alberto.park@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>
Todor Prikumov <tono_pr@abv.bg>
Steve Mao <maochenyan@gmail.com>
Zack Hall <zackhall@outlook.com>
Bernhard M. Wiedemann <jquerybmw@lsmod.de>
Todor Prikumov <tono_pr@abv.bg>
Jha Naman <createnaman@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>
Manoj Kumar <nithmanoj@gmail.com>
David Broder-Rodgers <broder93@gmail.com>
Alex Louden <alex@louden.com>
Alex Padilla <alexonezero@outlook.com>
南漂一卒 <shiy007@qq.com>
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>
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>
tmybr11 <tomas.perone@gmail.com>
Luis Emilio Velasco Sanchez <emibloque@gmail.com>
Ed S <ejsanders@gmail.com>
Bert Zhang <enbo@users.noreply.github.com>
Andrei Fangli <andrei_fangli@outlook.com>
Marja Hölttä <marja.holtta@gmail.com>
abnud1 <ahmad13932013@hotmail.com>
buddh4 <mail@jharrer.de>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -117,7 +117,7 @@
// If this is the main Shiny window, wire up our custom message handler.
if (window.Shiny) {
Shiny.addCustomMessageHandler('reactlog', function(message) {
Shiny.addCustomMessageHandler('showcase-src', function(message) {
if (message.srcref && message.srcfile) {
highlightSrcref(message.srcref, message.srcfile);
}
@@ -267,4 +267,3 @@
if (window.hljs)
hljs.initHighlightingOnLoad();
})();

View File

@@ -12,6 +12,13 @@ pre.shiny-text-output.noplaceholder:empty {
height: 0;
}
/* Some browsers (like Safari) will wrap text in <pre> tags with Bootstrap's
CSS. This changes the behavior to not wrap.
*/
pre.shiny-text-output {
word-wrap: normal;
}
.shiny-image-output img.shiny-scalable, .shiny-plot-output img.shiny-scalable {
max-width: 100%;
max-height: 100%;
@@ -209,6 +216,10 @@ pre.shiny-text-output.noplaceholder:empty {
font-size: 80%;
}
.shiny-label-null {
display: none;
}
.crosshair {
cursor: crosshair;
}

View File

@@ -12,7 +12,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
var exports = window.Shiny = window.Shiny || {};
exports.version = "1.2.0"; // Version number inserted by Grunt
exports.version = "1.3.2.9001"; // Version number inserted by Grunt
var origPushState = window.history.pushState;
window.history.pushState = function () {
@@ -321,6 +321,24 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
if (op === "==") return diff === 0;else if (op === ">=") return diff >= 0;else if (op === ">") return diff > 0;else if (op === "<=") return diff <= 0;else if (op === "<") return diff < 0;else throw "Unknown operator: " + op;
};
function updateLabel(labelTxt, labelNode) {
// Only update if label was specified in the update method
if (typeof labelTxt === "undefined") return;
if (labelNode.length !== 1) {
throw new Error("labelNode must be of length 1");
}
// Should the label be empty?
var emptyLabel = $.isArray(labelTxt) && labelTxt.length === 0;
if (emptyLabel) {
labelNode.addClass("shiny-label-null");
} else {
labelNode.text(labelTxt);
labelNode.removeClass("shiny-label-null");
}
}
//---------------------------------------------------------------------
// Source file: ../srcjs/browser.js
@@ -545,8 +563,8 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
this.lastChanceCallback = [];
};
(function () {
this.setInput = function (name, value, opts) {
this.pendingData[name] = value;
this.setInput = function (nameType, value, opts) {
this.pendingData[nameType] = value;
if (!this.reentrant) {
if (opts.priority === "event") {
@@ -582,11 +600,10 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
this.lastSentValues = this.reset(initialValues);
};
(function () {
this.setInput = function (name, value, opts) {
var _splitInputNameType = splitInputNameType(name);
var inputName = _splitInputNameType.name;
var inputType = _splitInputNameType.inputType;
this.setInput = function (nameType, value, opts) {
var _splitInputNameType = splitInputNameType(nameType),
inputName = _splitInputNameType.name,
inputType = _splitInputNameType.inputType;
var jsonValue = JSON.stringify(value);
@@ -608,12 +625,11 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
for (var inputName in values) {
if (values.hasOwnProperty(inputName)) {
var _splitInputNameType2 = splitInputNameType(inputName);
var _splitInputNameType2 = splitInputNameType(inputName),
_name = _splitInputNameType2.name,
inputType = _splitInputNameType2.inputType;
var name = _splitInputNameType2.name;
var inputType = _splitInputNameType2.inputType;
cacheValues[name] = {
cacheValues[_name] = {
jsonValue: JSON.stringify(values[inputName]),
inputType: inputType
};
@@ -628,10 +644,10 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
this.target = target;
};
(function () {
this.setInput = function (name, value, opts) {
this.setInput = function (nameType, value, opts) {
var evt = jQuery.Event("shiny:inputchanged");
var input = splitInputNameType(name);
var input = splitInputNameType(nameType);
evt.name = input.name;
evt.inputType = input.inputType;
evt.value = value;
@@ -639,7 +655,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
evt.el = opts.el;
evt.priority = opts.priority;
$(document).trigger(evt);
$(opts.el).trigger(evt);
if (!evt.isDefaultPrevented()) {
name = evt.name;
@@ -657,25 +673,37 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
this.inputRatePolicies = {};
};
(function () {
this.setInput = function (name, value, opts) {
this.$ensureInit(name);
// Note that the first argument of setInput() and setRatePolicy()
// are passed both the input name (i.e., inputId) and type.
// https://github.com/rstudio/shiny/blob/67d3a/srcjs/init_shiny.js#L111-L126
// However, $ensureInit() and $doSetInput() are meant to be passed just
// the input name (i.e., inputId), which is why we distinguish between
// nameType and name.
this.setInput = function (nameType, value, opts) {
var _splitInputNameType3 = splitInputNameType(nameType),
inputName = _splitInputNameType3.name;
if (opts.priority !== "deferred") this.inputRatePolicies[name].immediateCall(name, value, opts);else this.inputRatePolicies[name].normalCall(name, value, opts);
this.$ensureInit(inputName);
if (opts.priority !== "deferred") this.inputRatePolicies[inputName].immediateCall(nameType, value, opts);else this.inputRatePolicies[inputName].normalCall(nameType, value, opts);
};
this.setRatePolicy = function (name, mode, millis) {
this.setRatePolicy = function (nameType, mode, millis) {
var _splitInputNameType4 = splitInputNameType(nameType),
inputName = _splitInputNameType4.name;
if (mode === 'direct') {
this.inputRatePolicies[name] = new Invoker(this, this.$doSetInput);
this.inputRatePolicies[inputName] = new Invoker(this, this.$doSetInput);
} else if (mode === 'debounce') {
this.inputRatePolicies[name] = new Debouncer(this, this.$doSetInput, millis);
this.inputRatePolicies[inputName] = new Debouncer(this, this.$doSetInput, millis);
} else if (mode === 'throttle') {
this.inputRatePolicies[name] = new Throttler(this, this.$doSetInput, millis);
this.inputRatePolicies[inputName] = new Throttler(this, this.$doSetInput, millis);
}
};
this.$ensureInit = function (name) {
if (!(name in this.inputRatePolicies)) this.setRatePolicy(name, 'direct');
};
this.$doSetInput = function (name, value, opts) {
this.target.setInput(name, value, opts);
this.$doSetInput = function (nameType, value, opts) {
this.target.setInput(nameType, value, opts);
};
}).call(InputRateDecorator.prototype);
@@ -684,8 +712,8 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
this.pendingInput = {};
};
(function () {
this.setInput = function (name, value, opts) {
if (/^\./.test(name)) this.target.setInput(name, value, opts);else this.pendingInput[name] = { value: value, opts: opts };
this.setInput = function (nameType, value, opts) {
if (/^\./.test(nameType)) this.target.setInput(nameType, value, opts);else this.pendingInput[name] = { value: value, opts: opts };
};
this.submit = function () {
for (var name in this.pendingInput) {
@@ -701,12 +729,12 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
this.target = target;
};
(function () {
this.setInput = function (name, value, opts) {
if (!name) throw "Can't set input with empty name.";
this.setInput = function (nameType, value, opts) {
if (!nameType) throw "Can't set input with empty name.";
opts = addDefaultInputOpts(opts);
this.target.setInput(name, value, opts);
this.target.setInput(nameType, value, opts);
};
}).call(InputValidateDecorator.prototype);
@@ -733,8 +761,8 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
return opts;
}
function splitInputNameType(name) {
var name2 = name.split(':');
function splitInputNameType(nameType) {
var name2 = nameType.split(':');
return {
name: name2[0],
inputType: name2.length > 1 ? name2[1] : ''
@@ -1572,13 +1600,15 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
function getTabIndex($tabset, tabsetId) {
// The 0 is to ensure this works for empty tabsetPanels as well
var existingTabIds = [0];
var leadingHref = "#tab-" + tabsetId + "-";
// loop through all existing tabs, find the one with highest id
// (since this is based on a numeric counter), and increment
$tabset.find("> li").each(function () {
var $tab = $(this).find("> a[data-toggle='tab']");
if ($tab.length > 0) {
var index = $tab.attr("href").replace(leadingHref, "");
// remove leading url if it exists. (copy of bootstrap url stripper)
var href = $tab.attr("href").replace(/.*(?=#[^\s]+$)/, '');
// remove tab id to get the index
var index = href.replace("#tab-" + tabsetId + "-", "");
existingTabIds.push(Number(index));
}
});
@@ -1771,7 +1801,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
var $container = $('.shiny-progress-container');
if ($container.length === 0) {
$container = $('<div class="shiny-progress-container"></div>');
$('body').append($container);
$(document.body).append($container);
}
// Add div for just this progress ID
@@ -1857,10 +1887,9 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
// Returns a URL which can be queried to get values from inside the server
// function. This is enabled with `options(shiny.testmode=TRUE)`.
this.getTestSnapshotBaseUrl = function () {
var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var _ref2$fullUrl = _ref2.fullUrl;
var fullUrl = _ref2$fullUrl === undefined ? true : _ref2$fullUrl;
var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref2$fullUrl = _ref2.fullUrl,
fullUrl = _ref2$fullUrl === undefined ? true : _ref2$fullUrl;
var loc = window.location;
var url = "";
@@ -1929,22 +1958,21 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
var fadeDuration = 250;
function show() {
var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var _ref3$html = _ref3.html;
var html = _ref3$html === undefined ? '' : _ref3$html;
var _ref3$action = _ref3.action;
var action = _ref3$action === undefined ? '' : _ref3$action;
var _ref3$deps = _ref3.deps;
var deps = _ref3$deps === undefined ? [] : _ref3$deps;
var _ref3$duration = _ref3.duration;
var duration = _ref3$duration === undefined ? 5000 : _ref3$duration;
var _ref3$id = _ref3.id;
var id = _ref3$id === undefined ? null : _ref3$id;
var _ref3$closeButton = _ref3.closeButton;
var closeButton = _ref3$closeButton === undefined ? true : _ref3$closeButton;
var _ref3$type = _ref3.type;
var type = _ref3$type === undefined ? null : _ref3$type;
var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref3$html = _ref3.html,
html = _ref3$html === undefined ? '' : _ref3$html,
_ref3$action = _ref3.action,
action = _ref3$action === undefined ? '' : _ref3$action,
_ref3$deps = _ref3.deps,
deps = _ref3$deps === undefined ? [] : _ref3$deps,
_ref3$duration = _ref3.duration,
duration = _ref3$duration === undefined ? 5000 : _ref3$duration,
_ref3$id = _ref3.id,
id = _ref3$id === undefined ? null : _ref3$id,
_ref3$closeButton = _ref3.closeButton,
closeButton = _ref3$closeButton === undefined ? true : _ref3$closeButton,
_ref3$type = _ref3.type,
type = _ref3$type === undefined ? null : _ref3$type;
if (!id) id = randomId();
@@ -2025,7 +2053,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
if ($panel.length > 0) return $panel;
$('body').append('<div id="shiny-notification-panel">');
$(document.body).append('<div id="shiny-notification-panel">');
return $panel;
}
@@ -2088,13 +2116,11 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
// content is non-Bootstrap. Bootstrap modals require some special handling,
// which is coded in here.
show: function show() {
var _ref4 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var _ref4$html = _ref4.html;
var html = _ref4$html === undefined ? '' : _ref4$html;
var _ref4$deps = _ref4.deps;
var deps = _ref4$deps === undefined ? [] : _ref4$deps;
var _ref4 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref4$html = _ref4.html,
html = _ref4$html === undefined ? '' : _ref4$html,
_ref4$deps = _ref4.deps,
deps = _ref4$deps === undefined ? [] : _ref4$deps;
// If there was an existing Bootstrap modal, then there will be a modal-
// backdrop div that was added outside of the modal wrapper, and it must be
@@ -2105,7 +2131,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
var $modal = $('#shiny-modal-wrapper');
if ($modal.length === 0) {
$modal = $('<div id="shiny-modal-wrapper"></div>');
$('body').append($modal);
$(document.body).append($modal);
// If the wrapper's content is a Bootstrap modal, then when the inner
// modal is hidden, remove the entire thing, including wrapper.
@@ -2476,6 +2502,8 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
// Register the various event handlers
// ----------------------------------------------------------
if (opts.clickId) {
imageutils.disableDrag($el, $img);
var clickHandler = imageutils.createClickHandler(opts.clickId, opts.clickClip, opts.coordmap);
$el.on('mousedown2.image_output', clickHandler.mousedown);
@@ -2487,6 +2515,8 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
}
if (opts.dblclickId) {
imageutils.disableDrag($el, $img);
// We'll use the clickHandler's mousedown function, but register it to
// our custom 'dblclick2' event.
var dblclickHandler = imageutils.createClickHandler(opts.dblclickId, opts.clickClip, opts.coordmap);
@@ -2497,6 +2527,8 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
}
if (opts.hoverId) {
imageutils.disableDrag($el, $img);
var hoverHandler = imageutils.createHoverHandler(opts.hoverId, opts.hoverDelay, opts.hoverDelayType, opts.hoverClip, opts.hoverNullOutside, opts.coordmap);
$el.on('mousemove.image_output', hoverHandler.mousemove);
$el.on('mouseout.image_output', hoverHandler.mouseout);
@@ -2506,17 +2538,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
}
if (opts.brushId) {
// Make image non-draggable (Chrome, Safari)
$img.css('-webkit-user-drag', 'none');
// Firefox, IE<=10
$img.on('dragstart.image_output', function () {
return false;
});
// Disable selection of image and text when dragging in IE<=10
$el.on('selectstart.image_output', function () {
return false;
});
imageutils.disableDrag($el, $img);
var brushHandler = imageutils.createBrushHandler(opts.brushId, $el, opts, opts.coordmap, outputId);
$el.on('mousedown.image_output', brushHandler.mousedown);
@@ -2557,6 +2579,24 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
var imageutils = {};
imageutils.disableDrag = function ($el, $img) {
// Make image non-draggable (Chrome, Safari)
$img.css('-webkit-user-drag', 'none');
// Firefox, IE<=10
// First remove existing handler so we don't keep adding handlers.
$img.off('dragstart.image_output');
$img.on('dragstart.image_output', function () {
return false;
});
// Disable selection of image and text when dragging in IE<=10
$el.off('selectstart.image_output');
$el.on('selectstart.image_output', function () {
return false;
});
};
// Modifies the panel objects in a coordmap, adding scaleImgToData(),
// scaleDataToImg(), and clipImg() functions to each one. The panel objects
// use img and data coordinates only; they do not use css coordinates. The
@@ -4301,7 +4341,12 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
var textInputBinding = new InputBinding();
$.extend(textInputBinding, {
find: function find(scope) {
return $(scope).find('input[type="text"], input[type="search"], input[type="url"], input[type="email"]');
var $inputs = $(scope).find('input[type="text"], input[type="search"], input[type="url"], input[type="email"]');
// selectize.js 0.12.4 inserts a hidden text input with an
// id that ends in '-selectized'. The .not() selector below
// is to prevent textInputBinding from accidentally picking up
// this hidden element as a shiny input (#2396)
return $inputs.not('input[type="text"][id$="-selectized"]');
},
getId: function getId(el) {
return InputBinding.prototype.getId.call(this, el) || el.name;
@@ -4326,7 +4371,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
receiveMessage: function receiveMessage(el, data) {
if (data.hasOwnProperty('value')) this.setValue(el, data.value);
if (data.hasOwnProperty('label')) $(el).parent().find('label[for="' + $escape(el.id) + '"]').text(data.label);
updateLabel(data.label, this._getLabelNode(el));
if (data.hasOwnProperty('placeholder')) el.placeholder = data.placeholder;
@@ -4334,7 +4379,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
},
getState: function getState(el) {
return {
label: $(el).parent().find('label[for="' + $escape(el.id) + '"]').text(),
label: this._getLabelNode(el).text(),
value: el.value,
placeholder: el.placeholder
};
@@ -4344,6 +4389,9 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
policy: 'debounce',
delay: 250
};
},
_getLabelNode: function _getLabelNode(el) {
return $(el).parent().find('label[for="' + $escape(el.id) + '"]');
}
});
inputBindings.register(textInputBinding, 'shiny.textInput');
@@ -4399,16 +4447,19 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
if (data.hasOwnProperty('max')) el.max = data.max;
if (data.hasOwnProperty('step')) el.step = data.step;
if (data.hasOwnProperty('label')) $(el).parent().find('label[for="' + $escape(el.id) + '"]').text(data.label);
updateLabel(data.label, this._getLabelNode(el));
$(el).trigger('change');
},
getState: function getState(el) {
return { label: $(el).parent().find('label[for="' + $escape(el.id) + '"]').text(),
return { label: this._getLabelNode(el).text(),
value: this.getValue(el),
min: Number(el.min),
max: Number(el.max),
step: Number(el.step) };
},
_getLabelNode: function _getLabelNode(el) {
return $(el).parent().find('label[for="' + $escape(el.id) + '"]');
}
});
inputBindings.register(numberInputBinding, 'shiny.numberInput');
@@ -4444,6 +4495,8 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
receiveMessage: function receiveMessage(el, data) {
if (data.hasOwnProperty('value')) el.checked = data.value;
// checkboxInput()'s label works different from other
// input labels...the label container should always exist
if (data.hasOwnProperty('label')) $(el).parent().find('span').text(data.label);
$(el).trigger('change');
@@ -4572,7 +4625,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
}
}
if (data.hasOwnProperty('label')) $el.parent().find('label[for="' + $escape(el.id) + '"]').text(data.label);
updateLabel(data.label, this._getLabelNode(el));
var domElements = ['data-type', 'time-format', 'timezone'];
for (var i = 0; i < domElements.length; i++) {
@@ -4614,7 +4667,9 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
$el.ionRangeSlider(opts);
},
_getLabelNode: function _getLabelNode(el) {
return $(el).parent().find('label[for="' + $escape(el.id) + '"]');
},
// Number of values; 1 for single slider, 2 for range slider
_numValues: function _numValues(el) {
if ($(el).data('ionRangeSlider').options.type === 'double') return 2;else return 1;
@@ -4776,7 +4831,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
if (startview === 2) startview = 'decade';else if (startview === 1) startview = 'year';else if (startview === 0) startview = 'month';
return {
label: $el.find('label[for="' + $escape(el.id) + '"]').text(),
label: this._getLabelNode(el).text(),
value: this.getValue(el),
valueString: $input.val(),
min: min,
@@ -4790,7 +4845,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
receiveMessage: function receiveMessage(el, data) {
var $input = $(el).find('input');
if (data.hasOwnProperty('label')) $(el).find('label[for="' + $escape(el.id) + '"]').text(data.label);
updateLabel(data.label, this._getLabelNode(el));
if (data.hasOwnProperty('min')) this._setMin($input[0], data.min);
@@ -4845,6 +4900,9 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
this._setMax($input[0], $input.data('max-date'));
}
},
_getLabelNode: function _getLabelNode(el) {
return $(el).find('label[for="' + $escape(el.id) + '"]');
},
// Given a format object from a date picker, return a string
_formatToString: function _formatToString(format) {
// Format object has structure like:
@@ -4862,18 +4920,33 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
if (date === undefined) return;
if (date === null) {
$(el).bsDatepicker('setStartDate', null);
} else {
date = this._newDate(date);
date = this._UTCDateAsLocal(date);
if (!isNaN(date)) {
// Workaround for https://github.com/eternicode/bootstrap-datepicker/issues/2010
// If the start date when there's a two-digit year format, it will set
// the date value to null. So we'll save the value, set the start
// date, and the restore the value.
var curValue = $(el).bsDatepicker('getUTCDate');
$(el).bsDatepicker('setStartDate', date);
$(el).bsDatepicker('setUTCDate', curValue);
}
return;
}
date = this._newDate(date);
// If date parsing fails, do nothing
if (date === null) return;
date = this._UTCDateAsLocal(date);
if (isNaN(date)) return;
// Workaround for https://github.com/eternicode/bootstrap-datepicker/issues/2010
// If the start date when there's a two-digit year format, it will set
// the date value to null. So we'll save the value, set the start
// date, and the restore the value.
var curValue = $(el).bsDatepicker('getUTCDate');
$(el).bsDatepicker('setStartDate', date);
$(el).bsDatepicker('setUTCDate', curValue);
// Workaround for https://github.com/rstudio/shiny/issues/2335
// We only set the start date *after* the value in this special
// case so we don't effect the intended behavior of having a blank
// value when it falls outside the start date
if (typeof date.toDateString !== 'function') return;
if (typeof curValue.toDateString !== 'function') return;
if (date.toDateString() === curValue.toDateString()) {
$(el).bsDatepicker('setStartDate', null);
$(el).bsDatepicker('setUTCDate', curValue);
$(el).bsDatepicker('setStartDate', date);
}
},
// Given an unambiguous date string or a Date object, set the max (end) date
@@ -4882,15 +4955,28 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
if (date === undefined) return;
if (date === null) {
$(el).bsDatepicker('setEndDate', null);
} else {
date = this._newDate(date);
date = this._UTCDateAsLocal(date);
if (!isNaN(date)) {
// Workaround for same issue as in _setMin.
var curValue = $(el).bsDatepicker('getUTCDate');
$(el).bsDatepicker('setEndDate', date);
$(el).bsDatepicker('setUTCDate', curValue);
}
return;
}
date = this._newDate(date);
// If date parsing fails, do nothing
if (date === null) return;
date = this._UTCDateAsLocal(date);
if (isNaN(date)) return;
// Workaround for same issue as in _setMin.
var curValue = $(el).bsDatepicker('getUTCDate');
$(el).bsDatepicker('setEndDate', date);
$(el).bsDatepicker('setUTCDate', curValue);
// Workaround for same issue as in _setMin.
if (typeof date.toDateString !== 'function') return;
if (typeof curValue.toDateString !== 'function') return;
if (date.toDateString() === curValue.toDateString()) {
$(el).bsDatepicker('setEndDate', null);
$(el).bsDatepicker('setUTCDate', curValue);
$(el).bsDatepicker('setEndDate', date);
}
},
// Given a date string of format yyyy-mm-dd, return a Date object with
@@ -4991,7 +5077,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
if (startview === 2) startview = 'decade';else if (startview === 1) startview = 'year';else if (startview === 0) startview = 'month';
return {
label: $el.find('label[for="' + $escape(el.id) + '"]').text(),
label: this._getLabelNode(el).text(),
value: this.getValue(el),
valueString: [$startinput.val(), $endinput.val()],
min: min,
@@ -5008,7 +5094,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
var $startinput = $inputs.eq(0);
var $endinput = $inputs.eq(1);
if (data.hasOwnProperty('label')) $el.find('label[for="' + $escape(el.id) + '"]').text(data.label);
updateLabel(data.label, this._getLabelNode(el));
if (data.hasOwnProperty('min')) {
this._setMin($startinput[0], data.min);
@@ -5064,6 +5150,9 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
},
unsubscribe: function unsubscribe(el) {
$(el).off('.dateRangeInputBinding');
},
_getLabelNode: function _getLabelNode(el) {
return $(el).find('label[for="' + $escape(el.id) + '"]');
}
});
inputBindings.register(dateRangeInputBinding, 'shiny.dateRangeInput');
@@ -5095,10 +5184,14 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
return $(el).val();
},
setValue: function setValue(el, value) {
var selectize = this._selectize(el);
if (typeof selectize !== 'undefined') {
selectize.setValue(value);
} else $(el).val(value);
if (!this._is_selectize(el)) {
$(el).val(value);
} else {
var selectize = this._selectize(el);
if (selectize) {
selectize.setValue(value);
}
}
},
getState: function getState(el) {
// Store options in an array of objects, each with with value and label
@@ -5109,7 +5202,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
}
return {
label: $(el).parent().find('label[for="' + $escape(el.id) + '"]').text(),
label: this._getLabelNode(el),
value: this.getValue(el),
options: options
};
@@ -5191,7 +5284,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
this.setValue(el, data.value);
}
if (data.hasOwnProperty('label')) $(el).parent().parent().find('label[for="' + $escape(el.id) + '"]').text(data.label);
updateLabel(data.label, this._getLabelNode(el));
$(el).trigger('change');
},
@@ -5214,6 +5307,18 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
initialize: function initialize(el) {
this._selectize(el);
},
_getLabelNode: function _getLabelNode(el) {
var escaped_id = $escape(el.id);
if (this._is_selectize(el)) {
escaped_id += "-selectized";
}
return $(el).parent().parent().find('label[for="' + escaped_id + '"]');
},
// Return true if it's a selectize input, false if it's a regular select input.
_is_selectize: function _is_selectize(el) {
var config = $(el).parent().find('script[data-for="' + $escape(el.id) + '"]');
return config.length > 0;
},
_selectize: function _selectize(el, update) {
if (!$.fn.selectize) return undefined;
var $el = $(el);
@@ -5286,7 +5391,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
}
return {
label: $(el).parent().find('label[for="' + $escape(el.id) + '"]').text(),
label: this._getLabelNode(el).text(),
value: this.getValue(el),
options: options
};
@@ -5305,7 +5410,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
if (data.hasOwnProperty('value')) this.setValue(el, data.value);
if (data.hasOwnProperty('label')) $(el).parent().find('label[for="' + $escape(el.id) + '"]').text(data.label);
updateLabel(data.label, this._getLabelNode(el));
$(el).trigger('change');
},
@@ -5317,6 +5422,10 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
unsubscribe: function unsubscribe(el) {
$(el).off('.radioInputBinding');
},
// Get the DOM element that contains the top-level label
_getLabelNode: function _getLabelNode(el) {
return $(el).parent().find('label[for="' + $escape(el.id) + '"]');
},
// Given an input DOM object, get the associated label. Handles labels
// that wrap the input as well as labels associated with 'for' attribute.
_getLabel: function _getLabel(obj) {
@@ -5382,7 +5491,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
label: this._getLabel($objs[i]) };
}
return { label: $(el).find('label[for="' + $escape(el.id) + '"]').text(),
return { label: this._getLabelNode(el).text(),
value: this.getValue(el),
options: options
};
@@ -5401,7 +5510,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
if (data.hasOwnProperty('value')) this.setValue(el, data.value);
if (data.hasOwnProperty('label')) $el.find('label[for="' + $escape(el.id) + '"]').text(data.label);
updateLabel(data.label, this._getLabelNode(el));
$(el).trigger('change');
},
@@ -5413,6 +5522,10 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
unsubscribe: function unsubscribe(el) {
$(el).off('.checkboxGroupInputBinding');
},
// Get the DOM element that contains the top-level label
_getLabelNode: function _getLabelNode(el) {
return $(el).find('label[for="' + $escape(el.id) + '"]');
},
// Given an input DOM object, get the associated label. Handles labels
// that wrap the input as well as labels associated with 'for' attribute.
_getLabel: function _getLabel(obj) {
@@ -5578,7 +5691,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
this.iframe.id = iframeId;
this.iframe.name = iframeId;
this.iframe.setAttribute('style', 'position: fixed; top: 0; left: 0; width: 0; height: 0; border: none');
$('body').append(this.iframe);
$(document.body).append(this.iframe);
var iframeDestroy = function iframeDestroy() {
// Forces Shiny to flushReact, flush outputs, etc. Without this we get
// invalidated reactives, but observers don't actually execute.
@@ -5908,10 +6021,10 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
_enableDocumentEvents: function _enableDocumentEvents() {
var _this2 = this;
var $doc = $("html");
var _ZoneClass = this._ZoneClass;
var ACTIVE = _ZoneClass.ACTIVE;
var OVER = _ZoneClass.OVER;
var $doc = $("html"),
_ZoneClass = this._ZoneClass,
ACTIVE = _ZoneClass.ACTIVE,
OVER = _ZoneClass.OVER;
this._enableDraghover($doc).on({
"draghover:enter.draghover": function draghoverEnterDraghover(e) {
@@ -5983,27 +6096,25 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
// support the FileList object though, so the user's expectation that DnD is
// supported based on this highlighting would be incorrect.
if (!this._isIE9()) {
(function () {
if ($fileInputs.length === 0) _this3._enableDocumentEvents();
$fileInputs = $fileInputs.add(el);
var $zone = _this3._zoneOf(el);
var OVER = _this3._ZoneClass.OVER;
if ($fileInputs.length === 0) this._enableDocumentEvents();
$fileInputs = $fileInputs.add(el);
var $zone = this._zoneOf(el),
OVER = this._ZoneClass.OVER;
_this3._enableDraghover($zone).on({
"draghover:enter.draghover": function draghoverEnterDraghover(e) {
$zone.addClass(OVER);
},
"draghover:leave.draghover": function draghoverLeaveDraghover(e) {
$zone.removeClass(OVER);
// Prevent this event from bubbling to the document handler,
// which would deactivate all zones.
e.stopPropagation();
},
"draghover:drop.draghover": function draghoverDropDraghover(e, dropEvent) {
_this3._handleDrop(dropEvent, el);
}
});
})();
this._enableDraghover($zone).on({
"draghover:enter.draghover": function draghoverEnterDraghover(e) {
$zone.addClass(OVER);
},
"draghover:leave.draghover": function draghoverLeaveDraghover(e) {
$zone.removeClass(OVER);
// Prevent this event from bubbling to the document handler,
// which would deactivate all zones.
e.stopPropagation();
},
"draghover:drop.draghover": function draghoverDropDraghover(e, dropEvent) {
_this3._handleDrop(dropEvent, el);
}
});
}
},
@@ -6442,14 +6553,14 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
// Need to register callbacks for each Bootstrap 3 class.
var bs3classes = ['modal', 'dropdown', 'tab', 'tooltip', 'popover', 'collapse'];
$.each(bs3classes, function (idx, classname) {
$('body').on('shown.bs.' + classname + '.sendImageSize', '*', filterEventsByNamespace('bs', sendImageSize));
$('body').on('shown.bs.' + classname + '.sendOutputHiddenState ' + 'hidden.bs.' + classname + '.sendOutputHiddenState', '*', filterEventsByNamespace('bs', sendOutputHiddenState));
$(document.body).on('shown.bs.' + classname + '.sendImageSize', '*', filterEventsByNamespace('bs', sendImageSize));
$(document.body).on('shown.bs.' + classname + '.sendOutputHiddenState ' + 'hidden.bs.' + classname + '.sendOutputHiddenState', '*', filterEventsByNamespace('bs', sendOutputHiddenState));
});
// This is needed for Bootstrap 2 compatibility and for non-Bootstrap
// related shown/hidden events (like conditionalPanel)
$('body').on('shown.sendImageSize', '*', sendImageSize);
$('body').on('shown.sendOutputHiddenState hidden.sendOutputHiddenState', '*', sendOutputHiddenState);
$(document.body).on('shown.sendImageSize', '*', sendImageSize);
$(document.body).on('shown.sendOutputHiddenState hidden.sendOutputHiddenState', '*', sendOutputHiddenState);
// Send initial pixel ratio, and update it if it changes
initialValues['.clientdata_pixelratio'] = pixelRatio();
@@ -6547,6 +6658,25 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
e.preventDefault();
});
$(document).on('keydown', function (e) {
if (e.which !== 115 || !e.ctrlKey && !e.metaKey || e.shiftKey || e.altKey) return;
var url = 'reactlog/mark?w=' + window.escape(exports.shinyapp.config.workerId) + "&s=" + window.escape(exports.shinyapp.config.sessionId);
// send notification
$.get(url, function (result) {
if (result !== "marked") return;
var html = '<span id="shiny-reactlog-mark-text">Marked time point in reactlog</span>';
exports.notifications.show({
html: html,
closeButton: true
});
});
e.preventDefault();
});
//---------------------------------------------------------------------
// Source file: ../srcjs/_end.js
})();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -20,5 +20,5 @@ not to perform HTML escaping on it.
\examples{
el <- div(HTML("I like <u>turtles</u>"))
cat(as.character(el))
}
}

View File

@@ -22,7 +22,7 @@ interpreted as multiple namespaces, in increasing order of specificity
}
\value{
If \code{id} is missing, returns a function that expects an id string
as its only argument and returns that id with the namespace prepended.
as its only argument and returns that id with the namespace prepended.
}
\description{
The \code{NS} function creates namespaced IDs out of bare IDs, by joining

View File

@@ -9,7 +9,7 @@
\code{shinyServer} to the server function.}
\item{min}{The value that represents the starting point of the
progress bar. Must be less tham \code{max}.}
progress bar. Must be less than \code{max}.}
\item{max}{The value that represents the end of the progress bar.
Must be greater than \code{min}.}
@@ -43,7 +43,7 @@ Reports progress to the user during long-running operations.
}
\details{
This package exposes two distinct programming APIs for working with
progress. \code{\link{withProgress}} and \code{\link{setProgress}}
progress. \code{\link[=withProgress]{withProgress()}} and \code{\link[=setProgress]{setProgress()}}
together provide a simple function-based interface, while the
\code{Progress} reference class provides an object-oriented API.
@@ -57,28 +57,28 @@ If you want to use the old styling (for example, you may have used customized
CSS), you can use \code{style="old"} each time you call
\code{Progress$new()}. If you don't want to set the style each time
\code{Progress$new} is called, you can instead call
\code{\link{shinyOptions}(progress.style="old")} just once, inside the server
\code{\link[=shinyOptions]{shinyOptions(progress.style="old")}} just once, inside the server
function.
\strong{Methods}
\describe{
\item{\code{initialize(session, min = 0, max = 1)}}{
Creates a new progress panel (but does not display it).
}
\item{\code{set(value = NULL, message = NULL, detail = NULL)}}{
Updates the progress panel. When called the first time, the
progress panel is displayed.
}
\item{\code{inc(amount = 0.1, message = NULL, detail = NULL)}}{
Like \code{set}, this updates the progress panel. The difference is
that \code{inc} increases the progress bar by \code{amount}, instead
of setting it to a specific value.
}
\item{\code{close()}}{
Removes the progress panel. Future calls to \code{set} and
\code{close} will be ignored.
}
}
\describe{
\item{\code{initialize(session, min = 0, max = 1)}}{
Creates a new progress panel (but does not display it).
}
\item{\code{set(value = NULL, message = NULL, detail = NULL)}}{
Updates the progress panel. When called the first time, the
progress panel is displayed.
}
\item{\code{inc(amount = 0.1, message = NULL, detail = NULL)}}{
Like \code{set}, this updates the progress panel. The difference is
that \code{inc} increases the progress bar by \code{amount}, instead
of setting it to a specific value.
}
\item{\code{close()}}{
Removes the progress panel. Future calls to \code{set} and
\code{close} will be ignored.
}
}
}
\examples{
## Only run examples in interactive R sessions
@@ -108,6 +108,6 @@ shinyApp(ui, server)
}
}
\seealso{
\code{\link{withProgress}}
\code{\link[=withProgress]{withProgress()}}
}
\keyword{datasets}

View File

@@ -77,6 +77,5 @@ such as \code{"100px"} (100 pixels) or \code{"25\%"}.
For arcane HTML reasons, to have the panel fill the page or parent you should
specify \code{0} for \code{top}, \code{left}, \code{right}, and \code{bottom}
rather than the more obvious \code{width = "100\%"} and \code{height =
"100\%"}.
rather than the more obvious \code{width = "100\%"} and \code{height = "100\%"}.
}

View File

@@ -15,10 +15,10 @@ actionLink(inputId, label, icon = NULL, ...)
\item{label}{The contents of the button or link--usually a text label, but
you could also use any other HTML, like an image.}
\item{icon}{An optional \code{\link{icon}} to appear on the button.}
\item{icon}{An optional \code{\link[=icon]{icon()}} to appear on the button.}
\item{width}{The width of the input, e.g. \code{'400px'}, or \code{'100\%'};
see \code{\link{validateCssUnit}}.}
see \code{\link[=validateCssUnit]{validateCssUnit()}}.}
\item{...}{Named attributes to be applied to the button or link.}
}
@@ -54,7 +54,7 @@ shinyApp(ui, server)
}
\seealso{
\code{\link{observeEvent}} and \code{\link{eventReactive}}
\code{\link[=observeEvent]{observeEvent()}} and \code{\link[=eventReactive]{eventReactive()}}
Other input elements: \code{\link{checkboxGroupInput}},
\code{\link{checkboxInput}}, \code{\link{dateInput}},

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