Compare commits

...

2347 Commits

Author SHA1 Message Date
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
914baf594b Merge pull request #2241 from rstudio/joe/bugfix/icon-examples
Remove icon examples
2018-11-01 20:41:58 -05:00
Joe Cheng
02b0802886 Add note about FontAwesome path change 2018-11-01 15:08:59 -07:00
Joe Cheng
0725239397 Remove icon examples
These cause browser windows to pop up during R CMD check, which is
against CRAN policy. @wch will merge a PR that has other examples
once we release v1.2.
2018-11-01 14:56:15 -07:00
Joe Cheng
d72e8a06a7 Fix error in global reactiveTimer
When reactiveTimer is created without a default reactive domain
(i.e. outside of a session, i.e. global) there's no session to
call cycleStartAction on. Instead, invalidation should proceed
right away.

Fixes #2228
2018-10-29 11:43:03 -05:00
Joe Cheng
cf79fec720 Merge pull request #2226 from rstudio/joe/bugfix/cycle-queue-stall-2
Fix input event queue stall
2018-10-25 15:19:23 -07:00
Joe Cheng
31dda45d1c Update NEWS 2018-10-25 12:13:37 -07:00
Joe Cheng
9836b72661 Fix #2225: Input event queue can stall in apps that use async 2018-10-25 12:12:21 -07:00
Winston Chang
6ede0194c6 Update license information in README 2018-10-25 12:15:57 -05:00
Winston Chang
5ec38581ca Add support for Font-Awesome 5 brands (#2221)
* Add support for Font-Awesome 5 brands

* Fix glyphicon support
2018-10-24 16:13:36 -05:00
Winston Chang
2629e59ace Re-document 2018-10-18 22:52:43 -05:00
Alan Dipert
f3eb770e20 Add to fontawesome news entry (#2214)
* Add FontAwesome upgrade information to NEWS.md

* Update NEWS.md

* Update NEWS.md
2018-10-18 11:57:29 -05:00
Winston Chang
0683b79fac Fix datepicker DST bug (#2212)
* Fix datepicker DST bug. Closes #2204

This fix is borrowed from:
13885397de (diff-dd513a8bab7ad1033c8784c4a1b9ce15)

* Update NEWS.md
2018-10-17 15:01:39 -05:00
Alan Dipert
fcd09e2bae Simplify DnD for fileInputs, fix #2142 (Firefox 57+)
- Simplified dragHover "plugin" by counting children instead of storing them.
  Counting children fixes Firefox 57+ bug (to be found or filed) that causes
  text object of input element to produce drag events
- Removed multimethod since it's no longer used anywhere
- Firefox 57+ appears not to trigger a change event when the `files` field is modified,
  which prevented uploads from occuring. This commit triggers a change event manually
  and doesn't impact the functioning of other browsers.
2018-10-08 21:24:18 -07:00
Joe Cheng
b25cb0f2d5 Merge pull request #2200 from rstudio/joe/bugfix/brush-webkit
Fix brushes not being properly cleared
2018-09-27 14:23:27 -07:00
Barret Schloerke
0704aec01b Follow js event namespacing conventions and only possibly init brush once (#2202)
* underscore the shiny_image_interaction namespace

* namespace dragstart

* use `one` instead of `on`

* compile
2018-09-27 16:24:53 -04:00
Barret Schloerke
d38b939c63 use naturalHeight and naturalWidth for default dim values. Followup comments from winston (#2201) 2018-09-27 16:16:11 -04:00
Joe Cheng
112466de1e Fix brushes not being properly cleared
Actually three separate issues addressed. Fixes #2197.

- brush.importOldBrush() was not being called anymore, due to it being
  registered as a load handler after the image was already loaded (this
  was a very recent regression, less than 24 hours old).
- Each time the brush changes, the plot is redrawn twice. This was
  because importing the old brush introduced floating point errors that
  led to a slightly different new brush being created.
- Sometimes the image's load event wasn't firing at all. This is due to
  behavior in WebKit where assigning an image's src to its existing
  value is a no-op.
2018-09-26 22:57:16 -07:00
Barret Schloerke
1d0edd2ad0 Initialize brush dims for renderImage objects (#2198)
* wait for image to be loaded in browser before initializing handlers

reverts similar behavior in 3354a47e8a

* default the height and width to the image clientHeight and clientWidth

* use raw image clientWidth and clientHeight instead of container clientWidth and clientHeight

prevents being able to brush on non image areas
2018-09-26 13:29:42 -04:00
Joe Cheng
37736119be Merge pull request #2195 from rstudio/joe/bugfix/selectize-choices-data-frame
Fix custom selectize rendering
2018-09-25 14:23:29 -07:00
Joe Cheng
c5df150acb Improve robustness of optgroup construction
Instead of providing alternate defaults for optgroupField,
optgroupLabelField, and optgroupValueField, respect the
selectize instance's settings for those fields.
2018-09-25 14:13:39 -07:00
Joe Cheng
49a346334b Fix custom selectize rendering
Fixes #2192. Two problems here:

1. It's not documented but apparently we supported data frames for
   choices in updateSelectInput/updateSelectizeInput (it doesn't
   appear to work correctly for selectInput/selectizeInput though).
   This was used in 023-optgroup-server as well as by the user who
   reported #2172.
2. The example in 023-optgroup-server was also counting on the
   default value of optgroupLabelField, which (starting post-Shiny
   v1.1) was being set to a new default of "group". That now won't
   happen unless optgroupField is also blank. I'm less confident
   about the ramifications of this change. The selectize docs with
   the relevant bits are here:
  https://github.com/selectize/selectize.js/blob/master/docs/usage.md#data_searching
2018-09-25 13:23:35 -07:00
Joe Cheng
e7c4656e8f Fix selectize bug where value is set merely on query results (#2193)
This bug is new since v1.1. When results are returned from selectize's
server-side endpoint, iff no results have been selected before, then
the control should be set to either its specified initial value (the
one specified in selectInput/selectizeInput) or, if none was provided
AND the selectize control is multiple=FALSE, then select the first
entry automatically.

That's the desired behavior; the bug was that last part, "select the
first entry automatically", was happening whether results had already
been selected before or not. This was causing merely typing in the
control to cause the value to be changed.

Fixes #2191
2018-09-25 12:21:16 -07:00
Joe Cheng
85bed0582a Rebuild JS (to update version number) 2018-09-19 09:51:36 -07:00
Joe Cheng
b9e6f867c6 Bump version 2018-09-19 09:47:41 -07:00
Joe Cheng
a5b80168bd Refactor v1.2 news 2018-09-19 09:47:35 -07:00
Alan Dipert
3cea5fb2d0 Upgrade FontAwesome to 5.3.1 (#2186)
* Upgrade FontAwesome to 5.3.1

- Upgrades FontAwesome to a new major (breaking) version, but
  is backwards compatible because we include the v4-shims CSS that maps
  old names to new.
- This is a step toward full V5 adoption that doesn't require us to
  come up with a plan for deprecating V4 icon names.
- Details: https://fontawesome.com/how-to-use/on-the-web/setup/upgrading-from-version-4
- Related to #2156 and #1966

* Improvements to icon

- Clarify in docs that fontawesome V5 icons accessible with V4-style names
- Make icons browseable: icon('address-book') will now open the Viewer
  pane of RStudio IDE so that icons can be experimented with more easily.

* Update LICENSE with CC for FontAwesome .svgs

* Update NEWS
2018-09-18 13:30:14 -07:00
Joe Cheng
c89d782048 Merge pull request #2187 from rstudio/joe/bugfix/selectize-nonempty
Fix spurious duplicate values being sent by selectInput
2018-09-18 13:27:55 -07:00
Barret Schloerke
1fd4179e07 News item for #2180 (#2189)
* news item for #2180

* fix news item
2018-09-18 14:58:57 -04:00
Joe Cheng
3b62400298 Code review feedback--use an arrow function instead of aliasing this 2018-09-18 10:52:50 -07:00
Joe Cheng
ba0fe938a1 Merge pull request #2188 from rstudio/jcheng5-patch-1
Remove unneeded Remotes
2018-09-18 10:33:47 -07:00
Joe Cheng
d4560171a8 Remove unneeded Remotes 2018-09-18 09:59:40 -07:00
Barret Schloerke
9963ba6cf5 merge master 2018-09-18 12:26:57 -04:00
Barret Schloerke
f5a23826c8 add domain to reactlog for context exit (#2180)
* add domain to reactlog for context exit

* use if statement vs %OR% when using envs

* Simplify graphExitContext domain argument
2018-09-18 12:23:01 -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
Joe Cheng
ef63679ff0 Update NEWS 2018-09-17 16:13:32 -07:00
Joe Cheng
ef7e1c385a Fix spurious duplicate values being sent by selectInput 2018-09-17 16:12:15 -07:00
Barret Schloerke
3a0a6cdbbb Add css and image locations to plot click and brush events (#2183) 2018-09-17 15:25:34 -04:00
Joe Cheng
340df3e956 Merge pull request #2174 from AliciaSchep/master
Add informative errors when xvar or yvar not in data for brushedPoints
2018-09-17 11:33:16 -07:00
AliciaSchep
eeb264da8e add var name to error msg 2018-09-17 09:45:40 -07:00
AliciaSchep
00f08b8ec6 revert two previous commits 2018-09-17 09:41:04 -07:00
AliciaSchep
67ae2a39ba update documentation to reflect new options for xvar and yvar 2018-09-16 23:07:08 -07:00
AliciaSchep
72dda25835 evaluate xvar and yvar for nearPoints and brushedPoints 2018-09-16 22:45:53 -07:00
Alan Dipert
8c9ce1994a Merge pull request #2028 from rstudio/joe/misc/selectize-upgrade
Upgrade Selectize to 0.12.4
2018-09-14 14:42:28 -07:00
Alan Dipert
606b05fdaf Merge remote-tracking branch 'origin' into joe/misc/selectize-upgrade 2018-09-14 14:40:54 -07:00
Alan Dipert
420ba9549f Merge pull request #2047 from rstudio/joe/bugfix/post-message
Fix #2033: Rstudio Viewer window not closed on shiny::stopApp()
2018-09-14 14:26:58 -07:00
Alan Dipert
51fbb5cfac Update NEWS 2018-09-14 14:25:43 -07:00
Alan Dipert
ca2c2b60f2 Grunt 2018-09-14 14:25:09 -07:00
Alan Dipert
d6064636d4 Merge pull request #2182 from rstudio/joe/misc/fontawesome-4-note
Add note that Font Awesome support is for 4.7.0
2018-09-14 13:52:30 -07:00
Joe Cheng
9646c9b0a0 Add note that Font Awesome support is for 4.7.0 2018-09-12 08:57:53 -07: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
AliciaSchep
7177618c25 add informative errors when xvar or yvar not in data for nearPoints and brushedPoints 2018-09-04 20:51:51 -07:00
Winston Chang
3bdd4af75c Merge pull request #2168 from rstudio/create-scope-dir
Check for existence of bookmark scope directory before creating
2018-08-24 13:43:15 -05:00
Winston Chang
98d4b5e487 Check for existence of bookmark scope directory before creating 2018-08-24 12:43:45 -05:00
Joe Cheng
8b5639bfdb Tweaks to NEWS 2018-08-24 10:41:08 -07:00
Joe Cheng
1c70b8b1bf Merge pull request #2147 from nathancday/master
Fixes #174, allowing specific days of the week to be disabled.
2018-08-24 10:39:12 -07:00
Joe Cheng
b5a7e03879 Merge branch 'master' into master 2018-08-24 10:38:44 -07:00
Winston Chang
32913f9d95 Merge pull request #2160 from rstudio/digest-xxhash
Use xxhash64 instead of sha256 for hash algorithm
2018-08-17 11:59:47 -05:00
Winston Chang
cbabf9a2a3 Use xxhash64 instead of sha256 for hash algorithm 2018-08-16 15:54:54 -05:00
Winston Chang
03e92c3336 Update NEWS 2018-08-10 21:18:05 -05:00
Winston Chang
997c39fdc0 Merge pull request #2125 from rstudio/plot-interact-scaled
Fix plot interaction for scaled plots
2018-08-10 21:13:16 -05:00
Winston Chang
bba2d1ee18 Grunt 2018-08-10 19:42:11 -05:00
Winston Chang
a60301810f Update coordmap tests 2018-08-10 19:42:11 -05:00
Winston Chang
6b261f76b1 Bump version and update NEWS 2018-08-07 15:25:31 -05:00
Winston Chang
3db5f21d90 Update data structure comment 2018-08-07 15:11:43 -05:00
Winston Chang
121bfcb984 Import old brush after image has loaded 2018-08-07 15:11:43 -05:00
Winston Chang
265de66946 Make sure not to have multiple reset event handlers 2018-08-07 14:51:23 -05:00
Winston Chang
79c5c9f95e Add isnan() function for IE 2018-08-07 14:51:23 -05:00
Winston Chang
3354a47e8a Add width/height to coordmap instead of using naturalWidth/Height
This eliminates the need to use an on load callback.
2018-08-07 14:51:23 -05:00
Winston Chang
a1e1416d7a More consistent use of img to css conversion functions 2018-08-07 10:48:42 -05:00
Winston Chang
24b7a9907f renderCachedPlot: add note about interactive plots to help page 2018-08-07 10:48:42 -05:00
Nate
214abd0cd4 moved new dateInput arguments to last and added formatting conditional if datesdisabled is Date object 2018-08-07 10:16:48 -04:00
Joe Cheng
0bb53e8ca5 Inputs in renderUI/uiOutput don't work with bookmarks (#2139)
* hasCurrentRestoreContext returns FALSE from server side

Fixes #2138.

* Add NEWS item for renderUI bookmarking fix
2018-08-06 15:04:16 -07:00
Winston Chang
ec12caaeba Include x and y pixelratio in coordinfo 2018-08-06 12:51:08 -05:00
Winston Chang
5bbf2aa57a Use canonical CSS property name
Firefox doesn't support shorthand properties like "border-left", but instead
requires "border-left-width".
2018-08-06 12:51:08 -05:00
Winston Chang
84ad9997da Reposition div when resized (without new image) 2018-08-06 12:51:08 -05:00
Winston Chang
9f6ce87443 Remove redundant isEquivalent function 2018-08-06 12:51:08 -05:00
Winston Chang
1ff6c382bf Remove unnecessary ggplot2 workaround 2018-08-06 12:51:07 -05:00
Winston Chang
c366c10ae1 Initialize coordmap only after image loads 2018-08-06 12:51:07 -05:00
Winston Chang
950df1e25c Add support for scaled images and brushing 2018-08-06 12:51:07 -05:00
Winston Chang
909bfa8c14 Allow plot interaction to handle scaled images 2018-08-06 12:51:07 -05:00
Winston Chang
598b48d078 DiskCache: use mtime instead of atime, check for time resolution (#2146)
* DiskCache: check for atime support while running

* Use mtime instead of atime

* Remove mtime resolution checks
2018-08-06 10:50:05 -07:00
Nate
4c7b7f236a added datesdisabled parameter to dateInput() allows disabling of specific yyyy-mm-dd format strings 2018-08-04 19:04:40 -04:00
Nate
896c5b41cb Fixes #174, allowing specific days of the week to be disabled. 2018-08-04 15:47:13 -04:00
Winston Chang
205c35d5e5 Re-document with roxygen2 6.1.0 2018-08-03 17:50:47 -05:00
Winston Chang
bf0dd7d725 Merge pull request #1997 from rstudio/plot-cache
Add renderCachedPlot()
2018-08-03 13:30:13 -05:00
Winston Chang
ba2b811172 Fix argument name in documentation 2018-07-31 15:06:59 -05:00
Winston Chang
be347c3ed4 Don't cache plotResult 2018-07-31 12:07:21 -05:00
Joe Cheng
c01abdb6a9 Merge branch 'master' into plot-cache 2018-07-26 15:52:11 -07:00
Winston Chang
95a5a965a5 Documentation fixes 2018-07-20 16:12:21 -05:00
Winston Chang
fc2849a8ff Remove env and quoted arguments 2018-07-20 16:10:15 -05:00
Winston Chang
fcc900f3e0 Simplify resize logic 2018-07-20 14:48:57 -05:00
Winston Chang
9d0bcd5637 Add shiny-scalable class 2018-07-20 14:44:21 -05:00
Winston Chang
6ebbad5273 Safer file removal order 2018-07-19 23:20:51 -05:00
Winston Chang
930459899a Small logic cleanup 2018-07-19 23:20:40 -05:00
Winston Chang
fe730e2d76 Make session cache public, so that user can set it 2018-07-19 21:57:32 -05:00
Winston Chang
e58b2e9a47 Fix app- and session-level cache initialization 2018-07-19 21:57:07 -05:00
Winston Chang
719dbab0c2 DiskCache: make destroy_on_finalize default to FALSE 2018-07-19 14:35:59 -05:00
Winston Chang
86ea023e2e Update caches from code review feedback 2018-07-19 14:33:53 -05:00
Winston Chang
bc0fb3f44c Restructure drawReactive/renderFunc code 2018-07-18 14:52:39 -05:00
Winston Chang
6d37f6b4dd Set max-width and max-height to 100% for cached plots 2018-07-12 10:55:26 -05:00
Winston Chang
958ab85297 Add exec_missing parameter 2018-07-12 10:55:26 -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
Winston Chang
a23f973433 Suppress console logging for renderCachedPlot 2018-07-02 17:04:44 -05:00
Winston Chang
c124256bad Improved logging for DiskCache and MemoryCache 2018-07-02 17:04:28 -05:00
Winston Chang
f1b035bcca Update renderCachedPlot documentation and examples 2018-07-02 15:27:51 -05:00
Joe Cheng
81cc7c591e Merge pull request #2080 from schloerke/barret-freeze-thaw
freeze/thaw reactiveValues with namespace support
2018-06-29 15:58:52 -07:00
Winston Chang
a0ca560c3b Doc entries 2018-06-29 13:24:01 -05:00
Winston Chang
d1f20a9c73 Add 'missing' parameter to get() 2018-06-29 13:23:47 -05:00
Barret Schloerke
013059c5b9 merged in master 2018-06-29 11:35:20 -04:00
Winston Chang
fe6ad235ac Add sentinel value cache miss 2018-06-28 23:58:55 -05:00
Winston Chang
67af26ffe6 When MemoryCache is used, we can cache the displayList 2018-06-26 23:11:25 -05:00
Winston Chang
0fce9de04f Change default DiskCache size 2018-06-26 22:59:32 -05:00
Winston Chang
a8b8df21d6 DiskCache: make default finalizer behavior conditional on whether a temp directory is used 2018-06-26 22:57:35 -05:00
Winston Chang
ab2e304f02 DiskCache: Add info about sharing across processes 2018-06-26 22:56:59 -05:00
Winston Chang
574f2c53d4 DiskCache: Make destroy() work robustly with multiple processes 2018-06-26 22:26:27 -05:00
Winston Chang
bc85d812d2 DiskCache: Avoid errors from a race condition 2018-06-26 16:38:43 -05:00
Winston Chang
364990a29f Fix .rds directory search pattern 2018-06-26 16:37:58 -05:00
Winston Chang
9ac9e36873 DiskCache: Check if cache has been destroyed by someone else 2018-06-26 15:48:08 -05:00
Winston Chang
6745e09688 Add support for promisey cache key 2018-06-25 21:13:55 -05:00
Winston Chang
e758927c84 DiskCache: Add warning when caching reference objects 2018-06-25 15:53:47 -05:00
Winston Chang
90fbf7d50f Add comment about pruning 2018-06-25 15:34:43 -05:00
Winston Chang
75f1ee0082 Prune after setting value 2018-06-25 15:30:30 -05:00
Winston Chang
750aaf451a NEWS edits 2018-06-25 14:11:42 -05:00
Winston Chang
b44bfe9109 Grunt 2018-06-25 14:10:22 -05:00
Winston Chang
aa392f8563 Merge pull request #2102 from tmastny/selectize-remote
Resolves #1933: Serve-side selectize extended to all inputs
2018-06-25 14:07:56 -05:00
Winston Chang
ac7228f6c4 Merge branch 'master' into selectize-remote 2018-06-25 14:07:20 -05:00
Winston Chang
dcb12addaa Merge pull request #2108 from tmastny/par-oma
Resolves #1935: Fix coordinate outputs when modifying outer margins
2018-06-25 14:05:39 -05:00
Winston Chang
ad398b5f8a Merge branch 'master' into par-oma 2018-06-25 14:05:30 -05:00
Timothy Mastny
803cb4806e add new item 2018-06-25 14:04:31 -05:00
Timothy Mastny
1a468bbb61 add news items 2018-06-25 14:02:18 -05:00
Winston Chang
c332c051f3 Merge pull request #2099 from tmastny/slider-type
Fix #2019: `updateSliderInput` changes formatting
2018-06-25 14:00:18 -05:00
Timothy Mastny
db48befcb7 removed TODO comments 2018-06-25 13:51:24 -05:00
Winston Chang
b02edb05ac DiskCache: Use temp file when setting value 2018-06-25 11:56:01 -05:00
Winston Chang
d7009fd1c8 DiskCache: Don't call exists() before reading file 2018-06-25 11:55:29 -05:00
Winston Chang
ce3755676c Fix files that were split 2018-06-25 10:45:12 -05:00
Winston Chang
db3c1b728d Merge branches 'plot-cache-split-1' and 'plot-cache-split-2' into plot-cache 2018-06-25 10:42:52 -05:00
Winston Chang
1761de4740 Rename cache.R cache-context.R 2018-06-25 10:42:49 -05:00
Winston Chang
09d496925b Rename cache.R cache-memory.R 2018-06-25 10:41:49 -05:00
Winston Chang
3af5327f1c Rename cache.R cache-disk.R 2018-06-25 10:41:04 -05:00
Winston Chang
06cb14d7ec Rename $has method to $exists 2018-06-22 23:27:49 -05:00
Winston Chang
7be1a9d7fa Add memoryCache and make it the default 2018-06-22 23:08:39 -05:00
Winston Chang
95243fb35c DiskCache: Add LRU eviction policy 2018-06-22 22:44:27 -05:00
Winston Chang
26438a3979 DiskCache cleanup 2018-06-22 19:32:29 -05:00
Winston Chang
28db097a71 Use promise domain for currentOutputName 2018-06-22 19:31:20 -05:00
Winston Chang
76fdd8ae04 Make renderCachedPlot work with async 2018-06-22 16:47:55 -05:00
Barret Schloerke
003dc39d76 add shinyreactlog as remote 2018-06-22 10:49:57 -04:00
Timothy Mastny
3a73bfb142 changed output coordinate system to "ndc" to account for margin changes 2018-06-21 17:48:16 -05:00
Timothy Mastny
a24bdabf08 Updates to Winston's feedback: removed for ... of iteration that is not supported in IE. 2018-06-21 16:17:42 -05:00
Winston Chang
8815f293a2 Update R version check information 2018-06-21 15:55:35 -05:00
Barret Schloerke
0b04c28011 move renderReactLog calculation above addResourcePath 2018-06-21 16:54:23 -04:00
Winston Chang
9af2775539 Fix absolutePath to correctly handle absolute paths 2018-06-21 15:48:54 -05:00
Winston Chang
ae5deae6e9 Use output name in cache key 2018-06-21 14:55:30 -05:00
Winston Chang
61c2126498 Add diskCache function, and app- and session-level caches 2018-06-21 14:44:09 -05:00
Timothy Mastny
881fe0cfce explicitly set number to null instead of implicitly for better documentation 2018-06-21 14:30:15 -05:00
Timothy Mastny
a999bf389c update NEWS.md 2018-06-21 14:30:15 -05:00
Timothy Mastny
ff3b97b630 refactored data type checking for consistency 2018-06-21 14:30:15 -05:00
Timothy Mastny
639b520d39 updateInputSlider can now change from date to date-time formatting. fixes #2019 2018-06-21 14:30:15 -05: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
Timothy Mastny
19dc29ea17 changes as per Winston's feedback; additional comments, and edge cases, and removed unnecessary JS. 2018-06-21 12:47:14 -05:00
Barret Schloerke
ae9d38b59c remove old .graph methods and use shinyreactlog pkg for rendering 2018-06-21 10:31:12 -04:00
Timothy Mastny
97bebae8d7 fixed default selection for multiple-select 2018-06-20 15:41:35 -05:00
Timothy Mastny
cf534ce6da remove "thiz"s and replace with explicit "selectize" 2018-06-20 15:41:35 -05:00
Timothy Mastny
f25f691a55 fix selectize for default selected value NULL 2018-06-20 15:41:35 -05:00
Timothy Mastny
cbebf8be7b improve performance of R input processing 2018-06-20 15:41:35 -05:00
Timothy Mastny
165ce26b2f Fixes #1933. Fixed JSON encoding of input data, and added more optgroup controls on JS-side 2018-06-20 15:41:35 -05: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
Winston Chang
572c863bff Merge pull request #2106 from schloerke/js_patch
compile with grunt to get version in js code (v1.1.0.9000)
2018-06-20 12:47:43 -05:00
Barret Schloerke
d3c85d67b8 gruntfile should run 'default' task to make sure everything is built like normal 2018-06-20 13:31:10 -04:00
Barret Schloerke
ff3434f77e add a R test to make sure shiny.js {{ VERSION }} was replaced 2018-06-20 12:53:07 -04:00
Barret Schloerke
762528c044 add a grunt test to make sure {{ VERSION }} was replaced 2018-06-20 12:52:46 -04:00
Barret Schloerke
1891af0d4a compile with grunt 2018-06-20 11:39:12 -04:00
Winston Chang
583ad036f7 Streamline renderCachedPlot API 2018-06-19 16:00:49 -05:00
Barret Schloerke
893b9c1b38 merged master -> barret/reactlog 2018-06-19 09:24:39 -04:00
Winston Chang
ac92bf98d4 WIP 2018-06-18 16:25:36 -05:00
Winston Chang
fd90ff7ff7 Use DiskCache class 2018-06-18 16:25:36 -05:00
Winston Chang
d06dbbe5db Change cacheResetEventExpr to cacheResetExpr 2018-06-18 16:25:36 -05:00
Winston Chang
bffc4995d7 Rename normalizePath2 to absolutePath 2018-06-18 16:25:36 -05:00
Winston Chang
4b8b406bed Add sizeGrowthRatio function 2018-06-18 16:25:36 -05:00
Winston Chang
5641153272 renderCachedPlot: pass ... args 2018-06-18 16:25:36 -05:00
Winston Chang
08c6c7781f Rename cacheClearExpr to cacheResetEventExpr 2018-06-18 16:25:36 -05:00
Winston Chang
ad2ad391a7 Isolate user code 2018-06-18 16:25:36 -05:00
Winston Chang
caac88be0d Don't re-run user code when only dimensions change 2018-06-18 16:25:36 -05:00
Winston Chang
10660aa373 Rename cacheInvalidationExpr to cacheClearExpr 2018-06-18 16:25:36 -05:00
Winston Chang
cfaf97aee4 Add entries to staticdocs 2018-06-18 16:25:36 -05:00
Winston Chang
55f14576f0 Remove createCachedPlot function 2018-06-18 16:25:36 -05:00
Winston Chang
4dca94ac99 Code reorganization 2018-06-18 16:25:36 -05:00
Winston Chang
14779d3d27 Only invalidate plot when fitted dimensions change 2018-06-18 16:25:36 -05:00
Winston Chang
66d1e710b5 Allow renderCachedPlot to take a directory for scope 2018-06-18 16:25:36 -05:00
Winston Chang
12ae3c17e9 Allow onStop to take a NULL session 2018-06-18 16:25:35 -05:00
Winston Chang
36e4da0709 Add app/session scoping for renderCachedPlot 2018-06-18 16:25:35 -05:00
Winston Chang
91631cb081 Note bug fix in R 2018-06-18 16:25:35 -05:00
Winston Chang
224f082e1f Implement cache invalidation 2018-06-18 16:25:35 -05:00
Winston Chang
76b239a6ea Convert renderCachedPlot to take expr and cacheKeyExpr 2018-06-18 16:25:35 -05:00
Winston Chang
cb476b510d Initial implementation of renderCachedPlot 2018-06-18 16:25:35 -05:00
Winston Chang
334f233968 Move plot caching code into separate file 2018-06-18 16:25:35 -05:00
Winston Chang
e1f21250b9 Rename plotCache to createCachedPlot 2018-06-18 16:25:35 -05:00
Winston Chang
8d087e4f20 Get output info and auto-size plot cache images 2018-06-18 16:25:35 -05:00
Winston Chang
9e35e8c947 Allow user code to pass width/height/pixelratio 2018-06-18 16:25:35 -05:00
Winston Chang
f98faef024 Document cache scoping and minor code cleanup 2018-06-18 16:25:35 -05:00
Winston Chang
0f9346ead5 Add different scoping levels for plotCache 2018-06-18 16:25:35 -05:00
Winston Chang
fc8118c694 First implementation of plotCache 2018-06-18 16:25:35 -05:00
Barret Schloerke
6f8166ca0f add todo 2018-06-14 16:06:12 -05:00
Joe Cheng
026b7278c1 Merge pull request #2091 from schloerke/barret-varSelectInput
Variable Select Input
2018-06-14 09:39:09 -05:00
Barret Schloerke
375a7e7e5c respect existing class values and add tests 2018-06-11 10:31:49 -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
7a1aecb1a4 varSelectInput doc tweaks 2018-06-08 10:59:45 -04:00
Barret Schloerke
b3690e8680 add bullets in details for output return value (update corresponding example) 2018-06-08 10:51:37 -04:00
Barret Schloerke
97d490cfb4 remove rogue staticdocs fn 2018-06-07 16:52:35 -04:00
Barret Schloerke
2081dda6fc merged master -> varSelectInput 2018-06-07 16:46:31 -04:00
Barret Schloerke
ea912fc50c staticdocs for varSelectInput 2018-06-07 16:43:20 -04:00
Barret Schloerke
b655fdf68f added news for varSelectInput 2018-06-07 16:43:05 -04:00
Barret Schloerke
4749f46a4f add shiny.symbol and shiny.symbolList tests 2018-06-07 16:10:47 -04:00
Barret Schloerke
f95bb9c82d compile and document 2018-06-07 16:10:24 -04:00
Barret Schloerke
6529529cdb add new input 'varSelectInput()' and input binding 'shiny.symbol' and 'shiny.symbolList' 2018-06-07 16:10:01 -04:00
Tim Mastny
3a2a3f21d4 Improve optgroup documentation per #1864 (#2084)
* improves optgroup documentation per #1864

* change PR number in NEWS.md

* change addresses to addressed
2018-06-07 12:09:45 -05:00
Tim Mastny
631bc1c481 Addresses #2042: lighten dates outside of range for datepicker (#2087)
* dehighlight dates outside of range for datepicker

* override color in shiny.css instead of package css

* refactored css styling to use specific references instead of !important
2018-06-07 11:57:27 -05: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
597af36759 added news item 2018-05-31 10:31:35 -04:00
Barret Schloerke
691062f687 white space 2018-05-31 10:30:19 -04:00
Barret Schloerke
6651c4ea48 when freezing a reactivevalues key, use the name space similar to $.reactivevalues 2018-05-31 10:23:30 -04:00
Barret Schloerke
116559e5a0 use utils namespace 2018-05-31 10:21:55 -04:00
Barret Schloerke
7818e8ed64 white space 2018-05-31 10:21:42 -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
Winston Chang
2880391620 runApp: add support for IPv6 addresses 2018-05-25 16:19:51 -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
Joe Cheng
f742605a1b Bump version to *.9000. Back to work! 2018-05-17 17:20:17 -07: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
Winston Chang
2afff67e89 Bump version to 1.1.0 2018-05-16 15:18:59 -07:00
Winston Chang
fe7bd53250 Bump httpuv version and add NEWS note 2018-05-16 15:18:59 -07:00
Joe Cheng
6df3509869 Merge pull request #2062 from rstudio/joe/bugfix/windows-rounding
Fix #2061: Tests failing on Windows due to rounding errors
2018-05-16 15:17:04 -07:00
Joe Cheng
062dc771aa Fix #2061: Tests failing on Windows due to rounding errors 2018-05-16 14:44:34 -07: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
Joe Cheng
d4688db31c Update NEWS 2018-05-03 14:08:52 -07:00
Joe Cheng
c49a289619 Fix #2033: Rstudio Viewer window not closed on shiny::stopApp() 2018-05-03 14:06:34 -07: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
Joe Cheng
9c3a0c86ca Take dependency on later >=0.7.2 2018-05-01 20:37:25 -05: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
Winston Chang
01b24e984c Merge pull request #2038 from rstudio/joe/bugfix/cycle-start-bugs
Fix #2037: With enableBookmarking="url", clientData is not available …
2018-04-25 13:34:34 -05:00
Joe Cheng
9dd4302fe9 Fix #2037: With enableBookmarking="url", clientData is not available when observers are first run
Also fixed reactiveTimer firing even while async tasks are active
2018-04-25 10:54:00 -07:00
Winston Chang
c2f03aa833 Merge pull request #2036 from rstudio/joe/misc/renderui-experimental
Remove "experimental feature" tag from renderUI
2018-04-24 12:42:04 -05:00
Barbara Borges Ribeiro
2260459422 brought observeEvent/eventReactive documentation up to date 2018-04-24 17:44:54 +01: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
Winston Chang
e838cc3fe9 Re-document 2018-04-24 10:24:52 -05:00
Winston Chang
74457b95e9 NEWS edits 2018-04-24 10:17:03 -05:00
Barret Schloerke
ceb19c7573 use an rLog object to do all logging 2018-04-24 10:49:16 -04:00
Joe Cheng
d5754515a6 Remove "experimental feature" tag from renderUI 2018-04-23 17:05:08 -07:00
Joe Cheng
4ed13c04f5 Merge pull request #2032 from rstudio/fix-flushed-callbacks
Set default reactive domain when executing flushedCallbacks. Fixes #1975
2018-04-21 10:02:10 -07:00
Winston Chang
5a5294cc44 Set default reactive domain when executing flushedCallbacks. Fixes #1975 2018-04-21 11:46:50 -05:00
Joe Cheng
3a5d48ae7c Remove outdated Remotes
Live code review by @wch
2018-04-19 14:59:10 -07:00
Joe Cheng
6b605804d2 Upgrade Selectize to 0.12.4 2018-04-19 14:19:12 -07:00
Joe Cheng
ffe883ab72 Merge pull request #2022 from rstudio/joe/bugfix/timer-leak
Fix #2021: Memory leak with reactiveTimer and invalidateLater
2018-04-19 14:16:35 -07:00
Joe Cheng
31c4e0fdfe Add test to demonstrate vectorized unscheduling 2018-04-19 12:52:30 -07:00
Joe Cheng
66f970e0bd Merge pull request #2026 from rstudio/fix-ggplot2
Fix plot coordmap for devel version of ggplot2. Closes #2016
2018-04-19 12:46:38 -07:00
Winston Chang
07b223dcb0 Fix plot coordmap for devel version of ggplot2. Closes #2016 2018-04-19 11:40:26 -05:00
Joe Cheng
f1e27b6ffb Fix #1922: Warning: partial match of 'y' to 'yintercept' 2018-04-19 10:00:24 -05:00
Joe Cheng
389463aea5 Merge branch 'joe/feature/undedupe-inputs' 2018-04-18 19:33:37 -07:00
Joe Cheng
b11ab9a31c Update NEWS 2018-04-18 12:53:31 -07:00
Joe Cheng
5fe85b07b7 Merge remote-tracking branch 'andrewsali/master' 2018-04-18 12:52:40 -07:00
Joe Cheng
3c7b1e7d21 Update NEWS 2018-04-18 12:32:16 -07:00
Joe Cheng
c556cf1e69 Fix #2021: Memory leak with reactiveTimer and invalidateLater 2018-04-18 12:30:14 -07:00
Joe Cheng
722e5fb5f7 Modify internal JS to use Shiny.setInputValue, {priority: "event"} 2018-04-18 12:05:18 -07:00
Joe Cheng
e90cc591b7 Update NEWS 2018-04-18 11:57:55 -07:00
Barret Schloerke
7336d327b3 first pass at adding domain to all rlog functions 2018-04-18 11:49:11 -04:00
Joe Cheng
c555725201 Change {immediate: ...} to {priority: "deferred|immediate|event"}
This was the product of a long discussion between @wch, @alandipert, @bborgesr
and myself. The conflation of immediate (no throttle/debounce) and non-dedupe
in a single "immediate" flag was deemed unacceptable, because UI controls often
want immediacy but also dedupe. Introducing a second "dedupe" flag would work
but {immediate: false, dedupe: false} doesn't make much sense, and dedupe not
only implies that InputNoResendDecorator should behave differently but also
InputBatchSender (i.e. no deduplication AND no coalescing).

We decided to remove the "immediate" boolean option and replace it with a
string option that would have three possibilities at this time. The only con
to this approach is if anyone is calling onInputChange with immediate:true
today, and I can't imagine anyone is. The immediate flag only has any effect
if the input id that's being set has been put in debounce/throttle mode, and
I don't even think that is documented today, and I'm not even sure it's
possible to do it from custom JS (that's not part of a custom input binding).
2018-04-17 16:39:05 -07: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
Joe Cheng
cef1f3c7ee withReactiveDomain now acts as a promise domain
Without this change, async handlers won't return any
value for getDefaultReactiveDomain().

    library(shiny)
    library(promises)

    ui <- fluidPage(
      p("This app tests if async handlers have reactive domains. You'll get a yes/no answer below."),
      h3(
        "Does it work?",
        textOutput("answer", inline = TRUE)
      )
    )

    server <- function(input, output, session) {
      output$answer <- renderText({
        promise_resolve(TRUE) %...>% {
          if (!is.null(getDefaultReactiveDomain()))
            "Yes!"
          else
            "No :("
        }
      })
    }

    shinyApp(ui, server)
2018-04-16 20:51:46 -05:00
Joe Cheng
e5d1fa1ea4 Fix #2008: Allow eventReactive and observeEvent eventExprs to be async (#2014)
* Fix #2008: Allow eventReactive and observeEvent eventExprs to be async

This makes it possible to monitor e.g. async reactives.

In the process of fixing this, also discovered that observers don't
filter out shiny.silent.error (i.e. req(FALSE)) when they come back
from async operations. For example, this will kill the current
Shiny session instead of being ignored:

  observe({
    promise_resolve(TRUE) %...>%
      {req(FALSE)}
  })

This issue is also fixed in this commit.

* Enable deep stack trace by default, now that it's fast
2018-04-16 20:50:28 -05:00
Joe Cheng
3ccf2937b4 Fix #928: allow inputs to trigger reactive flow even if the value of input hasn't changed
We already had an `immediate` input option, which was used to override client side rate
limiting mechanisms (debounce/throttle). This commit extends the semantics of that option
to also mean that duplicate values should not be ignored on the client side.

Previous to this commit, circumventing the client side dedupe logic was not enough. The
server side ReactiveValues object was also subject to deduping. With this commit, the
low-level ReactiveValues class's constructor now has a `dedupe` option, which defaults
to TRUE; the ReactiveValues used for a session's input has it turned to FALSE. I figure
if I had to work this hard to get the client to stop sending duplicates, and the input
values are only expected to ever be updated by the client, then there's really no reason
for server side deduping to be performed for this particular ReactiveValues object.

It would make sense as a future feature to also make deduping optional for user-created
reactiveValues and reactiveVal objects.
2018-04-16 18:37:47 -07:00
Joe Cheng
b7b696630f Fix #2003: Long stack traces are truncated 2018-04-16 17:16:12 -05:00
Joe Cheng
84aba546bc Fix #2000: Implicit calls to xxxOutput not working inside modules (#2010)
* Fix #2000: Implicit calls to xxxOutput not working inside modules

* Add comment, update NEWS

* Credits in NEWS
2018-04-16 16:57:13 -05: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
Winston Chang
741236df56 Merge pull request #2011 from rstudio/joe/feature/output-null
Fix #1989: Allow outputs to be removed by assigning NULL to them
2018-04-12 16:29:48 -05:00
Winston Chang
e3584f0a61 Merge pull request #2013 from rstudio/joe/bugfix/render-plot-args
renderPlot's ... args were being dropped
2018-04-12 16:27:46 -05:00
Joe Cheng
432482c5a7 renderPlot's ... args were being dropped 2018-04-12 11:57:32 -07:00
Joe Cheng
323ad46bba Implement #1989: Allow outputs to be removed by assigning NULL to them 2018-04-11 18:40:32 -07:00
Alan Dipert
ace0fe1802 Merge pull request #2005 from rstudio/alan/bugfix/dndfix
Fix dragging and dropping in the presence of jQuery 3.0
2018-04-11 11:53:42 -07:00
Alan Dipert
36f244fece Merge branch 'master' into alan/bugfix/dndfix 2018-04-11 11:52:23 -07:00
Joe Cheng
99e5ef99ec Move some bullets around 2018-04-10 10:08:15 -07:00
Alan Dipert
d6d3ed5bbc NEWS 2018-04-10 08:40:40 -07:00
Alan Dipert
49d09ecf30 Grunt 2018-04-10 08:30:46 -07:00
Alan Dipert
c529a03096 DnD: Fix in the presence of jQuery 3.0 (removed .size()) 2018-04-10 08:29:03 -07:00
Andras Sali
101d9aa0fa Move trigger after value change 2018-04-07 16:51:03 +02:00
Andras Sali
b4864e1180 Trigger shiny:value even if same data is received. Fixes #1978 2018-04-07 16:46:34 +02:00
Winston Chang
cba7304ab9 Merge pull request #1996 from rstudio/fix-selectize
fix selectize capitalization (regression introduced by #1861)
2018-04-05 11:58:20 -05:00
Barbara Borges Ribeiro
2d058b0519 move attribute setting to after choices is reassigned (this was getting lost after the changes in #1861). 2018-04-04 19:55:15 +01:00
Barbara Borges Ribeiro
eed9231884 fix selectize capitalization (regression introduced by #1861) 2018-04-04 14:00:44 +01:00
Joe Cheng
5c84eaf2a5 Merge pull request #1990 from rstudio/joe/feature/better-stacks
Better stack traces
2018-03-27 19:31:51 -07:00
Joe Cheng
2ef7226be0 Use seq_along instead of 1:length(x)
It behaves when length(x) == 0
2018-03-27 18:30:24 -07:00
Joe Cheng
e5d1c61cdf Merge branch 'master' into joe/feature/better-stacks 2018-03-27 18:04:05 -07:00
Joe Cheng
e635055ab8 Update NEWS 2018-03-27 18:02:41 -07:00
Joe Cheng
d8d4e3b262 Don't error when entire stack trace is stripped/pruned 2018-03-27 16:35:45 -07:00
Joe Cheng
8f29543479 Use qualified name for utils::tail (R CMD check NOTE) 2018-03-27 16:19:28 -07:00
Joe Cheng
c11a8ea24b Fix tests 2018-03-27 15:35:29 -07:00
Joe Cheng
86646d7faa Make srcref offsetting optional 2018-03-27 15:35:06 -07:00
Joe Cheng
6e44915e08 Merge pull request #1984 from rstudio/joe/feature/faster-deep-stacks
Lazily format stack traces
2018-03-27 10:31:00 -07:00
Joe Cheng
f8b99cf4e9 Add deprecation docs 2018-03-26 11:38:12 -07:00
Joe Cheng
0e7d6ff192 Refactoring and deprecation in conditions.R
- Refactor printError so a working printStackTrace falls out
- Deprecate extractStackTrace and formatStackTrace, see if anyone uses them
2018-03-26 11:35:50 -07:00
Barbara Borges Ribeiro
66501dac97 Add new autoclose = TRUE param to dateInput() and dateRangeInput (#1987)
* Add new `autoclose = TRUE` param to both dateInput() and dateRangeInput()

* added NEWS item
2018-03-23 09:40:19 -07:00
Joe Cheng
195907b2ec printError implements lobstr::cst analysis and deep-stack-aware frame suppression 2018-03-22 12:22:46 -07:00
Joe Cheng
be11b44864 First steps to improved stack traces
- Adds functions we will need for tracking ..stacktraceon/off..
  across deep stacks
- Adds functions we will need for pruning according to lobstr::cst
  logic

These functions are not yet integrated, that will occur in a
separate commit.
2018-03-20 16:45:47 -07:00
Joe Cheng
bc7cd21c13 Update NEWS.md 2018-03-20 16:39:52 -07:00
Carson Sievert
0555cbdd28 relay offsetWidth/offsetHeight of htmlwidgets to clientData (#1981)
Addresses #1980
2018-03-20 16:37:43 -07:00
Joe Cheng
97498451bb Lazily format stack traces
With deep stack traces enabled, whenever then() is called, we need
to grab the current stack, just in case a downstream callback throws
an error and we need to form a deep stack trace.

Previously, we were calling formatStackTrace at the time that we
grab the current stack (i.e. no error has happened yet) because I
wasn't sure whether holding a reference to sys.calls() for a long
time was a good idea from a garbage collection perspective; would it
prevent the stack frame environments from being collected? But the
answer is no, sys.calls() is just calls, which can be confirmed with
.Internal(inspect(sys.calls()).

By deferring the formatStackTrace call to when we actually need to
print the stack trace, we save ourselves a ton of work--it turns out
it's quite expensive to format the stack traces, much more expensive
than sys.calls() alone.
2018-03-20 12:35:17 -07:00
Joe Cheng
2e0d9b5475 Bump httpuv dependency version 2018-03-18 19:07:43 -07:00
Barbara Borges Ribeiro
62395f3103 Improve error handling when addResourcePath() fails (especially for runtime: shiny_prerendered documents) (#1968)
* A copy of yihui's PR for rmarkdown (https://github.com/rstudio/rmarkdown/pull/1171/) to avoid to error "Error in normalizePath: path[1]="": No such file or directory" when running any tutorial

* first try

* limited the scope of the `tryCatch` wrapper to the one important line that needed it; added news item
2018-03-16 15:36:12 -07:00
Winston Chang
6b31cd6aee Merge pull request #1965 from rstudio/joe/bugfix/plot-dim-error
Fix #1964: renderPlot cache breaks when width/height throw
2018-03-01 11:12:56 -06:00
Joe Cheng
e67a8ba369 Fix #1964: renderPlot cache breaks when width/height throw
Fixed by moving the isolate(getDims()) call into the (effectively)
try/catch that does a non-isolated getDims() if an error occurs
2018-02-28 15:40:55 -08:00
Joe Cheng
133d301925 Merge pull request #1961 from rstudio/fix-date-sliders-bookmarking
Fix URL-encoded bookmarking with date/date-time sliders
2018-02-28 14:26:50 -08:00
Joe Cheng
17c40a5d1d Merge pull request #1960 from rstudio/slider-formatting
Don't show commas after decimal mark in sliderInput
2018-02-28 13:01:20 -08:00
Winston Chang
042211e5f6 Grunt 2018-02-28 14:34:09 -06:00
Winston Chang
d12830d700 sliderInput: don't show commas after decimal 2018-02-28 14:33:42 -06:00
Winston Chang
b411c70280 Fix URL-encoded bookmarking with date/date-time sliders 2018-02-27 20:42:11 -06:00
Winston Chang
2bc22cc7d5 Merge pull request #1955 from rstudio/update-slider
Update ion.rangeSlider to 2.2.0
2018-02-27 13:21:09 -06:00
Joe Cheng
b4c189c89b Merge pull request #1956 from rstudio/fix-slider-rounding
Avoid rounding errors in sliderInput
2018-02-27 10:55:11 -08:00
Winston Chang
fe3f351a2d Avoid rounding errors from pretty(). Fixes #1006 2018-02-27 10:50:36 -06:00
Winston Chang
076be9cba7 Remove unused keyboard_step parameter
keyboard_step was removed in ion.rangeSlider 2.2.0.
2018-02-26 15:57:30 -06:00
Winston Chang
f28dcd85fb Update to ion.rangeSlider 2.2.0 2018-02-26 15:57:30 -06:00
Joe Cheng
8e0f17c9d7 Merge pull request #1954 from rstudio/fix-bookmark-restore
Look for restore context associated with session
2018-02-26 09:25:33 -08:00
Winston Chang
d73817a0db Look for restore context associated with session. Fixes #1948 2018-02-26 10:55:38 -06:00
Joe Cheng
11874db825 Remove background-thread branch from httpuv remote 2018-02-16 15:41:32 -08:00
Joe Cheng
5d5a43ce90 Merge pull request #1932 from rstudio/async
Async
2018-02-16 07:41:36 -08:00
Winston Chang
75e548caab For installation of Rtools on Appveyor 2018-02-16 09:15:40 -06:00
Joe Cheng
c901e7ba06 Update TODO-promises.md 2018-02-13 15:23:06 -08:00
Joe Cheng
b1dc3dfca1 Restore label to plotObj reactive 2018-02-09 11:12:46 -08:00
Joe Cheng
ce4ed20c69 Fix remaining failing tests 2018-02-09 11:12:46 -08:00
Joe Cheng
d44df7f860 Stack traces were being lost inside hybrid_chain 2018-02-09 11:12:46 -08:00
Joe Cheng
54353e0e1f Fix coordmap unit tests 2018-02-09 11:12:46 -08:00
Joe Cheng
1c042b6efb Add to .Rbuildignore 2018-02-09 11:12:46 -08:00
Joe Cheng
b8df1f29c4 Remove unused function 2018-02-09 11:12:46 -08:00
Joe Cheng
18252f5b03 Use later >= 0.7.1 2018-02-09 11:12:46 -08:00
Joe Cheng
881370f284 Remove extraneous comments 2018-02-09 11:12:46 -08:00
Joe Cheng
35d1747bc3 Don't allow invalidation from a child process 2018-02-09 11:12:46 -08:00
Joe Cheng
91ac89a54e Update TODOs 2018-02-09 11:12:46 -08:00
Joe Cheng
3c694d9bd9 More robust process identification (thanks @HenrikBengtsson) 2018-02-09 11:12:46 -08:00
Joe Cheng
6a78e9df77 Detect (probably inadvertent) attempts to inherit reactive contexts across processes
Example (we want this to fail):

library(shiny)
library(future)
plan(multicore)

r <- reactiveVal(TRUE)
isolate({
  f <- future({
    r()
  })
  value(f)
})
2018-02-09 11:12:45 -08:00
Joe Cheng
078c6eb30a Add TODO 2018-02-09 11:12:45 -08:00
Joe Cheng
d35c6002a6 Respect pixelratio (retina) when redrawing cached images 2018-02-09 11:12:45 -08:00
Joe Cheng
f23fc3beaa Plots were not respecting pixel ratio (retina) 2018-02-09 11:12:45 -08:00
Joe Cheng
5a352e5ace Update TODOs 2018-02-09 11:12:45 -08:00
Joe Cheng
27cae0065e Fix bug where req(cancelOutput=TRUE) would leave things grey
This was introduced by some changes to shinyapp.js that were necessary
before async outputs and sync outputs were held/flushed together. Now
that async/sync outputs are held/flushed together, these changes are
not necessary and removing them fixes the problem.

The test app is in shiny-examples/205-async-req. I also moved a test
app from manualtests/async/download.R to shiny-examples/204-async-download.
2018-02-09 11:12:45 -08:00
Joe Cheng
50be2993fa Add TODO 2018-02-09 11:12:45 -08:00
Joe Cheng
d9ea15e9bc Update TODOs 2018-02-09 11:12:45 -08:00
Joe Cheng
03b1d45d7e Make deep stack traces opt-in; fix imports 2018-02-09 11:12:45 -08:00
Joe Cheng
e48d6878c4 Update TODOs 2018-02-09 11:12:45 -08:00
Joe Cheng
1a3b255848 Update TODOs 2018-02-09 11:12:45 -08:00
Joe Cheng
f00aa94d7e Suspend session during async download operation 2018-02-09 11:12:45 -08:00
Joe Cheng
f7980b19f4 Update TODOs 2018-02-09 11:12:45 -08:00
Joe Cheng
6a1f9677a5 Support async downloadHandler content functions
If a downloadHandler content function returns a promise (or future)
then Shiny will wait for the promise to resolve before serving up
the file download.
2018-02-09 11:12:45 -08:00
Joe Cheng
e844bb36a5 Update TODOs 2018-02-09 11:12:45 -08:00
Joe Cheng
ae364adfc2 wip2 2018-02-09 11:12:45 -08:00
Joe Cheng
c14a382b90 wip 2018-02-09 11:12:45 -08:00
Joe Cheng
da9c2beaaf Update TODO 2018-02-09 11:12:45 -08:00
Joe Cheng
a4a56476db Update TODO 2018-02-09 11:12:45 -08:00
Joe Cheng
39d3784b9b async support for renderDataTable 2018-02-09 11:12:45 -08:00
Joe Cheng
7d29df58f1 Support same-tick execution for synchronous outputs 2018-02-09 11:12:45 -08:00
Joe Cheng
05aa413683 promises::finally() was missing namespace prefix 2018-02-09 11:12:45 -08:00
Joe Cheng
132f90f45b Support promise domain wrapSync; fix renderPrint visibility
Also introduce promise_chain and hybrid_chain, for assembling chains of
operations without involving magrittr-style operators
2018-02-09 11:12:45 -08:00
Joe Cheng
4526fd1917 Update TODO 2018-02-09 11:12:45 -08:00
Joe Cheng
2602dc15b0 Changes to flush cycle to support async
- Moved (in|de)crementBusyCount calls out of Context and into Observer
- decrementBusyCount is (effectively) deferred for async observers until
  the async operation is complete
- invalidateLater didn't force(session), almost certainly was buggy
- invalidateLater, reactiveTimer, and manageInputs all now use a new
  session$cycleStartAction, which delays their effect until observers
  (including async ones) are done executing
2018-02-09 11:12:45 -08:00
Joe Cheng
2314f63424 Fix broken .shiny__stdout mechanism 2018-02-09 11:12:45 -08:00
Joe Cheng
c2410600ee Refactor list of TODOs 2018-02-09 11:12:45 -08:00
Joe Cheng
f7e4702685 Restore writing of _n_flushReact and _x_flushReact to stdout 2018-02-09 11:12:45 -08:00
Joe Cheng
71682512c4 Refactor flush cycle 2018-02-09 11:12:45 -08:00
Joe Cheng
20b82fbf77 Cleanup R CMD check 2018-02-09 11:12:45 -08:00
Joe Cheng
631f09847d Update TODOs 2018-02-09 11:12:45 -08:00
Joe Cheng
671585f68a Update TODOs 2018-02-09 11:12:45 -08:00
Joe Cheng
5feed888bb Add promises to remotes 2018-02-09 11:12:45 -08:00
Joe Cheng
47bef0f1b0 Remove extraneous debugging code 2018-02-09 11:12:45 -08:00
Joe Cheng
c1dc662a40 "promise" package was renamed to "promises" 2018-02-09 11:12:45 -08:00
Joe Cheng
16e1721fe8 Add TODO comment 2018-02-09 11:12:45 -08:00
Joe Cheng
f406e13600 Add TODO 2018-02-09 11:12:45 -08:00
Joe Cheng
9063133a7b Fix various rendering bugs 2018-02-09 11:12:45 -08:00
Joe Cheng
3fbb436187 Add call_async temporarily; this should probably live somewhere else 2018-02-09 11:12:45 -08:00
Joe Cheng
7c845d070b Sync to changes in promise. run_now aggressively in serviceApp. 2018-02-09 11:12:45 -08:00
Joe Cheng
5e905aa73e Implement execOnResize 2018-02-09 11:12:45 -08:00
Joe Cheng
e15654f265 Minor renderPlot cleanup 2018-02-09 11:12:45 -08:00
Joe Cheng
369c067efc Update TODO 2018-02-09 11:12:45 -08:00
Joe Cheng
c037e69793 Fix ggplot brushing 2018-02-09 11:12:45 -08:00
Joe Cheng
8c935ff44e Use proper promise:: prefix 2018-02-09 11:12:45 -08:00
Joe Cheng
74bf8b0554 renderPlots works!!! Testing needed. 2018-02-09 11:12:45 -08:00
Joe Cheng
6345972efe Adapt promise domains to handle multiple arguments 2018-02-09 11:11:35 -08:00
Joe Cheng
16242e87a1 Some steps toward renderPlot working. Move to promise package instead of system2.5. 2018-02-09 11:11:34 -08:00
Joe Cheng
8155320ba5 wip 2018-02-09 11:09:28 -08:00
Winston Chang
39a7f63972 Update NEWS 2018-01-29 13:45:23 -06:00
Dmitriy Selivanov
7b72209277 fixes #1859 (#1861)
* style & formatting. fixes #1859

* prepare `choices` in `updateSelectizeInput()` as per discussion in #1861

* remove duplicated block in selectizeJSON, simply `lab` assignement logic
2018-01-29 13:41:30 -06:00
Joe Cheng
cad20a0bfe Merge pull request #1856 from rstudio/wch-do-call
Quote arguments to do.call() for nicer stack traces
2017-10-31 11:28:47 -07:00
Winston Chang
ba8d79f202 Fix version text substitution 2017-10-17 10:51:19 -05:00
Winston Chang
176fe699b9 Point to RStudio Community website instead of shiny-discuss 2017-09-27 15:54:05 -05:00
Winston Chang
213ee7be13 Quote arguments to do.call() for nicer stack traces. Closes #1851 2017-09-26 14:36:19 -05:00
Joe Cheng
48fd869c71 Merge pull request #1848 from rstudio/barbara/fix/icon
Revert back the relative position of the icon and title in tabPanel's and navbarMenu's
2017-09-12 07:34:31 -07:00
Barbara Borges Ribeiro
53e47484e2 reverted the relative positioning of the icon and the title text in navbarMenus and tabPanels back to what it was before Shiny 1.0.5 (fixes #1840) 2017-09-12 12:16:06 +01:00
Winston Chang
dc18b20e5a Don't copy httpuv::decodeURIComponent at build time 2017-09-07 21:31:32 -05:00
Barbara Borges Ribeiro
b4c5debbdf Merge pull request #1844 from rstudio/barbara/fix/reactlog
Changed script tags in reactlog from HTTP to HTTPS
2017-09-07 01:43:46 +01:00
Barbara Borges Ribeiro
771d3d52b9 Changed script tags in reactlog from HTTP to HTTPS in order to avoid mixed content blocking by most browsers (thanks @jekriske-lilly) 2017-09-07 01:34:17 +01:00
Joe Cheng
2a53ac093d Merge pull request #1830 from rstudio/wch-compare-version
Add Shiny.compareVersion() function
2017-09-05 11:37:17 -07:00
Winston Chang
4fa2af72cc Avoid port 6697. Closes #1784 2017-08-28 16:40:51 -05:00
Winston Chang
e512d3cd61 Grunt 2017-08-25 14:46:19 -05:00
Winston Chang
16b7ee3985 Add Shiny.compareVersion() function 2017-08-25 14:46:06 -05:00
Winston Chang
4f3d26c31b Add Shiny.version to Javascript (#1826)
* Add Shiny.version to Javascript

* Grunt
2017-08-23 15:52:16 -05:00
Winston Chang
587bf94d69 Merge tag 'v1.0.5'
Shiny 1.0.5 on CRAN
2017-08-23 15:27:56 -05:00
Winston Chang
635ad77e0d Bump version to 1.0.5 2017-08-23 13:11:59 -05:00
Winston Chang
33258da6c3 Bump version to 1.0.5.9000 2017-08-23 13:07:15 -05:00
Joe Cheng
c2b3c3379d Fix #1824: HEAD request on static files causes app to stop (#1825)
* Fix #1824: HEAD request on static files causes app to stop

The problem was that for HEAD requests specifically, we implement
an explicit Content-Length header (normally we let httpuv figure
out the Content-Length based on the content, but for HEAD we don't
return any content but still want to include the Content-Length).

The Content-Length header was only implemented correctly for string
values, not for raw vectors or file-by-path. This change implements
the value correctly for all currently valid httpuv content.

* Update NEWS

* Code review feedback
2017-08-23 13:01:22 -05:00
Winston Chang
e30fac02ed Add safe wrapper for fromJSON 2017-08-21 19:55:48 -05:00
Winston Chang
e74592a654 Escape a few more characters for conditionalPanel expressions 2017-08-21 14:25:20 -05:00
Joe Cheng
ebd47aa73b Merge pull request #1820 from rstudio/wch-fix-conditionalpanel
Escape newline characters in conditionalPanel expression
2017-08-18 20:49:48 -04:00
Winston Chang
e2d19cbaba Grunt 2017-08-18 17:24:06 -05:00
Winston Chang
1f864a846f Escape newline chars in conditionalPanel expr. Fixes #1818 2017-08-18 17:24:06 -05:00
Winston Chang
fc32c2c944 Clarify that choices must be strings 2017-08-18 11:48:41 -05:00
Winston Chang
279e37f1cb Bump version to 1.0.4.9000 2017-08-18 11:47:19 -05:00
Winston Chang
3f9176176e Merge tag 'v1.0.4'
Shiny 1.0.4 on CRAN
2017-08-14 12:01:26 -05:00
Winston Chang
b3201ccafd Add functions to news item 2017-08-10 14:29:06 -05:00
Winston Chang
2a01a620a9 Add NEWS summary 2017-08-10 12:03:22 -05:00
Winston Chang
6f43cf7b82 NEWS cleanup 2017-08-09 18:49:34 -05:00
Winston Chang
1c6250f9c2 Bump version to 1.0.4 2017-08-09 18:49:34 -05:00
Barbara Borges Ribeiro
650075a9ab Fix appendTab for empty tabsetPanels (#1813)
* fix appendTab for empty tabsetPanels; use spread operator to avoid having to resort to apply; upgrade grunt.

* revert back to `Math.max.apply(null, existingTabIds) + 1;` there's no browser compatibility issues there
2017-08-09 18:45:25 -05:00
Winston Chang
668ee6f24a Add references to issues 2017-08-08 11:16:57 -05:00
Alan Dipert
c456ec2c4c drag/drop-able fileInputs (#1782)
* fileInput WIP: Show dropzones when file dragged over window

- Still need to validate dataTransfer contents

* WIP: Basic functionality working

* wip

* Grunt

* WIP state machine

* WIP generalize FSM to data+multimethod

* WIP multimethod

* WIP draghover

* wip multimethod

* WIP, such refactor

* WIP: rm multimethod

* WIP

* WIP resurrect multimethod

* WIP move draghover functions into input object

* WIP colors: use more muted, bootstrap-esque glows

* Grunt

* WIP: use whenAny, more descriptive args in default test/dispatch fns

* WIP more whenAny

* Grunt

* WIP dont use for...of, requires polyfill

* Grunt

* multimethod improvements, documentation. `equal` function.

* multimethod: simplified equal, removed need for forward decl. docs.

* dox

* multimethod improvements, docs

* minor

* IE 10+ drag/drop, first cut

* Grunt

* use functions not arrows for faux instance methods

* Grunt

* fix uploadDropped call

* Grunt

* cleanup drop handler, fix entry to invalid state via doc drop handler

* Grunt

* IE workaround #293932

* Grunt

* yeeeeeeeeeeessss IE WORKSSSSS

* Cleanup; support activeClass/overClass

* everything basically works everywhere \o/

* revert ability to specify classes, hardcode in JS

* MM fixes

* minor fixes

* Grunt

* DnD: Support dragging directly over zones
- Happens when source window occludes browser window

* woo

* Note Safari bug, use draghover for zones

* merge

* Grunt

* news

* include CSS
2017-08-08 11:12:21 -05:00
Joe Cheng
3b0c390a9e Merge pull request #1794 from rstudio/barbara/tabs
Dynamic tabs
2017-08-04 11:31:50 -07:00
Barbara Borges Ribeiro
b02eb11345 do inputId <- session$ns(inputId) in user facing functions for module functionality (rather than overriding the same functions in makeScope) 2017-08-04 18:02:43 +01:00
Barbara Borges Ribeiro
ed3ba303bc Joe's feedback 2017-08-04 17:56:58 +01:00
Barbara Borges Ribeiro
ee5da1410e make hide and remove work well when we want to hide/remove a tab inside a navbarMenu (or the whole menu) and it is selected (before this commit, it wasn't navigating to the first tab like it is supposed to) 2017-08-04 15:10:08 +01:00
Barbara Borges Ribeiro
494627c6e1 make this PR work for modules 2017-08-04 15:10:08 +01:00
Barbara Borges Ribeiro
82ac112dec added select argument to showTab function 2017-08-04 15:10:08 +01:00
Barbara Borges Ribeiro
40cfff33ff for dynamic tabs, send message on session$onFlush (instead of session$onFlushed) 2017-08-04 15:10:08 +01:00
Joe Cheng
c1c5873912 Abandon nearest neighbor tab-showing logic. Just grab the first tab. 2017-08-04 15:10:08 +01:00
Joe Cheng
c090efd562 Fix bug where last tab being removed, didn't update tabset input value 2017-08-04 15:10:08 +01:00
Joe Cheng
91dbb0e77b htmlDependencies are properly loaded with dynamic tabs 2017-08-04 15:10:08 +01:00
Joe Cheng
dde7b144f0 Add select=FALSE argument to insert/append/prependTab 2017-08-04 15:10:08 +01:00
Joe Cheng
f1873a014c Make tab prepend/append just edge cases of insert 2017-08-04 15:10:08 +01:00
Joe Cheng
48b8923b67 Properly escape jQuery selector strings 2017-08-04 15:10:08 +01:00
Barbara Borges Ribeiro
6f9f3fea83 implement navigation after hiding/removing selected tab 2017-08-04 15:10:08 +01:00
Barbara Borges Ribeiro
10f3320165 more JS code refactoring; improved documentation 2017-08-04 15:10:08 +01:00
Barbara Borges Ribeiro
d57aa33b40 insertion fully implemented 2017-08-04 15:10:08 +01:00
Barbara Borges Ribeiro
0e7c78bae3 refactored code and made insertion of navbarMenus possible 2017-08-04 15:10:08 +01:00
Barbara Borges Ribeiro
e6602786ec updated docs 2017-08-04 15:10:08 +01:00
Barbara Borges Ribeiro
31bbb3894c remove extra line 2017-08-04 15:10:08 +01:00
Barbara Borges Ribeiro
8bbf576807 typo: tag -> tab (makes a big difference!) 2017-08-04 15:10:08 +01:00
Barbara Borges Ribeiro
1ecc9b9d0e Fixed documentation problems and JS code logic 2017-08-04 15:10:08 +01:00
Barbara Borges Ribeiro
3adbebc3d9 document similar things together; add prependTab and appendTab 2017-08-04 15:10:08 +01:00
Barbara Borges Ribeiro
a4c086f51b now working for navbarPage and navlistPanel 2017-08-04 15:10:08 +01:00
Barbara Borges Ribeiro
0ecdcec698 clean up JS code (1 line only) 2017-08-04 15:10:08 +01:00
Barbara Borges Ribeiro
ae7f026d46 added NEWS and fixed typo 2017-08-04 15:10:08 +01:00
Barbara Borges Ribeiro
2813e0b706 update examples 2017-08-04 15:09:00 +01:00
Barbara Borges Ribeiro
a409562d00 delete extra brackets 2017-08-04 15:09:00 +01:00
Barbara Borges Ribeiro
b6b6661ea1 implement showTab and removeTab 2017-08-04 15:09:00 +01:00
Barbara Borges Ribeiro
fb7b6f667c implement removeTab 2017-08-04 15:09:00 +01:00
Barbara Borges Ribeiro
b94efe81e4 finish insertTab 2017-08-04 15:09:00 +01:00
Barbara Borges Ribeiro
72a1b3d2a0 add functions to index.r 2017-08-04 15:09:00 +01:00
Barbara Borges Ribeiro
20bff18bd4 changes 2017-08-04 15:09:00 +01:00
Winston Chang
ba5c5ef4fb Move isRunning function to better location 2017-07-27 14:56:01 -05:00
Barbara Borges Ribeiro
aff3ac0bb3 Add onStop function (#1770)
* NEWS item

* added `onStop` arg to `shinyApp()` (and renamed our internal `onEnd` - which is what was calling `on.exit()` already - to `onStop` as well)

* added onStop() function

* add entry for documentation

* make it work for all possible app structures (interactive, saved as app.R, saved as ui.R and server.R)

* fix #1772: make sure `onStart` works in all scenarios

* update NEWS

* improved wording

* more wording

* and more wording

* don't stop execution if a `onStop` callback function results in an error

* remove "(all sessions have been disconnected)" because it's misleading

* add @seealso documentation

* shamefully forgot to Cmd Shift D

* change code place

* Code review feedback

* onStop: use session argument instead of scope
2017-07-27 14:54:55 -05:00
Winston Chang
2c350daf01 Merge pull request #1802 from rstudio/bugfix/rstudio-debugger
work around RStudio debugger issue (closes #1474)
2017-07-27 13:14:01 -05:00
Winston Chang
cb7627c736 Update NEWS and add comment 2017-07-27 13:13:31 -05:00
Kevin Ushey
f731a5cae4 work around RStudio debugger issue (closes #1474) 2017-07-27 10:37:56 -07:00
Winston Chang
07cb7c9305 Add 'setSerializer' function (#1792) 2017-07-18 17:01:06 -05:00
Winston Chang
86e9cc4896 Add preprocessor for fileInputs that strips local path (#1789)
* Add preprocessor for fileInputs that strips local path

* Update NEWS

* Rename snapshotPreprocess to snapshotPreprocessOutput

* Add snapshotPreprocessInput function

* Remove unnecessary NEWS item

* Update NEWS

* Add getSnapshotPreprocessInput

* Add staticdocs entry for snapshotPreprocessInput

* Add private methods to get snapshotPreprocess functions

* Bump version to 1.0.3.9002
2017-07-13 16:07:16 -05:00
Joe Cheng
12c9405257 Merge pull request #1790 from rstudio/wch-warn-level
Don't reduce warn level when running app
2017-07-12 22:41:21 -07:00
Winston Chang
4708b44c59 Don't reduce warn level when running app. Fixes #1680 2017-07-12 19:29:49 -05:00
Winston Chang
4cb428bb92 Add a function to test if an app is running (#1785)
Squashed commit of the following:

commit 8667bed8962069a5cab8691f981e2b7ba9d449c3
Author: Winston Chang <winston@stdout.org>
Date:   Tue Jul 11 14:36:11 2017 -0500

    Edits

commit c4e8549ca5
Author: Konrad Rudolph <konrad.rudolph@gmail.com>
Date:   Fri Jul 7 17:57:33 2017 +0100

    Describe changes

commit 7b05c2e60f
Author: Konrad Rudolph <konrad.rudolph@gmail.com>
Date:   Fri Jul 7 17:54:40 2017 +0100

    Add new function to doc index

commit eb93ebfad8
Author: Konrad Rudolph <konrad.rudolph@gmail.com>
Date:   Fri Jul 7 17:54:30 2017 +0100

    Add documentatio for new function

commit 1a6c8a4d72
Author: Konrad Rudolph <konrad.rudolph@gmail.com>
Date:   Fri Jul 7 17:53:13 2017 +0100

    Add a function to test whether the app is running
2017-07-11 14:36:59 -05:00
Mine Cetinkaya-Rundel
d7391b19bc Convert examples to single file apps (#1685)
* - Convert all example apps to single file app.R file
- Make relevant updates to Readmes to match up with app.R structure
- Add color to plots (RStudio blue)
- In 04_mpg example: Show outliers by default, as opposed to hide, since this is more routine
- In 06_tabsets and 08_html examples: Don't name random data vector "data"
- Add extensive comments to app.R files and use consistent formatting of comments across examples
- In 09_upload example: Use req() to check for NULL entry

* add news entry summarizing changes

* use true RStudio blue, #75AADB

* Conver shinyApp calls at the end to drop argument name in examples 3-11, except for the custom HTML example. Kept them in for examples 1&2 for completeness in first exporuse to function.

* Pull news items that got added before this PR was merged

* Update comment for shinyApp function -- it creates an app object, doesn't run the app
2017-07-11 14:20:01 -05:00
Joe Cheng
db9e56d1ca Merge pull request #1768 from rstudio/wch-fix-with-private-seed
Fix withPrivateSeed
2017-07-11 12:17:02 -07:00
Winston Chang
e527af10f4 New version of httpuv is on CRAN 2017-07-11 13:45:45 -05:00
Joe Cheng
74c7be0a6d Merge pull request #1786 from rstudio/joe/bugfix/fileinput-content-type
Use a more suitable content type for file uploads
2017-07-10 15:39:13 -07:00
Joe Cheng
2d40e7b51a Use a more suitable content type for file uploads
application/x-www-form-urlencoded;charset=UTF-8 is the default, which shinyapps.io
cares about for some reason and tries to parse the data as such. By setting the
content type to the more accurate application/octet-stream, no middleware should
be tempted to futz with the contents.
2017-07-10 15:33:42 -07:00
Winston Chang
ea407fb2ea Don't include xtable comment in renderTable by default 2017-06-27 15:05:31 -05:00
Winston Chang
fca5b0529a Remove reinitalizeSeed
This function is no longer needed because the minimum R vesion supported by Shiny is 3.0.2.
2017-06-27 10:30:50 -05:00
Winston Chang
65fd1dd2d8 Remove branch name for httpuv remote 2017-06-26 22:40:37 -05:00
Winston Chang
0a7ede3818 Add tests for random streams 2017-06-26 21:59:52 -05:00
Winston Chang
24e84f3866 Prevent private random stream from leaking out. Fixes #1763 2017-06-26 21:59:51 -05:00
Winston Chang
c1c8e46c09 Refactor withPrivateSeed 2017-06-26 21:59:51 -05:00
Winston Chang
8591e4f301 Add working app for conditionalPanel example 2017-06-23 10:14:32 -05:00
Alan Dipert
10db7ad89c Support modules in conditionalPanel (#1735) 2017-06-23 10:12:15 -05:00
Joe Cheng
4ca4f442b9 Required R version is 3.0.2 due to sourcetools 2017-06-22 19:42:49 -05:00
Winston Chang
6d5ecbc9c4 Fix indentation 2017-06-22 13:18:05 -05:00
Winston Chang
ea685a5686 Don't send local package path to client when using htmlwidgets (#1756)
* Don't send local package path to client when using htmlwidgets. Fixes #1755

* Add scrubFile option
2017-06-22 13:16:19 -05:00
Winston Chang
376d3b6e91 Merge pull request #1760 from rstudio/wch-snapshot-preprocess
Add snapshotPreprocess function
2017-06-22 13:01:09 -05:00
Winston Chang
df7397af1f Bump version 2017-06-21 14:27:03 -05:00
Winston Chang
9ba9345b04 Add snapshotPreprocess function 2017-06-21 14:27:03 -05:00
Barbara Borges Ribeiro
9fc5758ae0 Triggers a new shiny:outputinvalidated event (#1758)
* trigger a new `shiny:invalidated` event when an output gets invalidated, at the same time that the `recalculating` CSS class is added (fixes #1688)

* add attribution to @andrewsali

* change event name from 'shiny:invalidated' to 'shiny:outputinvalidated'

* add binding and name to the new event 'shiny:outputinvalidated'
2017-06-21 12:28:51 -05:00
Winston Chang
25298a6182 In test mode, send message to client even when no outputs change (#1747)
* In test mode, send message to client even when no outputs change

* Update NEWS.md
2017-06-20 13:50:28 -05:00
Winston Chang
246da1bff6 Grunt 2017-06-16 13:48:38 -05:00
Carl Ganz
8b5d12b958 Add placeholder parameter to updateTextInput (#1742)
* add placeholder parameter

* add js placeholder code

* roxygenize

* grunt

* fix updateCheckBoxInput not to use placeholder

* simply roxygen

* add NEWS

* revert grunt
2017-06-15 22:00:39 -05:00
Alan Dipert
3817370d4e fileInput JS: Allow uploading the same file. (#1719)
* tools README: notes about entr + grunt

* fileInput JS: Allow uploading the same file. Fixes #1508

* Grunt

* Added note to NEWS.

* tools README: add Linux section, fix formatting
2017-06-15 15:09:37 -05:00
Alan Dipert
c29846a9da fileInput: Preserve extension of files uploaded from IE9 (#1717)
* fileInput: IE addendum to #1706

- Attempt to preserve the extension of files uploaded from IE9.

* maybeMoveIEUpload: Fix if spacing
2017-06-15 13:47:21 -05:00
Winston Chang
2158f906a7 Merge pull request #1736 from rstudio/wch-fix-unbind
Fix condition for calling exports.unbindAll()
2017-06-15 13:05:16 -05:00
Winston Chang
008dd280d6 Grunt 2017-06-08 17:03:21 -07:00
Winston Chang
fb99db011c Fix condition for calling exports.unbindAll(). (Correction to #1449) 2017-06-08 17:02:56 -07:00
Joe Cheng
c0fbd9cb3c Merge pull request #1732 from rstudio/barbara/mods
Fixed #1546: make it possible to write into a module's session$userData non-hackily
2017-06-07 22:45:47 -07:00
Barbara Borges Ribeiro
fb79b18002 More descriptive NEWS item and added an explanatory comment to the code 2017-06-07 13:28:51 -07:00
Barbara Borges Ribeiro
3841f22108 Fixed #1546: make it possible (without any hacks) to write arbitrary data into a module's session$userData 2017-06-07 12:11:05 -07:00
Winston Chang
379d523ac5 Add better error messages for errors parsing and evaluating JS code (#1727)
* Add better error messages for errors parsing and evaluating JS code

* Grunt
2017-06-02 14:31:06 -05:00
Winston Chang
07ec7f8c13 Update Rproj for new version of RStudio IDE 2017-06-02 13:31:33 -05:00
Alan Dipert
d0f29cc7a2 fileInput: If possible, retain uploaded file extensions on the server. (#1706) 2017-05-26 11:16:02 -05:00
Joe Cheng
0e23a487f7 Merge pull request #1713 from rstudio/jjallaire-contributing-links
Update links in CONTRIBUTING.md
2017-05-23 14:05:43 -07:00
JJ Allaire
ac10f7c426 Update links in CONTRIBUTING.md
Update the links to contributor agreements to reflect new versions that use my current email rather than rstudio.org based one.

I've made the same change in the rstudio and rmarkdown repos, we should make it in other repos that have a contributor agreement as well.
2017-05-23 16:38:02 -04:00
Joe Cheng
852c00009e Merge pull request #1712 from rstudio/wch-fix-reactiveval
Give each ReactiveVal separate dependents
2017-05-22 11:15:35 -07:00
Winston Chang
b365798e66 Add tests for ReactiveVal independence 2017-05-22 10:35:01 -05:00
Winston Chang
66a6097a49 Give each ReactiveVal separate dependents. Fixes #1710 2017-05-22 10:34:27 -05:00
Winston Chang
0e529d3d92 Fix partial arg match. Closes #1701 2017-05-10 10:08:05 -05:00
Winston Chang
06c75dd656 Bump version to 1.0.3.9000 2017-04-28 09:45:17 -05:00
Winston Chang
69c32d4d90 Bump version to 1.0.3 2017-04-25 15:33:10 -05:00
Winston Chang
36ffebd975 Workaround for NOTE about objects in yet-unreleased version of ggplot2 2017-04-25 15:33:10 -05:00
Winston Chang
deb56539fb Better reactivePoll example. Closes #1678 2017-04-25 10:48:29 -05:00
Winston Chang
af8d099b9f Don't call body(NULL). Fixes #1676 2017-04-24 13:42:22 -05:00
Winston Chang
eed869d321 Make fileInput progress bar change color on error (#1673)
* Make fileInput progress bar change color on error. Fixes #1672

* Grunt

* Update NEWS
2017-04-21 11:33:14 -05:00
Winston Chang
f8f2acf6c3 Bump version to 1.0.2.9000 2017-04-18 16:38:14 -05:00
Winston Chang
7be9f74827 Merge tag 'v1.0.2'
Shiny 1.0.2 on CRAN
2017-04-18 16:36:44 -05:00
Barbara Borges Ribeiro
ed77982330 Merge pull request #1670 from rstudio/joe/prebuilt
pre-built => prebuilt
2017-04-18 19:58:32 +01:00
Joe Cheng
e1b47eca90 pre-built => prebuilt 2017-04-18 11:09:45 -07:00
Winston Chang
bfa0b2d2bc Bump version to 1.0.2 and update NEWS 2017-04-13 14:13:41 -05:00
Winston Chang
d67783edbd Fix typo 2017-04-13 14:11:15 -05:00
Joe Cheng
77712b6664 Use RStudio replacement for deprecated MathJax CDN (#1664)
* Use RStudio replacement for deprecated MathJax CDN

* Add link to PR
2017-04-12 14:41:21 -05:00
Winston Chang
1633e7faa6 Fix Bootstrap URL. Closes #1662 2017-04-10 10:01:42 -05:00
Joe Cheng
2dc5ee5862 Merge pull request #1661 from rstudio/joe/bugfix/showcase-code-margin
Fix #1654: Empty space below showcase code
2017-04-07 17:39:01 -07:00
Joe Cheng
bbaea23eea Fix #1654: Empty space below showcase code 2017-04-07 17:37:54 -07:00
Barbara Borges Ribeiro
d112ac7eef fix documentation (worng/misleading code example) 2017-04-05 17:58:39 -05:00
Barbara Borges Ribeiro
cf21e987f2 Add shiny:sessionInit event (#1568)
* added a shiny:sessionInit JS event that is triggered at the end of the session's initialize method

* new entry

* update NEWS

* correct version number in NEWS.md

* fix typo
2017-04-05 10:50:42 -05:00
Barbara Borges Ribeiro
dae11765bc allow the choices argument in checkboxGroupInput() to be NULL (#1652)
* allow the `choices` argument in `checkboxGroupInput()` to be `NULL` or `c()` to keep backward compatibility with Shiny < 1.0.1 (fixes #1649)

* use vapply

* added one more test; reimplemented logic for checking if choice args are null
2017-04-05 10:12:44 -05:00
Barbara Borges Ribeiro
df30a3c7f4 PR: Add CC-BY-SA-4.0 license to showcase 2017-04-03 18:24:28 +01:00
Joe Cheng
aaa4600597 Bump version number to 1.0.1.9000 2017-04-03 10:22:08 -07:00
Joe Cheng
ba1730d26b Add CC-BY-SA-4.0 license to showcase 2017-04-03 10:18:04 -07:00
Barbara Borges Ribeiro
d1b5c812f7 re-run grunt to update version number embedded in shiny.min.js 2017-03-31 16:49:11 +01:00
Barbara Borges Ribeiro
5bfe6d1c84 bump version numbers in DESCRIPTION and NEWS 2017-03-31 16:48:13 +01:00
Winston Chang
9804a794fd Merge pull request #1641 from rstudio/fix-plot-rounding
Round brush coordinates
2017-03-31 10:46:44 -05:00
Winston Chang
0344645208 Grunt 2017-03-31 10:42:44 -05:00
Winston Chang
f56ad6e787 Update NEWS 2017-03-31 10:42:43 -05:00
Winston Chang
7492db592b Round brush coordinates to 14 digits. Fixes #1634 2017-03-31 10:42:43 -05:00
Winston Chang
2e80ecf8a7 Rebuild ion.rangeSlider.min.js 2017-03-30 15:48:47 -05:00
Joe Cheng
6993551a44 Fix #1637: Outputs stay faded on MS Edge (#1640) 2017-03-30 20:25:27 +01:00
Joe Cheng
9bff15adfe Fix #1632: Showcase mode comes up almost blank in IE9 & 11 (#1633)
If the width is made very wide in showcase mode with side-by-side
arrangement, the app shrinks to almost nothing. For some reason the
zoom CSS property (which we set using jQuery.animate) is set to
"1%" instead of "1".

Numbers and percentages are equally valid here, and the issue goes
away if we use percentage.
2017-03-29 10:16:17 -05:00
Winston Chang
0c24da2358 Stop propagation of mouse events on slider (#1631)
see (https://github.com/rstudio/shiny/issues/711)
2017-03-28 20:38:46 +01:00
Barbara Borges Ribeiro
4ee4adb43d doc changes 2017-03-27 21:12:46 +01:00
Joe Cheng
e33b028348 Merge pull request #1628 from rstudio/wch-rm-object-assign
Remove babel-polyfill
2017-03-27 13:07:47 -07:00
Winston Chang
384d9c1841 Grunt 2017-03-27 15:02:16 -05:00
Winston Chang
f78fcd6b5f Remove need for babel-polyfill 2017-03-27 15:02:03 -05:00
Barbara Borges Ribeiro
711a72989b Update NEWS.md 2017-03-27 17:53:59 +01:00
Barbara Borges Ribeiro
d62a2fc1d5 Allow arbitrary UI code in the choiceNames for radio buttons and checkbox group input (#1521) 2017-03-27 16:51:44 +01:00
Barbara Borges Ribeiro
f33f712a3a fix typo 2017-03-27 10:00:47 +01:00
Joe Cheng
3315b3310b Merge pull request #1614 from rstudio/joe/feature/reactiveVal
Add reactiveVal() for single reactive value
2017-03-24 13:08:47 -07:00
Joe Cheng
c7134b16ed Add link to PR for reactiveVal feature 2017-03-24 13:08:09 -07:00
Winston Chang
f36f710661 Make sure reactiveTimer gets session at creation time. Fixes #1621 2017-03-24 13:47:25 -05:00
Joe Cheng
00ab8681c7 Merge pull request #1619 from rstudio/wch-fileinput
Make fileInput text customizable. Closes #1617
2017-03-23 17:16:32 -07:00
Winston Chang
4137bbac94 NEWS 2017-03-23 14:33:22 -05:00
Winston Chang
750b2ad599 Make fileInput text customizable. Closes #1617 2017-03-23 14:31:31 -05:00
Joe Cheng
511c833fbb More code review feedback 2017-03-23 10:29:29 -07:00
Joe Cheng
29063a0c07 Code review feedback 2017-03-23 10:24:06 -07:00
Barbara Borges Ribeiro
67909b3557 updated tools/README.md (#1616) 2017-03-23 10:07:41 -05:00
Joe Cheng
102c12d36c Add NEWS item 2017-03-22 16:12:07 -07:00
Joe Cheng
dc51651665 Add S3 generics for format/print; freezeReactiveVal
Also changed the classes of reactive expressions and reactiveVal
from "reactive" and "reactiveVal" to c("reactiveExpr", "reactive")
and c("reactiveVal", "reactive")
2017-03-22 11:29:22 -07:00
Joe Cheng
8b563d6d5f Fix regex for old versions of R 2017-03-22 10:37:16 -07:00
Joe Cheng
eb8b88027e Automatic labelling of reactiveVals 2017-03-22 09:47:08 -07:00
Joe Cheng
a5b7f307ed Add reactiveVal() for single reactive value 2017-03-21 16:38:32 -07:00
Winston Chang
45fca425aa Change NS() to return a vectorized function (#1613)
* Change NS() to return a vectorized function

* Update NEWS

* Use vectorized ns()

* Use correct separator
2017-03-21 15:57:38 -05:00
Winston Chang
a0bd9b5fd7 Redocument with Roxygen 6.0.1 2017-03-21 14:02:29 -05:00
Winston Chang
c12e24e3e3 Properly register bookmark excludes for modules. Fixes #1598 (#1599)
* Scopes: properly register bookmark excludes. Fixes #1598

* Update NEWS
2017-03-21 13:56:08 -05:00
Winston Chang
d147c5a153 Don't use data-drag-interval for non-range sliders. Fixes #1605 (#1610)
* Don't use data-drag-interval for non-range sliders. Fixes #1605

* Update NEWS
2017-03-16 15:49:46 -05:00
Winston Chang
7a833456d9 Use consistent value caching format 2017-03-10 12:03:24 -06:00
Winston Chang
306f33dfc4 Fix value access 2017-03-09 16:16:16 -06:00
Winston Chang
a2745a4060 Grunt 2017-03-03 15:28:47 -06:00
Winston Chang
46b68c7b2a Bump version to 1.0.0.9001 2017-03-03 15:28:47 -06:00
Winston Chang
4264760113 Add binding and el fields to shiny:inputchanged event (#1596)
* Remove unused 'immediate' arguments

* Add opts argument to setInput methods

* Extract input values without opts

* Consistent interface for setting initial values

* Update NEWS

* Add binding and el when fileInputBinding triggers shiny:inputchanged

* Revert "Consistent interface for setting initial values"

This reverts commit 12c0b6e72a.

* Move InputDeferDecorater function

The new placement properly reflects the decorator stack

* Fix indentation

* bindInputs: make sure value is set immediately

* Only use opts where necessary in input decorators

* Properly send initial values

* Move initial value of .clientdata_allowDataUriScheme to better place

* Fix indentation

* Add InputValidateDecorator

* Better variable name

* Add function for default input options

* Simplify code
2017-03-03 15:27:32 -06:00
Winston Chang
42dedbbd9a Simplify user value check 2017-03-02 13:30:47 -06:00
Winston Chang
ea99bfdb16 Update NEWS 2017-02-28 10:48:48 -06:00
Winston Chang
2ccb934338 Merge pull request #1592 from akersting/master
fix: dateRangeInput did not respect weekstart arg
2017-02-28 10:46:35 -06:00
Winston Chang
367027cfbc Merge branch 'wch/redundant-setinput' 2017-02-28 09:23:17 -06:00
Winston Chang
c4ebd3b6d5 Merge pull request #1594 from rstudio/wch/fix-dynamic-input
Make sure input deduplication respects inputType. Closes #162
2017-02-28 09:19:41 -06:00
Winston Chang
5f8cd82a09 Update NEWS 2017-02-24 19:43:55 -06:00
Winston Chang
0ef15fa662 Remove redundant calls to setInput 2017-02-24 19:43:55 -06:00
Winston Chang
c05452af91 Update NEWS 2017-02-24 15:16:12 -06:00
Winston Chang
4c8bafcf9a Make sure input deduplication respects inputType. Closes #162 2017-02-24 15:11:18 -06:00
Andreas Kersting
034f30a49a fix: dateRangeInput did not respect weekstart arg 2017-02-23 07:38:10 +01:00
Winston Chang
0f13075e17 NEWS edits 2017-02-10 14:28:15 -06:00
Winston Chang
ad274a5981 Grunt 2017-02-10 14:26:48 -06:00
Winston Chang
fdbcbaec8a Merge pull request #1579 from albertosantini/fix-1577
Improve escapeHTML
2017-02-10 14:26:22 -06:00
Alberto Santini
9c09072ee6 Update NEWS 2017-02-10 20:16:57 +01:00
Alberto Santini
0a4ca56da9 Improve escapeHTML
Replacing one char after another is not a best practice, due to the order dependency of replacing, xss risk and performance.

Fix #1577
2017-02-10 18:15:07 +01:00
Winston Chang
2b494398f2 Merge pull request #1578 from rstudio/wch/ggplot-api
Add plot interaction support for ggplot2 api
2017-02-10 10:56:59 -06:00
Winston Chang
95585c2264 Update NEWS 2017-02-10 10:42:27 -06:00
Winston Chang
92f9f0da9e Restructure code for clarity 2017-02-09 11:23:13 -06:00
Winston Chang
fe943b5e95 Update plot interaction for ggplot2 > 2.2.1 2017-02-09 11:01:47 -06:00
Winston Chang
3479a4661a Prepare code for ggplot2 api 2017-02-02 11:12:22 -06:00
Winston Chang
7ba438cf7c Add entries to staticdocs index 2017-02-01 11:41:18 -06:00
Winston Chang
c761e9fba0 NEWS formatting fixes 2017-02-01 11:37:31 -06:00
Winston Chang
deae31ea4a Merge pull request #1570 from rstudio/wch/simplify-fileupload
Remove shiny:fileuploaded JS event
2017-02-01 11:34:01 -06:00
Winston Chang
547355a163 Grunt 2017-02-01 11:29:21 -06:00
Winston Chang
9be4cb132c NEWS 2017-02-01 11:29:21 -06:00
Winston Chang
3e25c9f3f4 Remove shiny:fileuploaded event 2017-02-01 11:17:08 -06:00
Barbara Borges Ribeiro
220c7e9139 decode URLs in staticHandler func - fixes #1565 (via #1566) 2017-02-01 06:27:11 +00:00
Winston Chang
79a085a9be Merge pull request #1547 from rstudio/wch/fix-progress
Fix progress bar
2017-01-31 20:38:38 -06:00
Winston Chang
b505c5a9d3 Grunt 2017-01-31 20:38:16 -06:00
Winston Chang
03ba660ea1 Update NEWS 2017-01-31 20:37:39 -06:00
Winston Chang
5aeb361f6d Set starting value to NULL 2017-01-31 20:36:28 -06:00
Winston Chang
0e519a4e97 Progress: store value as raw value instead of normalized 2017-01-31 20:36:28 -06:00
Winston Chang
4feee00d34 NULL value no longer makes progress bar go to 100%. Closes #1472
This also removes the documentation which said that using NULL would cause the
progress bar to be hidden.
2017-01-31 20:36:28 -06:00
Winston Chang
ef5e4cdc0a Merge pull request #1559 from rstudio/wch/download-event
Add shiny:filedownload Javascript event
2017-01-31 20:29:25 -06:00
Winston Chang
67c599f50b Update NEWS 2017-01-31 20:27:20 -06:00
Winston Chang
5af9b61357 Grunt 2017-01-31 20:25:00 -06:00
Winston Chang
1d6771b4ed Unexport markOutputAttrs and add snapshotExclude function 2017-01-31 20:24:52 -06:00
Winston Chang
c55dc0a58e Add ability to exclude outputs from snapshots 2017-01-31 20:23:34 -06:00
Winston Chang
c525d55db8 Add shiny:filedownload JS event 2017-01-31 20:23:34 -06:00
Winston Chang
408f66ef80 Merge pull request #1541 from rstudio/wch/file-input-event
Make fileInput trigger shiny:inputchanged.
2017-01-31 16:29:44 -06:00
Winston Chang
7f73a047a4 Grunt 2017-01-31 16:27:50 -06:00
Winston Chang
015bc98d60 Trigger inputchanged event when fileupload is completed 2017-01-31 16:27:16 -06:00
Winston Chang
5cd9ba609a Make fileInput trigger shiny:inputchanged. Closes #1511 2017-01-31 16:27:16 -06:00
Winston Chang
c8ed6544db Fix documentation link. Closes #1567 2017-01-31 11:40:24 -06:00
Winston Chang
1162113d3b Re-document 2017-01-30 13:47:20 -06:00
Winston Chang
1612503e7b Update selectize URLs. Closes #1564 2017-01-30 13:46:12 -06:00
Joe Cheng
34ba85df3b Merge pull request #1563 from rstudio/barbara/userInfo
Barbara/user info
2017-01-30 09:55:56 -08:00
Barbara Borges Ribeiro
8206e7d2a2 delete old message handler 2017-01-27 20:42:04 +00:00
Barbara Borges Ribeiro
3e29672c70 news item 2017-01-27 20:40:07 +00:00
Barbara Borges Ribeiro
f67aaafe4f some adjustments after feedback 2017-01-27 20:39:18 +00:00
Barbara Borges Ribeiro
ed704afc07 remove console.log and re-grunt 2017-01-27 20:38:17 +00:00
Barbara Borges Ribeiro
bbbfacb4b2 grunt 2017-01-27 20:37:58 +00:00
Barbara Borges Ribeiro
cf16d2e52d listify 2017-01-27 20:37:32 +00:00
Barbara Borges Ribeiro
6268e6e1c9 will be removed!! only for testing purposes 2017-01-27 20:36:37 +00:00
Barbara Borges Ribeiro
99b8e5b303 stuff 2017-01-27 20:36:37 +00:00
Winston Chang
73446af330 Convert tabs to spaces in examples 2017-01-26 15:06:15 -06:00
Barbara Borges Ribeiro
a0b917a207 support pushState for pseudo-nav
see the documentation for details (`?updateQueryString` and `?getQueryString`)
2017-01-25 23:45:26 +00:00
Winston Chang
53ec7edd06 Another typo 2017-01-17 12:02:39 -05:00
Winston Chang
ff804c0ff8 Typo 2017-01-17 12:01:51 -05:00
Winston Chang
9d69ff01b3 Update ion.rangeSlider to 2.1.6 (#1540)
* Update ion.rangeSlider to 2.1.6

* Simplify code when slider separator is ""

* Add links to NEWS
2017-01-17 12:00:47 -05:00
Winston Chang
61831f530f Merge pull request #1525 from rstudio/wch/ggplot-coord-fixed
Add plot interaction support for coord_fixed
2017-01-16 13:01:01 -05:00
Winston Chang
6065db1d24 Update NEWS 2017-01-16 13:00:41 -05:00
Winston Chang
270b8415e8 Add plot interaction support for coord_fixed. Closes #1121 2017-01-16 12:58:58 -05:00
Winston Chang
1987331a70 Bump version to 1.0.0.9000 2017-01-16 12:55:36 -05:00
Winston Chang
ab85216b96 Merge tag 'v1.0.0' 2017-01-13 13:36:23 -05:00
Winston Chang
b5cb78c77e Update URL 2017-01-10 10:13:06 -06:00
Winston Chang
e75c99672d Update NEWS 2017-01-09 12:38:48 -06:00
Winston Chang
7faba72ebe Fix URL 2017-01-09 12:38:48 -06:00
Winston Chang
cbe8fc1bdf Bump version to 1.0.0 2017-01-09 12:38:48 -06:00
Winston Chang
f66a7660e2 Merge pull request #1529 from rstudio/feature/res-path-numeric-prefix
Relax naming requirements for addResourcePath
2017-01-09 12:28:07 -06:00
Winston Chang
5f3159a203 Add link to PR in NEWS 2017-01-09 12:25:32 -06:00
JJ Allaire
76aeda4436 refine regex 2017-01-09 12:32:12 -05:00
JJ Allaire
fa791cd28c Relax naming requirements for addResourcePath()
First character no longer needs to be a letter. See https://github.com/rstudio/tutor/issues/4 for discussion.
2017-01-09 11:04:51 -05:00
Winston Chang
d836c68ee5 Grunt 2017-01-03 16:17:48 -06:00
Winston Chang
519d90f0a7 Update NEWS 2017-01-03 16:17:28 -06:00
Winston Chang
26400be6f7 Pressing Esc in a modal in a gadget only closes the modal. Closes #1453 (#1523) 2017-01-03 17:14:31 -05:00
Winston Chang
92ba7e9d54 Update yarn install instructions 2017-01-03 14:29:43 -06:00
Winston Chang
25eafe1e69 NEWS: more info on testing 2017-01-03 14:16:55 -06:00
Winston Chang
118a9ca861 Update NEWS 2017-01-03 12:54:06 -06:00
Winston Chang
174a1fe834 Update to font-awesome 4.7.0 2017-01-03 12:47:23 -06:00
Winston Chang
1e0f3f40a9 Replace structure(NULL) with structure(list())
In R-devel 71841, structure(NULL) was deprecated.
2016-12-28 16:43:29 -06:00
Barbara Borges Ribeiro
19623694f5 Added skipFirst arg to observeEvent (#1494)
* added skipFirs arg to observeEvent

* create getCurrentObserver() function

* better NEWS entry

* made code more consistent

* implemented `once` param to `observeEvent`; extensive documentation for `getCurrentObserver`

* implement dig param to `getCurrentObserver`

* fix bug that was causing unit tests to fail

* take two

* git commit

* removed function getCurrentObserver

* delete .globals$currentObserver variable

* update docs

* typo

* remove dupes in index.r (bah humbug)

* rerun devtools::document
2016-12-19 15:51:19 -08:00
Winston Chang
55a16043e1 Merge pull request #1510 from rstudio/joe/feature/debounce
Add reactive debounce and throttle functions
2016-12-16 11:10:02 -06:00
Winston Chang
29943b7edd Merge pull request #1482 from rstudio/barbara/runapp
Fixes #1358: more informative error message when calling runApp inside of an app's app.R
2016-12-16 10:15:35 -06:00
Joe Cheng
a1e2af9533 Add debounce/throttle tests, priority arg 2016-12-15 14:52:07 -08:00
Barbara Borges Ribeiro
c350e2a668 Fixes #1358: more informative error message when calling runApp inside of an app's app.R (or inside ui.R or server.R). 2016-12-15 21:50:39 +00:00
Joe Cheng
e0868ba2ab Fix #1013: flushReact should be called after app loads (#1503)
* Fix #1013: flushReact should be called after app loads

* Add link to pull request
2016-12-15 13:16:18 -06:00
Joe Cheng
bcefd1fbd8 Fix #117: Reactive expressions hold on to memory for longer than necessary (#1504)
* Fix #117: Reactive expressions hold on to memory for longer than necessary

* Fix broken link

* Add link to pull request
2016-12-15 13:15:00 -06:00
Joe Cheng
f5fbad0abf Add link to pull request 2016-12-15 11:14:48 -08:00
Joe Cheng
95b1a197be Remove unnecessary namespace 2016-12-15 11:11:29 -08:00
Joe Cheng
39169a36f5 Wording tweaks 2016-12-15 11:10:28 -08:00
Joe Cheng
3b1a409f07 Remove unnecessary link qualifier 2016-12-15 11:01:35 -08:00
Joe Cheng
accd70d4b4 Add session$userData feature (#1513)
* Add session$userData

* Tweak wording of NEWS

* Fix broken links
2016-12-15 12:50:20 -06:00
Winston Chang
3c7f4b760f Merge pull request #1514 from rstudio/joe/bugfix/fix-broken-links
Fix unqualified links to other packages
2016-12-15 12:42:19 -06:00
Joe Cheng
f7d7ccfd2c Fix unqualified links to other packages
R-devel warns on this now, causes Travis to fail
2016-12-15 10:35:46 -08:00
Joe Cheng
de98a03887 Add limitations section to debounce/throttle docs 2016-12-13 17:48:36 -08:00
Joe Cheng
0e11c240cb Add magrittr as Suggests because of ?debounce example 2016-12-13 17:29:29 -08:00
Joe Cheng
c0a298e484 Add reactive debounce and throttle functions 2016-12-13 17:22:12 -08:00
Winston Chang
907b9a9862 Merge pull request #1480 from rstudio/barbara/verbatim
Closes #1357: verbatimTextOutput should optionally be hidden if no content
2016-12-07 10:13:04 -06:00
Barbara Borges Ribeiro
8d70d91cf4 fix #1487: better error handling for insertUI when selector does not match anything in DOM (do console log) 2016-12-07 05:10:43 +00:00
Barbara Borges Ribeiro
6fb86859ce use past tense in all NEWS.md entries 2016-12-07 04:55:08 +00:00
Barbara Borges Ribeiro
fe733b319f Fixes #969: allow navbarPage's fluid param to control both containers 2016-12-07 00:22:33 +00:00
Barbara Borges Ribeiro
08b58f3055 allow navbarPage's fluid param to control both the content *and* the header containers 2016-12-07 00:21:49 +00:00
Barbara Borges Ribeiro
9f6659f526 added a new arg (placeholder = FALSE) to verbatimTextOutput() so that by default it doesn't show up when it is empty; improved the documentation example 2016-12-07 00:02:40 +00:00
Barbara Borges Ribeiro
d28397df93 Fix #1359: shinyApp options argument ignored when passed to runApp (#1483) 2016-12-06 20:52:19 +00:00
Barbara Borges Ribeiro
2e1c37146b Add ... arg to downloadButton (merge #1492)
Add ... arg to downloadButton
2016-12-05 15:37:02 +00:00
Barbara Borges Ribeiro
903adc8f97 Added ability to pass arguments to the a tag function called inside downloadButton() and downloadLink(). Closes #986. 2016-12-05 15:35:31 +00:00
Winston Chang
fc7f454382 Merge pull request #1449 from rstudio/barbara/bugfix/insert-ui
Fix #1438: `unbindAll()` should not be called when inserting content with `insertUI()`
2016-12-01 15:45:23 -06:00
Winston Chang
ef35fc63a1 Grunt 2016-12-01 15:41:51 -06:00
Barbara Borges Ribeiro
52a193b183 unbindAll() should not be called when inserting content with insertUI() 2016-12-01 15:40:17 -06:00
Winston Chang
dad401a6ec Merge pull request #1464 from rstudio/testmode-inject-js
Add support for injecting JS code when in test mode
2016-12-01 14:52:12 -06:00
Winston Chang
ec3f8118db Grunt 2016-12-01 10:47:02 -06:00
Winston Chang
cfc0194c00 Sort input, output, export by name 2016-12-01 10:46:46 -06:00
Winston Chang
dd28f52301 Add sortByName function 2016-12-01 10:46:46 -06:00
Winston Chang
9dcbd532e6 Add getTestSnapshotBaseUrl function 2016-12-01 10:46:46 -06:00
Winston Chang
16b4a2cad2 Rename testEndpointUrl to testSnapshotUrl 2016-12-01 10:46:46 -06:00
Winston Chang
bd9d8a035a Change arguments from plural to singular 2016-12-01 10:46:46 -06:00
Winston Chang
d55ffb0212 Change default snapshot format to JSON 2016-12-01 10:46:46 -06:00
Winston Chang
e76ddfd005 Emit message when running in test mode 2016-12-01 10:46:46 -06:00
Winston Chang
59145a3b40 Add testmode as an option to runApp 2016-12-01 10:46:46 -06:00
Winston Chang
c993f5343b Bump version to 0.14.2.9001 2016-12-01 10:46:46 -06:00
Winston Chang
b62acec5ee Use singular form of input, export, and output 2016-12-01 10:46:46 -06:00
Winston Chang
b34ab9cdd5 Add shiny:fileuploaded JS event 2016-12-01 10:46:46 -06:00
Winston Chang
e0a8ab852e Update NEWS 2016-12-01 10:46:46 -06:00
Winston Chang
bd5ebd0e41 Remove token check 2016-12-01 10:46:46 -06:00
Winston Chang
661e21d25b Safer method for injecting code in test mode 2016-12-01 10:46:46 -06:00
Winston Chang
dc69a2bc94 Make sure test values are named vectors 2016-12-01 10:46:46 -06:00
Winston Chang
e6fec6b27d Rename variable 2016-12-01 10:46:46 -06:00
Winston Chang
27b92f9838 Add args to getTestEndpointUrl 2016-12-01 10:46:46 -06:00
Winston Chang
3446def4dd Basic code injection support 2016-12-01 10:46:46 -06:00
Barbara Borges Ribeiro
2700206715 Improve documentation for submitButton and change 07_widgets example to use an action button (#1475)
* update 07_widgets example

* improved documentation for submitButton (including a warnign section and an full-app example)

* typo

* update documentation based on Winton's feedback
2016-11-28 13:19:40 -06:00
Winston Chang
fdfc6f70f3 Merge branch 'barbara/contributing' 2016-11-22 14:56:25 -06:00
Winston Chang
065c288edb Edits to contribution guidelines 2016-11-22 14:56:12 -06:00
Barbara Borges Ribeiro
3121d2c23e mention support for the optgroup tag in the documention for selectInput (specifically in the choices arg). Added example app too. 2016-11-22 11:28:35 -06:00
Barbara Borges Ribeiro
7cd3bb524c add download attribute to the a tag that generates downloadButoon and downloadLink 2016-11-18 21:00:28 -08:00
Barbara Borges Ribeiro
6b8cc97779 drafted new contribution guidelines 2016-11-19 02:22:16 +00:00
Winston Chang
b7112a1edd Add link to NEWS 2016-11-10 15:26:46 -06:00
Jonathan
28965b7356 Render HTML dependency <meta> tag contents correctly (#1463)
* render HTML dependency <meta> tag contents correctly

* use direct address rather than loop; update NEWS
2016-11-10 15:10:18 -06:00
Dean Attali
bd3aa28416 fix typo in dateInput documentation (#1454) 2016-11-02 09:53:40 -05:00
Winston Chang
9fed4ce24c Bump version to 0.14.2.9000 2016-11-01 15:40:04 -05:00
Winston Chang
90383e30dd Bump version to 0.14.2 2016-10-31 10:19:40 -05:00
Winston Chang
13f184e957 Remove NEWS entry for change that was reverted 2016-10-31 10:17:51 -05:00
Winston Chang
a7a2c6d7ff Add list2env wrapper, for R <3.2.0 (#1446)
* Add list2env wrapper, for R <3.2.0

* Update NEWS
2016-10-28 13:56:52 -05:00
Winston Chang
d1bf39d0ac Add exportTestValues function (#1436)
* Add onTestSnapshot function

* Add shiny.testing option

* Add entry to staticdocs index

* Bump version to 0.14.1.9002 and update NEWS

* Document params for onTestSnapshot

* Add session$enableTestEndpoint() method

* Un-export applyInputHandlers

* Grunt

* Provide inputs, outputs, and snapshot at test endpoint

* Remove non-working example

* Fix var name in documentation

* Rename shiny.testing to shiny.testmode

* Rename onTestSnapshot to exportTestValues and add example

* Add session$getTestEndpointUrl

* Grunt

* Add module support to exportTestValues

* Test endpoint allows specifying specific values

* session$getTestEndpointUrl: add arguments for choosing which values to return
2016-10-27 21:08:34 -05:00
Joe Cheng
7dff6b8415 Merge pull request #1387 from sipemu/master
options render for updateSelectizeInput did not worked in modules
2016-10-27 11:11:16 -07:00
Dean Attali
656e019829 allow overriding a JS custom message handler; fixes #1419 (#1445)
* allow overriding existing custom JS message handlers

* when a JS handler gets re-defined, only use the most recent one

* JS handler overwrite: changes re winston's comments

* overwrite JS handler: add NEWS item

* fix wrong URL in NEWS
2016-10-27 13:07:34 -05:00
Winston Chang
2133b0f498 Use === in Javascript 2016-10-26 21:01:36 -05:00
Winston Chang
bc4dcee2b1 allow shiny.trace option to specify which type of messages to relay; fixes #1422 (#1428)
Squashed commit of the following:

commit bdc4080032ff6b5b2de0f799aa307272f3905003
Author: Dean Attali <daattali@gmail.com>
Date:   Mon Oct 17 18:18:03 2016 -0700

    add PR link to news item

commit 22c695cde2b270ba8ec37d4862ad1f30de76ce68
Author: Dean Attali <daattali@gmail.com>
Date:   Mon Oct 17 15:01:24 2016 -0700

    update NEWS for #1422 fix

commit e669548c13f84f0929e4131c641a8333e08baa26
Author: Dean Attali <daattali@gmail.com>
Date:   Sat Oct 15 12:45:49 2016 -0700

    allow shiny.trace option to specify which type of messages to relay; fixes #1422
2016-10-26 12:24:00 -05:00
Winston Chang
0e8cf95739 Pass shinysession to applyInputHandlers
This fixes a problem where input handlers that require a session object
would throw errors.
2016-10-25 10:27:03 -05:00
Joe Cheng
e133290c57 Fix #1399: Duplicate binding error with insertUI and nested uiOutput (#1402)
* Fix #1399: Duplicate binding error with insertUI and nested uiOutput

* Update NEWS.md
2016-10-18 20:22:02 -05:00
shrektan
1429b0677e fix a typo: option() -> options() 2016-10-18 14:56:38 -05:00
Barbara Borges Ribeiro
d03ee36647 Fixes #1427: add event delegation so that modals do not close by mistake (#1430)
* Fixes #1427: add event delegation so that modal does not close when an element inside it is triggered as hidden

* use `this === e.target` instead

* added NEWS item

* `e.target` must be equal to `$(#shiny-modal)`, not `this`
2016-10-18 14:54:27 -05:00
Winston Chang
6e5880c642 Bump version and update NEWS 2016-10-18 13:51:43 -05:00
Winston Chang
fa93cffafb Add entry to staticdocs 2016-10-18 13:51:43 -05:00
Winston Chang
ce9af0fb57 Export function that applies input handlers 2016-10-18 13:51:43 -05:00
Winston Chang
95700d8d51 Fix dategrange comment 2016-10-17 13:37:25 -05:00
Winston Chang
fb15e98519 Merge pull request #1429 from rstudio/slider-setvalue
Make sliderInputBinding.setValue update value immediately
2016-10-17 12:29:14 -05:00
Winston Chang
3054cb7971 Update NEWS 2016-10-17 12:28:36 -05:00
Winston Chang
f84587cf5a Grunt 2016-10-17 12:22:21 -05:00
Winston Chang
538f38f314 sliderInputBinding: setValue changes value immediately 2016-10-17 12:22:21 -05:00
Winston Chang
06578349c7 Document InputBinding.subscribe's callback argument 2016-10-17 12:18:12 -05:00
Winston Chang
a807476171 sliderInputBinding: rename 'updating' to 'immediate' 2016-10-17 12:13:15 -05:00
Winston Chang
7aacf9ca89 Use Yarn instead of npm (#1416) 2016-10-12 12:51:05 -05:00
Winston Chang
50dae5fb83 Remove unneeded npm package 2016-10-11 13:04:38 -05:00
Winston Chang
0853c425fe Bump version to 0.14.1.9000 in DESCRIPTION 2016-10-11 12:59:06 -05:00
Barbara Borges Ribeiro
edcc676693 add "fade" arg to modalDialog() (#1414)
* add "fade" arg to modalDialog() that can be set to FALSE to remove default modal animation

* added documentation

* reflow comments

* news item
2016-10-10 15:03:25 -05:00
Winston Chang
c8a742a121 Bump version and update NEWS 2016-10-05 09:32:36 -05:00
Winston Chang
ee14a7e15f Merge tag 'v0.14.1'
Shiny 0.14.1 on CRAN
2016-10-05 09:29:07 -05:00
Winston Chang
e1eaccf409 Fix tests for compiled code on R-devel. Closes #1404 2016-10-03 16:23:11 -05:00
Winston Chang
d2aae52868 Update NEWS 2016-09-30 15:39:31 -05:00
Winston Chang
9158fb4745 Bump version to 0.14.1 2016-09-30 15:39:31 -05:00
Winston Chang
0ff5ef5337 Remove file 2016-09-30 15:39:13 -05:00
Joe Cheng
1ace145f85 Merge pull request #1392 from rstudio/ggplot-fix
Add plot interaction support for ggplot>2.1.0
2016-09-30 12:40:30 -07:00
Winston Chang
565eb4b450 Merge pull request #1397 from rstudio/barbara/bugfix/radio
Maintain names of factors when updating radio buttons' choices
2016-09-29 14:18:24 -05:00
Barbara Borges Ribeiro
f39861c43f more comments and NEWS 2016-09-29 19:48:56 +01:00
Barbara Borges Ribeiro
72838c248f news entry 2016-09-29 19:16:46 +01:00
Barbara Borges Ribeiro
9be8765ccf more tests 2016-09-29 19:14:18 +01:00
Barbara Borges Ribeiro
48732c4393 deleted commented out lines 2016-09-29 19:14:18 +01:00
Barbara Borges Ribeiro
5bf0b7c920 a better fix 2016-09-29 19:14:18 +01:00
Barbara Borges Ribeiro
51a4580d0f maintain names of factors when updating radio buttons' choices 2016-09-29 19:14:18 +01:00
Winston Chang
266e611afa Update NEWS 2016-09-27 23:07:23 -05:00
Winston Chang
22598b693c Add more plot interaction tests 2016-09-27 23:07:23 -05:00
Winston Chang
008fe38f10 Add support for coord_flip 2016-09-27 23:07:17 -05:00
Winston Chang
24e8123240 Add plot interaction support for ggplot>2.1.0 2016-09-27 16:02:28 -05:00
Simon Müller
6054f03c0d Update update-input.R 2016-09-21 22:53:41 +02:00
Winston Chang
476f6d83e2 Remove no-longer-necessary CSS class 2016-09-19 10:22:40 -05:00
Winston Chang
ec57109f39 Merge pull request #1374 from rstudio/datepicker-noconflict
Fix datepicker conflicts
2016-09-19 09:30:22 -05:00
Winston Chang
d73488f887 Grunt 2016-09-19 09:27:28 -05:00
Winston Chang
3201380c29 Set value after min in max when updating dates 2016-09-19 09:27:28 -05:00
Winston Chang
1f04b39ae3 Rename bootstrapDP to bsDatepicker 2016-09-16 23:39:26 -05:00
Winston Chang
9e2b47027c Update NEWS 2016-09-16 23:39:26 -05:00
Winston Chang
662149a98a Update to jQuery UI 1.12.1 2016-09-16 23:39:26 -05:00
Winston Chang
fafa31589d Update NEWS 2016-09-16 23:39:26 -05:00
Winston Chang
43a5940b9e Updates to dateRangeInputBinding for new datepicker API 2016-09-16 23:39:26 -05:00
Winston Chang
33908624fd Fix off-by-one error for datepicker's setStartDate and setEndDate 2016-09-16 23:39:26 -05:00
Winston Chang
ffef8a341f Add workaround for bootstrap datepicker bug with setStartDate and setEndDate 2016-09-16 23:39:26 -05:00
Winston Chang
a48c5df844 Don't try to set min/max date when undefined 2016-09-16 23:39:26 -05:00
Winston Chang
37b6a668ab Fix off-by-one dates 2016-09-16 23:39:26 -05:00
Winston Chang
2a9a7cc897 Enable noconflict for Bootstrap Datepicker. Closes #1346 2016-09-16 23:39:26 -05:00
Winston Chang
c62e6b5734 Update to Bootstrap Datepicker 1.6.4. Closes #1218 2016-09-16 23:39:26 -05:00
Winston Chang
6ec1d0b935 grunt clean && grunt 2016-09-16 23:37:31 -05:00
Winston Chang
6c5769fdd8 Add missing files to Grunt clean 2016-09-16 23:36:57 -05:00
Winston Chang
09acc5920c Allow using no separator for sliderInput numbers. Fixes #1369 2016-09-15 12:03:25 -05:00
Winston Chang
9613c58bf8 Merge pull request #1370 from rstudio/fix-bookmark-fileinput
Fix bookmark fileinput
2016-09-13 15:38:52 -05:00
Winston Chang
147f9ac64b Remove sourcetools workaround for Travis
This is no longer necessary because the new version of sourcetools on CRAN
does not need C++11
2016-09-13 12:52:15 -05:00
Winston Chang
cc1e8961a1 Update NEWS 2016-09-13 11:24:42 -05:00
Winston Chang
3b1b2f401d Use createUniqueId for consistency 2016-09-13 11:14:29 -05:00
Winston Chang
58a87b9b61 Copy restored file to temp directory 2016-09-13 11:13:21 -05:00
Winston Chang
f09475a6b5 Better check for unsafe paths in restored file inputs 2016-09-13 11:12:16 -05:00
Winston Chang
750422d858 Mark restored file inputs with correct serializer. Closes #1368 2016-09-13 10:49:08 -05:00
Winston Chang
03d911d335 Use cache for AppVeyor 2016-09-13 10:47:00 -05:00
Winston Chang
2747c11a46 Bump version to 0.14.0.9000 2016-09-13 10:46:15 -05:00
Winston Chang
5a9fe2637f Update shiny.rstudio.com URLs in NEWS 2016-09-12 09:36:32 -05:00
Winston Chang
a5787f9988 Bump version to 0.14 2016-09-08 15:03:19 -05:00
Winston Chang
85e22bb515 Ignore revdep 2016-09-08 15:03:19 -05:00
Barbara Borges Ribeiro
5e1e90ad80 fix validation bug 2016-09-07 19:22:02 +01:00
Winston Chang
fe85421c7e Fixes for R CMD check 2016-09-06 14:43:57 -05:00
Winston Chang
38af6ce279 Merge pull request #1362 from rstudio/barbara/update-old-release-news
Barbara/update old release news
2016-09-06 11:05:24 -05:00
Barbara Borges Ribeiro
fe92f16da4 updated 0.12 section 2016-09-05 20:55:38 +01:00
Barbara Borges Ribeiro
edc4b562f2 removed unnecessary line breaks now that we switched to .md 2016-09-05 20:42:20 +01:00
Barbara Borges Ribeiro
7b6a91064c updated 0.11 section 2016-09-05 19:52:28 +01:00
Winston Chang
a32414c6fc NEWS edits 2016-09-02 21:09:39 -05:00
Winston Chang
259b4e29de NEWS improvements (#1354) 2016-09-02 16:28:08 -05:00
Winston Chang
e56f80b546 Check whether hosting environment supports bookmarking (#1360)
* Check whether hosting environment supports bookmarking

* Show notification on startup if hosting environment doesn't support saved-to-server bookmarking
2016-09-02 14:19:41 -07:00
Barbara Borges Ribeiro
1ff52c5290 Merge branch 'master' of https://github.com/rstudio/shiny 2016-09-02 00:42:26 +01:00
Barbara Borges Ribeiro
70bd249f43 make explicit link to external package 2016-09-02 00:42:14 +01:00
Winston Chang
f2f7e43579 Convert validate example to single-file app. Closes #1345 (#1347) 2016-09-01 13:08:03 -07:00
Winston Chang
c36d60fcd4 Fix reactive highlighting in showcase mode. Closes #1350 (#1351) 2016-09-01 13:07:39 -07:00
Barbara Borges Ribeiro
0950f307d1 Merge pull request #1344 from rstudio/joe/feature/root-scope
Let modules get at the root scope (undocumented for now)
2016-09-01 04:58:17 +01:00
Joe Cheng
a9b7e4a85e Let modules get at the root scope (undocumented for now) 2016-08-31 14:44:45 -07:00
Winston Chang
912a886539 updateQueryString: add note about not working in IE9. Closes #1336 (#1339) 2016-08-31 12:17:49 -07:00
Winston Chang
f7484f49e5 Fix file uploads in IE9. Closes #1332 (#1342)
* Fix file uploads in IE9. Closes #1332

* Grunt
2016-08-31 12:17:24 -07:00
Joe Cheng
9f68be1925 Merge pull request #1341 from rstudio/ie9-fix-remove-modal
Fix modal removal for IE 9
2016-08-31 12:02:43 -07:00
Joe Cheng
ef298f8d7a Merge pull request #1337 from rstudio/fix-replay-plot
Make sure displaylist is on for recording/replaying plots
2016-08-31 12:01:32 -07:00
Winston Chang
c038f0e6ee Grunt 2016-08-31 11:36:16 -05:00
Winston Chang
3c53a93447 Wrap DOM object in jQuery. Fixes #1335 2016-08-31 11:35:48 -05:00
Winston Chang
7e86e65cce Make sure displaylist is on for recording/replaying plots 2016-08-30 20:09:45 -05:00
Winston Chang
ad171d6cbb Better checks in modal example 2016-08-30 17:02:07 -05:00
Joe Cheng
76ffc20836 Merge pull request #1329 from rstudio/progress-compatibility
Add old-style rendering option to progress bars
2016-08-30 14:50:32 -07:00
Winston Chang
c4cc5b6dfc Fix renderPlot's execOnResize logic
This was found in #1331, but the real problem with that issue is that
the mclust::mclust2Dplot function has changed since we wrote the example
app.
2016-08-30 16:37:50 -05:00
Winston Chang
878c9210d3 Add shinyOption for progress.style 2016-08-30 16:15:55 -05:00
Winston Chang
35c982b367 Grunt 2016-08-29 19:48:56 -05:00
Winston Chang
9c4ff080af Update NEWS 2016-08-29 19:47:12 -05:00
Winston Chang
d32ca64a03 Add old-style rendering as an option for progress 2016-08-29 16:05:43 -05:00
Winston Chang
53b89390be Rename shiny-progress CSS class to shiny-progress-notification 2016-08-29 15:19:45 -05:00
Winston Chang
a8e09d7fe6 Add sections to NEWS and add some new items 2016-08-26 12:26:30 -05:00
Winston Chang
0c7cf20e7e Switch NEWS to Markdown 2016-08-26 11:43:38 -05:00
Winston Chang
6ebcee33c5 Add some issue numbers to NEWS 2016-08-26 11:39:20 -05:00
Winston Chang
c73544fb59 Fix misplaced NEWS items 2016-08-26 11:29:10 -05:00
Winston Chang
37c1f93bcb Merge pull request #1320 from rstudio/joe/docs/sendBinaryMessage
NEWS, doc update for sendBinaryMessage
2016-08-25 20:25:51 -05:00
Joe Cheng
95aa2e10fc NEWS, doc update for sendBinaryMessage 2016-08-25 15:48:53 -07:00
Joe Cheng
279e6e3925 Merge remote-tracking branch 'origin/daef/feature/binary-messages' 2016-08-25 13:08:29 -07:00
Joe Cheng
8a661d5ee4 Code review feedback 2016-08-25 13:04:58 -07:00
Joe Cheng
67fcb40455 Merge pull request #1313 from rstudio/bookmarking-arg
Add enableBookmarking argument to shinyApp()
2016-08-25 13:00:04 -07:00
Winston Chang
641524c80e More docs for enableBookmarking 2016-08-24 17:00:59 -05:00
Winston Chang
55802354d4 Add enableBookmarking arg to shinyApp() 2016-08-24 14:47:56 -05:00
Winston Chang
75f4f5c0bd Merge pull request #1317 from MangoTheCat/fix/updatedaterangeinteraction
Fix updateDateRangeInput interaction from #1299 & #1315
2016-08-24 13:43:55 -05:00
Gábor Csárdi
382e9dee55 Grunt 2016-08-23 22:52:41 +01:00
Gábor Csárdi
6861d4029e Fix updateDateRangeInput interaction from #1299 & #1315 2016-08-23 22:51:18 +01:00
Joe Cheng
370ef16854 Slight tweaks
- Rename arguments to type/message
- Check tag length after converting to bytes
2016-08-23 14:08:04 -07:00
Gábor Csárdi
9dbe434792 Fix updateDateRangeInput when only one of start/end updated (#1315) 2016-08-23 16:07:53 -05:00
Joe Cheng
21a0e95623 Merge commit 'd1353e8eaebc3f878fe9074919948d662caf8a89' 2016-08-23 13:58:40 -07:00
Winston Chang
57c6307479 Merge pull request #1314 from rstudio/joe/bugfix/downloadhandler-no-data
Fix #1122: Do something sensible when downloadHandler doesn't create a file
2016-08-23 15:50:39 -05:00
Winston Chang
01d8b1f468 Merge pull request #1299 from MangoTheCat/fix/resetdateinput
Allow updateDateInput to set input fields to empty
2016-08-23 15:48:25 -05:00
Joe Cheng
ef6b82a0a3 Fix #1122: Do something sensible when downloadHandler doesn't create a
file
2016-08-23 13:48:24 -07:00
Gábor Csárdi
19b7d1a7c5 Rebuild minified files 2016-08-23 21:21:35 +01:00
Gábor Csárdi
097d901191 Updates can clear date and date range inputs 2016-08-23 21:21:10 +01:00
Winston Chang
a1b5846b29 Remove extra comma 2016-08-23 10:54:37 -05:00
Winston Chang
dbdb353e69 Add updateQueryString example 2016-08-23 10:30:12 -05:00
Winston Chang
4456eac1fd Make staticdocs tests work in R CMD check 2016-08-22 21:04:19 -05:00
Winston Chang
ba3f8f432e Update staticdocs index. Closes #1293 2016-08-22 20:38:16 -05:00
Winston Chang
bab539f52c Make it possible to have multiple bookmark buttons (#1310) 2016-08-19 21:13:27 -07:00
Winston Chang
42dbb128be Fix handling of NULLs in updateDateInput 2016-08-19 14:36:29 -05:00
Winston Chang
5e4a6cb15f Fix priority of onRestore observer 2016-08-19 14:12:57 -05:00
Winston Chang
73e45ce911 Clarify observer priority documentation 2016-08-19 14:10:24 -05:00
Winston Chang
1e40043456 Add travis fix for R-devel (thanks to @jimhester) 2016-08-19 12:12:42 -05:00
Winston Chang
7f3b952ec7 Documentation updates 2016-08-19 11:53:31 -05:00
Winston Chang
82887dc1c1 Do Travis checks on multiple R versions 2016-08-19 10:16:54 -05:00
Winston Chang
71380ab37a Replace stopWithCondition with reactiveStop 2016-08-18 16:54:42 -05:00
Winston Chang
5d00804758 Add size argument to modalDialog (#1308) 2016-08-18 14:43:57 -07:00
Winston Chang
84364c65b0 Bookmark/restore actionButtons (#1304) 2016-08-18 14:42:19 -07:00
Winston Chang
1b59b705ae Fix bookmark URL when there are no input values. Fixes #1306 2016-08-17 14:00:14 -05:00
Winston Chang
bc90fe6f99 Don't error when creating tabsets without id
The bug was introduced by #1296.
2016-08-16 20:02:33 -05:00
Winston Chang
c8d6a0833e Merge pull request #1301 from daattali/patch-2
fix typo in NEWS
2016-08-16 13:57:28 -05:00
Winston Chang
d8fc7d27ec Add Dismiss button to URL modal 2016-08-16 13:00:42 -05:00
Winston Chang
1e44b19ff0 Fix typo in bookmarkButton documentation 2016-08-16 12:28:37 -05:00
Winston Chang
cc8b2cd20e Document that enableBookamrking can be called in onStart function 2016-08-16 12:22:19 -05:00
Dean Attali
057b1e294c fix typo in NEWS 2016-08-13 01:54:43 -07:00
Winston Chang
0653e790c7 Merge pull request #1298 from rstudio/handle-malformed-dates
Don't crash on malformed date input values. Closes #803
2016-08-12 21:29:00 -05:00
Winston Chang
6d72bbcb76 Don't crash on malformed date input values. Closes #803 2016-08-12 21:28:43 -05:00
Winston Chang
59e6f08455 Merge branch 'textarea' 2016-08-12 21:26:55 -05:00
Winston Chang
8fdccf50a8 Update NEWS 2016-08-12 21:26:19 -05:00
Winston Chang
1c7e11c5d9 Textarea refinements 2016-08-12 21:26:18 -05:00
Winston Chang
1756fbbb23 Merge branch 'fix-update-date' 2016-08-12 21:23:42 -05:00
Winston Chang
7bb939ab7f Restore bookmarked tabs. Fixes #1282 (#1296) 2016-08-12 18:53:44 -07:00
Nuno Agostinho
4fa0abbd5a Add arguments height, rows and cols 2016-08-12 17:19:23 -05:00
Nuno Agostinho
1e5f0266ef Fix missing parenthesis 2016-08-12 17:19:23 -05:00
Nuno Agostinho
3dee62105e Fix value assignment of text area input 2016-08-12 17:19:23 -05:00
Nuno Agostinho
680b2323d5 Update documentation relative to textarea input 2016-08-12 17:19:23 -05:00
Nuno Agostinho
562b4dad4d Add textarea input 2016-08-12 17:19:23 -05:00
Joe Cheng
079a82dfe4 executeElapsed gets stuck returning TRUE even when nothing was executed
Fixes #1278
2016-08-12 17:19:23 -05:00
Winston Chang
16f7eb43b0 Merge branch 'joe/bugfix/too-often-flush' 2016-08-12 15:24:25 -05:00
Winston Chang
4b0ed3f224 updateDateInput: better handling of malformed dates. Closes #1179 2016-08-12 14:43:44 -05:00
Winston Chang
1d453b694d Add documentation about using selected=character(0). Closes #1182 2016-08-12 14:22:54 -05:00
Barbara Borges Ribeiro
751e8c189e fixes #1093 (#1291)
* fixes #1093

* check if NULL
2016-08-11 14:29:24 -07:00
Winston Chang
183e9a3d0b Bookmarking fixes (#1281)
* Rename invalidateReactiveValue to freezeReactiveValue

* Make onFlush and onFlushed use 'once' argument

* session$flushOutput: schedule another flush if needed

* Catch all errors before they propagate to websocket

* Restore original logic for progressKeys
2016-08-11 11:58:07 -07:00
Winston Chang
5f0f4dd485 Merge pull request #1285 from rstudio/joe/bugfix/update-radio-buttons-modules
Fix #1144: RadioButtons and checkBoxGroup do not work in modules when they are updated
2016-08-11 13:17:15 -05:00
Barbara Borges Ribeiro
20f05662aa fixes #1289 2016-08-11 15:57:31 +01:00
Joe Cheng
963471b43f Fix #1144: RadioButtons and checkBoxGroup do not work in modules when they are updated
The unqualified input ID was being used to generate name attributes
on radio and checkboxes.
2016-08-10 15:52:01 -07:00
Joe Cheng
fdb52e0243 executeElapsed gets stuck returning TRUE even when nothing was executed
Fixes #1278
2016-08-10 15:10:31 -07:00
Winston Chang
d1e4483f98 Upgrade to Font-Awesome 4.6.3. Closes #1274 2016-08-10 15:40:10 -05:00
Winston Chang
b194ada316 Upgrade to Bootstrap 3.3.7 2016-08-10 15:34:51 -05:00
Winston Chang
3a25a2dcbf Upgrade to jQuery 1.12.4. Closes #1251 2016-08-10 15:24:08 -05:00
Barbara Borges Ribeiro
85e4497fbe fixes #1270; improves docs; export isTruthy() 2016-08-10 20:45:40 +01:00
Barbara Borges Ribeiro
0bfa5e7ea6 allow sticky nodes in reactlog if users drags them (#1283) 2016-08-10 20:04:37 +01:00
Barbara Borges Ribeiro
013599890f Solve updateSelectInput bug in IE 11.0 (#1277) 2016-08-09 19:08:30 +01:00
Winston Chang
519e552405 Unset shinyOptions when set to NULL 2016-08-08 13:03:07 -05:00
Winston Chang
32a4ec49f7 Bump version and update NEWS 2016-08-05 16:19:57 -05:00
Winston Chang
3223332906 Merge pull request #1209 from wch/bookmarkable-state
Bookmarkable state
2016-08-05 16:15:12 -05:00
Winston Chang
f78bd08440 Correctly handle bookmark options for global.R and app.R 2016-08-05 16:08:49 -05:00
Winston Chang
99b5f92d7a Add showBookmarkUrlModal 2016-08-03 16:07:49 -05:00
Barbara Borges Ribeiro
1cc5e84104 Barbara/error handling/port as socket (#1263)
* catch if `port` is being used incorrectly (see issue #971)

* better error msg
2016-08-03 15:50:30 -05:00
Winston Chang
8346f5ab08 Fix enableBookmarking references 2016-08-03 14:41:37 -05:00
Winston Chang
dc60a39ba9 Fix tests to be order-insensitive 2016-08-02 13:47:42 -05:00
Winston Chang
16f7872553 Grunt 2016-08-02 11:18:25 -05:00
Winston Chang
75e7c4b2ca More informative error messages 2016-08-02 11:10:17 -05:00
Barbara Borges Ribeiro
c5d5ddd7d2 Merge pull request #1267 from rstudio/barbara/remove-example-extra-line
had forgotten to run devtools::document for #1266...
2016-08-02 04:16:22 +01:00
Barbara Borges Ribeiro
98a81e3708 had forgotten to run devtools::document 2016-08-02 04:13:35 +01:00
Barbara Borges Ribeiro
ecdbdb944a Merge pull request #1266 from rstudio/barbara/remove-example-extra-line
Barbara/remove example extra line
2016-08-02 03:51:41 +01:00
Barbara Borges Ribeiro
8a2846461b removed extra line at end of example section 2016-08-02 03:40:56 +01:00
Winston Chang
6bfb9a2f57 More details in enableBookmarking man page 2016-08-01 20:46:43 -05:00
Winston Chang
85dfb2d4eb urlModal: always show Ctrl/Cmd-C copy message 2016-08-01 20:00:05 -05:00
Barbara Borges Ribeiro
8f1d0c2b8f Barbara/appveyor (#1265) 2016-08-02 01:26:48 +01:00
Winston Chang
829494c03e Make bookmark button work when there's a submitButton 2016-08-01 16:13:43 -05:00
Winston Chang
f89fade28d Rename updateLocationBar to updateQueryString 2016-08-01 16:13:25 -05:00
Winston Chang
9081b1dadd Simplify list2env 2016-08-01 14:22:23 -05:00
Winston Chang
84dac544af Make sure state ID doesn't have invalid chars 2016-08-01 14:16:50 -05:00
Winston Chang
49f1ac333d Always get appDir when shinyApp object is created 2016-08-01 14:01:16 -05:00
Winston Chang
a2b761094b Fix bookmark values tests 2016-07-29 16:07:18 -05:00
Winston Chang
54bd3d480f Fixes for R CMD check 2016-07-29 15:59:56 -05:00
Winston Chang
b281f8fa32 Merge branch 'master' into bookmarkable-state 2016-07-29 15:47:31 -05:00
Winston Chang
a3732f845e Update bookmarking documentation 2016-07-27 16:17:21 -05:00
Winston Chang
38f6d0a020 When restoring modules, only provide state$dir if present 2016-07-26 16:24:36 -05:00
Winston Chang
a0e5da758a Ensure that state$values can't be replaced 2016-07-26 16:24:02 -05:00
Winston Chang
dcbe10c1c7 Keep state$values as an environment for save and restore 2016-07-26 14:33:04 -05:00
Joe Cheng
1f823d2a1b Fix #931: Observer memory leak (#1256)
* Fix #931: Observer memory leak

Observers were being prevented from being garbage collected by
their own onReactiveDomainEnded() event handlers. This commit
fixes that by making sure that those event handlers are only
registered when autoDestroy=TRUE, and that they are unregistered
both on destruction and when autoDestroy is changed.

* Remove extraneous self$ prefixes

* Add comment explaining autoDestroyHandle
2016-07-26 11:56:47 -07:00
Winston Chang
12d0a9e11a For session_proxy, add [[ and disallow $<- and [[<- (#1262) 2016-07-26 11:21:38 -07:00
Winston Chang
326b294c83 Change progress indicators to use notification API (#1160) 2016-07-26 11:16:08 -07:00
Winston Chang
50f213ae71 Fixes for R CMD check 2016-07-26 10:32:13 -05:00
Winston Chang
91d4fd8849 Fix behavior when onBookmark() is called multiple times in a module 2016-07-25 22:29:55 -05:00
Winston Chang
3f1985a9dc Allow modules to exclude their inputs 2016-07-25 22:10:38 -05:00
Winston Chang
573a71f09d New version of reactiveValuesToList 2016-07-25 16:32:01 -05:00
Winston Chang
57900fa287 Move methods out of ShinySaveState 2016-07-25 16:31:47 -05:00
Winston Chang
fa721d9614 More informative comments 2016-07-25 16:31:47 -05:00
Winston Chang
e64bbacf68 Define filterNamespace and unNamespace locally 2016-07-25 16:31:47 -05:00
Winston Chang
6ee2edc757 Add namespace support to reactiveValuesToList 2016-07-25 13:58:21 -05:00
Winston Chang
9ec4faf0d0 Allow modules to call onBookmark, onRestore, onRestored 2016-07-25 10:52:41 -05:00
Winston Chang
5be3ba2ffa Use Callbacks objects for bookmarking callbacks 2016-07-21 15:28:43 -05:00
Winston Chang
a5ee96656b Add onFlush, onFlushed, and onSessionEnded functions 2016-07-21 14:33:43 -05:00
Winston Chang
2db71d0323 Re-document 2016-07-21 14:33:42 -05:00
Winston Chang
dcf321047f When bookmarkStore="disable", don't restore 2016-07-21 13:55:45 -05:00
Winston Chang
4982110be9 configureBookmarking affect next shinyApp object creation 2016-07-21 12:49:36 -05:00
Winston Chang
28547e90d1 Fix memory leak (#1254)
* When observer is destroyed, invalidate context. Closes #1253

* Update NEWS
2016-07-20 21:01:29 -07:00
Joe Cheng
f70187597f Merge pull request #1222 from rstudio/joe/bugfix/chrome-unsafe-ports
Don't use randomly-chosen port numbers that Chrome deems unsafe
2016-07-20 13:36:05 -07:00
Barbara Borges Ribeiro
333e454e78 Merge pull request #1252 from rstudio/barbara/bugfix/where
give `where` arg a default value ("replace")
2016-07-20 19:47:00 +01:00
Barbara Borges Ribeiro
61cfd11644 give where arg a default value ("replace") 2016-07-20 19:05:20 +01:00
Winston Chang
1c970c8176 Remove configureBookmarking; add onBookmark, onBookmarked, onRestore, onRestored 2016-07-19 14:31:57 -05:00
Winston Chang
1fea54ca5a Merge pull request #1249 from daattali/patch-1
fix typo
2016-07-19 13:31:12 -05:00
Dean Attali
faccc42b22 fix typo 2016-07-17 02:42:41 -04:00
Winston Chang
460f4769a5 Add _inputs_ for url-encoded state 2016-07-12 12:30:37 -05:00
Winston Chang
f465643b75 Rename 'type' argument to 'store' 2016-07-11 23:00:19 -05:00
Winston Chang
a8afd71f96 Rename shiny_persist to shiny_bookmarks 2016-07-08 16:36:58 -05:00
Winston Chang
2274d60207 Change function(req) to function(request) 2016-07-08 16:23:52 -05:00
Winston Chang
57159bccfd Show copy instructions in bookmark dialog 2016-07-08 16:22:09 -05:00
Winston Chang
2888124752 Capture and log errors in initializing RestoreContext 2016-07-08 15:22:58 -05:00
Winston Chang
408e751dcf Capture and log errors in bookmarking/restoring 2016-07-08 15:22:21 -05:00
Winston Chang
af5dcc38a4 Add onRestored argument 2016-07-07 12:44:10 -05:00
Winston Chang
81434640d6 Use name 'bookmark' in more places 2016-07-07 12:04:53 -05:00
Barbara Borges Ribeiro
a45b58d956 Merge pull request #1239 from rstudio/joe/bugfix/insert-ui-initialize
Fix insertUI bug. Closes #1220, #1231, #1232
2016-07-06 22:04:00 +01:00
Barbara Borges Ribeiro
2c5e9a5e76 moved multiple to 4th argument 2016-07-06 21:44:52 +01:00
Barbara Borges Ribeiro
9fb847b179 Merge branch 'master' into joe/bugfix/insert-ui-initialize 2016-07-06 21:37:58 +01:00
Barbara Borges Ribeiro
b8341b2ba8 Merge pull request #1238 from rstudio/feature/pool-scheduler
Better pool support (minor change)
2016-07-06 21:35:21 +01:00
Barbara Borges Ribeiro
26d6e4da2c allows callback to be garbage collected 2016-07-06 21:29:12 +01:00
Barbara Borges Ribeiro
b16ed602d5 rebased with master
Merge branch 'joe/bugfix/insert-ui-initialize' of https://github.com/rstudio/shiny into joe/bugfix/insert-ui-initialize

# Conflicts:
#	inst/www/shared/shiny.js.map
#	inst/www/shared/shiny.min.js
#	inst/www/shared/shiny.min.js.map
2016-07-06 18:04:17 +01:00
Barbara Borges Ribeiro
24aab4d5d3 Merge branch 'master' of https://github.com/rstudio/shiny 2016-07-06 14:18:49 +01:00
Barbara Borges Ribeiro
2eb69d421a fix to outputOptions 2016-07-06 14:18:38 +01:00
Winston Chang
cb52706f2f Add bookmarking example with arbitrary values 2016-07-05 16:35:00 -05:00
Winston Chang
f44d232e8b Add check that UI is a function when restoring state 2016-07-05 16:28:09 -05:00
Winston Chang
a0ac79b9dd Documentation updates 2016-07-05 15:30:42 -05:00
Joe Cheng
177a2a8a1e Input not being initialized with insertUI if beforeStart/afterEnd 2016-07-05 11:06:25 -07:00
Barbara Borges Ribeiro
8b21a87175 Update NEWS 2016-06-30 17:51:09 +01:00
Barbara Borges Ribeiro
6d2dd8e315 Merge pull request #1226 from rstudio/feature/pool-scheduler
Support pool package
2016-06-30 17:49:52 +01:00
Joe Cheng
8b3aff599b Don't use randomly-chosen port numbers that Chrome deems unsafe
Still OK to use these ports if the user asks for them explicitly
2016-06-25 22:37:22 -07:00
Joe Cheng
deb9b74f27 Do equivalent of "mkdir -p" when making state dir 2016-06-25 14:25:22 -07:00
Winston Chang
591de3cbe8 Don't restore state if in a subapp 2016-06-20 16:00:25 -05:00
Winston Chang
f7151e2132 Change '_state_id' to '__state_id__' 2016-06-20 15:12:10 -05:00
Winston Chang
44521be6dd Gracefully handle errors in restoring state 2016-06-20 12:54:04 -05:00
Winston Chang
30416cdbb5 Grunt 2016-06-16 12:44:28 -05:00
Winston Chang
d04da2d256 Add asList method 2016-06-16 12:44:09 -05:00
Winston Chang
231d8a1949 Move loading and decoding of query string into RestoreContext 2016-06-16 10:39:45 -05:00
Winston Chang
3207bec805 Add ShinyRestoreContext class 2016-06-15 15:08:33 -05:00
Winston Chang
425a71e382 Replace bookmarkConfig with bookmarkObserver 2016-06-15 14:55:29 -05:00
Winston Chang
daa12ab2ec Revise how onSave is called; move persist() and encode() into ShinyState object 2016-06-15 14:55:06 -05:00
Joe Cheng
4c652389c5 Support pool package 2016-06-14 12:58:50 -07:00
Winston Chang
f69d88a656 Refinements to save button 2016-06-14 13:23:58 -05:00
Winston Chang
098cbc1456 Better splitting of state query string 2016-06-13 23:19:35 -05:00
Winston Chang
5f2da953a9 Add invalidateReactiveValue function 2016-06-13 22:12:28 -05:00
Winston Chang
638d999fcc Replace updateQueryString with updateLocationBar 2016-06-13 16:47:24 -05:00
Winston Chang
fa80fd64da Make 'restorable' opt-out instead of opt-in 2016-06-13 14:24:21 -05:00
Winston Chang
e4dad82dde Rename 'save' to 'persist' 2016-06-13 12:38:43 -05:00
Winston Chang
d65ff924c8 Add bookmarkButton 2016-06-10 12:47:31 -05:00
Winston Chang
96e9661aaa Fix reactive dependencies when restoring values 2016-06-10 10:55:18 -05:00
Winston Chang
8829d2ebd4 Properly mark actionButtons and passwordInputs as unserializable 2016-06-10 10:44:51 -05:00
Winston Chang
c019280d8a Call onRestore only if it exists 2016-06-10 10:38:04 -05:00
Winston Chang
8d3e5fc160 Refinements 2016-06-10 10:11:55 -05:00
Winston Chang
d3f1312c0b Remove 'enable' argument 2016-06-09 14:55:33 -05:00
Winston Chang
c58f48a1e4 Add support for bookmarking arbitrary values 2016-06-09 14:45:55 -05:00
Winston Chang
979e93509e parseQueryString: ignore extra ampersands 2016-06-09 13:01:36 -05:00
Winston Chang
135c3709b4 Prepare things for separate values 2016-06-09 11:57:45 -05:00
Winston Chang
08400d3f18 Add configureBookmarking function 2016-06-08 12:56:03 -05:00
Winston Chang
bf52075d1b Merge pull request #1197 from rstudio/joe/feature/resetBrush
Add ability to reset brush with session$resetBrush/Shiny.resetBrush
2016-06-03 13:04:01 -05:00
Winston Chang
56befda288 Remove outdated example 2016-06-03 12:48:34 -05:00
Winston Chang
3d68f1dc62 Remove bookmarkOutput; add saveStateModal and encodeStateModal 2016-06-03 12:48:33 -05:00
Winston Chang
970036ce1a Remove clipboard.js 2016-06-03 12:48:33 -05:00
Winston Chang
62108f28f4 Fix argument defaults 2016-06-02 12:52:19 -05:00
Winston Chang
66bbb072c3 Remove createBookmark function 2016-06-02 12:48:17 -05:00
Winston Chang
6c52c26a62 Make names consistent 2016-06-02 11:47:56 -05:00
Winston Chang
d52943d1bf Remove unused code path 2016-06-01 21:41:35 -05:00
Winston Chang
7cb1bbe3d6 Use new ID each time state is saved 2016-06-01 18:12:07 -05:00
Winston Chang
2548c46b8b Check for '..' in restored file input path 2016-06-01 18:07:33 -05:00
Winston Chang
dd5118116b Use wrapper functions for saving/restoring state 2016-06-01 17:10:56 -05:00
Winston Chang
77a9b66028 Merge pull request #1201 from rstudio/bugfix/avoid-radix-sort-overflow
avoid overflow in R 3.3.0 radix sort
2016-05-27 21:39:15 -05:00
Kevin Ushey
e813dab81c avoid overflow in R 3.3.0 radix sort 2016-05-27 14:48:44 -07:00
Winston Chang
6696880178 Add ability to save and restore fileInputs. Also improve fileInput appearance 2016-05-27 14:42:00 -05:00
Winston Chang
8e5952d9ae Add serializers 2016-05-26 12:43:01 -05:00
Joe Cheng
360c1d5953 Add ability to reset brush with session$resetBrush/Shiny.resetBrush 2016-05-25 15:37:27 -07:00
Winston Chang
a7aa6ced19 Save each state in a subdirectory 2016-05-20 14:52:52 -05:00
Winston Chang
97eea669d4 Better error handling when saving/restoring state 2016-05-20 14:17:23 -05:00
Winston Chang
c84777928e Use same state ID throughout a session 2016-05-20 14:10:26 -05:00
Winston Chang
490064a953 Remove unneeded randomID function 2016-05-20 14:10:26 -05:00
Winston Chang
d5975195b3 Initial version of saving state 2016-05-20 14:10:26 -05:00
Winston Chang
9588c36abb Merge branch 'joe/feature/insert-UI' 2016-05-18 15:53:50 -05:00
Barbara Borges Ribeiro
f9200ac135 small fixes; documentation; got rid of unnecassary things 2016-05-18 12:35:25 +01:00
Winston Chang
fffb9606ec Merge pull request #1185 from rstudio/barbara/showcase-update
Barbara/showcase update
2016-05-16 10:04:24 -05:00
Winston Chang
781e15cb84 Restore values only if 'restorable' option is set 2016-05-13 21:06:06 -05:00
Winston Chang
9742001a71 Add shiny options 2016-05-13 20:37:58 -05:00
Barbara Borges Ribeiro
e92eee5ffc removed constraint that forced elements inserted with insertUI to be wrapped in a div/span 2016-05-13 15:28:02 -05:00
Barbara Borges Ribeiro
293c1d471c tiny fix 2016-05-13 14:34:07 -05:00
Barbara Borges Ribeiro
384240b6a4 added NEWS item for IncludeWWW 2016-05-13 14:26:45 -05:00
Winston Chang
6fd626a3ec Disable seralizing of passwords and actionButtons 2016-05-12 17:03:25 -05:00
Winston Chang
bb4ce2f978 Don't clear bookmark DOM elements on error 2016-05-12 15:27:17 -05:00
Barbara Borges Ribeiro
2269e05058 code highliting; dropdown menu for the www files 2016-05-12 15:12:04 -05:00
Winston Chang
ca2a07b816 Add ability to invalidate a reactive value 2016-05-12 10:21:29 -05:00
Winston Chang
38c7bb35e0 Code cleanup 2016-05-12 10:06:44 -05:00
Winston Chang
4f6408f3e1 Add optional update button for bookmarkOutput 2016-05-12 10:06:44 -05:00
Winston Chang
7910d9fde4 Add argument to exclude values from bookmarking 2016-05-12 10:06:43 -05:00
Winston Chang
0258d7e24f Make sure bookmark output is not a text input 2016-05-12 10:06:43 -05:00
Winston Chang
85556ed532 Don't error when no restore context available 2016-05-12 10:06:43 -05:00
Winston Chang
cecb04b097 Make restore context available from server code 2016-05-12 10:06:43 -05:00
Winston Chang
8a7c5c18d0 Add tooltip on copy 2016-05-12 10:06:43 -05:00
Winston Chang
14a1a3f574 Rename functions 2016-05-12 10:06:43 -05:00
Winston Chang
c19f2a7499 Add license info for clipboard.js 2016-05-12 10:05:37 -05:00
Winston Chang
df95be5455 Add bookmarkOutput 2016-05-12 10:05:37 -05:00
Winston Chang
00bef13f1c Add ability for inputs to restore bookmarked values 2016-05-12 10:05:37 -05:00
Winston Chang
a6a35905a7 Clearer variable names 2016-05-12 10:05:37 -05:00
Winston Chang
93f28ef55c Preserve type of bookmarked data 2016-05-12 10:05:36 -05:00
Joe Cheng
bbcb9573cd Add example 2016-05-12 10:05:36 -05:00
Winston Chang
43cc6e19d4 Fixes 2016-05-12 10:05:36 -05:00
Joe Cheng
f4a44664c7 Bookmarkable state wip 2016-05-12 10:05:36 -05:00
Barbara Borges Ribeiro
dd7a3269ad added wwwFiles boolean option to DESCRIPTION file 2016-05-07 17:21:59 +01:00
Winston Chang
157d1b20c5 Fixes for R CMD check 2016-05-06 15:14:09 -05:00
Winston Chang
85fe0c00c2 Fix tests 2016-05-06 15:10:45 -05:00
Winston Chang
91092b8a96 Fix function labels for profiling 2016-05-06 15:03:00 -05:00
Barbara Borges Ribeiro
1ed237cfcc init commit 2016-05-05 16:11:43 +01:00
Barbara Borges Ribeiro
c7044498d5 added NEWS item for insertUI / removeUI 2016-05-05 14:17:45 +01:00
Joe Cheng
1d2a2fbcae Merge remote-tracking branch 'origin/joe/feature/insert-UI' 2016-05-04 11:34:54 -07:00
Barbara Borges Ribeiro
9b015e8cae documentation update 2016-05-03 13:57:10 +01:00
Barbara Borges Ribeiro
0a8c26fff4 call sendImageSize from unbindOutputs 2016-05-02 20:02:41 +01:00
Barbara Borges Ribeiro
506de72666 fixed typos; included argument defaults; removed 'shown', 'hidden' triggers following chat with Winston 2016-05-02 18:27:15 +01:00
Barbara Borges Ribeiro
a5b4156b56 moved insertAdjacentElement to the right place 2016-05-02 15:19:08 +01:00
Barbara Borges Ribeiro
da4b42cb1d ran grunt 2016-05-02 14:50:46 +01:00
Barbara Borges Ribeiro
53790f8247 various updates 2016-05-02 14:48:05 +01:00
Barbara Borges Ribeiro
69780d4727 added sendImage and sendOUtputHIddenState 2016-04-29 06:33:06 +01:00
Barbara Borges Ribeiro
aa2b644684 updated documentation; added ... argument to onFlush() and onFlushed() in order to be able to pass in arguments to the func 2016-04-29 05:27:26 +01:00
Barbara Borges Ribeiro
a12e8875a6 changed everything from sendCustomMessage to session$sendMessage 2016-04-29 05:16:22 +01:00
Barbara Borges Ribeiro
9e91b265ce sendInsertUI now uses sendMessage instead of sendCustomMessage 2016-04-29 04:46:09 +01:00
Barbara Borges Ribeiro
8c12e3ab90 added insertAdjacentElement for compatibility with Firefox 2016-04-28 12:03:15 +01:00
Winston Chang
7e303b4fc0 Merge pull request #1157 from rstudio/modal
Add modal dialogs
2016-04-27 15:30:02 -05:00
Winston Chang
40e0fcff30 Change modal example 2016-04-27 15:29:18 -05:00
Winston Chang
3c9e74b23e Re-document 2016-04-26 15:30:03 -05:00
Barbara Borges Ribeiro
6b001eb7c3 updated insertUI; added removeUI 2016-04-25 23:03:18 +01:00
Winston Chang
f81621aa66 Merge pull request #1158 from rstudio/example-cleanup
Clean up examples
2016-04-22 13:05:36 -05:00
Winston Chang
08c7484087 Rename argument 2016-04-21 15:22:43 -05:00
Barbara Borges Ribeiro
a8c68f3e30 updated shiny-options text 2016-04-18 17:28:45 +01:00
Barbara Borges Ribeiro
0e6698d760 updated NEWS 2016-04-18 02:12:47 +01:00
Barbara Borges Ribeiro
f3d4f9ff23 Merge pull request #1156 from rstudio/barbara/error-hiding
Barbara/error hiding
2016-04-18 01:56:26 +01:00
Barbara Borges Ribeiro
d711f17081 changed sanitization default to FALSE (on local development) 2016-04-18 01:44:43 +01:00
Barbara Borges Ribeiro
d35eba45c5 tiny fix 2016-04-18 01:34:50 +01:00
Barbara Borges Ribeiro
cd53e79b19 removed classError argument to safeError function 2016-04-18 01:27:37 +01:00
Barbara Borges Ribeiro
3db7029534 Merge branch 'master' of https://github.com/rstudio/shiny 2016-04-15 16:50:19 +01:00
Barbara Borges Ribeiro
ad1e52bf19 got rid of warning that popped up when renderFunc took no arguments; there really isn't a good reason to require this (not at this point at least) 2016-04-15 16:49:52 +01:00
Barbara Borges Ribeiro
e08791a284 update to safeError 2016-04-14 18:20:49 +01:00
Barbara Borges Ribeiro
8d1deeb568 undo last commit to be able to merge automatically 2016-04-14 18:02:16 +01:00
Barbara Borges Ribeiro
375c7789a2 updated NEWS 2016-04-14 17:52:10 +01:00
Barbara Borges Ribeiro
ec8a81aedb Merge pull request #1163 from bborgesr/barbara/fix-tabsetpanel
deprecated position arg to tabsetPanel; updated NEWS; cc @jcheng5 @wch
2016-04-14 17:49:51 +01:00
Barbara Borges Ribeiro
033d513aee added version to shinyDeprecated call; updated NEWS 2016-04-14 17:43:24 +01:00
Barbara Borges Ribeiro
fb3e4e4881 Changed customStop to stop(safeError). Refactored some middleware.R code. Fixed downloadHandler's bug of not responding to safeError. 2016-04-14 17:31:34 +01:00
Joe Cheng
8a30c006e7 Prototype insertUI functionality 2016-04-13 16:12:30 -07:00
Barbara Borges Ribeiro
3f76679673 another update to NEWS 2016-04-07 22:22:51 +01:00
Barbara Borges Ribeiro
1cee5d4b41 deprecated position arg to tabsetPanel; updated NEWS 2016-04-07 22:15:15 +01:00
Barbara Borges Ribeiro
3107eec697 removed unnecessary line 2016-04-07 02:01:56 +01:00
Barbara Borges Ribeiro
477d46316e updated customStop() documentation example to match Winston's pattern 2016-04-06 14:08:11 +01:00
Winston Chang
3133693a0e Update NEWS 2016-04-05 20:58:48 -05:00
Winston Chang
bc7d701298 Make examples runnable with shinyApp() 2016-04-05 20:53:59 -05:00
Winston Chang
5d6d75b4f3 Remove shinyUI() and shinyServer() from examples 2016-04-05 15:23:23 -05:00
Winston Chang
73d48a7b37 Grunt 2016-04-05 13:19:27 -05:00
Winston Chang
ed7b9a9989 Modal dialog refinements 2016-04-05 13:18:57 -05:00
Winston Chang
e1a955752f Add modal dialogs 2016-04-05 13:18:56 -05:00
Winston Chang
0bdc8f0b2b Update package.json 2016-04-05 09:45:06 -05:00
Barbara Borges Ribeiro
a692b3ced8 implemented error hiding for ui.R and downloadHandler() cases 2016-04-05 15:28:05 +01:00
Winston Chang
2f5b93861d Merge pull request #1152 from daattali/master
add placeholder option to passwordInput()
2016-04-04 11:23:44 -05:00
Joe Cheng
110183585c Merge pull request #1143 from rstudio/joe/feature/output-arg-passthrough
Joe/feature/output arg passthrough
2016-04-03 08:10:44 -07:00
Barbara Borges Ribeiro
7eb29586a7 a few minor tweaks 2016-04-03 15:24:17 +01:00
Barbara Borges Ribeiro
401065a23e a lot of not very productive experimentation 2016-04-03 15:00:59 +01:00
Dean Attali
4e5e0fb0ce add placeholder option to passwordInput() 2016-04-02 18:44:10 -07:00
Barbara Borges Ribeiro
d41a06611e fixed documentation 2016-04-01 23:57:20 +01:00
Barbara Borges Ribeiro
26c3c27726 a few tweaks to customStop() 2016-04-01 22:47:58 +01:00
Barbara Borges Ribeiro
19ab63e041 a little code refactoring and added a customStop() function 2016-04-01 02:45:41 +01:00
Barbara Borges Ribeiro
5dafdab3d7 made the tracker construct - now an R6 class - easier to understand (more obvious); fixed the shinysession and name issues related to the renderFunc's 2016-04-01 00:44:14 +01:00
Barbara Borges Ribeiro
afbb17d428 errors are now sanitized in the app by default (must use options(shiny.sanitize.errors = FALSE) to override this behavior) 2016-03-30 07:29:58 +01:00
Winston Chang
8a721fbd25 Bump version to 0.13.2.9001 and update NEWS
Shiny 0.13.2 was released from another branch so its changes to NEWS were
incorporated here
2016-03-29 22:46:46 -05:00
Winston Chang
5d91a409e7 Merge pull request #1150 from rstudio/navbarpage-selected
Allow setting selected item in navbarPage. Closes #970
2016-03-29 22:35:18 -05:00
Winston Chang
8470f7caf8 Allow setting selected item in navbarPage. Closes #970 2016-03-29 22:32:00 -05:00
Winston Chang
67e279928e Merge pull request #1147 from rstudio/navbar-horizontal-divider
navbarMenu horizontal dividers
2016-03-29 22:13:24 -05:00
Winston Chang
77ac3a62b7 Check that tab arguments are unnamed 2016-03-29 12:56:43 -05:00
Winston Chang
12eaa3a162 Reconnection refinements 2016-03-29 12:41:46 -05:00
Joe Cheng
bbd5dd7b4f Merge pull request #1132 from rstudio/joe/reactive-graph-data
Update reactive graph sample data to include time
2016-03-29 09:54:15 -07:00
Joe Cheng
38fcd6e267 Merge pull request #1074 from rstudio/reconnect
Reconnect
2016-03-29 09:53:20 -07:00
Winston Chang
fd7f683eaa Remove bootstrapDependency function. Closes #1069 2016-03-29 11:12:05 -05:00
Winston Chang
e15f9acd91 Grunt 2016-03-28 21:30:27 -05:00
Winston Chang
7cb0882c73 Add "force" option to allowReconnect 2016-03-28 21:29:23 -05:00
Winston Chang
486d4d1c88 Add 'action' parameter to notifications 2016-03-28 15:15:07 -05:00
Winston Chang
ded8b13e96 Reconnect UI refinements 2016-03-28 14:42:44 -05:00
Barbara Borges Ribeiro
c7eb7ba861 passed error through if handler accepts it 2016-03-28 20:31:39 +01:00
Barbara Borges Ribeiro
4920bff8fd tiny documentation update 2016-03-28 18:24:04 +01:00
Barbara Borges Ribeiro
d78edf5dda removed func arg from render functions; fixed issue introduced by rebase a few commits ago 2016-03-28 18:19:37 +01:00
Winston Chang
7510c02d83 Update ion.RangeSlider to 2.1.2 2016-03-28 11:17:07 -05:00
Barbara Borges Ribeiro
2d7b729473 got rid of unnecessary lines 2016-03-28 15:46:55 +01:00
Barbara Borges Ribeiro
0495fe2d71 updated renderFunc's to include a shinysession arg 2016-03-28 15:31:33 +01:00
Barbara Borges Ribeiro
d7da5df734 updated integration etsts 2016-03-28 13:18:22 +01:00
Barbara Borges Ribeiro
4462b6bd39 changed a warning to an error, following the "fail fast" principle 2016-03-28 13:00:11 +01:00
Barbara Borges Ribeiro
80e1edeeb2 a better way to check which context the render function is being called from 2016-03-28 12:54:54 +01:00
Winston Chang
11af421f10 Use notification API for reconnection interface 2016-03-25 16:39:58 -05:00
Winston Chang
686ff235e7 New reconnect UI 2016-03-25 15:43:08 -05:00
Winston Chang
31f76a6d4d Add back gray-out on disconnect 2016-03-25 15:40:46 -05:00
Winston Chang
50078078e0 Export show/hideReconnectDialog functions 2016-03-25 15:40:45 -05:00
Winston Chang
be85e1e2f7 Add onConnected and onDisconnected 2016-03-25 15:40:45 -05:00
Winston Chang
9ad1574292 Allow Shiny Server to properly override methods 2016-03-25 15:40:45 -05:00
Winston Chang
4b71825707 Increase reconnect delay time with subsequent attempts 2016-03-25 15:40:45 -05:00
Winston Chang
fb1fd88947 Tweaks to disconnection/reconnection UI 2016-03-25 15:40:45 -05:00
Winston Chang
dca527d8b6 Allow app to control reconnection behavior 2016-03-25 15:40:45 -05:00
Winston Chang
3452a445fe Show box when trying to reconnect 2016-03-25 15:40:45 -05:00
Winston Chang
a06e9d2bef Implement auto-reconnect 2016-03-25 15:39:31 -05:00
Winston Chang
7a3961a280 Add support for menu section headers 2016-03-25 09:29:42 -05:00
Winston Chang
54729d8fb4 Update NEWS 2016-03-24 20:11:44 -05:00
Winston Chang
c2e17ee182 Add support for horizontal dividers in navbarMenu 2016-03-24 20:11:07 -05:00
Barbara Borges Ribeiro
bc0064d4b9 harcoded colors used for the color-coding of the time labels (creditted colorbrewer) 2016-03-25 00:51:19 +00:00
Barbara Borges Ribeiro
03685dbb61 added check for valid arguments if passed via outputArgs 2016-03-25 00:22:48 +00:00
Barbara Borges Ribeiro
26fcba8ed5 really not a solution... 2016-03-24 22:42:57 +00:00
Barbara Borges Ribeiro
bc15b65538 added outputArgs to all other renderXXX functions following the pattern used for renderPlot 2016-03-24 22:42:57 +00:00
Joe Cheng
e9ab34a9c1 Provide xxxOutput args via renderXXX passthrough
This will allow you to customize outputs when used in an R Markdown
document
2016-03-24 22:21:48 +00:00
Winston Chang
0bf512ebdd Grunt 2016-03-24 17:11:12 -05:00
Winston Chang
7646fbeaa0 Bump version to 0.13.1.9002 2016-03-24 17:11:02 -05:00
Winston Chang
84b4766013 Merge pull request #1141 from rstudio/notifications
Notification interface
2016-03-24 17:09:31 -05:00
Winston Chang
3a48734b2f Re-document 2016-03-24 14:07:37 -05:00
Winston Chang
36ae332959 Remove some public methods for notifications 2016-03-24 14:01:28 -05:00
Winston Chang
3e0d8da9d6 Add exports.renderContent to modularize content rendering in JS 2016-03-23 15:46:44 -05:00
Winston Chang
2fcb4dbe50 Modularize dependency handling in R 2016-03-23 15:46:03 -05:00
Winston Chang
09c93bfb39 Escape ID 2016-03-23 14:34:20 -05:00
Winston Chang
34068b1598 Rename 'style' to 'type' 2016-03-23 14:34:20 -05:00
Barbara Borges Ribeiro
a67da1c99a added color scale for time labels 2016-03-23 16:26:33 +00:00
Winston Chang
0d6754761d Add style argument 2016-03-22 15:36:21 -05:00
Winston Chang
898f7b66cf Rename argument from 'html' to 'ui' 2016-03-22 14:47:07 -05:00
Winston Chang
c18f3e86f0 Add note about IDs 2016-03-22 13:46:21 -05:00
Barbara Borges Ribeiro
de51922f10 Trigger 2016-03-22 17:02:38 +00:00
Winston Chang
be0cb18bfc Merge pull request #1138 from rstudio/joe/bugfix/htmltemplate-doc-update
Update htmlTemplate docs for htmltools 0.3.5
2016-03-22 11:13:31 -05:00
Winston Chang
39fd1db3c0 Bump htmltools required version to 0.3.5 2016-03-22 11:07:15 -05:00
Winston Chang
b4565e7354 Fix coordmap tests 2016-03-22 11:05:59 -05:00
Winston Chang
e28cada4dd Handle tag inputs and escape HTML text 2016-03-21 20:10:17 -05:00
Joe Cheng
6daac65968 Add missing entries to staticdocs index 2016-03-21 17:00:17 -07:00
Joe Cheng
1ecc49c450 Update htmlTemplate docs for htmltools 0.3.5 2016-03-21 16:53:46 -07:00
Barbara Borges Ribeiro
f96e7d9aaa stored the timeElapsed float on the node instead of the fully formatted string; made sure we're not showing any time elapsed info while the node is active (it could be confusing) 2016-03-21 23:25:17 +00:00
Barbara Borges Ribeiro
c637bba867 changed time label color; updated default argument to renderReactLog 2016-03-21 22:19:45 +00:00
Barbara Borges Ribeiro
bdc6554ca8 added time argument 2016-03-21 21:53:34 +00:00
Winston Chang
ecb59e9c31 Add R notification functions 2016-03-21 16:47:55 -05:00
Winston Chang
1b39184e98 Add randomID function 2016-03-21 16:43:35 -05:00
Barbara Borges Ribeiro
2a35ba64f7 fixed y positioning 2016-03-21 21:15:32 +00:00
Barbara Borges Ribeiro
3a5123627d updated multilineTextNode 2016-03-21 21:04:27 +00:00
Barbara Borges Ribeiro
a18eeecd59 separated text label and time label 2016-03-21 20:59:32 +00:00
Winston Chang
85e3f04738 Restyle notifications and add close button 2016-03-21 13:59:12 -05:00
Barbara Borges Ribeiro
cc59864377 experimenting 2016-03-21 18:49:47 +00:00
Barbara Borges Ribeiro
5b10cbf2e2 added 'time elapsed' to nodes' labels 2016-03-21 14:30:42 +00:00
Barbara Borges Ribeiro
fc6b83bb5d Merge pull request #1136 from rstudio/barbara/renderTable-fixes
tiny update following the bigger renderTable() PR
2016-03-18 21:20:29 +00:00
Barbara Borges Ribeiro
bc509f55d9 added NEWS item documenting the change to renderTable() and fixed tiny bug (stop() message was spanning two lines with only one string) 2016-03-18 21:15:35 +00:00
Winston Chang
f81301ece6 Simplify notification API 2016-03-18 15:53:40 -05:00
Winston Chang
382e5c1f43 Rename Shiny.Notification to Shiny.notifications 2016-03-18 14:59:43 -05:00
Winston Chang
0243f74dcd Add delay before removal 2016-03-18 14:45:21 -05:00
Winston Chang
58737ef454 Add notification JS API 2016-03-18 14:45:21 -05:00
Winston Chang
940cea82ca Merge pull request #1133 from rstudio/es6
Add tooling for ES6
2016-03-18 14:44:53 -05:00
Winston Chang
5683e36733 Add estraverse-fb npm dependency 2016-03-18 14:42:03 -05:00
Winston Chang
f5137b7935 Grunt 2016-03-18 14:16:47 -05:00
Winston Chang
0c2af42c69 Make ESLint gave warnings instead of errors 2016-03-18 14:15:36 -05:00
Winston Chang
760dc5d0c6 Add babel polyfill 2016-03-18 14:15:36 -05:00
Winston Chang
5331aa08a7 Fixes for eslint 2016-03-18 14:15:36 -05:00
Winston Chang
375d7cc7b1 Update eslint rules 2016-03-18 14:15:36 -05:00
Winston Chang
a05f3dd640 Update npm packages 2016-03-18 14:15:36 -05:00
Winston Chang
b91c1b44ba Switch from jshint to eslint 2016-03-18 14:15:36 -05:00
Winston Chang
6efb01a397 Use Babel for ES6->ES5 transpilation 2016-03-18 14:15:36 -05:00
Barbara Borges Ribeiro
1843eca6c0 Merge pull request #1134 from bborgesr/updateActionButton
Verify button icon format and created updateActionButton()
2016-03-18 19:01:50 +00:00
Barbara Borges Ribeiro
506e3e8a48 added another check in the JS to make sure that we're finding the correct icon 2016-03-18 18:55:38 +00:00
Barbara Borges Ribeiro
0e5a3cc5aa throw error instead of warning in validateIcon(); updated documentation 2016-03-18 15:32:06 +00:00
Barbara Borges Ribeiro
d2dd76e13d fixed typo 2016-03-17 21:36:51 +00:00
Barbara Borges Ribeiro
470b82fd64 compiled documentation 2016-03-17 21:20:42 +00:00
Barbara Borges Ribeiro
e04dd3a4b1 check icon validity more robustly; set icon=character(0) as the way to get rid of a previous icon; updated documentaion and NEWS 2016-03-17 21:15:49 +00:00
Barbara Borges Ribeiro
2d39e06c97 find i-tag elements with *any* class (to circumvent the issue of selecting italicized text) 2016-03-17 14:55:57 +00:00
Barbara Borges Ribeiro
e1fc74bdc1 updates to input_binding_actionbutton.js and got rid of isIcon function (substituted by simple check instead) 2016-03-17 14:47:48 +00:00
Barbara Borges Ribeiro
3ab5d7f861 verify that button icons are in the right format (not necessarily valid though) and added updateActionButton() 2016-03-15 22:19:21 +00:00
Winston Chang
d63dd6086a Merge pull request #1129 from bborgesr/newRenderTable
Improved renderTable()
2016-03-15 12:43:02 -05:00
Barbara Borges Ribeiro
a8d9895a9b updated documentation 2016-03-15 16:58:42 +00:00
Barbara Borges Ribeiro
f8a7257af3 improved defaultAlignment function and changed names of spacing value options 2016-03-15 16:47:35 +00:00
Barbara Borges Ribeiro
4703028988 actually with multiple tables, their ids would all be identical (bad), so switched back to using classes to gain specificity 2016-03-15 00:55:48 +00:00
Barbara Borges Ribeiro
87523cdbd5 created table id to add css specificity 2016-03-15 00:38:18 +00:00
Barbara Borges Ribeiro
d9567ed035 check valid spacing 2016-03-15 00:01:52 +00:00
Barbara Borges Ribeiro
0ab277662a updated documentation 2016-03-14 23:00:15 +00:00
Barbara Borges Ribeiro
2eeb94e39c changed bordered to spacing with four possible values, rather than only two 2016-03-14 22:48:55 +00:00
Joe Cheng
4b441d10b3 Update reactive graph sample data to include time 2016-03-14 15:37:53 -07:00
Barbara Borges Ribeiro
37a1d3d61e improved defaultAlignment function 2016-03-14 20:24:41 +00:00
Barbara Borges Ribeiro
3839338c15 mostly spacing 2016-03-14 20:13:08 +00:00
Barbara Borges Ribeiro
bdee5790e6 added alignment default character ("?") 2016-03-14 18:39:51 +00:00
Barbara Borges Ribeiro
d0dab25dae tried fix 2016-03-14 16:16:22 +00:00
Barbara Borges Ribeiro
b14b7b00c2 actually padding is necessary for headers too 2016-03-14 14:57:38 +00:00
Barbara Borges Ribeiro
248bfcccda padding on all cells 2016-03-14 14:54:31 +00:00
Barbara Borges Ribeiro
9b5833205b made the check for empty data frame more robust 2016-03-12 18:24:19 +00:00
Barbara Borges Ribeiro
07f8589090 coerce the input to a data frame (important if the input was a matrix for example, as some parts of the code might not apply) 2016-03-12 18:12:50 +00:00
Barbara Borges Ribeiro
f77f83dfeb fixed a tiny bug introduced by the previous commit 2016-03-12 18:03:59 +00:00
Barbara Borges Ribeiro
e3d3d916ba improved regex for substitution (less fragile, less hack-ish) 2016-03-12 17:59:45 +00:00
Barbara Borges Ribeiro
cccf219cd2 simplified alignment vector and got rid of an unnecessary variable 2016-03-12 17:55:05 +00:00
Barbara Borges Ribeiro
0896b2f7b8 initialize header_alignments in a clearer way 2016-03-12 17:36:01 +00:00
Barbara Borges Ribeiro
cc406262ac added spaces after commas in a couple of places where they were missing 2016-03-12 17:23:43 +00:00
Barbara Borges Ribeiro
0f20063eb8 added "$" to regex to make sure we're subbing "</table>" only at the end of the input 2016-03-12 17:04:29 +00:00
Barbara Borges Ribeiro
5f32b165f2 updated createWrapper() per Joe's suggestion, added spaces between the "=" 2016-03-12 16:46:59 +00:00
Winston Chang
3cadd1789b Merge pull request #1130 from dmpe/master
update bootstrap to 3.3.6
2016-03-11 16:03:24 -06:00
dmpe
e486778b36 note to news file and upgrade number in R file 2016-03-11 19:21:25 +01:00
Barbara Borges Ribeiro
7fe6453bbb vectorized form to add format args to classNames 2016-03-11 16:26:07 +00:00
Barbara Borges Ribeiro
9f88d2b6d6 made isNumber() 1000x more elegant 2016-03-11 16:19:29 +00:00
Barbara Borges Ribeiro
8f9d52699d return NULL instead of the empty string if no data is provided 2016-03-11 16:00:00 +00:00
Barbara Borges Ribeiro
0a774a8c55 "the" changed to "of" 2016-03-11 15:52:05 +00:00
Barbara Borges Ribeiro
d4ced34a11 2nd update to width documentation (copied straight from plotOutput() ) 2016-03-11 15:36:14 +00:00
Barbara Borges Ribeiro
85a762a0b9 updated width documentation 2016-03-11 15:34:34 +00:00
dmpe
b255fecc6e update bootstrap to 3.3.6
see https://github.com/rstudio/shiny/issues/1056
2016-03-11 13:23:38 +01:00
Barbara Borges Ribeiro
734d2e2594 latex bug in documentation (but shouldn't this be allowed?) 2016-03-11 10:58:18 +00:00
Barbara Borges Ribeiro
2e292b4636 commenting and documenting 2016-03-11 10:44:02 +00:00
Barbara Borges Ribeiro
f0bc7356ac made sure theader is only present the argument colnames is set to TRUE 2016-03-10 23:34:53 +00:00
Winston Chang
1bcb6ab931 Add note about grunt clean 2016-03-10 11:08:15 -06:00
Barbara Borges Ribeiro
ef65937662 replaced format argument with 4 flags (striped, bordered, hover, condensed) and made headers look like bootstrap's 2016-03-09 22:54:11 +00:00
Barbara Borges Ribeiro
3369b8b5b2 finally got headers to align nicely with columns 2016-03-09 01:44:23 +00:00
Winston Chang
28db561cd9 Bump version to 0.13.1.9001 and update NEWS 2016-03-08 17:03:55 -06:00
Winston Chang
0622326e1b Merge pull request #1126 from rstudio/commas
Add code diagnostics for missing/extra commas, and for unmatched }, ), and ]
2016-03-08 17:01:36 -06:00
Winston Chang
c6e2593e4e Streamline diagnoseCode 2016-03-08 16:54:28 -06:00
Barbara Borges Ribeiro
d0e3279a67 aesthethics 2016-03-08 17:00:42 +00:00
Winston Chang
aee5bda9ec Add workaround for quartz res bug
The quartz device is hard-coded to use 72 ppi in some places, and this
causes problems with grid unit calculations when a value other than 72
is used.
2016-03-08 09:54:01 -06:00
Barbara Borges Ribeiro
979b4a8861 used row.names() function instead of rownames() to avoid naming conflicts 2016-03-07 12:56:00 +00:00
Barbara Borges Ribeiro
c10cd4b474 removed unnecessary css, and garbage collection for renderBootstrapTable 2016-03-07 12:33:43 +00:00
Barbara Borges Ribeiro
4aa1d19845 replaced renderTable with renderBootstrapTable (but kept name renderTable) and ensured backward compatibility 2016-03-07 12:29:43 +00:00
Barbara Borges Ribeiro
7ff51d89fc check if rownames are numbers or strings 2016-03-07 12:21:39 +00:00
Winston Chang
ea9d94e42f Add code diagnostics (missing/extra commas) 2016-03-04 15:11:34 -06:00
Barbara Borges Ribeiro
a9ba0fdb0b added arguments, minimal functional code 2016-03-04 15:13:07 +00:00
Barbara Borges Ribeiro
af19c3331c added more function arguments 2016-03-04 00:01:09 +00:00
Winston Chang
5e98b930ee Move tests from inst/ to tests/ 2016-03-03 15:00:51 -06:00
Barbara Borges Ribeiro
057d160392 changes to make function compatible with table demo app 2016-03-03 15:54:59 +00:00
Barbara Borges Ribeiro
6b2899c219 fixed small width bug 2016-03-02 15:22:16 +00:00
Barbara Borges Ribeiro
85290e687c added customizable width 2016-03-02 15:05:50 +00:00
Barbara Borges Ribeiro
d778e81f42 aesthetic changes 2016-03-02 14:03:56 +00:00
Barbara Borges Ribeiro
2bfad21604 added renderBootstrapTable 2016-03-01 15:55:58 +00:00
Winston Chang
373e0d3a9f Fix NEWS after weird merge 2016-02-22 11:37:30 -06:00
Joe Cheng
5e83403d0c Update NEWS 2016-02-22 09:30:22 -08:00
Winston Chang
cbe76aab83 Merge pull request #1117 from rstudio/joe/feature/abort-output
Add ability to abort the processing of outputs
2016-02-22 11:20:17 -06:00
Joe Cheng
26de088520 s/abortOutput/cancelOutput/; add req option 2016-02-22 09:12:30 -08:00
Winston Chang
98430edb17 Merge branch 'replay-plot' 2016-02-19 13:57:38 -06:00
Winston Chang
48c6784e51 Change 'replay' option to 'execOnResize' 2016-02-19 13:52:36 -06:00
Winston Chang
dc0f5af3ef Rename 'render' to 'plotObj' 2016-02-19 13:42:54 -06:00
Winston Chang
af85e6f2a6 Merge pull request #1116 from yihui/warn-non-UTF8
Closes #810: check if the input file is encoded in UTF-8 and warn if not
2016-02-19 10:00:49 -06:00
Joe Cheng
4e91af4d64 Add ability to abort the processing of outputs
abortOutput() leaves the state of the output unchanged,
unlike req(), validate(), or stop().
2016-02-19 00:33:10 -08:00
Yihui Xie
faf87a5dee Closes #810: check if the input file is encoded in UTF-8 and warn if not
The validUTF8() function is still in R-devel, and they probably will never export it, so let's use iconv(x, from = 'UTF-8', to = 'UTF-8') to test if x is encoded in UTF-8

also closes #1113
2016-02-19 00:02:25 -06:00
Winston Chang
517c5d356f Merge tag 'v0.13.1'
Manually bumped version to 0.13.1.9000.
2016-02-18 12:45:00 -06:00
Winston Chang
931be22247 Bump version to 0.13.1 2016-02-17 12:03:55 -06:00
Joe Cheng
8697360eb7 Really fix docs. 2016-02-17 11:46:02 -06:00
Joe Cheng
e3a867132a Use parent.frame() instead of sys.parent() 2016-02-17 11:45:48 -06:00
Joe Cheng
c96debadc5 Fix docs/check 2016-02-17 11:45:16 -06:00
Joe Cheng
02520d4f54 Pass tests. reactive(function() { ... }) is NO LONGER supported. 2016-02-17 11:45:10 -06:00
Joe Cheng
5070b63d5b Partial fix of debugger breakage
There are two problems I'm trying to solve here.

1) Somewhere along the way, exprToFunction gained a hardcoded
   assumption that two stack frames up is a variable "expr",
   meaning anything that called installExprFunction had to have
   the first argument be exactly "expr". I think I got this
   fixed, now the only assumption made by both installExprFunc
   and exprToFunc is if they are called with quoted = FALSE,
   then the caller is merely passing through code that originated
   exactly one more level up the stack frame. If the code is
   less than one level up, i.e. an end user is directly passing
   code into installExprFunction or exprToFunction, then it won't
   work; and if the code is more than one level up (someone is
   passing code into function A which passes through to function
   B which calls installExprFunction, with quoted = FALSE) then
   it also won't work.

2) registerDebugHook calls were broken in various places by the
   name/envir registered with the hook being different than the
   name/envir through which the function was actually called.
   This generally seems fixable by moving the registerDebugHook
   call closer to the name/envir that will ultimately be called
   (e.g. call registerDebugHook directly from wrapFunctionLabel).

There still seems to be a problem here in that breakpoints in
RStudio are hit but then the IDE automatically runs "n" multiple
times. Also the unit tests don't currently pass, I haven't
investigated that yet.
2016-02-17 11:45:01 -06:00
Joe Cheng
eaa722b10d Fix flexCol on RStudio Desktop for Win/Linux
RStudio Desktop requires the older -webkit vendor-prefixed
flex box properties. I missed the one for flex-direction.
2016-02-17 11:32:47 -06:00
Winston Chang
1bc3c90286 Update NEWS 2016-02-16 14:07:49 -06:00
Winston Chang
afd00edee3 Add replay option 2016-02-16 14:07:49 -06:00
Winston Chang
b712398208 If plot code errors, re-execute on resize 2016-02-16 13:39:11 -06:00
Winston Chang
7586e91b4f Fix coordmap tests 2016-02-15 16:01:34 -06:00
Winston Chang
9eba82c107 Fix vars 2016-02-15 16:01:15 -06:00
Winston Chang
ccdc219a09 More cleanup 2016-02-15 15:39:05 -06:00
Yihui Xie
60d01e76e9 Merge pull request #1109 from vnijs/master
Closes #692
2016-02-13 00:38:06 -06:00
mostly-harmless
b5cfd4152e Fix for https://github.com/rstudio/shiny/issues/692 2016-02-12 22:15:34 -08:00
Winston Chang
32c4c8ae32 Code cleanup 2016-02-12 15:59:10 -06:00
Winston Chang
bd4c506d22 Implement replayPlot when width/height changes 2016-02-12 13:26:41 -06:00
Winston Chang
476dd7cd56 Collect needed data structures 2016-02-11 14:45:43 -06:00
Winston Chang
8176f84715 Restructure ggplot2 coordmap extraction 2016-02-11 14:25:20 -06:00
Winston Chang
6bd33721d8 Separate rendering code into a reactive 2016-02-11 12:27:16 -06:00
Winston Chang
c9d9671288 More restructuring 2016-02-11 12:25:38 -06:00
Winston Chang
2a821edf5f Small restructure of renderPlot 2016-02-11 12:25:38 -06:00
Winston Chang
68b85bdc87 Merge branch 'fix-plot-flicker2' 2016-02-11 12:20:45 -06:00
Winston Chang
83cf5907c3 Merge pull request #1072 from rstudio/rm-do-call
Remove unneeded do.call
2016-02-11 10:24:47 -06:00
Winston Chang
c912b6547c Merge pull request #1106 from yihui/travis-cache
Set sudo to false explicitly to enable caching
2016-02-11 10:23:08 -06:00
Yihui Xie
bf04b74f87 Set sudo to false explicitly to enable caching 2016-02-10 13:34:24 -06:00
Joe Cheng
9d1e008990 Merge pull request #1099 from rstudio/joe/bugfix/debug-fix
Partial fix of debugger breakage
2016-02-10 08:15:59 -08:00
Joe Cheng
d9e5285a3b Really fix docs. 2016-02-09 16:29:15 -08:00
Joe Cheng
84937b7a0b Use parent.frame() instead of sys.parent() 2016-02-09 16:29:15 -08:00
Joe Cheng
924b3e16cf Fix docs/check 2016-02-09 16:29:15 -08:00
Joe Cheng
2a8cf01410 Pass tests. reactive(function() { ... }) is NO LONGER supported. 2016-02-09 16:29:14 -08:00
Joe Cheng
a3a5cfee6c Partial fix of debugger breakage
There are two problems I'm trying to solve here.

1) Somewhere along the way, exprToFunction gained a hardcoded
   assumption that two stack frames up is a variable "expr",
   meaning anything that called installExprFunction had to have
   the first argument be exactly "expr". I think I got this
   fixed, now the only assumption made by both installExprFunc
   and exprToFunc is if they are called with quoted = FALSE,
   then the caller is merely passing through code that originated
   exactly one more level up the stack frame. If the code is
   less than one level up, i.e. an end user is directly passing
   code into installExprFunction or exprToFunction, then it won't
   work; and if the code is more than one level up (someone is
   passing code into function A which passes through to function
   B which calls installExprFunction, with quoted = FALSE) then
   it also won't work.

2) registerDebugHook calls were broken in various places by the
   name/envir registered with the hook being different than the
   name/envir through which the function was actually called.
   This generally seems fixable by moving the registerDebugHook
   call closer to the name/envir that will ultimately be called
   (e.g. call registerDebugHook directly from wrapFunctionLabel).

There still seems to be a problem here in that breakpoints in
RStudio are hit but then the IDE automatically runs "n" multiple
times. Also the unit tests don't currently pass, I haven't
investigated that yet.
2016-02-09 16:29:14 -08:00
Joe Cheng
2c04441591 Merge pull request #1105 from rstudio/travis-update
Take advantage of new travis features
2016-02-09 16:28:33 -08:00
Winston Chang
a4eab8e216 Grunt 2016-02-09 16:03:59 -06:00
Winston Chang
189f9589d4 Unset attributes in img that aren't present in new data 2016-02-09 16:02:39 -06:00
Hadley Wickham
880721e0d0 Take advantage of new travis features
This will cache package install between checks, which should make it run quite a bit faster - this is what @jimhester's has been working on
2016-02-09 14:02:33 -06:00
Winston Chang
6ab65e2031 Fix plot flickering on Safari and Firefox. Closes #776
Previously, a new img tag was added when a new plot was sent, but now it uses
the same img tag and changes the src attribute.
2016-02-09 11:08:25 -06:00
Winston Chang
e871934cfd Fix package name for Travis 2016-02-06 14:46:51 -06:00
Winston Chang
686390c1f2 Merge pull request #1096 from yihui/bugfix/datatables-warning
Fixes #561: make sure DataTables always gets a correct number of columns of data
2016-02-04 10:12:56 -06:00
Yihui Xie
a8b9fb1708 use the CRAN version of htmltools 2016-02-03 14:02:24 -06:00
Yihui Xie
55d3764169 Fixes #561: should discard the query when the number of columns in the request is different with the number of columns of the actual data 2016-02-03 10:54:37 -06:00
Winston Chang
cb5bc3d631 Merge pull request #1088 from rstudio/joe/bugfix/flexcol
Fix flexCol on RStudio Desktop for Win/Linux
2016-01-20 12:12:51 -06:00
Joe Cheng
543e66eb00 Fix flexCol on RStudio Desktop for Win/Linux
RStudio Desktop requires the older -webkit vendor-prefixed
flex box properties. I missed the one for flex-direction.
2016-01-20 09:20:22 -08:00
Winston Chang
b658983fb8 Remove JavaScript events vignette
This vignette has been migrated to a Shiny Dev Center article.
2016-01-15 15:30:24 -06:00
Winston Chang
cfb3e42337 Merge pull request #1080 from rstudio/internal-messages
Don't use sendCustomMessage for messages internal to Shiny
2016-01-15 12:19:23 -06:00
Winston Chang
36815b5e43 Concat and minify shiny.js 2016-01-15 11:54:10 -06:00
Winston Chang
897e077aca Convert internal use of sendCustomMessage to sendMessage 2016-01-15 11:53:51 -06:00
Winston Chang
f395960ffa Add session$sendMessage wrapper function 2016-01-15 11:46:23 -06:00
Winston Chang
fb301717f5 Bump version to 0.13.0.9000 2016-01-14 10:49:08 -06:00
Winston Chang
46da93519f Bump version to 0.13.0 2016-01-12 13:33:14 -06:00
Winston Chang
ce0f2c51a9 Use explicit namespaces 2016-01-12 13:33:14 -06:00
Winston Chang
04b4b8da4f Use --run-donttest when checking package 2016-01-12 13:16:18 -06:00
Winston Chang
877d7451dd Merge pull request #1073 from rstudio/joe/staticdocs-tweaks
Tweaks for improved staticdocs rendering
2016-01-12 13:00:07 -06:00
Joe Cheng
7e6a68a2b1 CRAN maintainers prefer \donttest over \dontrun 2016-01-12 10:10:16 -08:00
Joe Cheng
caca515ba0 Tweaks for improved staticdocs rendering
- \donttest -> \dontrun, otherwise staticdocs hangs
- Put NS before ns.sep so usage entries are in a better order
2016-01-11 12:20:19 -08:00
Winston Chang
d548b78dee Remove unneeded do.call
It's OK to remove this do.call now that we are using R6 instead of Ref Classes.
2016-01-08 16:19:08 -06:00
Joe Cheng
f2410abc48 Merge remote-tracking branch 'origin/image-attr'
Conflicts:
	inst/www/shared/shiny.js
	inst/www/shared/shiny.js.map
	inst/www/shared/shiny.min.js
	inst/www/shared/shiny.min.js.map
2016-01-08 10:55:21 -08:00
Joe Cheng
483a7d34c5 Merge pull request #1071 from rstudio/fix-hidden-slider
Fix hidden slider
2016-01-08 10:53:00 -08:00
Winston Chang
e872411285 Build and minify shiny.js 2016-01-08 10:00:22 -06:00
Winston Chang
fc7e6bf542 Revert "Simpler fix for updating hidden sliders (#1010)"
This reverts commit 4e1caee7da.
See https://github.com/rstudio/shiny/issues/1010#issuecomment-169971201
2016-01-08 10:00:05 -06:00
Winston Chang
16d42b6421 Add bootstrapLib to staticdocs index 2016-01-07 13:51:56 -06:00
Winston Chang
2f25d25eec Use setAttribute() function to set image attributes. Fixes #936 2016-01-06 15:03:18 -06:00
Winston Chang
be1081a4b9 Add note about printing ggplot2 graphics 2016-01-06 14:11:16 -06:00
Joe Cheng
1608b652d7 Fix unnecessary ::: 2016-01-05 14:02:16 -08:00
Winston Chang
5dd19a878c Bump version to 0.2.12.9009 2016-01-05 15:59:19 -06:00
Winston Chang
3314f4b5b8 Update NEWS 2016-01-05 15:59:11 -06:00
Winston Chang
5977e0fe89 Update NEWS 2016-01-05 15:30:59 -06:00
Winston Chang
f477dcba4a Merge pull request #1068 from rstudio/template-bootstrap
Pull out bootstrapLib into separate function
2016-01-05 15:30:32 -06:00
Winston Chang
6c5f0c5379 Pull out bootstrapLib into separate function 2016-01-05 14:57:53 -06:00
Winston Chang
257eb1bed0 Merge pull request #1067 from rstudio/joe/bugfix/module-progress
Fix progress for Shiny modules
2016-01-05 11:59:18 -06:00
Joe Cheng
9c4d142c2d Fix progress for Shiny modules
Too-specific class check was being used, interfered with the
"duck typed" session-like objects we use for modules.
2016-01-05 09:41:52 -08:00
Winston Chang
8e89a1f154 Add README file with info about jQuery UI 2016-01-05 11:03:34 -06:00
Winston Chang
b0952c0374 Use jQuery UI build without datepicker. Fixes #1042 2016-01-05 11:03:07 -06:00
Winston Chang
ac95dcb3f2 Fix head content location 2016-01-04 10:52:26 -06:00
Joe Cheng
ce4043f038 Update metadata 2015-12-31 11:07:35 -08:00
Joe Cheng
0d26857e31 Merge pull request #1031 from rstudio/template
Use templates from htmltools
2015-12-31 11:03:14 -08:00
Joe Cheng
85bea95f6b Merge pull request #1029 from rstudio/joe/feature/knit_print.reactive
Add knit_print.reactive
2015-12-30 14:31:26 -08:00
Winston Chang
10a46c507f Merge pull request #1059 from rstudio/joe/bugfix/gadgets
Fix stopApp bug, and fix flex box in RStudio
2015-12-30 09:38:24 -06:00
Joe Cheng
d35d76e1d0 Use flex box layout vendor prefixes
This fixes fillRow/fillCol for RStudio on Windows and Linux
2015-12-29 15:59:04 -08:00
Joe Cheng
aaa05b22df Return stopApp value without visibility info 2015-12-29 15:57:23 -08:00
Winston Chang
c5fa30f0de Make encoding tests work cleanly on different platforms 2015-12-29 15:41:49 -06:00
Joe Cheng
43fe1a9a0e Merge pull request #1057 from rstudio/joe/feature/gadget-cancel
runGadget automatically handles cancel button
2015-12-28 14:18:17 -08:00
Joe Cheng
aa296fcb69 Don't show call when user cancels gadget 2015-12-28 13:56:01 -08:00
Joe Cheng
b9c7023489 Refactor server function arg matching 2015-12-28 12:43:06 -08:00
Joe Cheng
efcd286039 Code review feedback 2015-12-28 11:56:13 -08:00
Joe Cheng
98014f9495 Code review feedback 2015-12-28 11:53:52 -08:00
Joe Cheng
2702a18ea2 Add stopApp test 2015-12-28 11:18:15 -08:00
Joe Cheng
4a8da3e1e2 runGadget handles cancel automatically (by default) 2015-12-27 23:46:47 -08:00
Joe Cheng
cfe38c00f3 Proper visibility and error handling from stopApp 2015-12-27 23:35:09 -08:00
Joe Cheng
af0463ed46 Update metadata 2015-12-23 17:47:24 -08:00
Joe Cheng
c02f4691e0 Merge pull request #1054 from rstudio/joe/feature/runGadget
Migrate runGadget and viewer functions from shinygadgets
2015-12-23 17:45:12 -08:00
Joe Cheng
5d89393fff Merge pull request #1039 from rstudio/joe/feature/fillPage
Add fillPage, flexRow, flexCol
2015-12-23 17:44:56 -08:00
Joe Cheng
e7ce28204b Remove dependency on newer htmltools
The css function is in htmltools 0.2.11, which isn't on CRAN
yet. Due to a perfect storm of release scheduling we need to
get fillPage onto shiny master before we have a chance to
put htmltools on CRAN.
2015-12-23 17:32:46 -08:00
Joe Cheng
8fc4a75e8c Specify remote repo for htmltools
Makes devtools install easier
2015-12-23 17:31:11 -08:00
Joe Cheng
26c89a09e8 Update staticdocs index 2015-12-23 17:31:11 -08:00
Joe Cheng
25a1493520 Add details to fillRow doc about containers 2015-12-23 17:31:10 -08:00
Joe Cheng
b18722f776 flex:none is more appropriate than flex:initial for weight=NA 2015-12-23 17:31:10 -08:00
Joe Cheng
3f3fd9ae21 Install htmltools from github until 0.2.11 goes to cran 2015-12-23 17:31:10 -08:00
Joe Cheng
94ea3c7dab Require htmltools 0.2.11 2015-12-23 17:31:10 -08:00
Joe Cheng
6c2fea7926 Rename flexRow/flexCol to fillRow/fillCol, other tweaks
I decided against the name flexRow/flexCol as the "flex" prefix
is too general for these implementations, which are mostly just
useful for filling the space with the children. Flex box has a
lot more features than that, such as centering, wrapping,
justifying, etc., but I don't currently know how to design an
API that presents the full power of flex box without also
presenting the full complexity of it as well.

This commit also includes some tweaks to the impl of flexfill
to fix the behavior of children with 100% size along the main
axis, and also introduces support for NA flex values, which
sizes the flex item according to its contents.
2015-12-23 17:31:10 -08:00
Joe Cheng
e08fd47b0e Add fillPage, flexRow, flexCol 2015-12-23 17:31:05 -08:00
Joe Cheng
0fd76e8768 Migrate runGadget and viewer functions from shinygadgets 2015-12-23 15:33:30 -08:00
Joe Cheng
72aaf3055a Update NEWS 2015-12-23 11:46:03 -08:00
Winston Chang
94a943a68c Merge pull request #1052 from rstudio/joe/font-awesome-4.5.0
Upgrade Font Awesome to 4.5.0
2015-12-23 13:08:27 -06:00
Joe Cheng
e867dcfdb1 Upgrade Font Awesome to 4.5.0 2015-12-23 11:03:33 -08:00
Joe Cheng
9a22a89b06 Add docs 2015-12-23 09:42:29 -08:00
Joe Cheng
791e8200bc Install htmltools from github until 0.2.11 goes to cran 2015-12-22 14:06:51 -08:00
Joe Cheng
d96217d49a Merge pull request #1049 from rstudio/joe/bugfix/http-iframe-stampede
Defer subapp iframe loading
2015-12-22 12:26:49 -08:00
Joe Cheng
6bfd65aa19 Guard against window.Shiny not being defined 2015-12-22 11:53:36 -08:00
Joe Cheng
2da9bc07ac Defer subapp iframe loading until main app has loaded
Fixes #1047 defer loading of iframes
2015-12-22 11:14:12 -08:00
Joe Cheng
6d7a562b7a Fix staticdocs
Without this fix, current version of staticdocs won't pull plotOutput
into the index page.
2015-12-21 11:57:01 -08:00
Joe Cheng
0aa1dfb8e1 Merge pull request #1032 from yihui/yihui/encoding-tests
Add some tests for character encodings
2015-12-17 13:36:26 -08:00
Winston Chang
e9e7dc298f Merge pull request #1037 from rstudio/joe/feature/req
Add `req` function for validating required inputs/values
2015-12-17 14:39:40 -06:00
Joe Cheng
ed3b71e396 Remove unnecessary environment() call 2015-12-17 11:58:17 -08:00
Joe Cheng
3450a037a9 Further simplification and more tests for req() 2015-12-17 11:29:54 -08:00
Joe Cheng
f57626d256 req() simplification 2015-12-17 11:16:09 -08:00
Joe Cheng
c1c3fa4d3a Fix a couple of req edge cases 2015-12-17 11:13:33 -08:00
Joe Cheng
300433f7de req() now short-circuits on falsy values 2015-12-17 10:53:13 -08:00
Joe Cheng
eee6f4ed81 Add req function for validating required inputs/values 2015-12-16 10:04:05 -08:00
Joe Cheng
2eb29bd8aa Merge pull request #1034 from rstudio/fix-hidden-slider
Simpler fix for updating hidden sliders (#1010)
2015-12-15 15:57:11 -08:00
Joe Cheng
3a0ce86f51 Remove unnecessary check (feedback from @wch) 2015-12-15 15:36:43 -08:00
Joe Cheng
6041b8cbb2 Roxygenize 2015-12-14 18:00:29 -08:00
Joe Cheng
3ba8fcb7b8 Merge pull request #1036 from rstudio/joe/bugfix/validation-error-behavior
Fix validation error handling
2015-12-14 17:16:04 -08:00
Joe Cheng
f74d9c93a2 Merge pull request #1035 from rstudio/joe/bugfix/multiline-label
Fix warnings when renderXXX is called with explicit namespace
2015-12-14 17:15:50 -08:00
Joe Cheng
739c162281 Fix warnings when renderXXX is called with explicit namespace
Calling shiny::renderText(...) instead of renderText(...) would
cause warnings.
2015-12-14 17:11:40 -08:00
Joe Cheng
a2700c900d Fix validation error handling
Validation errors were behaving too much like real errors: they were
being printed with stack traces, and passed to the options(shiny.error)
function. Also, if a reactive() cached a validation error, on future
calls the error would be re-raised (which is correct) without the
custom class names attached (which is not).
2015-12-14 16:31:57 -08:00
Winston Chang
4e1caee7da Simpler fix for updating hidden sliders (#1010)
This also reverts the previous fix, #1026
2015-12-10 15:49:23 -06:00
Yihui Xie
76a54249bb Add some tests for character encodings 2015-12-10 15:23:31 -06:00
Winston Chang
0e894cb043 Use templates from htmltools 2015-12-10 15:11:03 -06:00
Winston Chang
01bbee59eb Merge pull request #1023 from yihui/bugfix/1018
Fixes #1018: make sure the selected value is always returned from sever-side selectize
2015-12-10 13:44:24 -06:00
Joe Cheng
26a0c3520c Add knit_print.reactive
Allows us to drop reactive expressions right into Rmd docs
with runtime:shiny and have them stay up-to-date.
2015-12-09 16:34:55 -08:00
Yihui Xie
6056c35de3 add a news item, and run grunt 2015-12-09 11:56:13 -06:00
Yihui Xie
4202991ca5 Fixes #1018: make sure the selected value is always returned from server-side selectize 2015-12-09 11:52:50 -06:00
Joe Cheng
788931c7c7 Merge pull request #1027 from yihui/yihui/srcfilecopy
Check R version at runtime (#968)
2015-12-09 09:48:59 -08:00
Yihui Xie
b2d0505c7c Check R version at runtime (#968) 2015-12-08 20:33:52 -06:00
Winston Chang
8b710d651f Merge pull request #1026 from rstudio/joe/bugfix/hidden-slider-update
Fix #1010: updateSliderInput doesn't update hidden sliders
2015-12-08 19:50:12 -06:00
Joe Cheng
93697bb01d Merge pull request #1025 from yihui/bugfix/source-utf8
Fixes #1003: correctly parse source code that can be represented with native encoding
2015-12-08 15:51:51 -08:00
Joe Cheng
89cd58e4f8 Fix #1010: updateSliderInput doesn't update hidden sliders 2015-12-08 11:13:18 -08:00
Yihui Xie
a622f029a0 Fixes #1003: when the source code can be represented via the native encoding on Windows, rewrite the code to a temporary file with the native encoding and parse it 2015-12-07 16:28:45 -06:00
Winston Chang
97afb52904 Re-document with Roxygen2 5.0.1 2015-12-02 15:43:25 -06:00
Joe Cheng
02ea31be08 Fix #1020: Graphics are cut off 2015-12-01 13:47:26 -08:00
david.zotloeterer
d1353e8eae fixed custom message obj 2015-12-01 13:36:32 +01:00
david.zotloeterer
935a76d16b cleanup 2015-12-01 13:29:41 +01:00
david.zotloeterer
db4c41f420 grunted 2015-12-01 12:59:48 +01:00
david.zotloeterer
62f5af8e0b fixed typo 2015-12-01 12:42:05 +01:00
david.zotloeterer
ff9aefb649 more tagging 2015-12-01 12:39:41 +01:00
david.zotloeterer
2b10d03e1f added binary tags 2015-12-01 12:11:44 +01:00
david.zotloeterer
a27efbd937 added binary messages, yes, ws can do dat! 2015-12-01 12:03:07 +01:00
Joe Cheng
b5e49a6619 Fix bugs reported by @daattali
- reactive srcrefToLabel call fails if code is pasted in at RStudio console
- Stack trace fails when promise (!?) is in call stack
2015-11-24 11:55:08 -08:00
Joe Cheng
179c931f85 Bump version 2015-11-23 10:58:30 -08:00
Joe Cheng
4d3fa2c8ac Merge pull request #1011 from rstudio/joe/feature/better-errors
Better errors
2015-11-20 12:56:24 -08:00
Joe Cheng
8e4f7387d0 Better stack traces for observeEvent and eventReactive 2015-11-16 13:10:53 -08:00
Joe Cheng
feb630b2c5 Catch errors when trying to print stack trace
No known errors will occur when printing the stack trace, but since
this is fairly complicated code executing when an error has already
happened, better to be on the defensive.
2015-11-16 13:08:34 -08:00
Joe Cheng
948dfbb56b More tests 2015-11-16 12:50:20 -08:00
Joe Cheng
5c3ac75b34 Errors in ui function should get stack traces 2015-11-16 12:46:22 -08:00
Joe Cheng
adc5c8e37a Normalize shiny dir in smoketest stack traces 2015-11-16 12:45:59 -08:00
Joe Cheng
52d594c143 Quick and dirty smoke test infrastructure 2015-11-16 12:29:02 -08:00
Joe Cheng
1018b0d966 Errors in ui.R, server.R, server func, etc. all get stack traces 2015-11-16 10:48:16 -08:00
Joe Cheng
0ce153d788 Doc updates
Code had to be rearranged to make the functions show up in the
help docs in the right order
2015-11-14 00:26:23 -08:00
Joe Cheng
ff9756c739 Add more exports, tests 2015-11-14 00:17:19 -08:00
Joe Cheng
b3dd7e5397 Remove support for shiny.observer.error option
It is dangerous to set this on a per-process basis. If we even
still want the ability to have observer errors not kill the
session (debatable) we should do so by having an option on the
session object that is set when the shiny server function runs.

Not only does this remove support for the option, but now we
will refuse to connect and will abort the session if the option
is set to a non-NULL value. This is to prevent apps from moving
forward with the assumption that their option will work, when
it really won't. (I doubt anyone is using it anyway, it's so
obscure...)
2015-11-13 18:13:14 -08:00
Joe Cheng
6ac0a80896 Update NEWS with stack trace announcement 2015-11-13 17:58:18 -08:00
Joe Cheng
93f774c7e7 Add exports/docs 2015-11-13 17:52:26 -08:00
Joe Cheng
661c08549d Automatically wrap installExprFunction with function labels
This lowers the burden on renderXXX functions having nice stack traces
2015-11-13 16:43:15 -08:00
Joe Cheng
c8acc44012 Add comments to stacktraceon/off directives indicating their partners 2015-11-13 16:25:22 -08:00
Joe Cheng
aabf00659e Random improvements to stack traces
- More selective removal of frames at end of call stack
- Add withLogErrors, printError, stripStackTrace convenience funcs
- Properly capture/log errors for various levels of unhandled errors
- Unhide stacks for flush/flushed/sessionended callbacks
2015-11-13 16:03:18 -08:00
Joe Cheng
a9bc41492c Collapse stack trace to eliminate irrelevant bits 2015-11-13 12:36:55 -08:00
Winston Chang
12b0484e9a Merge pull request #875 from wch/rendertable-args
Separate xtable from print.xtable args. Closes #761
2015-11-13 11:48:41 -06:00
Joe Cheng
d3605dbcb3 Collapse parts of the stack 2015-11-12 16:40:56 -08:00
Joe Cheng
d9a016f94c Move error-handling logic into conditions.R 2015-11-11 16:20:26 -08:00
Joe Cheng
a13657ac23 Work properly with non-call sys.call() objects 2015-11-11 15:54:03 -08:00
Joe Cheng
d6c95a9e89 wip 2015-11-11 12:14:42 -08:00
Winston Chang
825e9e04c1 Get xtable args using formals() 2015-11-05 13:04:43 -06:00
Winston Chang
c596e44c5a Separate xtable from print.xtable args. Closes #761 2015-11-05 13:01:01 -06:00
Winston Chang
66be9004fe Move renderTable to separate file 2015-11-05 12:59:04 -06:00
Winston Chang
119ebb0f07 Merge pull request #1005 from rstudio/joe/bootstrap-3.3.5
Upgrade bootstrap to 3.3.5
2015-11-05 12:43:34 -06:00
Joe Cheng
d509fcac29 Merge pull request #1004 from rstudio/joe/feature/autoreload
Implement autoreload
2015-11-05 10:43:13 -08:00
Joe Cheng
c79e933586 Merge pull request #1002 from rstudio/joe/bugfix/nested-scopes
Fix nested scopes
2015-11-05 10:32:23 -08:00
Joe Cheng
3c3cfc02a0 Merge pull request #997 from wch/download-fix
Update download to work correctly with R 3.2.2 on Mac. Fixes #996
2015-11-05 10:27:51 -08:00
Joe Cheng
e32de7b940 Update bootstrap htmlDependency version 2015-11-05 07:38:00 -08:00
Joe Cheng
d7b1759afb Upgrade bootstrap to 3.3.5 2015-11-04 22:56:31 -08:00
Joe Cheng
e391c1fda3 Implement autoreload 2015-11-04 13:44:38 -08:00
Joe Cheng
46d8d3b469 Fix nested scopes
The session$makeScope function was not correctly taking the session's
own namespace into account (i.e. all makeScope() method calls were
being performed against the root session).
2015-11-03 12:33:39 -08:00
Joe Cheng
9fde7509fa Merge pull request #1000 from rstudio/joe/feature/invalidatelater-default-session
Don't require session to be explicitly passed to invalidateLater
2015-11-02 16:21:43 -08:00
Joe Cheng
a767a61f43 Don't require session to be explicitly passed to invalidateLater
These functions were created before getDefaultReactiveDomain()
existed, so the only way to get ahold of the current session was
if the caller explicitly passed it.

This is slightly backwards incompatible, in that existing calls
to invalidateLater() that don't pass a session argument will
behave slightly differently (bound to the current session instead
of to no session), but those calls would have triggered a warning
for all but the very earliest versions of Shiny.
2015-11-02 14:19:29 -08:00
Joe Cheng
ad28e03536 Merge pull request #999 from yihui/bugfix/ie9-upload
Fixes #990: revert to checking if FileList is supported by the browser
2015-11-02 11:13:57 -08:00
Yihui Xie
5f9e9c2e03 grunt 2015-11-02 12:21:56 -06:00
Yihui Xie
2be6d7a65c Fixes #990: revert to checking if FileList is supported by the browser, instead of checking IE8 literally (partially reverted a41e1dafc2) 2015-11-02 12:21:30 -06:00
Winston Chang
712c4cb985 Update download to work correctly with R 3.2.2 on Mac. Fixes #996 2015-10-30 12:08:48 -05:00
Winston Chang
7948a0a4fa Merge pull request #993 from rstudio/joe/feature/reactlog-labels
Cleaner reactlog labels for reactives and outputs
2015-10-30 11:57:57 -05:00
Joe Cheng
ce9e95f256 Fix parseQueryString example (thanks Chris Beeley) 2015-10-29 16:15:47 -07:00
Joe Cheng
7ed5ca94a2 reactlog label code review feedback
- Don't clobber explicitly-provided label
- Let `reactives$x1 <- reactive(...)` work
2015-10-29 10:27:06 -07:00
Winston Chang
e635589c52 Merge pull request #992 from rstudio/joe/feature/reactlog-time
Add timing data to reactlog entries
2015-10-29 10:06:08 -05:00
Joe Cheng
490721437f Merge pull request #994 from RCura/patch-1
Correct a regression within modules
2015-10-28 18:23:36 -07:00
Robin Cura
7e25a1566f Correct a regression within modules
Commit 07f2792cf9 introduced an error, replacing `e$parent = parentSession` with `e$parent <= parentSession`, while it should have been `e$parent <- parentSession`
2015-10-29 02:03:12 +01:00
Joe Cheng
c08b3b0c30 Update metadata 2015-10-28 15:30:07 -07:00
Joe Cheng
f1de132a2a Merge pull request #988 from rstudio/joe/feature/namespaces
Shiny modules
2015-10-28 15:22:57 -07:00
Joe Cheng
c440c60bdf Cleaner reactlog labels for reactives and outputs
Instead of showing the code, try to just show the name
of the reactive/output. Uses a fairly flaky algorithm
for determining the name of the reactive; will only
work in cases where the definition of the reactive
begins with "foo <- reactive({".
2015-10-28 14:37:00 -07:00
Joe Cheng
c367176a17 Add timing data to reactlog entries 2015-10-28 13:49:39 -07:00
Joe Cheng
3d13c39a4c Also override registerDataObj (essential for data table, DT, and selectize server mode) 2015-10-27 16:33:46 -07:00
Joe Cheng
07f2792cf9 Change evil = to righteous <- 2015-10-27 15:01:44 -07:00
Joe Cheng
303fce5f15 Merge pull request #983 from wch/clear-checkbox
updateCheckboxGroup: allow clearing all choices. Fixes #981
2015-10-27 15:00:20 -07:00
Joe Cheng
46c8bfdd34 Merge pull request #979 from wch/profiling-enhancements
Enhancements to help profiling
2015-10-27 14:59:59 -07:00
Winston Chang
c36a22ad5e Add information about clearing choices 2015-10-23 16:04:17 -05:00
Winston Chang
49eba95a9c Simplify function naming 2015-10-23 15:48:33 -05:00
Winston Chang
2dacc6ce40 Use cleaner method for generating function 2015-10-23 15:44:24 -05:00
Winston Chang
5ccf02f5c3 Merge pull request #984 from rstudio/update-jquery
Update to jQuery 1.11.3 and jQuery UI 1.11.4
2015-10-23 15:08:27 -05:00
Joe Cheng
86c67de8ff Add session$ns(); some light refactoring; add tests; fix staticdocs 2015-10-20 23:49:24 -07:00
Joe Cheng
dac7eb5997 Simplify modules, by removing moduleUI and path arguments 2015-10-20 15:12:11 -07:00
Winston Chang
fd725552a5 Update to jQuery UI 1.11.4 2015-10-19 13:44:23 -05:00
Winston Chang
1e1a897970 Update to jQuery 1.11.3 2015-10-19 13:31:44 -05:00
Joe Cheng
59a643c006 Update metadata 2015-10-16 10:24:05 -07:00
Joe Cheng
1a492208e6 Merge pull request #982 from rstudio/feature/single-file-runapp
Let runApp accept single .R file parameters
2015-10-16 10:16:29 -07:00
Winston Chang
bc0a19f55d updateCheckboxGroup: allow clearing all choices. Fixes #981 2015-10-16 11:44:09 -05:00
Jonathan McPherson
85e2e00bc4 let runApp accept single .R file parameters 2015-10-16 09:22:44 -07:00
Joe Cheng
93dd8bbf28 Merge pull request #980 from rstudio/joe/bugfix/select-value-escaping
Fix improperly escaped quote chars in select choices
2015-10-15 12:56:54 -07:00
Joe Cheng
2f15a219df Fix improperly escaped quote chars in select choices 2015-10-15 12:49:50 -07:00
Yihui Xie
4fc73b1344 A news item for #968 2015-10-15 13:45:00 -05:00
Joe Cheng
48b56ba08d Merge pull request #968 from yihui/bugfix/961-unicode
A more fundamental fix to the Unicode issue
2015-10-15 11:17:45 -07:00
Joe Cheng
94ca733c7c Merge pull request #967 from rstudio/joe/bugfix/placeholder
Add placeholder parameter to textInput
2015-10-15 11:15:10 -07:00
Winston Chang
4af2436a0e Rename 'func' to more descriptive name 2015-10-14 16:40:33 -05:00
Winston Chang
20c2bb9d50 Simplify try(tryCatch()) to just tryCatch()
This also has the benefit of printing a more helpful error message at
the console. It now indicates which output an error occurred in.
2015-10-14 16:40:33 -05:00
Winston Chang
33bac0db3c Add function relabeler and relabel outputs 2015-10-14 16:40:33 -05:00
Winston Chang
8893db9098 Update NEWS 2015-10-12 22:39:51 -05:00
Winston Chang
c46658a5c8 Correctly handle unit and unit.list objects. Closes #962 2015-10-12 22:31:32 -05:00
Joe Cheng
5f651aed3e LICENSE typo 2015-10-12 12:32:00 -04:00
Joe Cheng
534bc9c6e2 Pass tests 2015-10-05 15:15:54 -04:00
Joe Cheng
bb09885237 Add callModule/moduleUI functions; scope sendInputMessage 2015-10-05 14:56:01 -04:00
Joe Cheng
3ff6aaa6db Add namespacing functionality
- Need to carefully review ShinySession, think about
  each and every public method and whether it should
  be passthrough or do something different for a
  namespaced session.

- It would be nice if we do the namespace splitting
  at the server.R level and actually have numerous
  reactivevalues objects for inputs, one for each
  namespace. This will make the reactive hooks for
  names() and reactiveValuesToList() correct, as
  right now they will oversubscribe if you're in a
  namespaced session. Also it would prevent outer
  sessions from seeing the values of their child
  namespaces. (Though is that good or bad...?)
2015-10-02 12:51:18 -07:00
Joe Cheng
fa7c034d16 Merge pull request #955 from rstudio/joe/feature/reactlog-bysession
Fix couple of issues with reactlog
2015-09-24 09:54:35 -07:00
Joe Cheng
ca870ccd75 Merge pull request #966 from rstudio/joe/bugfix/showcase-highlighting
Fix highlighting of reactives in showcase mode
2015-09-24 09:54:01 -07:00
Yihui Xie
89fe2ff217 a more fundamental fix of the Unicode issue
for R <= 3.2.2, Unicode chars don't work for shiny mainly because we want to
preserve the source reference, and unfortunately srcfilecopy() fails because of
the bug https://bugs.r-project.org/bugzilla3/show_bug.cgi?id=16264

here I use lines = '' to get around the bug, and assign the source lines to
the srcfile object later, so there is no grep("\n", multibyte_chars) occuring

I also replaced source() with a custom version, which is much simpler and works
better with Unicode chars
2015-09-23 23:00:20 -05:00
Joe Cheng
6c6775376e Add placeholder parameter to textInput 2015-09-23 18:28:08 -07:00
Yihui Xie
bd3b3881d8 after reading a file as UTF-8, try to convert it to native encoding on Windows only if the conversion is not lossy 2015-09-23 18:33:01 -05:00
Yihui Xie
078189599c the check is.na(iconv(x, 'UTF-8')) does not really make sense
e.g. iconv('\u2264', 'UTF-8') converts the smaller than or equal sign to an
equal sign in the English (US) locale, which is lossy

we just assume the input is UTF-8 instead, and do not fall back to native
encoding any more
2015-09-23 18:31:16 -05:00
Yihui Xie
9afcd2a411 don't declare encoding on the anonymouse file connection
otherwise writeLines(useBytes = TRUE) may not work (#961)
2015-09-22 01:11:38 -05:00
Yihui Xie
0afd1649c1 textConnection() can be lossy; use an anonymous file connection instead to preserve UTF-8 characters 2015-09-22 01:11:37 -05:00
Yihui Xie
0947a63103 make sure writeLines() does not convert text to native encoding 2015-09-22 01:11:32 -05:00
Yihui Xie
79223bddc5 make sure the UI HTML is rendered into a connection with UTF-8 encoding 2015-09-21 22:25:12 -05:00
Yihui Xie
1871dd6b71 convert the result of readLines() to UTF-8 using enc2utf8() instead, since enc2native() can be lossy 2015-09-21 21:49:12 -05:00
Yihui Xie
4adc3088d1 fixes #961: the JSON string from jsonlite::toJSON() is always encoded in UTF-8, so there is no need to re-encode it
actually re-encoding the string with native encoding can be lossy (some UTF-8 characters may not be representable in the native encoding)
2015-09-21 21:46:11 -05:00
Yihui Xie
79dd7e1bf5 shiny requires R >= 3.0.0 2015-09-21 21:44:03 -05:00
Joe Cheng
6718d377bb Fix and enhance reactlog
* Reactlog shortcut was broken due to reactlog.js not being included in
  the gruntfile
* Reactlog has always shown every reactive event since the beginning of
  the process (or at least since options(shiny.reactlog=TRUE)). This
  commit makes it so Ctrl+F3 invocation only shows reactives that either
  belong to no session (e.g. observers and reactives declared at the top
  of server.R), or belong to this session.
2015-09-13 02:46:52 -07:00
Joe Cheng
d4e3329d7a Fix highlighting of reactives in showcase mode
This has been broken since we switched to jsonlite. Reactives
don't highlight because their srcref is in an S3(?) class that
jsonlite doesn't recognize, whereas RJSONIO would treat it as
a numeric vector.
2015-09-12 20:20:13 -07:00
Joe Cheng
99295f0983 Bump version, update NEWS 2015-09-09 22:13:45 -07:00
Winston Chang
3606c36cb9 Merge pull request #950 from rstudio/joe/feature/grunt-clean
Add 'grunt clean' to delete shiny build artifacts
2015-09-09 15:49:02 -05:00
Winston Chang
3d5c184acc Merge pull request #949 from rstudio/joe/feature/shared-brush-id
Allow shared brush IDs
2015-09-09 15:48:43 -05:00
Joe Cheng
9e03b17498 Add 'grunt clean' to delete shiny build artifacts 2015-09-09 12:19:43 -07:00
Joe Cheng
129714b044 Allow shared brush IDs 2015-09-09 12:12:47 -07:00
Joe Cheng
67823556d2 Merge pull request #939 from yihui/bugfix/selectize-selected
Fixes #929: defer setValue() for selectize until the options have been loaded from server
2015-09-09 12:10:03 -07:00
Yihui Xie
273e71e3c4 run grunt 2015-09-09 13:38:51 -05:00
Yihui Xie
076ac26929 call setValue() only once
otherwise every time an Ajax response is returned (e.g. during searching), the value is set to data.value
2015-09-09 13:29:37 -05:00
Yihui Xie
5a022b0a2c upgrade selectize.js from 0.11.2 to 0.12.1 to fix the ordering issue in #929
also fixes #865
2015-09-09 13:29:37 -05:00
Yihui Xie
9ab493a81f make sure mop is numeric
it is funny that head(1:11,'100') produces a bunch of NA's, but  head(1:11,'12') does not
2015-09-09 13:29:37 -05:00
Yihui Xie
dfc1f32595 Fixes #929: defer setValue() for selectize until the options have been loaded from server
Previously I was using a hack to set the selected value of selectize, i.e. add the selected option(s) to selectize via addOptions() (because the selected option(s) may not have existed); this hack can be removed and we can set the value in the `success` callback of the Ajax request, by which time the options will be available
2015-09-09 13:29:37 -05:00
Joe Cheng
e6fd30fb78 Merge pull request #935 from yihui/doc/events-vignette
Add a package vignette for the JS events
2015-09-09 10:33:52 -07:00
Joe Cheng
c8d338912a Merge pull request #918 from rstudio/joe/bugfix/binding-getid
Fix #676: getId(el) not being used in output binding
2015-09-09 10:33:45 -07:00
Winston Chang
d9d9e0b33f Add info about printing ggplot objects 2015-09-08 15:07:40 -05:00
Yihui Xie
1da7b83956 Bump version 2015-09-04 20:54:30 -05:00
Yihui Xie
29c545d2e3 Bump fontawesome version after #932 2015-09-04 20:53:54 -05:00
Joe Cheng
431b345c82 Fix #676: getId(el) not being used in output binding 2015-09-04 15:14:04 -07:00
Joe Cheng
8773b1b38f Merge pull request #946 from yihui/bugfix/recalculating
Typo: name -> message.name
2015-09-04 14:53:43 -07:00
Yihui Xie
52efb3dc16 Typo: name -> message.name 2015-09-04 15:43:21 -05:00
Yihui Xie
9ccd179b04 document the rest of events: shiny:conditional, shiny:visualchange, shiny:recalculating, shiny:recalculated 2015-09-04 15:09:34 -05:00
Joe Cheng
a8b35c49a7 Merge pull request #932 from dselivanov/master
Bump fontawesome version to 4.4.0, fixes #913
2015-09-04 12:15:31 -07:00
Peter K. Shultz
4e027f1a45 Update README.md
Add missing period.
2015-09-04 12:13:28 -07:00
Joe Cheng
119182454b Merge pull request #943 from yihui/feature/events-more
Events shiny:visualchange, shiny:conditional, and shiny:recalculating
2015-09-04 11:34:48 -07:00
Yihui Xie
04e10a4f0d Run grunt 2015-09-02 12:30:22 -05:00
Yihui Xie
4e5c5f9c5b Move the shiny:conditional event to the top of $updateConditionals() 2015-09-02 12:29:37 -05:00
Yihui Xie
1ee2a25eca Trigger the recalculating event on null when the binding does not exist 2015-09-02 12:29:01 -05:00
Yihui Xie
838e132515 Trigger shiny:visualchange in doSendOutputHiddenState() as well. Added a 'visible' property to the event object. 2015-08-31 17:07:11 -05:00
Yihui Xie
da76a843ee DO not assume '# + message.name' is the element id. Find the element through this.$bindings[name].el instead. 2015-08-31 17:02:14 -05:00
Yihui Xie
b0676b8b31 Trigger shiny:recalculated after the calculation is done 2015-08-31 16:38:46 -05:00
Yihui Xie
83c3656d29 Events shiny:visualchange, shiny:conditional, and shiny:recalculating 2015-08-31 15:58:05 -05:00
Yihui Xie
5ddd6cc94e add a summary section to the events vignette 2015-08-27 23:21:36 -05:00
Yihui Xie
90419765af Merge pull request #938 from yihui/bugfix/933
Fixes #933: make sure type is of length one
2015-08-26 11:27:05 -05:00
Yihui Xie
acad9354a6 Fixes #933: make sure type is of length one 2015-08-26 10:48:38 -05:00
Yihui Xie
9105dd7b04 Add a package vignette for the JS events 2015-08-24 15:25:58 -05:00
Joe Cheng
71adee1f38 Merge pull request #930 from yihui/feature/events
More JS events
2015-08-24 12:50:10 -07:00
Yihui Xie
3cbe3831ec Run grunt 2015-08-21 16:19:02 -05:00
Yihui Xie
619aa4f05a Events shiny:busy and shiny:idle
Keep track of the number of observer callbacks during addPendingFlush() and executeFlushCallbacks(). Send a custom message when the number is 0.
2015-08-21 16:12:45 -05:00
Yihui Xie
4df37d6f3e The shiny:updateinput event for inputs 2015-08-21 16:10:01 -05:00
Yihui Xie
58f8b482f5 The message object may be exactly false (or a falsy value), in which case shiny will fail to send the message to the handler. Use .hasOwnProperty() instead. 2015-08-21 16:09:25 -05:00
Yihui Xie
168f5e32af The shiny:message event 2015-08-21 16:08:16 -05:00
Yihui Xie
06bf28f10c Events shiny:value and shiny:error for outputs 2015-08-21 16:07:33 -05:00
Yihui Xie
620fdc0d9f The shiny:inputchanged event 2015-08-21 16:06:56 -05:00
Yihui Xie
bdac0e2456 Events shiny:bound and shiny:unbound for inputs and outputs 2015-08-21 16:06:00 -05:00
Dmitry Selivanov
60a57a0a40 Bump fontawesome version to 4.4.0, fixes #913 2015-08-20 12:34:52 +03:00
Yihui Xie
a242ae3849 Merge pull request #920 from yihui/yihui/selectize-dragdrop
fixes #902: add jQueryUI dependency when the drag_drop plugin is used in selectize
2015-08-06 21:46:30 -05:00
Yihui Xie
ced4060b5c fixes #902: add jQueryUI dependency when the drag_drop plugin is used in selectize 2015-08-06 16:59:51 -05:00
Winston Chang
0b3eb7a237 Merge tag 'v0.12.2'
Shiny 0.12.2
2015-08-05 15:08:21 -05:00
Winston Chang
c82f87cd76 Fix NEWS formatting 2015-08-04 23:05:30 -05:00
Winston Chang
51d8a6d9bf Bump version to 0.12.2 2015-08-04 23:00:34 -05:00
Winston Chang
d334aa2088 Merge branch 'http-head' 2015-08-04 22:59:49 -05:00
Winston Chang
710e003bdc Update httpuv version 2015-08-04 22:59:26 -05:00
Winston Chang
b2f5b4f861 Update NEWS 2015-08-04 22:58:21 -05:00
Winston Chang
0ac87930c8 Add support for HTTP HEAD requests 2015-08-04 22:58:21 -05:00
Winston Chang
241a482236 Add explicit namespace to non-base functions 2015-08-04 12:30:41 -05:00
Winston Chang
2abaffafcf Move imageOutput docs to plotOutput 2015-08-04 12:23:06 -05:00
Winston Chang
4545fedf31 Update NEWS 2015-08-03 16:05:39 -05:00
Joe Cheng
a47a690a68 Merge pull request #909 from rstudio/joe/bugfix/runGist
Fix runGist
2015-07-22 14:08:05 -07:00
Joe Cheng
f89c44e899 Fix runGist
Looks like GitHub changed the format of gist downloads from .tar.gz
to .zip
2015-07-22 10:30:57 -07:00
Winston Chang
59b0df5c82 Concat and minify shiny.js 2015-07-21 13:01:16 -05:00
Yihui Xie
5ec6ffb30a Merge pull request #905 from rstudio/joe/doc-tweaks
Doc tweaks (fixes #898)
2015-07-21 12:51:28 -05:00
Winston Chang
5956d2009c Minor code cleanup in slider JS code 2015-07-21 12:32:40 -05:00
Winston Chang
d9c7f21c02 Make updateSliderInput work with date/datetimes. Fixes #904 2015-07-21 12:32:18 -05:00
Winston Chang
926e508b8d Fix updateSliderInput example code 2015-07-21 12:31:31 -05:00
Winston Chang
ac83772945 Don't use scientific notation for sending slider values 2015-07-21 12:31:17 -05:00
Joe Cheng
cddf5cf70f Doc tweaks 2015-07-21 10:15:04 -07:00
Winston Chang
d53acdb46a Update NEWS 2015-07-20 14:14:38 -05:00
Winston Chang
cfae8f4fc6 Update to ion.rangeSlider 2.0.12 2015-07-20 14:09:26 -05:00
Joe Cheng
74cd4cecbf Merge remote-tracking branch 'yihui/feature/events'
Conflicts:
	inst/www/shared/shiny.js
	inst/www/shared/shiny.js.map
	inst/www/shared/shiny.min.js
	inst/www/shared/shiny.min.js.map
2015-07-16 10:24:49 -07:00
Joe Cheng
3e9e6a1389 Merge pull request #885 from rstudio/slider-improvements
Slider improvements
2015-07-16 10:21:25 -07:00
Joe Cheng
9788450c08 Merge pull request #874 from wch/file-remove
Remove uploaded files when session ends. Fixes #798
2015-07-16 10:02:27 -07:00
Yihui Xie
10b27aed34 grunt build and bump version 2015-07-14 13:52:49 -05:00
Joe Cheng
64f95be828 Merge pull request #892 from yihui/feature/events-connect
Events shiny:connected and shiny:disconnected
2015-07-14 11:35:49 -07:00
Yihui Xie
a54634023b trigger shiny:connected when the socket is opened and shiny:disconnected when it is closed 2015-07-14 13:22:55 -05:00
Winston Chang
9d942b78ef Merge pull request #881 from yihui/updateNavbarPage
Add two aliases of updateTabsetPanel(): updateNavbarPage() and updateNavlistPanel()
2015-07-03 11:00:33 -05:00
Winston Chang
4cd5357241 Set dragRange=TRUE as the default 2015-07-02 20:25:42 -05:00
Winston Chang
f985a96988 Concat and minify shiny.js 2015-07-02 16:50:43 -05:00
Winston Chang
0e3938da79 Add timezone support 2015-07-02 16:50:43 -05:00
Winston Chang
ec9bfc4731 sliderInput: add timeFormat argument 2015-07-02 16:50:43 -05:00
Winston Chang
9b91ebb8d2 Add strftime Javascript library 2015-07-02 16:50:43 -05:00
Winston Chang
da3f2367d7 Add range dragging functionality 2015-07-02 16:50:43 -05:00
Winston Chang
17cdeec34b Add Date and POSIXt support to sliders 2015-07-02 16:50:43 -05:00
Winston Chang
3446afd087 Move input handlers to separate file 2015-07-02 16:50:43 -05:00
Winston Chang
b12fef652c Update to ionRangeSlider 2.0.10 2015-07-02 16:50:43 -05:00
Winston Chang
21c7193281 Remove unneeded copy of normalize.css 2015-07-02 12:48:16 -05:00
Yihui Xie
a5e64274a2 Add two aliases of updateTabsetPanel(): updateNavbarPage() and updateNavlistPanel()
https://groups.google.com/d/msg/shiny-discuss/8VctqPqeurw/uAQIBvA1CpAJ
2015-07-02 01:37:14 -05:00
Winston Chang
3817202875 Make sure that directory removal is safe 2015-07-01 13:41:54 -05:00
Winston Chang
874fcb12a1 Remove uploaded files when session ends. Closes #798 2015-07-01 13:32:06 -05:00
Winston Chang
e0c5783703 Refactor fileUpoadContext to use private members 2015-07-01 13:32:05 -05:00
Winston Chang
a57e037b05 Fix docs for submitButton 2015-06-26 16:40:32 -05:00
Winston Chang
8546918cbb Merge pull request #873 from wch/input-width
Add width option to input functions
2015-06-18 13:14:54 -05:00
Winston Chang
82284029f2 Update NEWS 2015-06-18 13:12:49 -05:00
Winston Chang
7c20e865a5 Add width option to input functions. Closes #834, closes #589 2015-06-18 13:12:49 -05:00
Winston Chang
79267d4e12 Move input functions to separate files 2015-06-18 10:47:00 -05:00
Winston Chang
50aeb70597 Update NEWS with changes from 0.12.1 2015-06-18 10:46:26 -05:00
Joe Cheng
1d22a79074 Bump version 2015-06-15 08:47:49 -07:00
Joe Cheng
7f442f4206 Un-deprecate data table functions until DT stabilizes 2015-06-11 23:27:21 -07:00
Yihui Xie
985326989c Bump version after #857 2015-06-11 00:09:13 -05:00
Joe Cheng
be8f2afa37 Merge pull request #857 from rstudio/joe/bugfix/rebind
Fix #856: Outputs can not be unbound and re-bound
2015-06-08 18:08:19 -07:00
Joe Cheng
98882984b4 Fix #856: Outputs can not be unbound and re-bound 2015-06-08 16:56:08 -07:00
Winston Chang
a6cd0fdb85 Update NEWS 2015-06-03 14:03:16 -05:00
Winston Chang
7bc5ba7e9a Merge pull request #852 from rstudio/slider-motion
Slider fixes
2015-06-03 13:47:51 -05:00
Winston Chang
37e552cd36 Move sliderInput code into separate file 2015-06-02 15:36:03 -05:00
Winston Chang
51e2a4de7d Concat and minify shiny.js 2015-06-02 14:14:54 -05:00
Winston Chang
91ce2fcb06 Remove no-longer-needed workaround 2015-06-02 14:14:54 -05:00
Winston Chang
925a379702 Don't overshoot end of slider
This previously resulted in a bug where the animation would loop even if
loop=FALSE.
2015-06-02 14:14:54 -05:00
Winston Chang
3153cfd0ff Move both handles when animating double sliders 2015-06-02 14:14:54 -05:00
Winston Chang
ac8831b4c7 Use methods() instead of .S3methods(). Fixes #849
The .S3methods() function was introduced in R 3.2.0, so this code was broken
on older versions of R.
2015-06-02 14:14:31 -05:00
Joe Cheng
acc535e1a4 Merge pull request #850 from rstudio/min-option
Add shiny.minified option for minified JavaScript. Closes #826
2015-06-01 21:54:33 -07:00
Winston Chang
fdacb4fe7d Add shiny.minified option for minified JavaScript. Closes #826 2015-06-01 20:58:10 -05:00
Winston Chang
fc7208469d Add staticdocs entries for interactive plots 2015-05-26 22:44:26 -05:00
Winston Chang
5c38cb733a Safer method for finding which method would be called 2015-05-26 17:05:10 -05:00
Winston Chang
515a67a320 Don't attempt to extract coordmap when print.ggplot is not used (#841) 2015-05-21 17:00:51 -05:00
Winston Chang
941348f1db Override print.ggplot method in renderPlot. Fixes #841 2015-05-21 16:45:39 -05:00
Joe Cheng
8d7752b0bc Merge pull request #840 from rstudio/joe/bugfix/methods-depend
Depend on methods so Rscript doesn't fail
2015-05-21 09:57:50 -07:00
Joe Cheng
15af660424 Depend on methods so Rscript doesn't fail 2015-05-21 09:56:16 -07:00
Joe Cheng
790555ae89 Bump version, NEWS 2015-05-20 14:35:04 -07:00
Joe Cheng
3cc4df4e29 Merge pull request #837 from rstudio/joe/bugfix/callbacks-fifo
Ensure that callbacks fire in a FIFO order
2015-05-20 14:32:46 -07:00
Joe Cheng
395d1cee70 Ensure that callbacks fire in a FIFO order
Version bump required so Leaflet can detect this fix
2015-05-20 13:29:56 -07:00
Yihui Xie
89bc7efbca query$field is either an atomic vector, or a matrix, so use c() to coerce the result to a vector (previously RJSONIO::fromJSON() would return a list, but now jsonlite returns a matrix) 2015-05-20 12:44:13 -05:00
Yihui Xie
8f893a9752 bump version 2015-05-19 16:15:24 -05:00
Yihui Xie
54e02e412c make sure q$search[['value']] is not of length zero 2015-05-19 16:14:30 -05:00
Winston Chang
808d7aab3f Merge tag 'v0.12.0'
Shiny 0.12.0 released to CRAN
2015-05-19 09:52:19 -05:00
Winston Chang
24a8f8f38b Merge pull request #830 from rstudio/check-htmlwidgets
Check htmlwidgets version
2015-05-18 11:58:48 -05:00
Winston Chang
90c00bed2f Check htmlwidgets version 2015-05-18 11:56:02 -05:00
Winston Chang
054c911a1f Clear cran comments 2015-05-18 11:07:42 -05:00
Winston Chang
c2d5432a5d Bump version to 0.12.0 2015-05-18 10:10:53 -05:00
Winston Chang
dd64b70f5b Concat and minify shiny.js 2015-05-18 09:49:10 -05:00
Yihui Xie
a69dbeb10f bump version 2015-05-17 15:20:16 -05:00
Yihui Xie
976a768446 white spaces 2015-05-17 15:19:04 -05:00
Yihui Xie
5612cec91f Merge pull request #829 from yihui/deprecate-datatables
closes #807: add messages of deprecation for DataTables in shiny
2015-05-17 15:17:51 -05:00
Yihui Xie
46996eb81c closes #807: add messages of deprecation for DataTables in shiny 2015-05-15 18:21:58 -05:00
Winston Chang
12990f9fb2 Merge pull request #823 from yihui/bugfix/814
Load selectize options after initialization in the server mode
2015-05-15 15:22:39 -05:00
Yihui Xie
77ff988232 a news item for the change in server-side selectize 2015-05-15 15:17:31 -05:00
Yihui Xie
8df98c29b8 this comment is no longer true 2015-05-15 15:06:23 -05:00
Yihui Xie
7554f8395b fixes #814: load options after initialization of selectize in the server mode 2015-05-15 15:05:39 -05:00
Winston Chang
6fc0bac106 Properly use hoverClip option 2015-05-13 11:42:18 -05:00
Winston Chang
d252feddc9 Change nullOutside default to TRUE 2015-05-13 11:16:48 -05:00
Winston Chang
8c2645498d Merge pull request #824 from rstudio/interaction-tweaks
Interaction tweaks
2015-05-13 11:14:46 -05:00
Winston Chang
528acc4aa4 Concat and minify shiny.js 2015-05-13 11:08:47 -05:00
Winston Chang
2ce45eab06 Hovering: add option to send NULL when mouse is outside 2015-05-13 11:08:16 -05:00
Winston Chang
074c24aa10 Show brush only after mouse starts moving 2015-05-11 22:31:56 -05:00
Winston Chang
79e4007732 Add option to return all selected rows for mouse interactions 2015-05-11 15:56:34 -05:00
Winston Chang
742ce6673c Re-document with roxygen2 4.1.1 2015-05-11 15:55:51 -05:00
Winston Chang
4a74f588b9 nearPoints: add _dist column even when zero rows selected 2015-05-05 23:11:33 -05:00
Winston Chang
f876dc066c Minify shiny.js 2015-05-05 23:11:33 -05:00
Winston Chang
65aaf386c2 Lock grunt-contrib-uglify version
Version changes of the grunt-contrib-uglify version can result in different
min.js output. Lock it so the minified files won't have spurious changes.
2015-05-05 23:01:24 -05:00
Winston Chang
4eae6bd362 Update NEWS 2015-05-01 14:59:23 -05:00
Joe Cheng
4f7cfd6bd4 Bump dev version for ggplot2 interactive graphics 2015-05-01 09:53:46 -07:00
Joe Cheng
1136ad09ee Merge pull request #802 from rstudio/interact-ggplot
BOOM!!!!
2015-05-01 09:51:51 -07:00
Winston Chang
4a67bd945c Concat and minify shiny.js 2015-05-01 11:29:31 -05:00
Winston Chang
a2841f7cf2 Allow selectBrush to operate in just x or y direction 2015-05-01 11:28:43 -05:00
Winston Chang
4fa6e9dafe Fix off-by-one positioning error for base graphics 2015-05-01 11:28:43 -05:00
Winston Chang
7ebd7959f9 Document data structures for plot interactions 2015-05-01 09:58:00 -05:00
Winston Chang
4386015cff brushedPoints and nearPoints: handle Dates and POSIXt properly 2015-05-01 09:56:24 -05:00
Winston Chang
5cd014f7e6 Fix restoring brush with new coordmap panel data format 2015-05-01 09:56:24 -05:00
Winston Chang
30d0bfbdf0 Rename selectBrush to brushedPoints 2015-05-01 09:56:24 -05:00
Joe Cheng
48cfeca220 Merge pull request #797 from rstudio/joe/input-logging
Add session$onInputReceived
2015-04-30 22:10:21 -07:00
Winston Chang
5fefc48a0b Fix variable name 2015-04-29 13:19:46 -05:00
Winston Chang
42f2ae16ec Document missing argument 2015-04-29 13:19:46 -05:00
Winston Chang
3d47b0201f Simplify selectBrush and nearPoints code 2015-04-29 12:17:28 -05:00
Winston Chang
7607b1215f Update tests 2015-04-29 11:49:36 -05:00
Winston Chang
eae2b40898 Add nearPoints() function 2015-04-29 11:48:33 -05:00
Winston Chang
8e253046d8 Merge pull request #813 from pvictor/master
Change in sliderInput
2015-04-29 10:37:02 -05:00
pvictor
b0e17f02b5 Change in sliderInput
Add `animate$playButton` and `animate$pauseButton` in `sliderTag` to add possibility to custom button with `animateOptions`
2015-04-29 15:30:25 +02:00
Winston Chang
3f3c131737 Automatically find var names for brush selection 2015-04-27 17:04:07 -05:00
Winston Chang
2b227fcca5 Send variable mappings in coordmap 2015-04-27 15:22:43 -05:00
Winston Chang
639e55b537 Rename underBrush to selectBrush 2015-04-25 18:00:11 -04:00
Winston Chang
8386404b25 Capture and display coordmap errors 2015-04-25 09:38:40 -04:00
Winston Chang
a093afb630 More informative comments 2015-04-23 11:53:31 -05:00
Winston Chang
7208688128 Simplify facet code 2015-04-23 11:53:31 -05:00
Winston Chang
b94d406bd9 Treat shiny.js as binary 2015-04-23 11:53:31 -05:00
Winston Chang
79188b7d62 Add underBrush() function 2015-04-23 11:53:31 -05:00
Winston Chang
85b2fc503d Make brush div 1px larger 2015-04-20 15:06:00 -05:00
Winston Chang
57f33109b2 Change default brush color to blue 2015-04-20 15:06:00 -05:00
Winston Chang
d039547886 Fixes for R CMD check in R 3.2.0 2015-04-20 11:55:31 -05:00
Winston Chang
55621b7826 Use NULL for session$user and $groups 2015-04-17 13:23:50 -05:00
Winston Chang
854730f258 Fix restoring brush with facets in IE8/9 2015-04-17 12:15:38 -05:00
Winston Chang
9c4f73f314 Respect falsy opts for imageOutput 2015-04-16 23:43:20 -05:00
Winston Chang
e1fa491af7 Fix panel matching logic 2015-04-16 23:40:08 -05:00
Winston Chang
b909a3e05c Allow brush resizing 2015-04-16 23:40:07 -05:00
Winston Chang
4159c539e7 Consolidate code for width/height 2015-04-16 23:40:07 -05:00
Winston Chang
d1bd232ec7 Grunt: jshint before uglify 2015-04-16 23:40:07 -05:00
Winston Chang
a47898a2c4 Fix min/max order for reversed scales 2015-04-16 23:40:07 -05:00
Winston Chang
06b69e516a Pixelratio workaround 2015-04-16 23:40:07 -05:00
Winston Chang
312c89aaee Fix panel scale order for facet_grid 2015-04-16 23:40:07 -05:00
Winston Chang
a5b1f020ae Simplify sendBrushInfo 2015-04-16 23:40:07 -05:00
Winston Chang
e9cd1bef43 Send brush data when reset 2015-04-16 23:40:07 -05:00
Winston Chang
7b17ce5de1 Fix brush import for reverse scales 2015-04-16 23:40:07 -05:00
Winston Chang
ffb1b06bf4 Use 'expand' parameter 2015-04-16 23:40:06 -05:00
Winston Chang
21e9ffec97 Cleaner coordmap initialization 2015-04-16 23:40:06 -05:00
Winston Chang
7cfaa2adfc Move clipToBounds from coordmap to panel 2015-04-16 23:40:06 -05:00
Winston Chang
4e80e35976 Get correct coordinates for panels 2015-04-16 23:40:06 -05:00
Winston Chang
e9f78f6ace Restore brush even when scales change 2015-04-16 23:40:06 -05:00
Winston Chang
4d074c6fa9 Fix log scaling 2015-04-16 23:40:06 -05:00
Winston Chang
0b3c2e198e Add functions to coordmap object 2015-04-16 23:40:06 -05:00
Winston Chang
48cd7200cc Partial implementation of scale/inv 2015-04-16 23:40:06 -05:00
Winston Chang
d925702b98 Fix height/width calculation of brush div 2015-04-16 23:40:06 -05:00
Winston Chang
55cbd72a47 Gruntfile: add watch support for concat 2015-04-16 23:40:06 -05:00
Winston Chang
fbf1ba172f Implement client-side support for facets 2015-04-16 23:40:05 -05:00
Winston Chang
abb722f405 Fix bottom/top reversal 2015-04-16 23:40:05 -05:00
Winston Chang
2c121dc2c3 Put shiftToRange in right place 2015-04-16 23:40:05 -05:00
Winston Chang
dbcdbda2ef Modularize brush code 2015-04-16 23:40:05 -05:00
Winston Chang
03784ba82e Partial refactoring of brush 2015-04-16 23:40:05 -05:00
Winston Chang
04768ad8fa Encode facet information 2015-04-16 23:40:05 -05:00
Winston Chang
29943408e5 Consolidate coordinate functions 2015-04-16 23:40:05 -05:00
Winston Chang
dbadc05bef Better function name 2015-04-16 23:40:05 -05:00
Winston Chang
2e6347b380 Safe handling of discrete scales 2015-04-16 23:40:05 -05:00
Winston Chang
6c65b1ffde Add support for ggplot2 log coord transforms 2015-04-16 23:40:05 -05:00
Winston Chang
358416687f Add support for ggplot2 log scales 2015-04-16 23:40:04 -05:00
Winston Chang
1dc061e1c6 Add support for ggplot2 reversed scales 2015-04-16 23:40:04 -05:00
Winston Chang
e7f2572f42 Treat minified JS and map files like binary 2015-04-16 23:40:04 -05:00
Winston Chang
463ee7e027 Add click/hover/brush support for ggplot2 2015-04-16 23:40:04 -05:00
Winston Chang
667bc95d02 Rename coordmap usr and bounds to domain and range 2015-04-16 23:40:04 -05:00
Winston Chang
3f8cc71814 Use separate function for getting coordmap 2015-04-16 23:40:04 -05:00
Winston Chang
a9a6e96a6a Move renderPlot into separate file 2015-04-16 23:40:04 -05:00
Winston Chang
02caf05aaf Fix formatting in flowLayout documentation 2015-04-14 11:04:50 -05:00
Joe Cheng
da88678a43 Merge pull request #796 from mikelove/patch-1
flowLayout's man page: % needs to be escaped
2015-04-14 09:03:15 -07:00
Mike Love
1e8a9de60a flowLayout's man page: % needs to be escaped
or else it turns into a commend in the Rd file
2015-04-14 11:51:38 -04:00
Joe Cheng
1a6181cb15 Merge pull request #778 from rstudio/bugfix/clear-select-choices
Allow updateSelectInput(choices=character(0)) to clear select choices
2015-04-14 08:42:53 -07:00
Yihui Xie
ece69342b8 bump version 2015-04-13 10:09:59 -05:00
Yihui Xie
fe601d631b bump version of mime to 0.3
due to the fact that mime < 0.3 used GPL but copied code from Rook, which is specifically GPL-2; mime >= 0.3 also uses GPL-2 since GPL implies 2 or 3, and 3 does not work with 2
2015-04-13 10:09:26 -05:00
Yihui Xie
42d2e5aaef bump version requirement of jsonlite due to toJSON(json_verbatim = TRUE) (#795) 2015-04-13 10:06:39 -05:00
Winston Chang
bac7ab7f01 Merge pull request #795 from yihui/feature/verbatim-json
Treat the output of toJSON() as a verbatim JSON string
2015-04-09 10:29:46 -05:00
Yihui Xie
c6ac1474d4 Treat the output of toJSON() as a verbatim JSON string, and do not double-encode it
https://github.com/jeroenooms/jsonlite/commit/6fd201b3ad

also see ramnathv/htmlwidgets#28
2015-04-08 21:38:15 -05:00
Winston Chang
5a9c4ad8f3 Reinstate image error message fix
This fix for #783 seems to have gotten lost when shiny.js was split up.
2015-04-08 10:01:45 -05:00
Winston Chang
babc59f85c Clarifcations in tools README 2015-03-30 20:00:13 -05:00
Winston Chang
982a4361cd Merge pull request #775 from rstudio/split-js
Split up shiny.js into multiple files
2015-03-30 16:20:05 -05:00
Winston Chang
15e53ca55e Remove extraneous indenting 2015-03-30 16:18:26 -05:00
Winston Chang
ceb428b8bd Split up shiny.js 2015-03-30 16:17:04 -05:00
Winston Chang
b7fe3ed745 Update to ionRangeSlider 2.0.6. Closes #777 2015-03-27 15:31:37 -05:00
Winston Chang
817b614e61 Clear error messages in imageOuputs. Fixes #783 2015-03-27 15:11:35 -05:00
Joe Cheng
3321e95b54 Add session$onInputReceived 2015-03-26 16:04:04 -07:00
Winston Chang
fd5f536d14 Use jsonlite 0.9.15 2015-03-26 09:55:45 -05:00
Joe Cheng
fdbcb32be7 Allow updateSelectInput(choices=character(0)) to clear select choices 2015-03-24 13:00:00 -07:00
Winston Chang
a774cee1d9 Remove unused function 2015-03-21 22:19:58 -05:00
Winston Chang
1c46f227d2 Update travis config to use dev version of jsonlite 2015-03-20 15:28:36 -05:00
Winston Chang
6191c96347 Update NEWS 2015-03-20 15:13:49 -05:00
Winston Chang
12918fdfee Actually udpate jsonlite version 2015-03-20 15:13:39 -05:00
Winston Chang
8cea246fc8 Bump version to 0.11.1.9003 and increase jsonlite version dependency 2015-03-20 15:00:07 -05:00
Winston Chang
41455a008b Update toJSON for more RJSONIO compatibility 2015-03-20 14:58:28 -05:00
Winston Chang
be22d5c95d Merge branch 'interactive-graphic'
Conflicts:
	inst/www/shared/shiny.min.js
	inst/www/shared/shiny.min.js.map

Re-minified the shiny.min.js file in the merge.
2015-03-20 14:51:52 -05:00
Winston Chang
b298a62e46 Merge pull request #773 from yihui/feature/json-date
use ISO8601 and UTC for POSIX[c|l]t time by default
2015-03-20 11:06:57 -05:00
Winston Chang
839045ed66 Clear pending event on double click 2015-03-20 10:51:01 -05:00
Yihui Xie
a4a5c61fef use ISO8601 and UTC for POSIX[c|l]t time by default 2015-03-19 16:17:33 -05:00
Winston Chang
dd6aa9e4cf Minify shiny.js 2015-03-19 16:00:12 -05:00
Winston Chang
e299b51779 Simplify pending event tracking 2015-03-19 15:58:35 -05:00
Winston Chang
d3f0129c3f Expand region to start a brush 2015-03-19 15:49:25 -05:00
Winston Chang
ae938c66cc Add overview comments 2015-03-19 15:26:15 -05:00
Winston Chang
8d662f4f89 Add distance threshold for two clicks to count as a double-click 2015-03-19 15:06:48 -05:00
Winston Chang
a1caa41fe9 Simplify conversion of options to HTML properties 2015-03-19 13:55:13 -05:00
Winston Chang
6028263681 Fix some simple issues from pull request feedback 2015-03-18 20:16:06 -05:00
Winston Chang
30f9291496 Update documentation 2015-03-17 13:08:05 -05:00
Winston Chang
2400a44ded Minify shiny.js 2015-03-17 12:43:44 -05:00
Winston Chang
952556a9d1 Add option to reset brush on new image 2015-03-17 12:40:20 -05:00
Winston Chang
2e18b5c64b Fix indentation 2015-03-17 12:40:20 -05:00
Winston Chang
cb499c6105 Remove no-longer-needed IE9/10 workaround 2015-03-16 22:35:31 -05:00
Winston Chang
7f87bcddc1 Rename file 2015-03-16 22:28:00 -05:00
Winston Chang
6798917bc9 Simplify namespacing of image interaction events 2015-03-16 22:25:18 -05:00
Winston Chang
cf3f32ac37 Add dblclick workaround for IE8 2015-03-16 22:00:41 -05:00
Winston Chang
a41e1dafc2 Add IE version detection code 2015-03-16 22:00:23 -05:00
Winston Chang
3ac5124e5c Make sure a double click doesn't trigger click event 2015-03-16 21:27:13 -05:00
Winston Chang
83a472794b Add double-click event 2015-03-16 19:44:40 -05:00
Winston Chang
af78d62b76 Change click/hover/brush arguments 2015-03-16 16:05:50 -05:00
Winston Chang
27a0708909 Add left/right or top/bottom borders for x or y brushing 2015-03-16 14:27:05 -05:00
Winston Chang
5ab58f86ba Change event listener bindings fo IE8 compatibility
Binding mousemove event listeners to the window apparently doesn't work in IE8,
so bind to document instead.
2015-03-16 13:49:45 -05:00
Winston Chang
2c0bfdccf9 Restructure brushing and dragging event handlers 2015-03-16 12:52:52 -05:00
Winston Chang
581618370b Remove unnecessary code 2015-03-16 12:52:51 -05:00
Winston Chang
bdf217c45a Reorganize event handlers so they work properly in IE<=10 2015-03-16 12:52:51 -05:00
Winston Chang
42d6594159 Fix drag and selection on IE 2015-03-13 16:26:56 -05:00
Winston Chang
fac1750b63 Document direction argument for brushOptions 2015-03-13 16:09:52 -05:00
Winston Chang
6907d192f5 Allow mousemove and mouseup outside of the image 2015-03-13 14:32:23 -05:00
Winston Chang
0380f36489 Make sure bounds are constrained to plotting region 2015-03-13 13:48:19 -05:00
Winston Chang
1524dc2680 Add x and y brushing
This also contains a big refactoring of the brushing code.
2015-03-13 12:49:59 -05:00
Winston Chang
c95cc0a52b Better function name 2015-03-11 23:13:25 -05:00
Winston Chang
a876c5a888 Fix plotting region check 2015-03-11 23:09:51 -05:00
Winston Chang
bdd925886a Show grab and grabbing cursor with brush 2015-03-11 23:09:51 -05:00
Winston Chang
4d3d292add Only respond to left mouse button 2015-03-11 15:58:13 -05:00
Winston Chang
48f03e79e2 Add brushOptions and hoverOptions functions
Also clean up help for imageOutput and plotOutput, and add examples.
2015-03-11 15:37:22 -05:00
Winston Chang
e3af622a36 Replace bind() with explicit function wrappers 2015-03-11 12:25:45 -05:00
Winston Chang
dcb300ab50 Merge pull request #763 from rstudio/joe/bugfix/slider-delay
Fix 250ms delay in updateSliderInput(value=...) roundtrip.
2015-03-11 00:17:08 -05:00
Winston Chang
95ffd46a63 Clear click/brush/hover values when new image is received 2015-03-10 23:19:25 -05:00
Winston Chang
6f5d4a8620 Fix off-by-one error in plot pixel coordinates 2015-03-10 22:48:57 -05:00
Winston Chang
62855cc969 Fix clipping bounds off-by-one error 2015-03-10 22:42:55 -05:00
Winston Chang
bdfe9dfab2 Fix bug where brush goes outside 2015-03-10 22:42:55 -05:00
Joe Cheng
465ddf1a01 Fix 250ms delay in updateSliderInput(value=...) roundtrip. 2015-03-10 15:17:40 -07:00
Winston Chang
18a4ac1653 Refactor click and hover handlers to use shared function 2015-03-10 16:52:27 -05:00
Winston Chang
76d55144c5 Add clip option for click, hover, and brush 2015-03-10 16:23:27 -05:00
Winston Chang
665590a2f9 Add isPending method to Throttler 2015-03-10 15:32:12 -05:00
Winston Chang
81aa9a31c6 Don't send brush info second time on mouseup 2015-03-10 14:34:57 -05:00
Winston Chang
082490708f Consolidate image output options 2015-03-10 14:26:14 -05:00
Winston Chang
20ca4f8260 Add debounce and throttle options to brush 2015-03-10 14:16:38 -05:00
Winston Chang
eda057eb07 Disable image dragging when brushing in Firefox 2015-03-10 13:31:32 -05:00
Winston Chang
aed31c0eba Simplify image render logic
Now this exits early if data is empty.
2015-03-10 11:10:25 -05:00
Winston Chang
ea585458c7 Fix offset calculation for brush dragging 2015-03-10 10:48:51 -05:00
Winston Chang
e226e1c045 Add click/hover/brush support to imageOutputs 2015-03-10 10:47:03 -05:00
Winston Chang
ef330dd613 Formatting tweaks 2015-03-10 09:52:09 -05:00
Winston Chang
1225560ccd Streamline hover handler 2015-03-10 09:52:09 -05:00
Winston Chang
eef184e6ef Use crosshair cursor for brush 2015-03-09 22:02:04 -05:00
Winston Chang
50337eb731 Get jQuery object just once 2015-03-09 22:01:46 -05:00
Winston Chang
77f5e7d581 Add ability to clear previous brush 2015-03-09 21:54:13 -05:00
Winston Chang
02118bac76 Add brush dragging 2015-03-09 21:48:02 -05:00
Winston Chang
8b41be238a Refactor createBrushHandler 2015-03-09 20:39:31 -05:00
Winston Chang
8791e70c67 Add visible overlay for brush 2015-03-09 20:13:37 -05:00
Winston Chang
34a4b8f2d2 Add basic brushing support 2015-03-09 16:18:32 -05:00
Winston Chang
54c936b010 Refactor hover handler 2015-03-09 14:52:44 -05:00
Winston Chang
ed8d95e055 Move mouse coordinates to separate function 2015-03-09 14:17:12 -05:00
Winston Chang
25e8d080af Use lists instead of named vectors
jsonlite 0.9.14 drops names of named vectors, so we'll use lists instead.
2015-03-09 13:29:51 -05:00
Winston Chang
4599a7ab4e Merge pull request #759 from wch/fix-checkbox
Fix checkbox input binding (closes #206)
2015-03-06 15:03:58 -06:00
Winston Chang
5a106e0e6d Fix checkbox input binding (closes #206)
This fixes the bug introduced by #754.
2015-03-06 14:19:48 -06:00
Winston Chang
e4a211ba02 Send HTTP errors as UTF-8
This fixes an issue related to #736.
2015-03-06 10:27:30 -06:00
Joe Cheng
7ab373c942 Merge pull request #756 from rstudio/feature/port-option
Add shiny.port option
2015-03-06 01:41:00 -06:00
Winston Chang
18f1ebf715 Merge pull request #758 from yihui/DataTables1.10.5
upgrade DataTables to 1.10.5
2015-03-05 22:11:43 -06:00
Yihui Xie
2c02f44b26 upgrade DataTables to 1.10.5
need to be able to set $.fn.dataTableExt.errMode = 'none' to fix #561
2015-03-05 21:38:45 -06:00
Joe Cheng
8ae8168252 Merge pull request #754 from rstudio/remove-old-multiinput
Remove old multiInput code
2015-03-05 16:45:33 -06:00
Joe Cheng
c504f0b166 Merge pull request #755 from yihui/mimetype
remove the list of content types and use mime::guess_type()
2015-03-05 09:26:45 -06:00
Winston Chang
23da559d26 Add shiny.port option 2015-03-04 20:15:43 -06:00
Winston Chang
8b3ca12658 Mention app.R in runApp help 2015-03-04 20:11:16 -06:00
Yihui Xie
0f70b5662c remove the list of content types and use mime::guess_type()
also fixes #575
2015-03-04 19:58:37 -06:00
Winston Chang
475541cac0 Re-generate shiny.min.js 2015-03-04 19:43:54 -06:00
Winston Chang
007d6ad9c3 Simplify return value 2015-03-04 19:43:35 -06:00
Winston Chang
79db8d91ab Remove obsolete multiInput code (#206)
This commit removes multiInput code that was used for the HTML structure
generated in Shiny <= 0.5.0, and is now obsolete.
2015-03-04 19:39:53 -06:00
Joe Cheng
a0c6feb386 Merge pull request #753 from rstudio/bugfix/content-type
Fix downloading of files with no extension. Fixes #575
2015-03-04 16:23:30 -06:00
Winston Chang
1c72601123 Fix downloading of files with no extension. Fixes #575 2015-03-04 15:50:51 -06:00
Winston Chang
c6bcdd1ae1 Re-generate shiny.min.js 2015-03-04 15:09:28 -06:00
Winston Chang
e0acdba626 Remove unneeded ID attributes. Fixes #684
The checkboxGroup and radioButtons <input> items had an unneeded ID for the
DOM element, which could conflict with other items in the web page.
2015-03-04 15:07:38 -06:00
Winston Chang
a73e72f267 Add backward compatibility for shinysession$session (Fixes #752) 2015-03-04 11:13:52 -06:00
Joe Cheng
ee6682e7c8 Merge pull request #750 from rstudio/bugfix/nested-uioutput-duplicate-id
Fix issue #749: Nested uiOutputs break outputs
2015-03-04 09:10:56 -06:00
Joe Cheng
6c36ea5753 Fix issue #749: Nested uiOutputs break outputs 2015-03-03 21:28:47 -06:00
Winston Chang
3f798f1843 Update slider example for Shiny 0.11. (Fixes #748) 2015-03-02 16:54:35 -06:00
Winston Chang
4a806db8ae toJSON: special case for length-1 atomic vectors 2015-02-27 21:42:43 -06:00
Winston Chang
9c12125512 Bump version to 0.11.1.9002 2015-02-27 15:53:32 -06:00
Winston Chang
51130793fe Assign request object in self
The session object is now gone, so the appropriate place to assign `request`
is in `self`.
2015-02-27 15:34:55 -06:00
Winston Chang
d39c93ee7e Set up onEnd before calling onStart
This fixes an annoying bug where, if the app crashes while loading (for
example, if you call library() with a missing package), R ends up in the app's
working directory instead of in the original working directory.
2015-02-27 15:25:53 -06:00
Winston Chang
e1cfb29fa0 Update NEWS 2015-02-27 15:12:39 -06:00
Winston Chang
4dde16d836 Merge pull request #746 from rstudio/session-refactor
Refactor ShinySession object
2015-02-27 15:04:50 -06:00
Winston Chang
1c57ef5931 Remove leading dot from private item names 2015-02-27 15:02:09 -06:00
Winston Chang
0f642fe3ad Add class attribute to ShinySession object 2015-02-27 15:02:09 -06:00
Winston Chang
46844b516a Remove unneeded 'session' object
The 'session' object is no longer needed. Now that the ShinySession object
has proper public and private members, that object can be passed directly.
2015-02-27 15:02:09 -06:00
Winston Chang
b9cef228d9 Use explicit 'self', and make ShinySession portable 2015-02-27 15:02:09 -06:00
Winston Chang
fd71d04000 Use explicit 'private' 2015-02-27 15:02:09 -06:00
Winston Chang
bedb811621 Remove leading dots from public methods 2015-02-27 15:02:09 -06:00
Winston Chang
173736fd69 Set message encoding to UTF-8 instead of native (Fixes #742) 2015-02-27 14:55:31 -06:00
Winston Chang
21eea27c4a Remove duplicated 'destdir' documentation 2015-02-27 14:53:50 -06:00
Winston Chang
b39f04fbc3 Use native R config for travis 2015-02-27 11:43:30 -06:00
Winston Chang
04f898be21 Bump version to 0.11.1.9001 2015-02-23 16:43:48 -06:00
Winston Chang
defedda891 Switch from RJSONIO to jsonlite (again) 2015-02-23 16:41:36 -06:00
Winston Chang
bb2c8e5fd2 Clean up observeEvent and eventReactive examples 2015-02-23 15:57:27 -06:00
Winston Chang
7410bb9e9a Re-document 2015-02-23 15:57:06 -06:00
Winston Chang
fd3907596b Restore ability to dynamically set selectInput labels. Closes #741 2015-02-18 14:59:07 -06:00
Winston Chang
221a233ea4 Update NEWS 2015-02-12 13:21:16 -06:00
Winston Chang
389adda8ec Merge pull request #688 from xbhsu/feature/runURL-destination-file
Specifying destination file for runURL, runGist, and runGitHub
2015-02-12 13:11:19 -06:00
bhsu
835b65740b fixed consistency issues 2015-02-12 13:11:00 -05:00
Winston Chang
b24abffd0d Bump version to 0.11.1.9000 for development 2015-02-12 11:39:31 -06:00
Winston Chang
2ddf578f07 Add NEWS item 2015-02-12 11:39:05 -06:00
Yihui Xie
61b83c3e34 replace `` with \code{} in Rd 2015-02-11 18:12:14 +08:00
Winston Chang
3454ddbb2a Merge tag 'v0.11.1'
Shiny 0.11.1 released to CRAN
2015-02-10 19:42:58 -06:00
Winston Chang
194e4ca70d Update cran comments 2015-02-10 13:12:13 -06:00
Winston Chang
074981a709 Update Date in DESCRIPTION 2015-02-10 13:08:44 -06:00
Winston Chang
f157200a9f Fix Bootstrap URL 2015-02-10 10:58:31 -06:00
Winston Chang
f70fcddaad Clarify docs for shinyServer and shinyUI 2015-02-10 10:46:51 -06:00
Winston Chang
aa2129b60e Bump version to 0.11.1 2015-02-06 11:49:49 -06:00
Winston Chang
3794541828 Update gruntfile to watch for changes in DESCRIPTION 2015-02-06 11:49:30 -06:00
Winston Chang
b1a6dc65ba Remove jsonlite NEWS item 2015-02-06 11:23:50 -06:00
Winston Chang
040ae293fb Revert switch to jsonlite
This reverts commits deffc90, ab4dc64, and 0755579, returning to RJSONIO.
The purpose of this is to prepare for a maintenance release for 0.11
without the switch to jsonlite, to reduce the risk of new bugs.
2015-02-06 10:53:54 -06:00
Winston Chang
b74e93f3a4 Update NEWS and bump version 2015-02-05 15:51:01 -06:00
Winston Chang
dd3cc29af1 Merge pull request #728 from rstudio/bugfix/tab-value-with-icon
Fix #725: Tabs with icons give incorrect values
2015-02-05 15:44:02 -06:00
Joe Cheng
72b9a93088 Don't error when tabPanel has value but tabsetPanel doesn't have id
Causes problems now that tabPanel always has a value
2015-02-05 13:38:04 -08:00
Joe Cheng
4446f0a1f3 Merge pull request #729 from wch/feature/select-size
selectInput: add control for size (height)
2015-02-05 13:34:40 -08:00
Winston Chang
35802c12f6 selectInput: add control for size (height) 2015-02-05 15:30:03 -06:00
Winston Chang
f5fd30883a Update NEWS 2015-02-05 15:28:09 -06:00
Winston Chang
f8e720d6dc Fix tests for new HTML structure 2015-02-05 15:25:09 -06:00
Winston Chang
9f8f04caec selectInput: use form-control for multiple and single 2015-02-05 14:51:40 -06:00
Winston Chang
69aec85882 Fix label spacing for inline checkbox and radio 2015-02-05 12:46:54 -06:00
Winston Chang
e84012ec38 Update input docs to explain NULL label (#727) 2015-02-05 12:40:14 -06:00
Joe Cheng
128df6a2bf Fix #725: Tabs with icons give incorrect values
When no explicit value argument was present on tabPanel,
the inner text of the HTML anchor was used. This gave
weird results when an icon was used, since it appears in
the anchor.

The fix is to always use the value parameter, defaulting
it to the title.
2015-02-05 10:23:04 -08:00
Winston Chang
7b76951461 navbarPage: Don't include empty row when header=NULL. Fixes #722 2015-02-05 10:18:24 -06:00
Winston Chang
38d87450e6 Fix typo in docs 2015-02-05 10:17:42 -06:00
Winston Chang
b9c6b84ff5 selectInput: use BS 3 styling when multiple && !selectize. Closes #724 2015-02-04 19:45:04 -06:00
Winston Chang
5ba1d57b1f Update NEWS and bump version 2015-02-04 13:32:27 -06:00
Winston Chang
a4d567e44d Merge pull request #704 from wch/html-replace2
htmlOutput: allow custom container function
2015-02-04 13:30:19 -06:00
Joe Cheng
19a62ba7e9 Merge pull request #723 from rstudio/bugfix/namespace-filtering
Fix oversubscription of shown/hidden events
2015-02-04 10:20:42 -08:00
Winston Chang
65d6c7b558 Minify shiny.js 2015-02-04 12:17:30 -06:00
Winston Chang
8c828e7881 Correct event namespace filtering 2015-02-04 12:15:23 -06:00
Winston Chang
b711780688 Debounce sendImageSize 2015-02-04 12:15:23 -06:00
Joe Cheng
855cdc8eda Fix oversubscription of shown/hidden events
Bootstrap 3 shown/hidden events look like "shown.bs.modal",
"shown.bs.popover", etc. When subscribing to these events, you also
automatically pick up "shown" and "hidden" events. This causes a 6X
oversubscription of hidden and shown events. We fix this by filtering
out these overbroad events.

Also, don't do anything if the show/hide would be a no-op; this
also helps cut down on extraneous events.
2015-02-04 12:15:23 -06:00
Winston Chang
c930cce914 Check for unnamed arguments in ... 2015-02-03 15:55:24 -06:00
Winston Chang
424696cf96 Update NEWS 2015-02-03 10:11:34 -06:00
Joe Cheng
ea4ef45289 Merge pull request #715 from rstudio/bugfix/download-ext
downloadHandler temp files should have same extension as final download
2015-02-02 10:27:07 -08:00
Winston Chang
d2e13e6ac6 Merge pull request #719 from aronatkins/typo_04_mpg
fix "formula" spelling in 04_mpg example
2015-02-02 09:47:19 -06:00
Aron Atkins
70ff6658eb fix spelling 2015-02-02 10:09:08 -05:00
Joe Cheng
3e1732b9c0 downloadHandler temp files should have same extension as final download file 2015-02-01 08:37:44 -08:00
Winston Chang
4eb18dc797 Update NEWS 2015-01-28 15:43:23 -06:00
Winston Chang
6e7adfadb0 Merge pull request #710 from rstudio/bugfix/fixedpanel-cursor
Fix fixedPanel default cursor handling
2015-01-28 14:00:07 -06:00
Winston Chang
a866224f7b Switch to modified ion.rangeSlider 2.0.3. Closes #711
This includes the stopPropagation change, and the Shiny skin.
2015-01-28 13:56:31 -06:00
Winston Chang
77c50d6880 Update shiny.min.js 2015-01-28 11:36:28 -06:00
Winston Chang
4290e2e7b0 Merge pull request #702 from wch/select-width
Allow control over selectize width
2015-01-28 11:17:06 -06:00
Winston Chang
4e8632de73 Better documentation of plotOutput height (#705) 2015-01-28 11:15:08 -06:00
Joe Cheng
2792fe82a3 Fix fixedPanel default cursor handling
Previous to this fix, calling fixedPanel without an
explicit cursor argument would cause an error due to
the vector of possible values being passed as the
actual cursor argument to absolutePanel.
2015-01-27 18:41:29 -08:00
Winston Chang
ab6df9f8a9 Remove unused width control in shiny.js 2015-01-26 22:31:31 -06:00
Winston Chang
425489c74b Control width for non-selectize select inputs 2015-01-26 22:31:30 -06:00
Winston Chang
9f04517801 htmlOutput: allow custom container function 2015-01-26 16:50:09 -06:00
Winston Chang
9954639096 Allow control over selectize width
Previously, you could set the width of a selectInput with a pixel value
like 500px, but it wouldn't respect percent widths like 100%. This
commit makes width control the wrapper container's width, so it will
respect percent values.
2015-01-26 15:35:02 -06:00
Winston Chang
cd78364c17 Merge pull request #698 from daattali/patch-1
parseQueryString returns a list, not a vector
2015-01-25 21:05:17 -06:00
Dean Attali
563b986591 parseQueryString returns a list, not a vector 2015-01-24 20:42:34 -08:00
Winston Chang
fe5552773d sliderInput: use width argument (#675) 2015-01-23 15:30:20 -06:00
Winston Chang
b7acaf9519 Convert donttest examples; check for interactive() instead 2015-01-22 13:21:23 -06:00
Winston Chang
deffc90531 Remove remaining RJSONIO references 2015-01-22 13:15:01 -06:00
Joe Cheng
f54a6e8513 Add IE=edge to X-UA-Compatible header 2015-01-22 10:52:27 -08:00
Winston Chang
ab4dc641af Make jsonlite::toJSON convert all objects
By default, toJSON won't convert objects with classes that aren't registered
explicitly. This caused errors when converting html_dependency objects.
Setting this option causes jsonlite to convert all objects, including
html_dependency objects.
2015-01-21 15:09:12 -06:00
Winston Chang
dfe725dd78 Bump version and update NEWS 2015-01-21 14:16:40 -06:00
Winston Chang
dbe1e24561 Merge pull request #606 from wch/feature/jsonlite
Switch from RJSONIO to jsonlite
2015-01-21 14:14:46 -06:00
Winston Chang
075557929a Switch from RJSONIO to jsonlite 2015-01-21 14:12:53 -06:00
Winston Chang
d994db9c74 Add other authors to DESCRIPTION 2015-01-20 15:19:54 -06:00
Winston Chang
c462c61ac9 Bump version to 0.11.0.9000 for development 2015-01-20 15:19:40 -06:00
Winston Chang
1ef9719059 Merge tag 'v/0/11'
Shiny 0.11 released to CRAN
2015-01-18 22:25:11 -06:00
Winston Chang
3df0e7899c Update cran-comments 2015-01-15 23:49:47 -08:00
Winston Chang
927972d889 More license updates 2015-01-15 23:34:58 -08:00
bhsu
00f409d21d explicit parameter check 2015-01-14 13:34:27 -05:00
bhsu
6aea95b486 fixed application exit
fixed on.exit behavior
2015-01-14 09:59:57 -05:00
bhsu
e4dd78b9a4 renamed to destDir and documentation
fixed typo
2015-01-14 09:59:38 -05:00
B. Hsu
d940d5a597 updated run-url.R
added ability for user to change destination file when using runUrl, runGist or runGitHub

added messages

added messages informing user of final destination of app files
2015-01-13 16:47:53 -05:00
Winston Chang
5f1ea21be1 Add cran-comments file 2015-01-12 12:04:26 -06:00
Winston Chang
278851224d Re-document with roxygen2 4.1.0 2015-01-12 12:02:24 -06:00
Winston Chang
0161bc5a4e Update LICENSE information 2015-01-12 12:02:24 -06:00
Winston Chang
9cf5168d3f Merge branch 'master' into v0.11-rc 2015-01-11 17:54:51 -06:00
Winston Chang
f066a4bdf4 Negative margin for checkboxes/radios only after label (#678) 2015-01-09 14:48:16 -06:00
Winston Chang
0009756ed9 Bump version to 0.11 2015-01-08 14:55:36 -06:00
Winston Chang
481e5437f1 Add CONTRIBUTING.md to .Rbuildignore 2015-01-08 14:55:10 -06:00
Winston Chang
238f54e011 Change name of shinyBootstrap2 package to shinybootstrap2 2015-01-08 14:55:00 -06:00
Winston Chang
180525f538 Merge pull request #685 from rstudio/bugfix/qt-radio-check
Suppress Bootstrap top margin around radios and checkboxes in Qt5
2015-01-08 13:47:56 -06:00
Jonathan McPherson
9a1958d19a restrict changes to Linux (Qt5 on Windows is unaffected) 2015-01-08 11:03:30 -08:00
Jonathan McPherson
e1469a1e44 suppress Bootstrap top margin around radios and checkboxes in Qt5 2015-01-08 10:49:16 -08:00
Winston Chang
85bde2b00d Fix reference to old shiny version number 2015-01-07 22:03:40 -06:00
Winston Chang
b20a15b4ed Fix label class for non-inline checkbox and radios 2015-01-07 21:06:37 -06:00
Yihui Xie
a67161f521 add a pointer to the DT package 2015-01-06 14:26:19 -06:00
Yihui Xie
8d621e87a0 should inherit params from textInput instead of passwordInput 2015-01-06 14:17:57 -06:00
Winston Chang
a560034614 Fix another URL 2015-01-06 13:21:57 -06:00
Winston Chang
b329c1f94c Fix URL 2015-01-06 11:33:07 -06:00
Winston Chang
06459c0f52 Remove duplicate text in reactive domain docs 2015-01-05 11:23:05 -06:00
Winston Chang
2655c2511d Fix docs for slider step size 2015-01-05 11:20:38 -06:00
Winston Chang
fa9fdb01b0 Add info about using shinyApp and runApp together 2015-01-02 11:47:51 -06:00
Winston Chang
4896948d38 Update NEWS 2015-01-02 11:40:36 -06:00
Winston Chang
133a37817e Fix class for radio buttons 2014-12-19 15:18:54 -06:00
Winston Chang
0cdee5ed7a Add staticdocs entry for passwordInput 2014-12-15 13:09:35 -06:00
Winston Chang
2c21989b47 Don't limit width of inline inputs. Fixes #675 2014-12-15 12:22:31 -06:00
Winston Chang
17f796847d Use correct class for inline checkbox/radio label 2014-12-15 12:22:31 -06:00
Joe Cheng
02578dbf3a Fix tabset input; event changed for bootstrap 3 2014-12-14 09:59:23 -08:00
Yihui Xie
9f86e84830 closes #668: emphasize that I() does not work for non-root level elements in the options list in renderDataTable() 2014-12-13 22:52:28 -06:00
Winston Chang
1ae65bb999 Fix URL 2014-12-12 22:35:00 -06:00
Winston Chang
ca72f5fa99 Update devtools install instructions 2014-12-12 17:01:42 -06:00
Winston Chang
bc9b537d84 Merge pull request #673 from wch/handler-domain
Make default reactive domain available in request handlers. Fixes #669
2014-12-12 14:03:24 -06:00
Winston Chang
d9336d0ba2 Make default reactive domain available in request handlers. Fixes #669 2014-12-12 10:19:25 -06:00
Joe Cheng
eb537eaa24 Merge pull request #672 from hadley/input-types
Add support for more html 5 input types
2014-12-11 22:23:05 -08:00
hadley
f1d814ba04 Implement passwordInput in the same style as textInput() 2014-12-11 16:21:34 -06:00
hadley
760d360e96 Listen for events on all html 5 text inputs types 2014-12-11 16:21:11 -06:00
Winston Chang
5431fa1405 Remove unused jslider css entries 2014-12-11 10:24:29 -06:00
Winston Chang
eb809e388b withProgress: check that session is a session object 2014-12-10 15:49:32 -06:00
Winston Chang
0205ad7a4f Fix appearance of download button 2014-12-10 11:01:11 -06:00
Winston Chang
9599b14aa8 Add note about tools in main README 2014-12-09 14:43:28 -06:00
Winston Chang
d72585e3d1 Make JSHint more lenient 2014-12-09 13:38:46 -06:00
Winston Chang
735a15d7c6 Merge tag 'v/0/10/2.2'
Shiny version 0.10.2.2

Conflicts:
	DESCRIPTION
2014-12-08 16:51:37 -06:00
Winston Chang
0984e6ed90 Clean up importBootstrap code 2014-12-08 16:37:39 -06:00
Winston Chang
6004a4250a README edits 2014-12-08 16:29:52 -06:00
Winston Chang
b39fcc75d0 Add minified shiny.js 2014-12-08 16:23:02 -06:00
Winston Chang
a401553c13 Re-minify datepicker 2014-12-08 15:56:19 -06:00
Winston Chang
a312abff31 Don't run shiny.js minification 2014-12-08 15:56:19 -06:00
Winston Chang
92c1722469 Simplify npm install instructions 2014-12-08 15:56:19 -06:00
Winston Chang
e48136d31e Add grunt watch task 2014-12-08 15:56:19 -06:00
Winston Chang
b9d6f6fd9c Update tools/README 2014-12-08 15:56:19 -06:00
Winston Chang
66c8dfe44d Add jshint task 2014-12-08 15:56:19 -06:00
Winston Chang
5910c936b9 Use grunt for minification of datepicker 2014-12-08 15:56:19 -06:00
Winston Chang
a2645061fe Add Gruntfile 2014-12-08 15:56:19 -06:00
Winston Chang
7b887d2fd5 Bump version 2014-12-08 11:34:52 -06:00
Winston Chang
8d2367ed82 Remove use of rstudio::viewer for R CMD check 2014-12-08 11:33:56 -06:00
Joe Cheng
075583f4be Use minified jquery 2014-12-08 09:17:04 -08:00
Winston Chang
ce86e0a54a Re-document 2014-12-08 10:10:17 -06:00
Winston Chang
9308f8ea69 Fix documentation typo 2014-12-08 10:10:09 -06:00
Winston Chang
cc8b425adc Update license information 2014-12-05 14:49:24 -06:00
Winston Chang
25cf7955c9 Bump version to 0.10.2.9006 2014-12-05 14:31:06 -06:00
Winston Chang
d393865efc Update Bootstrap link 2014-12-05 14:30:46 -06:00
Winston Chang
9d877320d8 Merge pull request #658 from wch/feature/ionslider
New sliders
2014-12-05 14:30:07 -06:00
Winston Chang
1f72bef393 Update license information for ion.rangeSlider 2014-12-05 14:28:21 -06:00
Winston Chang
99c1069229 Add instructions for installing old version 2014-12-05 10:33:40 -06:00
Winston Chang
28718429e3 Fix arrows for date inputs. Fixes #663
The date inputs used css classes like icon-arrow-right, which no longer works
with font-awesome 4.x. This change uses glyphicon arrows instead, which are
included with Bootstrap and therefore don't need a separate HTML dependency.
2014-12-02 09:53:11 -06:00
Winston Chang
e145021760 Update to ion.rangeSlider 2.0.2 (with shiny mods) 2014-12-02 09:21:47 -06:00
Winston Chang
b7dd51c594 Fix case of directory 2014-12-02 09:08:29 -06:00
Winston Chang
9ebc3e5eec Fix file input appearance for Bootstrap 3 2014-11-26 19:34:27 -06:00
Winston Chang
4db60d7b27 Merge pull request #655 from wch/showcase-dep
Dynamically get font-awesome dependency from showcase body
2014-11-26 16:57:25 -06:00
Winston Chang
5d85199ae3 Dynamically get font-awesome dependency from showcase body 2014-11-26 16:54:27 -06:00
Winston Chang
088c9e5450 Better help for reactiveValues. Fixes #620 2014-11-26 15:28:16 -06:00
Winston Chang
bd51d1b2a2 Update NEWS 2014-11-26 14:20:25 -06:00
Winston Chang
0de7b6f63a Remove web resources for jslider 2014-11-26 14:20:25 -06:00
Winston Chang
85706eac84 Re-document 2014-11-26 14:20:25 -06:00
Winston Chang
96b646b94a Rename slider2Input to replace sliderInput 2014-11-26 14:20:25 -06:00
Winston Chang
753f6fc671 Update heuristic for step size 2014-11-26 14:19:06 -06:00
Winston Chang
3f186747ed Add keyboard support for sliders 2014-11-26 14:19:06 -06:00
Winston Chang
e6748c0793 Re-document 2014-11-26 14:19:06 -06:00
Winston Chang
2383796c49 Add slider2Input documentation 2014-11-26 14:19:06 -06:00
Winston Chang
fb38ab4ef2 Improve tick and step heuristics 2014-11-26 14:19:06 -06:00
Winston Chang
1242a62bca Set HTML data values to "true" and "false" 2014-11-26 14:19:06 -06:00
Winston Chang
55ceb12277 Simplify label code for slider2Input 2014-11-26 14:19:05 -06:00
Winston Chang
6dd4e7676d Add deprecation message for format argument 2014-11-26 14:19:05 -06:00
Winston Chang
4abb58b239 Add format options to sliders 2014-11-26 14:19:05 -06:00
Winston Chang
385dab6036 Add script for updating ion.rangeSlider 2014-11-26 14:19:05 -06:00
Winston Chang
2e9291decf Add QT-webkit touch detection workaround
This is from commit 813939b of ion.rangeSlider
2014-11-26 14:19:05 -06:00
Winston Chang
623036f03b Add updateSlider2Input 2014-11-26 14:19:05 -06:00
Winston Chang
98075e9d9d Add animation to ionsliders 2014-11-26 14:19:05 -06:00
Winston Chang
b80ce0e222 Add customized ionslider skin for Shiny 2014-11-26 14:19:05 -06:00
Winston Chang
b0162efb1f Add slider2Input 2014-11-26 14:19:05 -06:00
Winston Chang
7b3e9ab031 Limit width of inputs 2014-11-26 14:16:40 -06:00
Winston Chang
b35e866d95 Fix appearance of DataTables examples 2014-11-26 12:49:59 -06:00
Winston Chang
2d273d1550 Merge pull request #646 from yihui/feature/DT-1.10.3
upgrade to DataTables 1.10.4 and use Bootstrap 3
2014-11-26 12:44:39 -06:00
Yihui Xie
6aef3eb879 update the version info of DataTables 2014-11-26 10:15:18 -06:00
Winston Chang
61b0393657 Update tests for new select input structure 2014-11-26 10:09:27 -06:00
Winston Chang
30bf6c0714 Wrap select inputs in div 2014-11-26 09:51:22 -06:00
Winston Chang
fc64b115d3 Fix DOM structure for selectize inputs 2014-11-26 09:42:00 -06:00
Yihui Xie
bdc3f24ca6 the css of the processing indicator seems to be okay with Bootstrap 3 now 2014-11-26 00:13:39 -06:00
Joe Cheng
b28300b184 Merge pull request #660 from wch/body-tag
Allow running apps that have a body tag
2014-11-25 11:49:29 -08:00
Winston Chang
5c1f9d03e4 Update version and NEWS 2014-11-25 13:46:22 -06:00
Winston Chang
ed368a225d Refactor showcase ui code to handle body tags 2014-11-25 13:46:22 -06:00
Winston Chang
2f17f392d5 If UI has body tag, don't wrap in another body 2014-11-25 13:46:21 -06:00
Yihui Xie
b00a6e526e Bootstrap 3 for DataTables 2014-11-25 00:01:13 -06:00
Yihui Xie
75031df45b upgrade to DataTables 1.10.4
need the bugfix DataTables/DataTables#391
2014-11-25 00:00:55 -06:00
Winston Chang
39148a99d0 Make sure progress bars draw above other Bootstrap components 2014-11-24 13:52:08 -06:00
Winston Chang
d72f5ee2ad Better examples and docs for renderTable and renderDataTable 2014-11-24 11:24:26 -06:00
Winston Chang
c6ea459a68 Bump version to 0.10.2.9004 2014-11-21 08:26:05 -06:00
Joe Cheng
e8525e5513 Merge pull request #611 from rstudio/feature/bootstrap3
Switch to Bootstrap 3
2014-11-20 14:38:09 -08:00
Winston Chang
9046b85ca4 Fix appearance of date ranges 2014-11-20 14:50:18 -06:00
Winston Chang
19257b76d2 Fix spacing of checkbox input 2014-11-20 14:27:06 -06:00
Winston Chang
fd8990b294 Fix appearance of labels for checkbox group and radio buttons 2014-11-20 13:55:53 -06:00
Winston Chang
eb34f5e058 Don't limit text/number/select input width 2014-11-20 13:48:19 -06:00
Winston Chang
f8ced7e065 Merge pull request #656 from andeek/master
fixed iconClass function
2014-11-20 13:15:17 -06:00
Winston Chang
bf0a2d3c6c Fixes for progress bars with Bootstrap 3 2014-11-18 21:26:39 -06:00
Winston Chang
f5878326f7 Add CONTRIBUTING file 2014-11-18 10:50:48 -06:00
Winston Chang
1f0b439168 Add meta viewport tag 2014-11-17 16:13:55 -06:00
Winston Chang
3464e11152 Modify HTML5 shims so they are handled by htmlDependency
This adds conditional JS code so that the shim code isn't actually run
in browsers other than IE 8.
2014-11-17 16:13:55 -06:00
Winston Chang
b5f99be635 Add HTML5 shims for Bootstrap 3 to work with IE8 2014-11-17 14:43:35 -06:00
Winston Chang
3153b35c2a icon: add support for glyphicon library 2014-11-17 14:43:35 -06:00
Winston Chang
c450f9abc2 Re-document 2014-11-17 14:43:35 -06:00
Winston Chang
912434b259 Update navlistPanel for Bootstrap 3 2014-11-17 14:43:35 -06:00
Winston Chang
5f61fc658a actionButton: add btn-default class, for Bootstrap 3 2014-11-17 14:43:35 -06:00
Winston Chang
a268b74636 Update navbarPage for Bootstrap 3 2014-11-17 14:43:34 -06:00
Winston Chang
82a1bf7afd Deprecate 'collapsable' in favor of 'collapsible' 2014-11-17 14:43:34 -06:00
Winston Chang
4392676667 Add Bootstrap 3 note to README 2014-11-17 14:43:34 -06:00
Winston Chang
c47ec2c3c0 Register callbacks for each Bootstrap 3 class 2014-11-17 14:43:34 -06:00
Winston Chang
e756fc8daa Update NEWS 2014-11-17 14:43:34 -06:00
Winston Chang
4629262a1a Re-document 2014-11-17 14:43:34 -06:00
Winston Chang
741aec198b Fix inline checkboxGroupInputs and radioButtons 2014-11-17 14:43:34 -06:00
Winston Chang
c3e5b4fa36 Set width of divs inside of flowLayout 2014-11-17 14:43:34 -06:00
Winston Chang
d022907bcd Update showcase CSS classes for Bootstrap 3 2014-11-17 14:43:34 -06:00
Winston Chang
8a7603a45c Update slider animation buttons for Bootstrap 3 2014-11-17 14:43:34 -06:00
Winston Chang
2ade82d883 Fix headerPanel spacing
The titlepanel spacing also is unnecessary now with Bootstrap 3.
2014-11-17 14:43:34 -06:00
Winston Chang
3d8c38b558 Reduce spacing above sliders 2014-11-17 14:43:34 -06:00
Winston Chang
0f2d0a33b4 Adjust CSS for slider and selectize
This makes sliders and selectize inputs have width 100%, just like other
inputs with Bootstrap 3.
2014-11-17 14:43:34 -06:00
Winston Chang
d4ce1499cc Wrap inputs in div with class 'form-group' to maintain bottom margin 2014-11-17 14:43:33 -06:00
Winston Chang
9ab7895772 Fix appearance of date inputs 2014-11-17 14:43:33 -06:00
Winston Chang
ee9f79e287 Update selectize's CSS to use Bootstrap 3 2014-11-17 14:43:33 -06:00
Winston Chang
ad61dafb9a Correctly find checkboxes and radio buttons with Bootstrap 3 2014-11-17 14:43:33 -06:00
Winston Chang
b81361ff6d Update tests for new DOM structure of choices 2014-11-17 14:43:33 -06:00
Winston Chang
52417c0a60 Fix appearance of inputs 2014-11-17 14:43:33 -06:00
Winston Chang
000beeb737 Update tab shown/hidden events for Bootstrap 3 2014-11-17 14:43:33 -06:00
Winston Chang
7cce94000b Switch from Bootstrap 2 to 3 2014-11-17 14:43:33 -06:00
Andee Kaplan
7a4bdb4191 fixed iconClass function 2014-11-17 14:08:04 -06:00
Winston Chang
279a8ff989 Make font-awesome a dependency for icon() 2014-11-13 15:04:30 -06:00
Winston Chang
ed06a05ff0 Update font-awesome to 4.2.0 2014-11-13 14:06:45 -06:00
Joe Cheng
a56ac24cc2 Merge pull request #653 from yihui/bugfix/DT-encoding
encode the JSON response for DataTables/selectize with UTF-8
2014-11-13 09:03:42 -08:00
Yihui Xie
c8efd31d08 encode the JSON response for DataTables/selectize with UTF-8
to make sure multibyte characters in DataTables/selectize work
2014-11-13 10:27:56 -06:00
Winston Chang
d8875151e8 Un-normalize Progress values
This fixes an issue where Progress objects with a min and max other than
0 and 1 would not display properly.
2014-11-12 22:15:24 -06:00
Winston Chang
6082431161 Fixes for jshint 2014-11-03 15:26:59 -06:00
Joe Cheng
846c23a5a2 Merge remote-tracking branch 'origin/feature/event-reactives'
Conflicts:
	inst/tests/test-reactivity.r
2014-10-30 14:52:39 -07:00
Joe Cheng
9a5faa92c4 Refactor for code review feedback 2014-10-30 14:50:30 -07:00
Winston Chang
f149be26ce Add shiny-options to staticdocs index 2014-10-30 11:37:31 -05:00
Yihui Xie
c6d6ca5a5c bump version 2014-10-29 12:34:35 -05:00
Joe Cheng
98dc8069e3 Merge pull request #627 from yihui/bugfix/DT-XSS
escape HTML entities to avoid XSS attacks in DataTables
2014-10-29 10:29:21 -07:00
Winston Chang
32f5d68e51 Merge pull request #638 from wch/option-docs
Add documentation for global options
2014-10-29 11:31:09 -05:00
Winston Chang
855347a5d3 Add documentation for global options 2014-10-29 11:30:28 -05:00
Yihui Xie
78c22cb74a a news item for #630 2014-10-28 16:17:05 -05:00
Yihui Xie
8626a3efea make escape support column names in renderDataTable(), per suggestion of @jcheng5 2014-10-28 16:11:06 -05:00
Yihui Xie
d7e0d64ac2 fix the issue reported by Tareef: dplyr tbl_df objects are also data frames, but the default non-standard evaluation df[, j] will fail in dataTablesJSON(), so we need to coerce such objects to data frames explicitly 2014-10-28 16:11:06 -05:00
Yihui Xie
e26b1e54c8 a news item for #627 [ci skip] 2014-10-28 16:11:06 -05:00
Yihui Xie
594024472d apply escape to column names as well 2014-10-28 16:11:06 -05:00
Yihui Xie
5f60d09d0b per-column escaping 2014-10-28 16:11:06 -05:00
Yihui Xie
a05abda25c escape HTML entities to avoid XSS attacks in DataTables
BTW, per recommendation of http://datatables.net/manual/server-side, the parameter `draw` is coerced to integer
2014-10-28 16:11:06 -05:00
Winston Chang
daa8e44180 Update NEWS 2014-10-28 15:52:13 -05:00
Winston Chang
837a26daee Bump version to 0.10.2.9002 2014-10-28 15:48:47 -05:00
Joe Cheng
1223d3a2b3 Merge pull request #604 from rstudio/bugfix/observer-errors
Better handling of observer errors
2014-10-28 13:12:13 -07:00
Joe Cheng
a4d7eaf709 Merge pull request #630 from yihui/feature/httpuv-encode
use encodeURI[Component]() and decodeURIComponent() from httpuv
2014-10-28 13:08:04 -07:00
Yihui Xie
4b12d5527b increase version requirement of httpuv 2014-10-28 14:13:09 -05:00
Joe Cheng
e7553d6f15 Add observeEvent and eventReactive functions 2014-10-23 12:03:45 -07:00
Yihui Xie
b795d7de4e use encodeURI[Component]() and decodeURIComponent() from httpuv 2014-10-20 18:15:25 -05:00
Joe Cheng
e732afd64c Merge remote-tracking branch 'origin/feature/bootstrap-navbar-pos' 2014-10-20 13:16:42 -07:00
Winston Chang
40b25e3826 Merge pull request #597 from rstudio/selectize-0.11.0
Update to selectize 0.11.0
2014-10-20 15:13:13 -05:00
Winston Chang
b4d80b467a Merge pull request #610 from yihui/bugfix/DT-POST
fixes #605: send DT requests via POST instead of the default GET
2014-10-20 15:11:59 -05:00
Joe Cheng
62ef377dd1 Remove eventFilter for now
After discussing with @trestletech, the semantics are just not right.
When using it with a renderXXX function, it doesn't actually stop
recomputation, so the only reasonable place to use it is in a reactive
expression, but if that's true then this is a terrible looking API.
Will replace soon with something better.
2014-10-14 12:36:03 -07:00
Joe Cheng
b85b1e95b4 Fix documentation bug in ?validate 2014-10-14 12:30:11 -07:00
Joe Cheng
f2ab7001d2 Fix eventFilter logic 2014-10-14 10:10:08 -07:00
Joe Cheng
7fa6a2e9ef Fix action button 2014-10-14 09:25:52 -07:00
Joe Cheng
c499bbaa9f Update NEWS, bump version 2014-10-13 14:28:44 -07:00
Joe Cheng
f2b16c414f Merge remote-tracking branch 'origin/pr/180'
Conflicts:
	NAMESPACE
	R/bootstrap.R
	R/reactives.R
	R/shiny.R
2014-10-13 14:24:44 -07:00
Winston Chang
2a73e28b19 Merge pull request #615 from seanchrismurphy/patch-1
Update shiny.R
2014-10-12 09:21:58 -05:00
Sean Murphy
9252d1290c Update shiny.R
Update link to shiny tutorial (http://shiny.rstudio.com/tutorial/)
2014-10-12 22:16:28 +10:00
Yihui Xie
049bd85b29 fixes #605: send DT requests via POST instead of the default GET 2014-10-07 17:17:48 -05:00
Winston Chang
f95a0fa1d3 Update NEWS 2014-10-07 09:45:27 -05:00
Winston Chang
416466a0e1 Bump version to 0.10.2.9000 2014-10-07 09:44:19 -05:00
Winston Chang
bfd97c5484 Remove explicit dependency install for travis 2014-10-06 13:15:16 -05:00
Winston Chang
9c1badd6aa Fix test to avoid printing message
The recent release of testthat now displays messages in test blocks, which revealed that
this test needed to be updated.
2014-10-06 12:33:09 -05:00
Winston Chang
929cb29c5b Add global option for error handler function 2014-10-06 12:26:03 -05:00
Winston Chang
1e788962ef Merge pull request #607 from saurfang/patch-1
Use ... in actionButton and actionLink
2014-10-06 11:51:52 -05:00
saurfang
26f8c11526 Add ... to actionButton and actionLink 2014-10-04 13:00:29 -04:00
Winston Chang
a9b3922061 Remove unused withCallingHandlers() 2014-10-03 16:19:03 -05:00
Winston Chang
00ebe4fab2 Add test for observer error handling 2014-10-03 16:17:49 -05:00
Joe Cheng
1bee6aece7 Better handling of observer errors
- Unhandled observer error no longer stops flushReact in mid-flush
- Observer errors are now warnings that include the observer label
- Session is explicitly being closed by Shiny instead of by httpuv

Also:
- session$close() is now public and closes the session
2014-10-02 11:13:23 -07:00
Winston Chang
f931222256 Update NEWS 2014-09-29 15:21:43 -05:00
Winston Chang
25ff7d430d Bump selectize version in html dependencies 2014-09-29 15:21:43 -05:00
Winston Chang
d9cf3ceb9b Update to Selectize 0.11.2 with computedstyle fix
Fixes #596
2014-09-29 15:21:43 -05:00
Joe Cheng
ab86232294 Add position parameter to navbarPage 2014-09-10 22:59:35 -07:00
Joe Cheng
49a08d14c3 Code review feedback, part 1 2013-06-19 11:39:09 -07:00
Joe Cheng
25990f59d8 Add observeEvent and eventFilter functions for handling events 2013-06-18 18:42:00 -07:00
647 changed files with 96146 additions and 41780 deletions

View File

@@ -6,8 +6,17 @@
^shiny\.cmd$
^run\.R$
^\.gitignore$
^smoketests$
^res$
^man-roxygen$
^\.travis\.yml$
^staticdocs$
^tools$
^srcjs$
^CONTRIBUTING.md$
^cran-comments.md$
^.*\.o$
^appveyor\.yml$
^revdep$
^TODO-promises.md$
^manualtests$

3
.gitattributes vendored
View File

@@ -1 +1,4 @@
/NEWS merge=union
/inst/www/shared/shiny.js -merge -diff
*.min.js -merge -diff
*.js.map -merge -diff

2
.gitignore vendored
View File

@@ -8,3 +8,5 @@
/src-x86_64/
shinyapps/
README.html
.*.Rnb.cached
tools/yarn-error.log

View File

@@ -1,29 +1,12 @@
# it is not really python, but there is no R support on Travis CI yet
language: python
language: r
r:
- oldrel
- release
- devel
sudo: false
cache: packages
# environment variables
env:
- R_LIBS_USER=~/R R_MY_PKG="$(basename $TRAVIS_REPO_SLUG)"
# install dependencies
install:
- sudo apt-add-repository -y "deb http://cran.rstudio.com/bin/linux/ubuntu `lsb_release -cs`/"
- sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E084DAB9
- sudo apt-add-repository -y ppa:marutter/c2d4u
- sudo apt-get -qq update
- sudo apt-get -qq install r-base r-cran-shiny r-cran-cairo r-cran-markdown r-cran-knitr
- "[ ! -d ~/R ] && mkdir ~/R"
- echo "options(repos = c(CRAN = 'http://cran.rstudio.com'))" > ~/.Rprofile
- Rscript -e "install.packages(c('mime'), quiet = TRUE, repos = 'http://rforge.net')"
- Rscript -e "install.packages(c('xtable', 'R6'), quiet = TRUE)"
- Rscript -e "update.packages(instlib = '~/R', ask = FALSE, quiet = TRUE)"
- Rscript -e "install.packages('$R_MY_PKG', dep = TRUE, quiet = TRUE)"
# run tests
script:
- cd ..; rm -f *.tar.gz; R CMD build $R_MY_PKG
- R CMD check $R_MY_PKG*.tar.gz --no-manual
after_failure:
- cat $R_MY_PKG.Rcheck/00install.out || true
- cat $R_MY_PKG.Rcheck/00check.log || true
notifications:
email:
on_success: change
on_failure: change

38
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,38 @@
We welcome contributions to the **shiny** package. To submit a contribution:
1. [Fork](https://github.com/rstudio/shiny/fork) the repository and make your changes.
2. Ensure that you have signed the [individual](https://rstudioblog.files.wordpress.com/2017/05/rstudio_individual_contributor_agreement.pdf) or [corporate](https://rstudioblog.files.wordpress.com/2017/05/rstudio_corporate_contributor_agreement.pdf) contributor agreement as appropriate. You can send the signed copy to jj@rstudio.com.
3. Submit a [pull request](https://help.github.com/articles/using-pull-requests).
We generally do not merge pull requests that update included web libraries (such as Bootstrap or jQuery) because it is difficult for us to verify that the update is done correctly; we prefer to update these libraries ourselves.
## How to make changes
Before you submit a pull request, please do the following:
* Add an entry to NEWS.md concisely describing what you changed.
* If appropriate, add unit tests in the tests/ directory.
* If you made any changes to the JavaScript files in the srcjs/ directory, make sure you build the output JavaScript files. See tools/README.md file for information on using the build system.
* Run Build->Check Package in the RStudio IDE, or `devtools::check()`, to make sure your change did not add any messages, warnings, or errors.
Doing these things will make it easier for the Shiny development team to evaluate your pull request. Even so, we may still decide to modify your code or even not merge it at all. Factors that may prevent us from merging the pull request include:
* breaking backward compatibility
* adding a feature that we do not consider relevant for Shiny
* is hard to understand
* is hard to maintain in the future
* is computationally expensive
* is not intuitive for people to use
We will try to be responsive and provide feedback in case we decide not to merge your pull request.
## Filing issues
If you find a bug in Shiny, you can also [file an issue](https://github.com/rstudio/shiny/issues/new). Please provide as much relevant information as you can, and include a minimal reproducible example if possible.

View File

@@ -1,65 +1,165 @@
Package: shiny
Type: Package
Title: Web Application Framework for R
Version: 0.10.2.1
Date: 2014-08-19
Author: RStudio, Inc.
Maintainer: Winston Chang <winston@rstudio.com>
Description: Shiny makes it incredibly easy to build interactive web
Version: 1.3.0
Authors@R: c(
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@rstudio.com"),
person("Joe", "Cheng", role = "aut", email = "joe@rstudio.com"),
person("JJ", "Allaire", role = "aut", email = "jj@rstudio.com"),
person("Yihui", "Xie", role = "aut", email = "yihui@rstudio.com"),
person("Jonathan", "McPherson", role = "aut", email = "jonathan@rstudio.com"),
person(family = "RStudio", role = "cph"),
person(family = "jQuery Foundation", role = "cph",
comment = "jQuery library and jQuery UI library"),
person(family = "jQuery contributors", role = c("ctb", "cph"),
comment = "jQuery library; authors listed in inst/www/shared/jquery-AUTHORS.txt"),
person(family = "jQuery UI contributors", role = c("ctb", "cph"),
comment = "jQuery UI library; authors listed in inst/www/shared/jqueryui/AUTHORS.txt"),
person("Mark", "Otto", role = "ctb",
comment = "Bootstrap library"),
person("Jacob", "Thornton", role = "ctb",
comment = "Bootstrap library"),
person(family = "Bootstrap contributors", role = "ctb",
comment = "Bootstrap library"),
person(family = "Twitter, Inc", role = "cph",
comment = "Bootstrap library"),
person("Alexander", "Farkas", role = c("ctb", "cph"),
comment = "html5shiv library"),
person("Scott", "Jehl", role = c("ctb", "cph"),
comment = "Respond.js library"),
person("Stefan", "Petre", role = c("ctb", "cph"),
comment = "Bootstrap-datepicker library"),
person("Andrew", "Rowls", role = c("ctb", "cph"),
comment = "Bootstrap-datepicker library"),
person("Dave", "Gandy", role = c("ctb", "cph"),
comment = "Font-Awesome font"),
person("Brian", "Reavis", role = c("ctb", "cph"),
comment = "selectize.js library"),
person("Kristopher Michael", "Kowal", role = c("ctb", "cph"),
comment = "es5-shim library"),
person(family = "es5-shim contributors", role = c("ctb", "cph"),
comment = "es5-shim library"),
person("Denis", "Ineshin", role = c("ctb", "cph"),
comment = "ion.rangeSlider library"),
person("Sami", "Samhuri", role = c("ctb", "cph"),
comment = "Javascript strftime library"),
person(family = "SpryMedia Limited", role = c("ctb", "cph"),
comment = "DataTables library"),
person("John", "Fraser", role = c("ctb", "cph"),
comment = "showdown.js library"),
person("John", "Gruber", role = c("ctb", "cph"),
comment = "showdown.js library"),
person("Ivan", "Sagalaev", role = c("ctb", "cph"),
comment = "highlight.js library"),
person(family = "R Core Team", role = c("ctb", "cph"),
comment = "tar implementation from R")
)
Description: Makes it incredibly easy to build interactive web
applications with R. Automatic "reactive" binding between inputs and
outputs and extensive pre-built widgets make it possible to build
outputs and extensive prebuilt widgets make it possible to build
beautiful, responsive, and powerful applications with minimal effort.
License: GPL-3
License: GPL-3 | file LICENSE
Depends:
R (>= 3.0.0)
R (>= 3.0.2),
methods
Imports:
tools,
utils,
httpuv (>= 1.2.2),
mime (>= 0.1.3),
RJSONIO,
grDevices,
httpuv (>= 1.5.0),
mime (>= 0.3),
jsonlite (>= 0.9.16),
xtable,
digest,
htmltools (>= 0.2.6),
R6 (>= 2.0)
htmltools (>= 0.3.6),
R6 (>= 2.0),
sourcetools,
later (>= 0.7.2),
promises (>= 1.0.1),
tools,
crayon,
rlang
Suggests:
datasets,
Cairo (>= 1.5-5),
testthat,
knitr (>= 1.6),
markdown
markdown,
rmarkdown,
ggplot2,
reactlog (>= 1.0.0),
magrittr
URL: http://shiny.rstudio.com
BugReports: https://github.com/rstudio/shiny/issues
Collate:
'app.R'
'bookmark-state-local.R'
'stack.R'
'bookmark-state.R'
'bootstrap-layout.R'
'map.R'
'globals.R'
'conditions.R'
'map.R'
'utils.R'
'bootstrap.R'
'cache.R'
'cache-context.R'
'cache-disk.R'
'cache-memory.R'
'cache-utils.R'
'diagnose.R'
'fileupload.R'
'stack.R'
'font-awesome.R'
'graph.R'
'reactives.R'
'reactive-domains.R'
'history.R'
'hooks.R'
'html-deps.R'
'htmltools.R'
'image-interact-opts.R'
'image-interact.R'
'imageutils.R'
'input-action.R'
'input-checkbox.R'
'input-checkboxgroup.R'
'input-date.R'
'input-daterange.R'
'input-file.R'
'input-numeric.R'
'input-password.R'
'input-radiobuttons.R'
'input-select.R'
'input-slider.R'
'input-submit.R'
'input-text.R'
'input-textarea.R'
'input-utils.R'
'insert-tab.R'
'insert-ui.R'
'jqueryui.R'
'middleware-shiny.R'
'middleware.R'
'modal.R'
'modules.R'
'notifications.R'
'priorityqueue.R'
'progress.R'
'react.R'
'reactive-domains.R'
'reactives.R'
'render-cached-plot.R'
'render-plot.R'
'render-table.R'
'run-url.R'
'serializers.R'
'server-input-handlers.R'
'server.R'
'shiny-options.R'
'shiny.R'
'shinyui.R'
'shinywrappers.R'
'showcase.R'
'slider.R'
'snapshot.R'
'tar.R'
'test-export.R'
'timer.R'
'update-input.R'
RoxygenNote: 6.1.1
Encoding: UTF-8

1835
LICENSE Normal file

File diff suppressed because it is too large Load Diff

113
NAMESPACE
View File

@@ -1,14 +1,17 @@
# Generated by roxygen2 (4.0.2): do not edit by hand
# Generated by roxygen2: do not edit by hand
S3method("$",reactivevalues)
S3method("$",session_proxy)
S3method("$",shinyoutput)
S3method("$<-",reactivevalues)
S3method("$<-",session_proxy)
S3method("$<-",shinyoutput)
S3method("[",reactivevalues)
S3method("[",shinyoutput)
S3method("[<-",reactivevalues)
S3method("[<-",shinyoutput)
S3method("[[",reactivevalues)
S3method("[[",session_proxy)
S3method("[[",shinyoutput)
S3method("[[<-",reactivevalues)
S3method("[[<-",shinyoutput)
@@ -19,11 +22,18 @@ S3method(as.shiny.appobj,list)
S3method(as.shiny.appobj,shiny.appobj)
S3method(as.tags,shiny.appobj)
S3method(as.tags,shiny.render.function)
S3method(format,reactiveExpr)
S3method(format,reactiveVal)
S3method(names,reactivevalues)
S3method(print,key_missing)
S3method(print,reactive)
S3method(print,shiny.appobj)
S3method(str,reactivevalues)
export("conditionStackTrace<-")
export(..stacktraceoff..)
export(..stacktraceon..)
export(HTML)
export(NS)
export(Progress)
export(a)
export(absolutePanel)
@@ -31,33 +41,62 @@ export(actionButton)
export(actionLink)
export(addResourcePath)
export(animationOptions)
export(appendTab)
export(as.shiny.appobj)
export(basicPage)
export(bookmarkButton)
export(bootstrapLib)
export(bootstrapPage)
export(br)
export(browserViewer)
export(brushOpts)
export(brushedPoints)
export(callModule)
export(captureStackTraces)
export(checkboxGroupInput)
export(checkboxInput)
export(clickOpts)
export(code)
export(column)
export(conditionStackTrace)
export(conditionalPanel)
export(createRenderFunction)
export(createWebDependency)
export(dataTableOutput)
export(dateInput)
export(dateRangeInput)
export(dblclickOpts)
export(debounce)
export(dialogViewer)
export(diskCache)
export(div)
export(downloadButton)
export(downloadHandler)
export(downloadLink)
export(em)
export(enableBookmarking)
export(eventReactive)
export(exportTestValues)
export(exprToFunction)
export(extractStackTrace)
export(fileInput)
export(fillCol)
export(fillPage)
export(fillRow)
export(fixedPage)
export(fixedPanel)
export(fixedRow)
export(flowLayout)
export(fluidPage)
export(fluidRow)
export(formatStackTrace)
export(freezeReactiveVal)
export(freezeReactiveValue)
export(getCurrentOutputInfo)
export(getDefaultReactiveDomain)
export(getQueryString)
export(getShinyOption)
export(getUrlHash)
export(h1)
export(h2)
export(h3)
@@ -66,8 +105,11 @@ export(h5)
export(h6)
export(headerPanel)
export(helpText)
export(hideTab)
export(hoverOpts)
export(hr)
export(htmlOutput)
export(htmlTemplate)
export(icon)
export(imageOutput)
export(img)
@@ -78,14 +120,21 @@ export(includeMarkdown)
export(includeScript)
export(includeText)
export(inputPanel)
export(insertTab)
export(insertUI)
export(installExprFunction)
export(invalidateLater)
export(is.key_missing)
export(is.reactive)
export(is.reactivevalues)
export(is.shiny.appobj)
export(is.singleton)
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)
@@ -94,20 +143,39 @@ export(mainPanel)
export(makeReactiveBinding)
export(markRenderFunction)
export(maskReactiveContext)
export(memoryCache)
export(modalButton)
export(modalDialog)
export(navbarMenu)
export(navbarPage)
export(navlistPanel)
export(nearPoints)
export(need)
export(ns.sep)
export(numericInput)
export(observe)
export(observeEvent)
export(onBookmark)
export(onBookmarked)
export(onFlush)
export(onFlushed)
export(onReactiveDomainEnded)
export(onRestore)
export(onRestored)
export(onSessionEnded)
export(onStop)
export(outputOptions)
export(p)
export(pageWithSidebar)
export(paneViewer)
export(parseQueryString)
export(passwordInput)
export(plotOutput)
export(plotPNG)
export(pre)
export(prependTab)
export(printError)
export(printStackTrace)
export(radioButtons)
export(reactive)
export(reactiveFileReader)
@@ -118,10 +186,19 @@ export(reactiveTable)
export(reactiveText)
export(reactiveTimer)
export(reactiveUI)
export(reactiveVal)
export(reactiveValues)
export(reactiveValuesToList)
export(reactlog)
export(reactlogReset)
export(reactlogShow)
export(registerInputHandler)
export(removeInputHandler)
export(removeModal)
export(removeNotification)
export(removeTab)
export(removeUI)
export(renderCachedPlot)
export(renderDataTable)
export(renderImage)
export(renderPlot)
@@ -130,29 +207,46 @@ export(renderTable)
export(renderText)
export(renderUI)
export(repeatable)
export(req)
export(restoreInput)
export(runApp)
export(runExample)
export(runGadget)
export(runGist)
export(runGitHub)
export(runUrl)
export(safeError)
export(selectInput)
export(selectizeInput)
export(serverInfo)
export(setBookmarkExclude)
export(setProgress)
export(setSerializer)
export(shinyApp)
export(shinyAppDir)
export(shinyAppFile)
export(shinyOptions)
export(shinyServer)
export(shinyUI)
export(showBookmarkUrlModal)
export(showModal)
export(showNotification)
export(showReactLog)
export(showTab)
export(sidebarLayout)
export(sidebarPanel)
export(singleton)
export(sizeGrowthRatio)
export(sliderInput)
export(snapshotExclude)
export(snapshotPreprocessInput)
export(snapshotPreprocessOutput)
export(span)
export(splitLayout)
export(stopApp)
export(strong)
export(submitButton)
export(suppressDependencies)
export(tabPanel)
export(tableOutput)
export(tabsetPanel)
@@ -163,26 +257,39 @@ export(tagAppendChildren)
export(tagList)
export(tagSetChildren)
export(tags)
export(textAreaInput)
export(textInput)
export(textOutput)
export(throttle)
export(titlePanel)
export(uiOutput)
export(updateActionButton)
export(updateCheckboxGroupInput)
export(updateCheckboxInput)
export(updateDateInput)
export(updateDateRangeInput)
export(updateNavbarPage)
export(updateNavlistPanel)
export(updateNumericInput)
export(updateQueryString)
export(updateRadioButtons)
export(updateSelectInput)
export(updateSelectizeInput)
export(updateSliderInput)
export(updateTabsetPanel)
export(updateTextAreaInput)
export(updateTextInput)
export(updateVarSelectInput)
export(updateVarSelectizeInput)
export(urlModal)
export(validate)
export(validateCssUnit)
export(varSelectInput)
export(varSelectizeInput)
export(verbatimTextOutput)
export(verticalLayout)
export(wellPanel)
export(withLogErrors)
export(withMathJax)
export(withProgress)
export(withReactiveDomain)
@@ -191,6 +298,8 @@ import(R6)
import(digest)
import(htmltools)
import(httpuv)
import(methods)
import(mime)
import(xtable)
importFrom(RJSONIO,fromJSON)
importFrom(grDevices,dev.cur)
importFrom(grDevices,dev.set)

680
NEWS
View File

@@ -1,680 +0,0 @@
shiny 0.10.2.1
--------------------------------------------------------------------------------
* Changed some examples to use \donttest instead of \dontrun.
shiny 0.10.2
--------------------------------------------------------------------------------
* The minimal version of R required for the shiny package is 3.0.0 now.
* Shiny apps can now consist of a single file, app.R, instead of ui.R and
server.R.
* Upgraded DataTables from 1.9.4 to 1.10.2. This might be a breaking change if
you have customized the DataTables options in your apps. (More info:
https://github.com/rstudio/shiny/pull/558)
* File uploading via `fileInput()` works for Internet Explorer 8 and 9 now. Note
IE8/9 do not support multiple files from a single file input. If you need to
upload multiple files, you have to use one file input for each file.
* Switched away from reference classes to R6.
* Reactive log performance has been greatly improved.
* Added `Progress` and `withProgress`, to display the progress of computation
on the client browser.
* Fixed #557: updateSelectizeInput(choices, server = TRUE) did not work when
`choices` is a character vector.
* Searching in DataTables is case-insensitive and the search strings are not
treated as regular expressions by default now. If you want case-sensitive
searching or regular expressions, you can use the configuration options
`search$caseInsensitive` and `search$regex`, e.g. `renderDataTable(...,
options = list(search = list(caseInsensitve = FALSE, regex = TRUE)))`.
* Added support for `htmltools::htmlDependency`'s new `attachment` parameter to
`renderUI`/`uiOutput`.
* Exported `createWebDependency`. It takes an `htmltools::htmlDependency` object
and makes it available over Shiny's built-in web server.
* Custom output bindings can now render `htmltools::htmlDependency` objects at
runtime using `Shiny.renderDependencies()`.
* Fixes to rounding behavior of sliderInput. (#301, #502)
shiny 0.10.1
--------------------------------------------------------------------------------
* Added Unicode support for Windows. Shiny apps running on Windows must use the
UTF-8 encoding for ui.R and server.R (also the optional global.R) if they
contain non-ASCII characters. See this article for details and examples:
http://shiny.rstudio.com/gallery/unicode-characters.html (#516)
* `runGitHub()` also allows the 'username/repo' syntax now, which is equivalent
to `runGitHub('repo', 'username')`. (#427)
* `navbarPage()` now accepts a `windowTitle` parameter to set the web browser
page title to something other than the title displayed in the navbar.
* Added an `inline` argument to `textOutput()`, `imageOutput()`, `plotOutput()`,
and `htmlOutput()`. When `inline = TRUE`, these outputs will be put in
`span()` instead of the default `div()`. This occurs automatically when these
outputs are created via the inline expressions (e.g. `r renderText(expr)`) in
R Markdown documents. See an R Markdown example at
http://shiny.rstudio.com/gallery/inline-output.html (#512)
* Added support for option groups in the select/selectize inputs. When the
`choices` argument for `selectInput()`/`selectizeInput()` is a list of
sub-lists and any sub-list is of length greater than 1, the HTML tag
`<optgroup>` will be used. See an example at
http://shiny.rstudio.com/gallery/option-groups-for-selectize-input.html (#542)
shiny 0.10.0
--------------------------------------------------------------------------------
* BREAKING CHANGE: By default, observers now terminate themselves if they were
created during a session and that session ends. See ?domains for more details.
* Shiny can now be used in R Markdown v2 documents, to create "Shiny Docs":
reports and presentations that combine narrative, statically computed output,
and fully dynamic inputs and outputs. For more info, including examples, see
http://rmarkdown.rstudio.com/authoring_shiny.html.
* The `session` object that can be passed into a server function (e.g.
shinyServer(function(input, output, session) {...})) is now documented: see
`?session`.
* Most inputs can now accept `NULL` label values to omit the label altogether.
* New `actionLink` input control; like `actionButton`, but with the appearance
of a normal link.
* `renderPlot` now calls `print` on its result if it's visible (i.e. no more
explicit `print()` required for ggplot2).
* Introduced Shiny app objects (see `?shinyApp`). These essentially replace the
little-advertised ability for `runApp` to take a `list(ui=..., server=...)`
as the first argument instead of a directory (though that ability remains for
backward compatibility). Unlike those lists, Shiny app objects are tagged with
class `shiny.appobj` so they can be run simply by printing them.
* Added `maskReactiveContext` function. It blocks the current reactive context,
to evaluate expressions that shouldn't use reactive sources directly. (This
should not be commonly needed.)
* Added `flowLayout`, `splitLayout`, and `inputPanel` functions for putting UI
elements side by side. `flowLayout` lays out its children in a left-to-right,
top-to-bottom arrangement. `splitLayout` evenly divides its horizontal space
among its children (or unevenly divides if `cellWidths` argument is provided).
`inputPanel` is like `flowPanel`, but with a light grey background, and is
intended to be used to encapsulate small input controls wherever vertical
space is at a premium.
* Added `serverInfo` to obtain info about the Shiny Server if the app is served
through it.
* Added an `inline` argument (TRUE/FALSE) in `checkboxGroupInput()` and
`radioButtons()` to allow the horizontal layout (inline = TRUE) of checkboxes
or radio buttons. (Thanks, @saurfang, #481)
* `sliderInput` and `selectizeInput`/`selectInput` now use a standard horizontal
size instead of filling up all available horizontal space. Pass `width="100%"`
explicitly for the old behavior.
* Added the `updateSelectizeInput()` function to make it possible to process
searching on the server side (i.e. using R), which can be much faster than the
client side processing (i.e. using HTML and JavaScript). See the article at
http://shiny.rstudio.com/articles/selectize.html for a detailed introduction.
* Fixed a bug of renderDataTable() when the data object only has 1 row and 1
column. (Thanks, ZJ Dai, #429)
* `renderPrint` gained a new argument 'width' to control the width of the text
output, e.g. renderPrint({mtcars}, width = 40).
* Fixed #220: the zip file for a directory created by some programs may not have
the directory name as its first entry, in which case runUrl() can fail. (#220)
* `runGitHub()` can also take a value of the form "username/repo" in its first
argument, e.g. both runGitHub("shiny_example", "rstudio") and
runGitHub("rstudio/shiny_example") are valid ways to run the GitHub repo.
shiny 0.9.1
--------------------------------------------------------------------------------
* Fixed warning 'Error in Context$new : could not find function "loadMethod"'
that was happening to dependent packages on "R CMD check".
shiny 0.9.0
--------------------------------------------------------------------------------
* BREAKING CHANGE: Added a `host` parameter to runApp() and runExample(),
which defaults to the shiny.host option if it is non-NULL, or "127.0.0.1"
otherwise. This means that by default, Shiny applications can only be
accessed on the same machine from which they are served. To allow other
clients to connect, as in previous versions of Shiny, use "0.0.0.0"
(or the IP address of one of your network interfaces, if you care to be
explicit about it).
* Added a new function `selectizeInput()` to use the JavaScript library
selectize.js (https://github.com/brianreavis/selectize.js), which extends
the basic select input in many aspects.
* The `selectInput()` function also gained a new argument `selectize = TRUE`
to makes use of selectize.js by default. If you want to revert back to the
original select input, you have to call selectInput(..., selectize = FALSE).
* Added Showcase mode, which displays the R code for an app right in the app
itself. You can invoke Showcase mode by passing `display.mode="showcase"`
to the `runApp()` function. Or, if an app is designed to run in Showcase
mode by default, add a DESCRIPTION file in the app dir with Title, Author,
and License fields; with "Type: Shiny"; and with "DisplayMode: Showcase".
* Upgraded to Bootstrap 2.3.2 and jQuery 1.11.0.
* Make `tags$head()` and `singleton()` behave correctly when used with
`renderUI()` and `uiOutput()`. Previously, "hoisting content to the head"
and "only rendering items a single time" were features that worked only
when the page was initially loading, not in dynamic rendering.
* Files are now sourced with the `keep.source` option, to help with debugging
and profiling.
* Support user-defined input parsers for data coming in from JavaScript using
the parseShinyInput method.
* Fixed the bug #299: renderDataTable() can deal with 0-row data frames now.
(reported by Harlan Harris)
* Added `navbarPage()` and `navbarMenu()` functions to create applications
with multiple top level panels.
* Added `navlistPanel()` function to create layouts with a a bootstrap
navlist on the left and tabPanels on the right
* Added `type` parameter to `tabsetPanel()` to enable the use of pill
style tabs in addition to the standard ones.
* Added `position` paramter to `tabsetPanel()` to enable positioning of tabs
above, below, left, or right of tab content.
* Added `fluidPage()` and `fixedPage()` functions as well as related row and
column layout functions for creating arbitrary bootstrap grid layouts.
* Added `hr()` builder function for creating horizontal rules.
* Automatically concatenate duplicate attributes in tag definitions
* Added `responsive` parameter to page building functions for opting-out of
bootstrap responsive css.
* Added `theme` parameter to page building functions for specifying alternate
bootstrap css styles.
* Added `icon()` function for embedding icons from the
[font awesome](http://fontawesome.io/) icon library
* Added `makeReactiveBinding` function to turn a "regular" variable into a
reactive one (i.e. reading the variable makes the current reactive context
dependent on it, and setting the variable is a source of reactivity).
* Added a function `withMathJax()` to include the MathJax library in an app.
* The argument `selected` in checkboxGroupInput(), selectInput(), and
radioButtons() refers to the value(s) instead of the name(s) of the
argument `choices` now. For example, the value of the `selected` argument
in selectInput(..., choices = c('Label 1' = 'x1', 'Label 2' = 'x2'),
selected = 'Label 2') must be updated to 'x2', although names/labels will
be automatically converted to values internally for backward
compatibility. The same change applies to updateCheckboxGroupInput(),
updateSelectInput(), and updateRadioButtons() as well. (#340)
* Now it is possible to only update the value of a checkbox group, select input,
or radio buttons using the `selected` argument without providing the
`choices` argument in updateCheckboxGroupInput(), updateSelectInput(), and
updateRadioButtons(), respectively. (#340)
* Added `absolutePanel` and `fixedPanel` functions for creating absolute-
and fixed-position panels. They can be easily made user-draggable by
specifying `draggable = TRUE`.
* For the `options` argument of the function `renderDataTable()`, we can
pass literal JavaScript code to the DataTables library via `I()`. This
makes it possible to use any JavaScript object in the options, e.g. a
JavaScript function (which is not supported in JSON). See
`?renderDataTable` for details and examples.
* DataTables also works under IE8 now.
* Fixed a bug in DataTables pagination when searching is turned on, which
caused failures for matrices as well as empty rows when displaying data
frames using renderDataTable().
* The `options` argument in `renderDataTable()` can also take a function
that returns a list. This makes it possible to use reactive values in the
options. (#392)
* `renderDataTable()` respects more DataTables options now: (1) either
bPaginate = FALSE or iDisplayLength = -1 will disable pagination (i.e. all
rows are returned from the data); besides, this means we can also use -1
in the length menu, e.g. aLengthMenu = list(c(10, 30, -1), list(10, 30,
'All')); (2) we can disable searching for individual columns through the
bSearchable option, e.g. aoColumns = list(list(bSearchable = FALSE),
list(bSearchable = TRUE),...) (the search box for the first column is
hidden); (3) we can turn off searching entirely (for both global searching
and individual columns) using the option bFilter = FALSE.
* Added an argument `callback` in `renderDataTable()` so that a custom
JavaScript function can be applied to the DataTable object. This makes it
much easier to use DataTables plug-ins.
* For numeric columns in a DataTable, the search boxes support lower and
upper bounds now: a search query of the form "lower,upper" (without
quotes) indicates the limits [lower, upper]. For a column X, this means
the rows corresponding to X >= lower & X <= upper are returned. If we omit
either the lower limit or the upper limit, only the other limit will be
used, e.g. ",upper" means X <= upper.
* `updateNumericInput(value)` tries to preserve numeric precision by avoiding
scientific notation when possible, e.g. 102145 is no longer rounded to
1.0214e+05 = 102140. (Thanks, Martin Loos. #401)
* `sliderInput()` no longer treats a label wrapped in HTML() as plain text,
e.g. the label in sliderInput(..., label = HTML('<em>A Label</em>')) will
not be escaped any more. (#119)
* Fixed #306: the trailing slash in a path could fail `addResourcePath()`
under Windows. (Thanks, ZJ Dai)
* Dots are now legal characters for inputId/outputId. (Thanks, Kevin
Lindquist. #358)
shiny 0.8.0
--------------------------------------------------------------------------------
* Debug hooks are registered on all user-provided functions and (reactive)
expressions (e.g., in renderPlot()), which makes it possible to set
breakpoints in these functions using the latest version of the RStudio
IDE, and the RStudio visual debugging tools can be used to debug Shiny
apps. Internally, the registration is done via installExprFunction(),
which is a new function introduced in this version to replace
exprToFunction() so that the registration can be automatically done.
* Added a new function renderDataTable() to display tables using the
JavaScript library DataTables. It includes basic features like pagination,
searching (global search or search by individual columns), sorting (by
single or multiple columns). All these features are implemented on the R
side; for example, we can use R regular expressions for searching.
Besides, it also uses the Bootstrap CSS style. See the full
documentation and examples in the tutorial:
http://rstudio.github.io/shiny/tutorial/#datatables
* Added a new option `shiny.error` which can take a function as an error
handler. It is called when an error occurs in an app (in user-provided
code), e.g., after we set options(shiny.error = recover), we can enter a
specified environment in the call stack to debug our code after an error
occurs.
* The argument `launch.browser` in runApp() can also be a function,
which takes the URL of the shiny app as its input value.
* runApp() uses a random port between 3000 and 8000 instead of 8100 now. It
will try up to 20 ports in case certain ports are not available.
* Fixed a bug for conditional panels: the value `input.id` in the condition
was not correctly retrieved when the input widget had a type, such as
numericInput(). (reported by Jason Bryer)
* Fixed two bugs in plotOutput(); clickId and hoverId did not give correct
coordinates in Firefox, or when the axis limits of the plot were changed.
(reported by Chris Warth and Greg D)
* The minimal required version for the httpuv package was increased to 1.2
(on CRAN now).
shiny 0.7.0
--------------------------------------------------------------------------------
* Stopped sending websocket subprotocol. This fixes a compatibility issue with
Google Chrome 30.
* The `input` and `output` objects are now also accessible via `session$input`
and `session$output`.
* Added click and hover events for static plots; see `?plotOutput` for details.
* Added optional logging of the execution states of a reactive program, and
tools for visualizing the log data. To use, start a new R session and call
`options(shiny.reactlog=TRUE)`. Then launch a Shiny app and interact with it.
Press Ctrl+F3 (or for Mac, Cmd+F3) in the browser to launch an interactive
visualization of the reactivity that has occurred. See `?showReactLog` for
more information.
* Added `includeScript()` and `includeCSS()` functions.
* Reactive expressions now have class="reactive" attribute. Also added
`is.reactive()` and `is.reactivevalues()` functions.
* New `stopApp()` function, which stops an app and returns a value to the caller
of `runApp()`.
* Added the `shiny.usecairo` option, which can be used to tell Shiny not to use
Cairo for PNG output even when it is installed. (Defaults to `TRUE`.)
* Speed increases for `selectInput()` and `radioButtons()`, and their
corresponding updater functions, for when they have many options.
* Added `tagSetChildren()` and `tagAppendChildren()` functions.
* The HTTP request object that created the websocket is now accessible from the
`session` object, as `session$request`. This is a Rook-like request
environment that can be used to access HTTP headers, among other things.
(Note: When running in a Shiny Server environment, the request will reflect
the proxy HTTP request that was made from the Shiny Server process to the R
process, not the request that was made from the web browser to Shiny Server.)
* Fix `getComputedStyle` issue, for IE8 browser compatibility (#196). Note:
Shiny Server is still required for IE8/9 compatibility.
* Add shiny.sharedSecret option, to require the HTTP header Shiny-Shared-Secret
to be set to the given value.
shiny 0.6.0
--------------------------------------------------------------------------------
* `tabsetPanel()` can be directed to start with a specific tab selected.
* Fix bug where multiple file uploads with 3 or more files result in incorrect
data.
* Add `withTags()` function.
* Add dateInput and dateRangeInput.
* `shinyServer()` now takes an optional `session` argument, which is used for
communication with the session object.
* Add functions to update values of existing inputs on a page, instead of
replacing them entirely.
* Allow listening on domain sockets.
* Added `actionButton()` to Shiny.
* The server can now send custom JSON messages to the client. On the client
side, functions can be registered to handle these messages.
* Callbacks can be registered to be called at the end of a client session.
* Add ability to set priority of observers and outputs. Each priority level
gets its own queue.
* Fix bug where the presence of a submit button would prevent sending of
metadata until the button was clicked.
* `reactiveTimer()` and `invalidateLater()` by default no longer invalidate
reactive objects after the client session has closed.
* Shiny apps can be run without a server.r and ui.r file.
shiny 0.5.0
--------------------------------------------------------------------------------
* Switch from websockets package for handling websocket connections to httpuv.
* New method for detecting hidden output objects. Instead of checking that
height and width are 0, it checks that the object or any ancestor in the DOM
has style display:none.
* Add `clientData` reactive values object, which carries information about the
client. This includes the hidden status of output objects, height/width plot
output objects, and the URL of the browser.
* Add `parseQueryString()` function.
* Add `renderImage()` function for sending arbitrary image files to the client,
and its counterpart, `imageOutput()`.
* Add support for high-resolution (Retina) displays.
* Fix bug #55, where `renderTable()` would throw error with an empty data frame.
shiny 0.4.1
--------------------------------------------------------------------------------
* Fix bug where width and height weren't passed along properly from
`reactivePlot` to `renderPlot`.
* Fix bug where infinite recursion would happen when `reactivePlot` was passed
a function for width or height.
shiny 0.4.0
--------------------------------------------------------------------------------
* Added suspend/resume capability to observers.
* Output objects are automatically suspended when they are hidden on the user's
web browser.
* `runGist()` accepts GitHub's new URL format, which includes the username.
* `reactive()` and `observe()` now take expressions instead of functions.
* `reactiveText()`, `reactivePlot()`, and so on, have been renamed to
`renderText()`, `renderPlot()`, etc. They also now take expressions instead
of functions.
* Fixed a bug where empty values in a numericInput were sent to the R process
as 0. They are now sent as NA.
shiny 0.3.1
--------------------------------------------------------------------------------
* Fix issue #91: bug where downloading files did not work.
* Add [[<- operator for shinyoutput object, making it possible to assign values
with `output[['plot1']] <- ...`.
* Reactive functions now preserve the visible/invisible state of their returned
values.
shiny 0.3.0
--------------------------------------------------------------------------------
* Reactive functions are now evaluated lazily.
* Add `reactiveValues()`.
* Using `as.list()` to convert a reactivevalues object (like `input`) to a list
is deprecated. The new function `reactiveValuesToList()` should be used
instead.
* Add `isolate()`. This function is used for accessing reactive functions,
without them invalidating their parent contexts.
* Fix issue #58: bug where reactive functions are not re-run when all items in
a checkboxGroup are unchecked.
* Fix issue #71, where `reactiveTable()` would return blank if the first
element of a data frame was NA.
* In `plotOutput`, better validation for CSS units when specifying width and
height.
* `reactivePrint()` no longer displays invisible output.
* `reactiveText()` no longer displays printed output, only the return value
from a function.
* The `runGitHub()` and `runUrl()` functions have been added, for running
Shiny apps from GitHub repositories and zip/tar files at remote URLs.
* Fix issue #64, where pressing Enter in a textbox would cause a form to
submit.
shiny 0.2.4
--------------------------------------------------------------------------------
* `runGist` has been updated to use the new download URLs from
https://gist.github.com.
* Shiny now uses `CairoPNG()` for output, when the Cairo package is available.
This provides better-looking output on Linux and Windows.
shiny 0.2.3
--------------------------------------------------------------------------------
* Ignore request variables for routing purposes
shiny 0.2.2
--------------------------------------------------------------------------------
* Fix CRAN warning (assigning to global environment)
shiny 0.2.1
--------------------------------------------------------------------------------
* [BREAKING] Modify API of `downloadHandler`: The `content` function now takes
a file path, not writable connection, as an argument. This makes it much
easier to work with APIs that only write to file paths, not connections.
shiny 0.2.0
--------------------------------------------------------------------------------
* Fix subtle name resolution bug--the usual symptom being S4 methods not being
invoked correctly when called from inside of ui.R or server.R
shiny 0.1.14
--------------------------------------------------------------------------------
* Fix slider animator, which broke in 0.1.10
shiny 0.1.13
--------------------------------------------------------------------------------
* Fix temp file leak in reactivePlot
shiny 0.1.12
--------------------------------------------------------------------------------
* Fix problems with runGist on Windows
* Add feature for on-the-fly file downloads (e.g. CSV data, PDFs)
* Add CSS hooks for app-wide busy indicators
shiny 0.1.11
--------------------------------------------------------------------------------
* Fix input binding with IE8 on Shiny Server
* Fix issue #41: reactiveTable should allow print options too
* Allow dynamic sizing of reactivePlot (i.e. using a function instead of a fixed
value)
shiny 0.1.10
--------------------------------------------------------------------------------
* Support more MIME types when serving out of www
* Fix issue #35: Allow modification of untar args
* headerPanel can take an explicit window title parameter
* checkboxInput uses correct attribute `checked` instead of `selected`
* Fix plot rendering with IE8 on Shiny Server
shiny 0.1.9
--------------------------------------------------------------------------------
* Much less flicker when updating plots
* More customizable error display
* Add `includeText`, `includeHTML`, and `includeMarkdown` functions for putting
text, HTML, and Markdown content from external files in the application's UI.
shiny 0.1.8
--------------------------------------------------------------------------------
* Add `runGist` function for conveniently running a Shiny app that is published
on gist.github.com.
* Fix issue #27: Warnings cause reactive functions to stop executing.
* The server.R and ui.R filenames are now case insensitive.
* Add `wellPanel` function for creating inset areas on the page.
* Add `bootstrapPage` function for creating new Bootstrap based
layouts from scratch.
shiny 0.1.7
--------------------------------------------------------------------------------
* Fix issue #26: Shiny.OutputBindings not correctly exported.
* Add `repeatable` function for making easily repeatable versions of random
number generating functions.
* Transcode JSON into UTF-8 (prevents non-ASCII reactivePrint values from
causing errors on Windows).
shiny 0.1.6
--------------------------------------------------------------------------------
* Import package dependencies, instead of attaching them (with the exception of
websockets, which doesn't currently work unless attached).
* conditionalPanel was animated, now it is not.
* bindAll was not correctly sending initial values to the server; fixed.
shiny 0.1.5
--------------------------------------------------------------------------------
* BREAKING CHANGE: JS APIs Shiny.bindInput and Shiny.bindOutput removed and
replaced with Shiny.bindAll; Shiny.unbindInput and Shiny.unbindOutput removed
and replaced with Shiny.unbindAll.
* Add file upload support (currently only works with Chrome and Firefox). Use
a normal HTML file input, or call the `fileInput` UI function.
* Shiny.unbindOutputs did not work, now it does.
* Generally improved robustness of dynamic input/output bindings.
* Add conditionalPanel UI function to allow showing/hiding UI based on a JS
expression; for example, whether an input is a particular value. Also works in
raw HTML (add the `data-display-if` attribute to the element that should be
shown/hidden).
* htmlOutput (CSS class `shiny-html-output`) can contain inputs and outputs.
shiny 0.1.4
--------------------------------------------------------------------------------
* Allow Bootstrap tabsets to act as reactive inputs; their value indicates which
tab is active
* Upgrade to Bootstrap 2.1
* Add `checkboxGroupInput` control, which presents a list of checkboxes and
returns a vector of the selected values
* Add `addResourcePath`, intended for reusable component authors to access CSS,
JavaScript, image files, etc. from their package directories
* Add Shiny.bindInputs(scope), .unbindInputs(scope), .bindOutputs(scope), and
.unbindOutputs(scope) JS API calls to allow dynamic binding/unbinding of HTML
elements
shiny 0.1.3
--------------------------------------------------------------------------------
* Introduce Shiny.inputBindings.register JS API and InputBinding class, for
creating custom input controls
* Add `step` parameter to numericInput
* Read names of input using `names(input)`
* Access snapshot of input as a list using `as.list(input)`
* Fix issue #10: Plots in tabsets not rendered
shiny 0.1.2
--------------------------------------------------------------------------------
Initial private beta release!

1448
NEWS.md Normal file

File diff suppressed because it is too large Load Diff

265
R/app.R
View File

@@ -3,11 +3,16 @@
#' 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 (\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.
#'
#' 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
#' 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}()}.
#'
#' @param ui The UI definition of the app (for example, a call to
#' \code{fluidPage()} with nested controls)
@@ -15,35 +20,58 @@
#' @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 `runApp` call. You
#' can also specify \code{width} and \code{height} parameters which provide a
#' hint to the embedding environment about the ideal height/width for the app.
#' @param options Named options that should be passed to the \code{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
#' 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
#' request. Note that the entire request path must match the regular
#' expression in order for the match to be considered successful.
#' @return An object that represents the app. Printing the object will run the
#' app.
#' @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.
#' @return An object that represents the app. Printing the object or passing it
#' to \code{\link{runApp}} will run the app.
#'
#' @examples
#' \donttest{
#' shinyApp(
#' ui = fluidPage(
#' numericInput("n", "n", 1),
#' plotOutput("plot")
#' ),
#' server = function(input, output) {
#' output$plot <- renderPlot( plot(head(cars, input$n)) )
#' },
#' options=list(launch.browser = rstudio::viewer)
#' )
#' ## Only run this example in interactive R sessions
#' if (interactive()) {
#' options(device.ask.default = FALSE)
#'
#' shinyAppDir(system.file("examples/01_hello", package="shiny"))
#' shinyApp(
#' ui = fluidPage(
#' numericInput("n", "n", 1),
#' plotOutput("plot")
#' ),
#' server = function(input, output) {
#' output$plot <- renderPlot( plot(head(cars, input$n)) )
#' }
#' )
#'
#' shinyAppDir(system.file("examples/01_hello", package="shiny"))
#'
#'
#' # The object can be passed to runApp()
#' app <- shinyApp(
#' ui = fluidPage(
#' numericInput("n", "n", 1),
#' plotOutput("plot")
#' ),
#' server = function(input, output) {
#' output$plot <- renderPlot( plot(head(cars, input$n)) )
#' }
#' )
#'
#' runApp(app)
#' }
#'
#' @export
shinyApp <- function(ui=NULL, server=NULL, onStart=NULL, options=list(),
uiPattern="/") {
uiPattern="/", enableBookmarking=NULL) {
if (is.null(server)) {
stop("`server` missing from shinyApp")
}
@@ -57,12 +85,24 @@ shinyApp <- function(ui=NULL, server=NULL, onStart=NULL, options=list(),
server
}
if (!is.null(enableBookmarking)) {
bookmarkStore <- match.arg(enableBookmarking, c("url", "server", "disable"))
enableBookmarking(bookmarkStore)
}
# Store the appDir and bookmarking-related options, so that we can read them
# from within the app.
shinyOptions(appDir = getwd())
appOptions <- consumeAppOptions()
structure(
list(
httpHandler = httpHandler,
serverFuncSource = serverFuncSource,
onStart = onStart,
options = options),
options = options,
appOptions = appOptions
),
class = "shiny.appobj"
)
}
@@ -72,7 +112,7 @@ shinyApp <- function(ui=NULL, server=NULL, onStart=NULL, options=list(),
#' file and either ui.R or www/index.html)
#' @export
shinyAppDir <- function(appDir, options=list()) {
if (!file_test('-d', appDir)) {
if (!utils::file_test('-d', appDir)) {
stop("No Shiny application exists at the path \"", appDir, "\"")
}
@@ -83,12 +123,22 @@ shinyAppDir <- function(appDir, options=list()) {
if (file.exists.ci(appDir, "server.R")) {
shinyAppDir_serverR(appDir, options = options)
} else if (file.exists.ci(appDir, "app.R")) {
shinyAppDir_appR(appDir, options = options)
shinyAppDir_appR("app.R", appDir, options = options)
} else {
stop("App dir must contain either app.R or server.R.")
}
}
#' @rdname shinyApp
#' @param appFile Path to a .R file containing a Shiny application
#' @export
shinyAppFile <- function(appFile, options=list()) {
appFile <- normalizePath(appFile, mustWork = TRUE)
appDir <- dirname(appFile)
shinyAppDir_appR(basename(appFile), appDir, options = options)
}
# 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.
shinyAppDir_serverR <- function(appDir, options=list()) {
@@ -105,7 +155,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, local = new.env(parent = globalenv()))$value
ui <- sourceUTF8(uiR, envir = new.env(parent = globalenv()))
if (!is.null(.globals$ui)) {
ui <- .globals$ui[[1]]
}
@@ -120,7 +170,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),
@@ -128,7 +185,7 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
# server.R.
.globals$server <- NULL
on.exit(.globals$server <- NULL, add = TRUE)
result <- sourceUTF8(serverR, local = new.env(parent = globalenv()))$value
result <- sourceUTF8(serverR, envir = new.env(parent = globalenv()))
if (!is.null(.globals$server)) {
result <- .globals$server[[1]]
}
@@ -151,43 +208,100 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
}
}
shinyOptions(appDir = appDir)
oldwd <- NULL
monitorHandle <- NULL
onStart <- function() {
oldwd <<- getwd()
setwd(appDir)
monitorHandle <<- initAutoReloadMonitor(appDir)
if (file.exists(file.path.ci(appDir, "global.R")))
sourceUTF8(file.path.ci(appDir, "global.R"))
}
onEnd <- function() {
onStop <- function() {
setwd(oldwd)
monitorHandle()
monitorHandle <<- NULL
}
structure(
list(
httpHandler = joinHandlers(c(uiHandler, wwwDir, fallbackWWWDir)),
staticPaths = staticPaths,
httpHandler = joinHandlers(c(uiHandler, fallbackWWWDir)),
serverFuncSource = serverFuncSource,
onStart = onStart,
onEnd = onEnd,
options = options),
onStop = onStop,
options = options
),
class = "shiny.appobj"
)
}
# This reads in an app dir in the case that there's a app.R present, and returns
# a shiny.appobj.
shinyAppDir_appR <- function(appDir, options=list()) {
fullpath <- file.path.ci(appDir, "app.R")
# Start a reactive observer that continually monitors dir for changes to files
# that have the extensions: r, htm, html, js, css, png, jpg, jpeg, gif. Case is
# ignored when checking extensions. If any changes are detected, all connected
# Shiny sessions are reloaded.
#
# Use options(shiny.autoreload = TRUE) to enable this behavior. Since monitoring
# for changes is expensive (we are polling for mtimes here, nothing fancy) this
# feature is intended only for development.
#
# You can customize the file patterns Shiny will monitor by setting the
# shiny.autoreload.pattern option. For example, to monitor only ui.R:
# options(shiny.autoreload.pattern = glob2rx("ui.R"))
#
# The return value is a function that halts monitoring when called.
initAutoReloadMonitor <- function(dir) {
if (!getOption("shiny.autoreload", FALSE)) {
return(function(){})
}
filePattern <- getOption("shiny.autoreload.pattern",
".*\\.(r|html?|js|css|png|jpe?g|gif)$")
lastValue <- NULL
obs <- observe({
files <- sort(list.files(dir, pattern = filePattern, recursive = TRUE,
ignore.case = TRUE))
times <- file.info(files)$mtime
names(times) <- files
if (is.null(lastValue)) {
# First run
lastValue <<- times
} else if (!identical(lastValue, times)) {
# We've changed!
lastValue <<- times
for (session in appsByToken$values()) {
session$reload()
}
}
invalidateLater(getOption("shiny.autoreload.interval", 500))
})
obs$destroy
}
# This reads in an app dir for a single-file application (e.g. app.R), and
# returns a shiny.appobj.
shinyAppDir_appR <- function(fileName, appDir, options=list())
{
fullpath <- file.path.ci(appDir, fileName)
# 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, "app.R", case.sensitive = FALSE,
appObj <- cachedFuncWithFile(appDir, fileName, case.sensitive = FALSE,
function(appR) {
result <- sourceUTF8(fullpath, local = new.env(parent = globalenv()))$value
result <- sourceUTF8(fullpath, envir = new.env(parent = globalenv()))
if (!is.shiny.appobj(result))
stop("app.R did not return a shiny.appobj object.")
unconsumeAppOptions(result$appOptions)
return(result)
}
)
@@ -203,23 +317,48 @@ shinyAppDir_appR <- function(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
monitorHandle <- NULL
onStart <- function() {
oldwd <<- getwd()
setwd(appDir)
monitorHandle <<- initAutoReloadMonitor(appDir)
if (!is.null(appObj()$onStart)) appObj()$onStart()
}
onEnd <- function() {
onStop <- function() {
setwd(oldwd)
monitorHandle()
monitorHandle <<- NULL
}
structure(
list(
httpHandler = joinHandlers(c(dynHttpHandler, wwwDir, fallbackWWWDir)),
# 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,
httpHandler = joinHandlers(c(dynHttpHandler, fallbackWWWDir)),
serverFuncSource = dynServerFuncSource,
onStart = onStart,
onEnd = onEnd,
onStop = onStop,
options = options
),
class = "shiny.appobj"
@@ -249,7 +388,10 @@ as.shiny.appobj.list <- function(x) {
#' @rdname shinyApp
#' @export
as.shiny.appobj.character <- function(x) {
shinyAppDir(x)
if (identical(tolower(tools::file_ext(x)), "r"))
shinyAppFile(x)
else
shinyAppDir(x)
}
#' @rdname shinyApp
@@ -264,11 +406,13 @@ is.shiny.appobj <- function(x) {
print.shiny.appobj <- function(x, ...) {
opts <- x$options %OR% list()
opts <- opts[names(opts) %in%
c("port", "launch.browser", "host", "quiet", "display.mode")]
c("port", "launch.browser", "host", "quiet",
"display.mode", "test.mode")]
args <- c(list(x), opts)
# Quote x and put runApp in quotes so that there's a nicer stack trace (#1851)
args <- c(list(quote(x)), opts)
do.call(runApp, args)
do.call("runApp", args)
}
#' @rdname shinyApp
@@ -283,7 +427,20 @@ as.tags.shiny.appobj <- function(x, ...) {
height <- if (is.null(opts$height)) "400" else opts$height
path <- addSubApp(x)
tags$iframe(src=path, width=width, height=height, class="shiny-frame")
deferredIFrame(path, width, height)
}
# Generate subapp iframes in such a way that they will not actually load right
# away. Loading subapps immediately upon app load can result in a storm of
# connections, all of which are contending for the few concurrent connections
# that a browser will make to a specific origin. Instead, we load dummy iframes
# and let the client load them when convenient. (See the initIframes function in
# init_shiny.js.)
deferredIFrame <- function(path, width, height) {
tags$iframe("data-deferred-src" = path,
width = width, height = height,
class = "shiny-frame shiny-frame-deferred"
)
}
#' Knitr S3 methods
@@ -334,8 +491,7 @@ knit_print.shiny.appobj <- function(x, ...) {
}
else {
path <- addSubApp(x)
output <- tags$iframe(src=path, width=width, height=height,
class="shiny-frame")
output <- deferredIFrame(path, width, height)
}
# If embedded Shiny apps ever have JS/CSS dependencies (like pym.js) we'll
@@ -359,3 +515,14 @@ knit_print.shiny.render.function <- function(x, ..., inline = FALSE) {
shiny_rmd_warning())
output
}
# 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({
x()
}), inline = inline)
}

28
R/bookmark-state-local.R Normal file
View File

@@ -0,0 +1,28 @@
# Function wrappers for saving and restoring state to/from disk when running
# Shiny locally.
#
# These functions provide a directory to the callback function.
#
# @param id A session ID to save.
# @param callback A callback function that saves state to or restores state from
# a directory. It must take one argument, \code{stateDir}, which is a
# directory to which it writes/reads.
saveInterfaceLocal <- function(id, callback) {
# Try to save in app directory
appDir <- getShinyOption("appDir", default = getwd())
stateDir <- file.path(appDir, "shiny_bookmarks", id)
if (!dirExists(stateDir))
dir.create(stateDir, recursive = TRUE)
callback(stateDir)
}
loadInterfaceLocal <- function(id, callback) {
# Try to load from app directory
appDir <- getShinyOption("appDir", default = getwd())
stateDir <- file.path(appDir, "shiny_bookmarks", id)
callback(stateDir)
}

1207
R/bookmark-state.R Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -11,8 +11,8 @@
#' @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.
#' @param responsive \code{TRUE} to use responsive layout (automatically adapt
#' and resize page elements based on the size of the viewing device)
#' @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"}.
@@ -24,15 +24,18 @@
#' alternative to low-level row and column functions you can also use
#' higher-level layout functions like \code{\link{sidebarLayout}}.
#'
#' @note See the
#' \href{https://github.com/rstudio/shiny/wiki/Shiny-Application-Layout-Guide}{
#' Shiny-Application-Layout-Guide} for additional details on laying out fluid
#' @note See the \href{http://shiny.rstudio.com/articles/layout-guide.html}{
#' Shiny-Application-Layout-Guide} for additional details on laying out fluid
#' pages.
#'
#' @seealso \code{\link{column}}, \code{\link{sidebarLayout}}
#'
#' @examples
#' shinyUI(fluidPage(
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' # Example of UI with fluidPage
#' ui <- fluidPage(
#'
#' # Application title
#' titlePanel("Hello Shiny!"),
@@ -53,9 +56,21 @@
#' plotOutput("distPlot")
#' )
#' )
#' ))
#' )
#'
#' shinyUI(fluidPage(
#' # Server logic
#' server <- function(input, output) {
#' output$distPlot <- renderPlot({
#' hist(rnorm(input$obs))
#' })
#' }
#'
#' # Complete app with UI and server components
#' shinyApp(ui, server)
#'
#'
#' # UI demonstrating column layouts
#' ui <- fluidPage(
#' title = "Hello Shiny!",
#' fluidRow(
#' column(width = 4,
@@ -65,11 +80,13 @@
#' "3 offset 2"
#' )
#' )
#' ))
#' )
#'
#' shinyApp(ui, server = function(input, output) { })
#' }
#' @rdname fluidPage
#' @export
fluidPage <- function(..., title = NULL, responsive = TRUE, theme = NULL) {
fluidPage <- function(..., title = NULL, responsive = NULL, theme = NULL) {
bootstrapPage(div(class = "container-fluid", ...),
title = title,
responsive = responsive,
@@ -80,7 +97,7 @@ fluidPage <- function(..., title = NULL, responsive = TRUE, theme = NULL) {
#' @rdname fluidPage
#' @export
fluidRow <- function(...) {
div(class = "row-fluid", ...)
div(class = "row", ...)
}
#' Create a page with a fixed layout
@@ -95,8 +112,8 @@ fluidRow <- function(...) {
#'
#' @param ... Elements to include within the container
#' @param title The browser window title (defaults to the host URL of the page)
#' @param responsive \code{TRUE} to use responsive layout (automatically adapt
#' and resize page elements based on the size of the viewing device)
#' @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"}.
@@ -109,15 +126,17 @@ fluidRow <- function(...) {
#' layout functions like \code{sidebarLayout}, rather, all layout must be done
#' with \code{fixedRow} and \code{column}.
#'
#' @note See the
#' \href{https://github.com/rstudio/shiny/wiki/Shiny-Application-Layout-Guide}{
#' @note See the \href{http://shiny.rstudio.com/articles/layout-guide.html}{
#' Shiny Application Layout Guide} for additional details on laying out fixed
#' pages.
#'
#' @seealso \code{\link{column}}
#'
#' @examples
#' shinyUI(fixedPage(
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fixedPage(
#' title = "Hello, Shiny!",
#' fixedRow(
#' column(width = 4,
@@ -127,11 +146,14 @@ fluidRow <- function(...) {
#' "3 offset 2"
#' )
#' )
#' ))
#' )
#'
#' shinyApp(ui, server = function(input, output) { })
#' }
#'
#' @rdname fixedPage
#' @export
fixedPage <- function(..., title = NULL, responsive = TRUE, theme = NULL) {
fixedPage <- function(..., title = NULL, responsive = NULL, theme = NULL) {
bootstrapPage(div(class = "container", ...),
title = title,
responsive = responsive,
@@ -162,33 +184,52 @@ fixedRow <- function(...) {
#' @seealso \code{\link{fluidRow}}, \code{\link{fixedRow}}.
#'
#' @examples
#' fluidRow(
#' column(4,
#' sliderInput("obs", "Number of observations:",
#' min = 1, max = 1000, value = 500)
#' ),
#' column(8,
#' plotOutput("distPlot")
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' fluidRow(
#' column(4,
#' sliderInput("obs", "Number of observations:",
#' min = 1, max = 1000, value = 500)
#' ),
#' column(8,
#' plotOutput("distPlot")
#' )
#' )
#' )
#'
#' fluidRow(
#' column(width = 4,
#' "4"
#' ),
#' column(width = 3, offset = 2,
#' "3 offset 2"
#' server <- function(input, output) {
#' output$distPlot <- renderPlot({
#' hist(rnorm(input$obs))
#' })
#' }
#'
#' shinyApp(ui, server)
#'
#'
#'
#' ui <- fluidPage(
#' fluidRow(
#' column(width = 4,
#' "4"
#' ),
#' column(width = 3, offset = 2,
#' "3 offset 2"
#' )
#' )
#' )
#' shinyApp(ui, server = function(input, output) { })
#' }
#' @export
column <- function(width, ..., offset = 0) {
if (!is.numeric(width) || (width < 1) || (width > 12))
stop("column width must be between 1 and 12")
colClass <- paste0("span", width)
colClass <- paste0("col-sm-", width)
if (offset > 0)
colClass <- paste0(colClass, " offset", offset)
colClass <- paste0(colClass, " col-sm-offset-", offset)
div(class = colClass, ...)
}
@@ -204,13 +245,19 @@ column <- function(width, ..., offset = 0) {
#'
#'
#' @examples
#' titlePanel("Hello Shiny!")
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' titlePanel("Hello Shiny!")
#' )
#' shinyApp(ui, server = function(input, output) { })
#' }
#' @export
titlePanel <- function(title, windowTitle=title) {
tagList(
tags$head(tags$title(windowTitle)),
h2(style = "padding: 10px 0px;", title)
h2(title)
)
}
@@ -228,8 +275,12 @@ titlePanel <- function(title, windowTitle=title) {
#' layout.
#'
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#' options(device.ask.default = FALSE)
#'
#' # Define UI
#' shinyUI(fluidPage(
#' ui <- fluidPage(
#'
#' # Application title
#' titlePanel("Hello Shiny!"),
@@ -250,8 +301,18 @@ titlePanel <- function(title, windowTitle=title) {
#' plotOutput("distPlot")
#' )
#' )
#' ))
#' )
#'
#' # Server logic
#' server <- function(input, output) {
#' output$distPlot <- renderPlot({
#' hist(rnorm(input$obs))
#' })
#' }
#'
#' # Complete app with UI and server components
#' shinyApp(ui, server)
#' }
#' @export
sidebarLayout <- function(sidebarPanel,
mainPanel,
@@ -288,13 +349,18 @@ sidebarLayout <- function(sidebarPanel,
#' @seealso \code{\link{fluidPage}}, \code{\link{flowLayout}}
#'
#' @examples
#' shinyUI(fluidPage(
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' verticalLayout(
#' a(href="http://example.com/link1", "Link One"),
#' a(href="http://example.com/link2", "Link Two"),
#' a(href="http://example.com/link3", "Link Three")
#' )
#' ))
#' )
#' shinyApp(ui, server = function(input, output) { })
#' }
#' @export
verticalLayout <- function(..., fluid = TRUE) {
lapply(list(...), function(row) {
@@ -310,8 +376,8 @@ 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. `plotOutput` at
#' its default setting of `width = "100%"`).
#' well with elements that have a percentage-based width (e.g.
#' \code{\link{plotOutput}} at its default setting of \code{width = "100\%"}).
#'
#' @param ... Unnamed arguments will become child elements of the layout. Named
#' arguments will become HTML attributes on the outermost tag.
@@ -321,11 +387,16 @@ verticalLayout <- function(..., fluid = TRUE) {
#' @seealso \code{\link{verticalLayout}}
#'
#' @examples
#' flowLayout(
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- flowLayout(
#' numericInput("rows", "How many rows?", 5),
#' selectInput("letter", "Which letter?", LETTERS),
#' sliderInput("value", "What value?", 0, 100, 50)
#' )
#' shinyApp(ui, server = function(input, output) { })
#' }
#' @export
flowLayout <- function(..., cellArgs = list()) {
@@ -348,7 +419,6 @@ flowLayout <- function(..., cellArgs = list()) {
#' suitable for wrapping inputs.
#'
#' @param ... Input controls or other HTML elements.
#'
#' @export
inputPanel <- function(...) {
div(class = "shiny-input-panel",
@@ -371,21 +441,34 @@ inputPanel <- function(...) {
#' of the layout.
#'
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#' options(device.ask.default = FALSE)
#'
#' # Server code used for all examples
#' server <- function(input, output) {
#' output$plot1 <- renderPlot(plot(cars))
#' output$plot2 <- renderPlot(plot(pressure))
#' output$plot3 <- renderPlot(plot(AirPassengers))
#' }
#'
#' # Equal sizing
#' splitLayout(
#' ui <- splitLayout(
#' plotOutput("plot1"),
#' plotOutput("plot2")
#' )
#' shinyApp(ui, server)
#'
#' # Custom widths
#' splitLayout(cellWidths = c("25%", "75%"),
#' ui <- splitLayout(cellWidths = c("25%", "75%"),
#' plotOutput("plot1"),
#' plotOutput("plot2")
#' )
#' shinyApp(ui, server)
#'
#' # All cells at 300 pixels wide, with cell padding
#' # and a border around everything
#' splitLayout(
#' ui <- splitLayout(
#' style = "border: 1px solid silver;",
#' cellWidths = 300,
#' cellArgs = list(style = "padding: 6px"),
@@ -393,6 +476,8 @@ inputPanel <- function(...) {
#' plotOutput("plot2"),
#' plotOutput("plot3")
#' )
#' shinyApp(ui, server)
#' }
#' @export
splitLayout <- function(..., cellWidths = NULL, cellArgs = list()) {
@@ -419,3 +504,193 @@ splitLayout <- function(..., cellWidths = NULL, cellArgs = list()) {
}, SIMPLIFY = FALSE)
))
}
#' Flex Box-based row/column layouts
#'
#' 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
#' 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
#' 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}
#' }
#'
#' @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
#' 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
#' 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
#' 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
#' not determined by the height of its contents.
#'
#' @examples
#' # Only run this example in interactive R sessions.
#' if (interactive()) {
#'
#' ui <- fillPage(fillRow(
#' plotOutput("plotLeft", height = "100%"),
#' fillCol(
#' plotOutput("plotTopRight", height = "100%"),
#' plotOutput("plotBottomRight", height = "100%")
#' )
#' ))
#'
#' server <- function(input, output, session) {
#' output$plotLeft <- renderPlot(plot(cars))
#' output$plotTopRight <- renderPlot(plot(pressure))
#' output$plotBottomRight <- renderPlot(plot(AirPassengers))
#' }
#'
#' shinyApp(ui, server)
#'
#' }
#' @export
fillRow <- function(..., flex = 1, width = "100%", height = "100%") {
flexfill(..., direction = "row", flex = flex, width = width, height = height)
}
#' @rdname fillRow
#' @export
fillCol <- function(..., flex = 1, width = "100%", height = "100%") {
flexfill(..., direction = "column", flex = flex, width = width, height = height)
}
flexfill <- function(..., direction, flex, width = width, height = height) {
children <- list(...)
attrs <- list()
if (!is.null(names(children))) {
attrs <- children[names(children) != ""]
children <- children[names(children) == ""]
}
if (length(flex) > length(children)) {
flex <- flex[seq_along(children)]
}
# The dimension along the main axis
main <- switch(direction,
row = "width",
"row-reverse" = "width",
column = "height",
"column-reverse" = "height",
stop("Unexpected direction")
)
# The dimension along the cross axis
cross <- if (main == "width") "height" else "width"
divArgs <- list(
class = sprintf("flexfill-container flexfill-container-%s", direction),
style = css(
display = "-webkit-flex",
display = "-ms-flexbox",
display = "flex",
.webkit.flex.direction = direction,
.ms.flex.direction = direction,
flex.direction = direction,
width = validateCssUnit(width),
height = validateCssUnit(height)
),
mapply(children, flex, FUN = function(el, flexValue) {
if (is.na(flexValue)) {
# If the flex value is NA, then put the element in a simple flex item
# that sizes itself (along the main axis) to its contents
tags$div(
class = "flexfill-item",
style = css(
position = "relative",
"-webkit-flex" = "none",
"-ms-flex" = "none",
flex = "none"
),
style = paste0(main, ":auto;", cross, ":100%;"),
el
)
} else if (is.numeric(flexValue)) {
# If the flex value is numeric, we need *two* wrapper divs. The outer is
# the flex item, and the inner is an absolute-fill div that is needed to
# make percentage-based sizing for el work correctly. I don't understand
# why this is needed but the truth is probably in this SO page:
# http://stackoverflow.com/questions/15381172/css-flexbox-child-height-100
tags$div(
class = "flexfill-item",
style = css(
position = "relative",
"-webkit-flex" = flexValue,
"-ms-flex" = flexValue,
flex = flexValue,
width = "100%", height = "100%"
),
tags$div(
class = "flexfill-item-inner",
style = css(
position = "absolute",
top = 0, left = 0, right = 0, bottom = 0
),
el
)
)
} else {
stop("Unexpected flex argument: ", flexValue)
}
}, SIMPLIFY = FALSE, USE.NAMES = FALSE)
)
do.call(tags$div, c(attrs, divArgs))
}
css <- function(..., collapse_ = "") {
props <- list(...)
if (length(props) == 0) {
return("")
}
if (is.null(names(props)) || any(names(props) == "")) {
stop("cssList expects all arguments to be named")
}
# Necessary to make factors show up as level names, not numbers
props[] <- lapply(props, paste, collapse = " ")
# Drop null args
props <- props[!sapply(props, empty)]
if (length(props) == 0) {
return("")
}
# Replace all '.' and '_' in property names to '-'
names(props) <- gsub("[._]", "-", tolower(gsub("([A-Z])", "-\\1", names(props))))
# Create "!important" suffix for each property whose name ends with !, then
# remove the ! from the property name
important <- ifelse(grepl("!$", names(props), perl = TRUE), " !important", "")
names(props) <- sub("!$", "", names(props), perl = TRUE)
paste0(names(props), ":", props, important, ";", collapse = collapse_)
}
empty <- function(x) {
length(x) == 0 || (is.character(x) && !any(nzchar(x)))
}

File diff suppressed because it is too large Load Diff

561
R/cache-disk.R Normal file
View File

@@ -0,0 +1,561 @@
#' 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}.
#'
#'
#' @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
#' 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}.
#' 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}.
#'
#' When the cache is created, you can supply a value for \code{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.
#'
#' 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
#' 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
#' 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.
#'
#' 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
#' prints the missing key, you could do this:
#'
#' \preformatted{
#' diskCache(
#' missing = function(key) {
#' stop("Attempted to get missing key: ", key)
#' },
#' exec_missing = TRUE
#' )
#' }
#'
#' If you use this, the code that calls \code{get()} should be wrapped with
#' \code{\link{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()}.
#'
#' 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
#' 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
#' 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.
#'
#' 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
#' individually.
#'
#' If the number of objects in the cache exceeds \code{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}.
#'
#' If the size of the objects in the cache exceeds \code{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
#' 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
#' 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},
#' 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
#' from the cache according to an eviction policy. The available eviction
#' policies are:
#'
#' \describe{
#' \item{\code{"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.
#' }
#' \item{\code{"fifo"}}{
#' First-in-first-out. The oldest objects will be removed.
#' }
#' }
#'
#' Both of these policies use files' mtime. Note that some filesystems (notably
#' FAT) have poor mtime resolution. (atime is not used because support for
#' atime is worse than mtime.)
#'
#'
#' @section Sharing among multiple processes:
#'
#' The directory for a DiskCache can be shared among multiple R processes. To
#' do this, each R process should have a DiskCache object that uses the same
#' directory. Each DiskCache will do pruning independently of the others, so if
#' they have different pruning parameters, then one DiskCache may remove cached
#' objects before another DiskCache would do so.
#'
#' Even though it is possible for multiple processes to share a DiskCache
#' directory, this should not be done on networked file systems, because of
#' slow performance of networked file systems can cause problems. If you need
#' a high-performance shared cache, you can use one built on a database like
#' 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)}
#' 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
#' 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
#' already been deleted.
#'
#'
#' @section Methods:
#'
#' 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
#' 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{\code{exists(key)}}{
#' Returns \code{TRUE} if the cache contains the key, otherwise
#' \code{FALSE}.
#' }
#' \item{\code{size()}}{
#' Returns the number of items currently in the cache.
#' }
#' \item{\code{keys()}}{
#' Returns a character vector of all keys currently in the cache.
#' }
#' \item{\code{reset()}}{
#' Clears all objects from the cache.
#' }
#' \item{\code{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}.
#' }
#' }
#'
#' @param dir Directory to store files for the cache. If \code{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.
#' @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.
#' @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.
#' @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
#' supported.
#' @param destroy_on_finalize If \code{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
#' 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
#' 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
#' 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.
#' @param logfile An optional filename or connection object to where logging
#' information will be written. To log to the console, use \code{stdout()}.
#'
#' @export
diskCache <- function(
dir = NULL,
max_size = 10 * 1024 ^ 2,
max_age = Inf,
max_n = Inf,
evict = c("lru", "fifo"),
destroy_on_finalize = FALSE,
missing = key_missing(),
exec_missing = FALSE,
logfile = NULL)
{
DiskCache$new(dir, max_size, max_age, max_n, evict, destroy_on_finalize,
missing, exec_missing, logfile)
}
DiskCache <- R6Class("DiskCache",
public = list(
initialize = function(
dir = NULL,
max_size = 10 * 1024 ^ 2,
max_age = Inf,
max_n = Inf,
evict = c("lru", "fifo"),
destroy_on_finalize = FALSE,
missing = key_missing(),
exec_missing = FALSE,
logfile = NULL)
{
if (exec_missing && (!is.function(missing) || length(formals(missing)) == 0)) {
stop("When `exec_missing` is true, `missing` must be a function that takes one argument.")
}
if (is.null(dir)) {
dir <- tempfile("DiskCache-")
}
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.")
if (!dirExists(dir)) {
private$log(paste0("initialize: Creating ", dir))
dir.create(dir, recursive = TRUE)
}
private$dir <- normalizePath(dir)
private$max_size <- max_size
private$max_age <- max_age
private$max_n <- max_n
private$evict <- match.arg(evict)
private$destroy_on_finalize <- destroy_on_finalize
private$missing <- missing
private$exec_missing <- exec_missing
private$logfile <- logfile
private$prune_last_time <- as.numeric(Sys.time())
},
get = function(key, missing = private$missing, exec_missing = private$exec_missing) {
private$log(paste0('get: key "', key, '"'))
self$is_destroyed(throw = TRUE)
validate_key(key)
private$maybe_prune_single(key)
filename <- private$key_to_filename(key)
# Instead of calling exists() before fetching the value, just try to
# fetch the value. This reduces the risk of a race condition when
# multiple processes share a cache.
read_error <- FALSE
tryCatch(
{
value <- suppressWarnings(readRDS(filename))
if (private$evict == "lru"){
Sys.setFileTime(filename, Sys.time())
}
},
error = function(e) {
read_error <<- TRUE
}
)
if (read_error) {
private$log(paste0('get: key "', key, '" is missing'))
if (exec_missing) {
if (!is.function(missing) || length(formals(missing)) == 0) {
stop("When `exec_missing` is true, `missing` must be a function that takes one argument.")
}
return(missing(key))
} else {
return(missing)
}
}
private$log(paste0('get: key "', key, '" found'))
value
},
set = function(key, value) {
private$log(paste0('set: key "', key, '"'))
self$is_destroyed(throw = TRUE)
validate_key(key)
file <- private$key_to_filename(key)
temp_file <- paste0(file, "-temp-", createUniqueId(8))
save_error <- FALSE
ref_object <- FALSE
tryCatch(
{
saveRDS(value, file = temp_file,
refhook = function(x) {
ref_object <<- TRUE
NULL
}
)
file.rename(temp_file, file)
},
error = function(e) {
save_error <<- TRUE
# Unlike file.remove(), unlink() does not raise warning if file does
# not exist.
unlink(temp_file)
}
)
if (save_error) {
private$log(paste0('set: key "', key, '" error'))
stop('Error setting value for key "', key, '".')
}
if (ref_object) {
private$log(paste0('set: value is a reference object'))
warning("A reference object was cached in a serialized format. The restored object may not work as expected.")
}
private$prune_throttled()
invisible(self)
},
exists = function(key) {
self$is_destroyed(throw = TRUE)
validate_key(key)
file.exists(private$key_to_filename(key))
},
# Return all keys in the cache
keys = function() {
self$is_destroyed(throw = TRUE)
files <- dir(private$dir, "\\.rds$")
sub("\\.rds$", "", files)
},
remove = function(key) {
private$log(paste0('remove: key "', key, '"'))
self$is_destroyed(throw = TRUE)
validate_key(key)
file.remove(private$key_to_filename(key))
invisible(self)
},
reset = function() {
private$log(paste0('reset'))
self$is_destroyed(throw = TRUE)
file.remove(dir(private$dir, "\\.rds$", full.names = TRUE))
invisible(self)
},
prune = function() {
# TODO: It would be good to add parameters `n` and `size`, so that the
# cache can be pruned to `max_n - n` and `max_size - size` before adding
# an object. Right now we prune after adding the object, so the cache
# can temporarily grow past the limits. The reason we don't do this now
# is because it is expensive to find the size of the serialized object
# before adding it.
private$log(paste0('prune'))
self$is_destroyed(throw = TRUE)
current_time <- Sys.time()
filenames <- dir(private$dir, "\\.rds$", full.names = TRUE)
info <- file.info(filenames)
info <- info[info$isdir == FALSE, ]
info$name <- rownames(info)
rownames(info) <- NULL
# Files could be removed between the dir() and file.info() calls. The
# entire row for such files will have NA values. Remove those rows.
info <- info[!is.na(info$size), ]
# 1. Remove any files where the age exceeds max age.
if (is.finite(private$max_age)) {
timediff <- as.numeric(current_time - info$mtime, units = "secs")
rm_idx <- timediff > private$max_age
if (any(rm_idx)) {
private$log(paste0("prune max_age: Removing ", paste(info$name[rm_idx], collapse = ", ")))
file.remove(info$name[rm_idx])
info <- info[!rm_idx, ]
}
}
# Sort objects by priority. The sorting is done in a function which can be
# called multiple times but only does the work the first time.
info_is_sorted <- FALSE
ensure_info_is_sorted <- function() {
if (info_is_sorted) return()
info <<- info[order(info$mtime, decreasing = TRUE), ]
info_is_sorted <<- TRUE
}
# 2. Remove files if there are too many.
if (is.finite(private$max_n) && nrow(info) > private$max_n) {
ensure_info_is_sorted()
rm_idx <- seq_len(nrow(info)) > private$max_n
private$log(paste0("prune max_n: Removing ", paste(info$name[rm_idx], collapse = ", ")))
rm_success <- file.remove(info$name[rm_idx])
info <- info[!rm_success, ]
}
# 3. Remove files if cache is too large.
if (is.finite(private$max_size) && sum(info$size) > private$max_size) {
ensure_info_is_sorted()
cum_size <- cumsum(info$size)
rm_idx <- cum_size > private$max_size
private$log(paste0("prune max_size: Removing ", paste(info$name[rm_idx], collapse = ", ")))
rm_success <- file.remove(info$name[rm_idx])
info <- info[!rm_success, ]
}
private$prune_last_time <- as.numeric(current_time)
invisible(self)
},
size = function() {
self$is_destroyed(throw = TRUE)
length(dir(private$dir, "\\.rds$"))
},
destroy = function() {
if (self$is_destroyed()) {
return(invisible(self))
}
private$log(paste0("destroy: Removing ", private$dir))
# First create a sentinel file so that other processes sharing this
# cache know that the cache is to be destroyed. This is needed because
# the recursive unlink is not atomic: another process can add a file to
# the directory after unlink starts removing files but before it removes
# the directory, and when that happens, the directory removal will fail.
file.create(file.path(private$dir, "__destroyed__"))
# Remove all the .rds files. This will not remove the setinel file.
file.remove(dir(private$dir, "\\.rds$", full.names = TRUE))
# Next remove dir recursively, including sentinel file.
unlink(private$dir, recursive = TRUE)
private$destroyed <- TRUE
invisible(self)
},
is_destroyed = function(throw = FALSE) {
if (!dirExists(private$dir) ||
file.exists(file.path(private$dir, "__destroyed__")))
{
# It's possible for another process to destroy a shared cache directory
private$destroyed <- TRUE
}
if (throw) {
if (private$destroyed) {
stop("Attempted to use cache which has been destroyed:\n ", private$dir)
}
} else {
private$destroyed
}
},
finalize = function() {
if (private$destroy_on_finalize) {
self$destroy()
}
}
),
private = list(
dir = NULL,
max_age = NULL,
max_size = NULL,
max_n = NULL,
evict = NULL,
destroy_on_finalize = NULL,
destroyed = FALSE,
missing = NULL,
exec_missing = FALSE,
logfile = NULL,
prune_throttle_counter = 0,
prune_last_time = NULL,
key_to_filename = function(key) {
validate_key(key)
# Additional validation. This 80-char limit is arbitrary, and is
# intended to avoid hitting a filename length limit on Windows.
if (nchar(key) > 80) {
stop("Invalid key: key must have fewer than 80 characters.")
}
file.path(private$dir, paste0(key, ".rds"))
},
# A wrapper for prune() that throttles it, because prune() can be
# expensive due to filesystem operations. This function will prune only
# once every 20 times it is called, or if it has been more than 5 seconds
# since the last time the cache was actually pruned, whichever is first.
# In the future, the behavior may be customizable.
prune_throttled = function() {
# Count the number of times prune() has been called.
private$prune_throttle_counter <- private$prune_throttle_counter + 1
if (private$prune_throttle_counter > 20 ||
private$prune_last_time - as.numeric(Sys.time()) > 5)
{
self$prune()
private$prune_throttle_counter <- 0
}
},
# Prunes a single object if it exceeds max_age. If the object does not
# exceed max_age, or if the object doesn't exist, do nothing.
maybe_prune_single = function(key) {
obj <- private$cache[[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)
}
},
log = function(text) {
if (is.null(private$logfile)) return()
text <- paste0(format(Sys.time(), "[%Y-%m-%d %H:%M:%OS3] DiskCache "), text)
writeLines(text, private$logfile)
}
)
)

366
R/cache-memory.R Normal file
View File

@@ -0,0 +1,366 @@
#' 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}.
#'
#' 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
#' 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
#' 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}.
#' 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}.
#'
#' When the cache is created, you can supply a value for \code{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.
#'
#' 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
#' 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
#' 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.
#'
#' 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
#' prints the missing key, you could do this:
#'
#' \preformatted{
#' diskCache(
#' missing = function(key) {
#' stop("Attempted to get missing key: ", key)
#' },
#' exec_missing = TRUE
#' )
#' }
#'
#' If you use this, the code that calls \code{get()} should be wrapped with
#' \code{\link{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()}.
#'
#' When a pruning occurs, if there are any objects that are older than
#' \code{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
#' individually.
#'
#' If the number of objects in the cache exceeds \code{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}.
#'
#' If the size of the objects in the cache exceeds \code{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
#' 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
#' 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},
#' 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
#' from the cache according to an eviction policy. The available eviction
#' policies are:
#'
#' \describe{
#' \item{\code{"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"}}{
#' First-in-first-out. The oldest objects will be removed.
#' }
#' }
#'
#' @section Methods:
#'
#' 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
#' 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{\code{exists(key)}}{
#' Returns \code{TRUE} if the cache contains the key, otherwise
#' \code{FALSE}.
#' }
#' \item{\code{size()}}{
#' Returns the number of items currently in the cache.
#' }
#' \item{\code{keys()}}{
#' Returns a character vector of all keys currently in the cache.
#' }
#' \item{\code{reset()}}{
#' Clears all objects from the cache.
#' }
#' \item{\code{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}.
#' }
#' }
#'
#' @inheritParams diskCache
#'
#' @export
memoryCache <- function(
max_size = 10 * 1024 ^ 2,
max_age = Inf,
max_n = Inf,
evict = c("lru", "fifo"),
missing = key_missing(),
exec_missing = FALSE,
logfile = NULL)
{
MemoryCache$new(max_size, max_age, max_n, evict, missing, exec_missing, logfile)
}
MemoryCache <- R6Class("MemoryCache",
public = list(
initialize = function(
max_size = 10 * 1024 ^ 2,
max_age = Inf,
max_n = Inf,
evict = c("lru", "fifo"),
missing = key_missing(),
exec_missing = FALSE,
logfile = NULL)
{
if (exec_missing && (!is.function(missing) || length(formals(missing)) == 0)) {
stop("When `exec_missing` is true, `missing` must be a function that takes one argument.")
}
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$max_size <- max_size
private$max_age <- max_age
private$max_n <- max_n
private$evict <- match.arg(evict)
private$missing <- missing
private$exec_missing <- exec_missing
private$logfile <- logfile
},
get = function(key, missing = private$missing, exec_missing = private$exec_missing) {
private$log(paste0('get: key "', key, '"'))
validate_key(key)
private$maybe_prune_single(key)
if (!self$exists(key)) {
private$log(paste0('get: key "', key, '" is missing'))
if (exec_missing) {
if (!is.function(missing) || length(formals(missing)) == 0) {
stop("When `exec_missing` is true, `missing` must be a function that takes one argument.")
}
return(missing(key))
} else {
return(missing)
}
}
private$log(paste0('get: key "', key, '" found'))
value <- private$cache[[key]]$value
value
},
set = function(key, value) {
private$log(paste0('set: key "', key, '"'))
validate_key(key)
time <- as.numeric(Sys.time())
# Only record size if we're actually using max_size for pruning.
if (is.finite(private$max_size)) {
# Reported size is rough! See ?object.size.
size <- as.numeric(object.size(value))
} else {
size <- NULL
}
private$cache[[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]])
},
keys = function() {
ls(private$cache, sorted = FALSE) # Faster with sorted=FALSE
},
remove = function(key) {
private$log(paste0('remove: key "', key, '"'))
validate_key(key)
rm(list = key, envir = private$cache)
invisible(self)
},
reset = function() {
private$log(paste0('reset'))
rm(list = self$keys(), envir = private$cache)
invisible(self)
},
prune = function() {
private$log(paste0('prune'))
info <- private$object_info()
# 1. Remove any objects where the age exceeds max age.
if (is.finite(private$max_age)) {
time <- as.numeric(Sys.time())
timediff <- time - info$mtime
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)
info <- info[!rm_idx, ]
}
}
# Sort objects by priority, according to eviction policy. The sorting is
# done in a function which can be called multiple times but only does
# the work the first time.
info_is_sorted <- FALSE
ensure_info_is_sorted <- function() {
if (info_is_sorted) return()
if (private$evict == "lru") {
info <<- info[order(info$atime, decreasing = TRUE), ]
} else if (private$evict == "fifo") {
info <<- info[order(info$mtime, decreasing = TRUE), ]
} else {
stop('Unknown eviction policy "', private$evict, '"')
}
info_is_sorted <<- TRUE
}
# 2. Remove objects if there are too many.
if (is.finite(private$max_n) && nrow(info) > private$max_n) {
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)
info <- info[!rm_idx, ]
}
# 3. Remove objects if cache is too large.
if (is.finite(private$max_size) && sum(info$size) > private$max_size) {
ensure_info_is_sorted()
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)
info <- info[!rm_idx, ]
}
invisible(self)
},
size = function() {
length(self$keys())
}
),
private = list(
cache = NULL,
max_age = NULL,
max_size = NULL,
max_n = NULL,
evict = NULL,
missing = NULL,
exec_missing = NULL,
logfile = NULL,
# Prunes a single object if it exceeds max_age. If the object does not
# exceed max_age, or if the object doesn't exist, do nothing.
maybe_prune_single = function(key) {
if (!is.finite(private$max_age)) return()
obj <- private$cache[[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)
}
},
object_info = function() {
keys <- ls(private$cache, sorted = FALSE)
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),
stringsAsFactors = FALSE
)
},
log = function(text) {
if (is.null(private$logfile)) return()
text <- paste0(format(Sys.time(), "[%Y-%m-%d %H:%M:%OS3] MemoryCache "), text)
writeLines(text, private$logfile)
}
)
)

33
R/cache-utils.R Normal file
View File

@@ -0,0 +1,33 @@
#' 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}}.
#'
#' @export
key_missing <- function() {
structure(list(), class = "key_missing")
}
#' @rdname key_missing
#' @export
is.key_missing <- function(x) {
inherits(x, "key_missing")
}
#' @export
print.key_missing <- function(x, ...) {
cat("<Key Missing>\n")
}
validate_key <- function(key) {
if (!is.character(key) || length(key) != 1 || nchar(key) == 0) {
stop("Invalid key: key must be single non-empty string.")
}
if (grepl("[^a-z0-9]", key)) {
stop("Invalid key: ", key, ". Only lowercase letters and numbers are allowed.")
}
}

627
R/conditions.R Normal file
View File

@@ -0,0 +1,627 @@
#' Stack trace manipulation functions
#'
#' 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}.
#'
#' @examples
#' # Keeps tryCatch and withVisible related calls off the
#' # pretty-printed stack trace
#'
#' visibleFunction1 <- function() {
#' stop("Kaboom!")
#' }
#'
#' visibleFunction2 <- function() {
#' visibleFunction1()
#' }
#'
#' hiddenFunction <- function(expr) {
#' expr
#' }
#'
#' # An example without ..stacktraceon/off.. manipulation.
#' # The outer "try" is just to prevent example() from stopping.
#' try({
#' # The withLogErrors call ensures that stack traces are captured
#' # and that errors that bubble up are logged using warning().
#' withLogErrors({
#' # tryCatch and withVisible are just here to add some noise to
#' # the stack trace.
#' tryCatch(
#' withVisible({
#' hiddenFunction(visibleFunction2())
#' })
#' )
#' })
#' })
#'
#' # Now the same example, but with ..stacktraceon/off.. to hide some
#' # of the less-interesting bits (tryCatch and withVisible).
#' ..stacktraceoff..({
#' try({
#' withLogErrors({
#' tryCatch(
#' withVisible(
#' hiddenFunction(
#' ..stacktraceon..(visibleFunction2())
#' )
#' )
#' )
#' })
#' })
#' })
#'
#'
#' @name stacktrace
#' @rdname stacktrace
#' @keywords internal
NULL
getCallNames <- function(calls) {
sapply(calls, function(call) {
if (is.function(call[[1]])) {
"<Anonymous>"
} else if (inherits(call[[1]], "call")) {
paste0(format(call[[1]]), collapse = " ")
} else if (typeof(call[[1]]) == "promise") {
"<Promise>"
} else {
paste0(as.character(call[[1]]), collapse = " ")
}
})
}
getLocs <- function(calls) {
vapply(calls, function(call) {
srcref <- attr(call, "srcref", exact = TRUE)
if (!is.null(srcref)) {
srcfile <- attr(srcref, "srcfile", exact = TRUE)
if (!is.null(srcfile) && !is.null(srcfile$filename)) {
loc <- paste0(srcfile$filename, "#", srcref[[1]])
return(paste0(" [", loc, "]"))
}
}
return("")
}, character(1))
}
getCallCategories <- function(calls) {
vapply(calls, function(call) {
srcref <- attr(call, "srcref", exact = TRUE)
if (!is.null(srcref)) {
srcfile <- attr(srcref, "srcfile", exact = TRUE)
if (!is.null(srcfile)) {
if (!is.null(srcfile$original)) {
return("pkg")
} else {
return("user")
}
}
}
return("")
}, 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
#' because these calls catch the error and prevent it from traveling up to the
#' condition handler installed by \code{captureStackTraces}.
#'
#' @param expr The expression to wrap.
#' @rdname stacktrace
#' @export
captureStackTraces <- function(expr) {
promises::with_promise_domain(createStackTracePromiseDomain(),
expr
)
}
#' @include globals.R
.globals$deepStack <- NULL
createStackTracePromiseDomain <- function() {
# These are actually stateless, we wouldn't have to create a new one each time
# if we didn't want to. They're pretty cheap though.
d <- promises::new_promise_domain(
wrapOnFulfilled = function(onFulfilled) {
force(onFulfilled)
# Subscription time
if (deepStacksEnabled()) {
currentStack <- sys.calls()
currentParents <- sys.parents()
attr(currentStack, "parents") <- currentParents
currentDeepStack <- .globals$deepStack
}
function(...) {
# Fulfill time
if (deepStacksEnabled()) {
origDeepStack <- .globals$deepStack
.globals$deepStack <- c(currentDeepStack, list(currentStack))
on.exit(.globals$deepStack <- origDeepStack, add = TRUE)
}
withCallingHandlers(
onFulfilled(...),
error = doCaptureStack
)
}
},
wrapOnRejected = function(onRejected) {
force(onRejected)
# Subscription time
if (deepStacksEnabled()) {
currentStack <- sys.calls()
currentParents <- sys.parents()
attr(currentStack, "parents") <- currentParents
currentDeepStack <- .globals$deepStack
}
function(...) {
# Fulfill time
if (deepStacksEnabled()) {
origDeepStack <- .globals$deepStack
.globals$deepStack <- c(currentDeepStack, list(currentStack))
on.exit(.globals$deepStack <- origDeepStack, add = TRUE)
}
withCallingHandlers(
onRejected(...),
error = doCaptureStack
)
}
},
wrapSync = function(expr) {
withCallingHandlers(expr,
error = doCaptureStack
)
},
onError = doCaptureStack
)
}
deepStacksEnabled <- function() {
getOption("shiny.deepstacktrace", TRUE)
}
doCaptureStack <- function(e) {
if (is.null(attr(e, "stack.trace", exact = TRUE))) {
calls <- sys.calls()
parents <- sys.parents()
attr(calls, "parents") <- parents
attr(e, "stack.trace") <- calls
}
if (deepStacksEnabled()) {
if (is.null(attr(e, "deep.stack.trace", exact = TRUE)) && !is.null(.globals$deepStack)) {
attr(e, "deep.stack.trace") <- .globals$deepStack
}
}
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
#' (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}.
#' @rdname stacktrace
#' @export
withLogErrors <- function(expr,
full = getOption("shiny.fullstacktrace", FALSE),
offset = getOption("shiny.stacktraceoffset", TRUE)) {
withCallingHandlers(
{
result <- captureStackTraces(expr)
# Handle expr being an async operation
if (promises::is.promise(result)) {
result <- promises::catch(result, function(cond) {
# Don't print shiny.silent.error (i.e. validation errors)
if (inherits(cond, "shiny.silent.error")) return()
if (isTRUE(getOption("show.error.messages"))) {
printError(cond, full = full, offset = offset)
}
})
}
result
},
error = function(cond) {
# Don't print shiny.silent.error (i.e. validation errors)
if (inherits(cond, "shiny.silent.error")) return()
if (isTRUE(getOption("show.error.messages"))) {
printError(cond, full = full, offset = offset)
}
}
)
}
#' @details \code{printError} prints the error and stack trace (if any) using
#' \code{warning(immediate.=TRUE)}. \code{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
#' deems uninteresting will be hidden.
#' @param offset If \code{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
#' left alone (traditional R treatment where the srcref is of the callsite).
#' @rdname stacktrace
#' @export
printError <- function(cond,
full = getOption("shiny.fullstacktrace", FALSE),
offset = getOption("shiny.stacktraceoffset", TRUE)) {
warning(call. = FALSE, immediate. = TRUE, sprintf("Error in %s: %s",
getCallNames(list(conditionCall(cond))), conditionMessage(cond)))
printStackTrace(cond, full = full, offset = offset)
}
#' @rdname stacktrace
#' @export
printStackTrace <- function(cond,
full = getOption("shiny.fullstacktrace", FALSE),
offset = getOption("shiny.stacktraceoffset", TRUE)) {
should_drop <- !full
should_strip <- !full
should_prune <- !full
stackTraceCalls <- c(
attr(cond, "deep.stack.trace", exact = TRUE),
list(attr(cond, "stack.trace", exact = TRUE))
)
stackTraceParents <- lapply(stackTraceCalls, attr, which = "parents", exact = TRUE)
stackTraceCallNames <- lapply(stackTraceCalls, getCallNames)
stackTraceCalls <- lapply(stackTraceCalls, offsetSrcrefs, offset = offset)
# Use dropTrivialFrames logic to remove trailing bits (.handleSimpleError, h)
if (should_drop) {
# toKeep is a list of logical vectors, of which elements (stack frames) to keep
toKeep <- lapply(stackTraceCallNames, dropTrivialFrames)
# We apply the list of logical vector indices to each data structure
stackTraceCalls <- mapply(stackTraceCalls, FUN = `[`, toKeep, SIMPLIFY = FALSE)
stackTraceCallNames <- mapply(stackTraceCallNames, FUN = `[`, toKeep, SIMPLIFY = FALSE)
stackTraceParents <- mapply(stackTraceParents, FUN = `[`, toKeep, SIMPLIFY = FALSE)
}
delayedAssign("all_true", {
# List of logical vectors that are all TRUE, the same shape as
# stackTraceCallNames. Delay the evaluation so we don't create it unless
# we need it, but if we need it twice then we don't pay to create it twice.
lapply(stackTraceCallNames, function(st) {
rep_len(TRUE, length(st))
})
})
# stripStackTraces and lapply(stackTraceParents, pruneStackTrace) return lists
# of logical vectors. Use mapply(FUN = `&`) to boolean-and each pair of the
# logical vectors.
toShow <- mapply(
if (should_strip) stripStackTraces(stackTraceCallNames) else all_true,
if (should_prune) lapply(stackTraceParents, pruneStackTrace) else all_true,
FUN = `&`,
SIMPLIFY = FALSE
)
dfs <- mapply(seq_along(stackTraceCalls), rev(stackTraceCalls), rev(stackTraceCallNames), rev(toShow), FUN = function(i, calls, nms, index) {
st <- data.frame(
num = rev(which(index)),
call = rev(nms[index]),
loc = rev(getLocs(calls[index])),
category = rev(getCallCategories(calls[index])),
stringsAsFactors = FALSE
)
if (i != 1) {
message("From earlier call:")
}
if (nrow(st) == 0) {
message(" [No stack trace available]")
} else {
width <- floor(log10(max(st$num))) + 1
formatted <- paste0(
" ",
formatC(st$num, width = width),
": ",
mapply(paste0(st$call, st$loc), st$category, FUN = function(name, category) {
if (category == "pkg")
crayon::silver(name)
else if (category == "user")
crayon::blue$bold(name)
else
crayon::white(name)
}),
"\n"
)
cat(file = stderr(), formatted, sep = "")
}
st
}, SIMPLIFY = FALSE)
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
#' and line number, if available). It was deprecated after shiny 1.0.5 because
#' it doesn't support deep stack traces.
#' @rdname stacktrace
#' @export
extractStackTrace <- function(calls,
full = getOption("shiny.fullstacktrace", FALSE),
offset = getOption("shiny.stacktraceoffset", TRUE)) {
shinyDeprecated(NULL,
"extractStackTrace is deprecated. Please contact the Shiny team if you were using this functionality.",
version = "1.0.5")
srcrefs <- getSrcRefs(calls)
if (offset) {
# Offset calls vs. srcrefs by 1 to make them more intuitive.
# E.g. for "foo [bar.R:10]", line 10 of bar.R will be part of
# the definition of foo().
srcrefs <- c(utils::tail(srcrefs, -1), list(NULL))
}
calls <- setSrcRefs(calls, srcrefs)
callnames <- getCallNames(calls)
# Hide and show parts of the callstack based on ..stacktrace(on|off)..
if (full) {
toShow <- rep.int(TRUE, length(calls))
} else {
# Remove stop(), .handleSimpleError(), and h() calls from the end of
# the calls--they don't add any helpful information. But only remove
# the last *contiguous* block of them, and then, only if they are the
# last thing in the calls list.
hideable <- callnames %in% c("stop", ".handleSimpleError", "h")
# What's the last that *didn't* match stop/.handleSimpleError/h?
lastGoodCall <- max(which(!hideable))
toRemove <- length(calls) - lastGoodCall
# But don't remove more than 5 levels--that's an indication we might
# have gotten it wrong, I guess
if (toRemove > 0 && toRemove < 5) {
calls <- utils::head(calls, -toRemove)
callnames <- utils::head(callnames, -toRemove)
}
# This uses a ref-counting scheme. It might make sense to switch this
# to a toggling scheme, so the most recent ..stacktrace(on|off)..
# directive wins, regardless of what came before it.
# Also explicitly remove ..stacktraceon.. because it can appear with
# score > 0 but still should never be shown.
score <- rep.int(0, length(callnames))
score[callnames == "..stacktraceoff.."] <- -1
score[callnames == "..stacktraceon.."] <- 1
toShow <- (1 + cumsum(score)) > 0 & !(callnames %in% c("..stacktraceon..", "..stacktraceoff..", "..stacktracefloor.."))
# doTryCatch, tryCatchOne, and tryCatchList are not informative--they're
# just internals for tryCatch
toShow <- toShow & !(callnames %in% c("doTryCatch", "tryCatchOne", "tryCatchList"))
}
calls <- calls[toShow]
calls <- rev(calls) # Show in traceback() order
index <- rev(which(toShow))
width <- floor(log10(max(index))) + 1
data.frame(
num = index,
call = getCallNames(calls),
loc = getLocs(calls),
category = getCallCategories(calls),
stringsAsFactors = FALSE
)
}
stripStackTraces <- function(stackTraces, values = FALSE) {
score <- 1L # >=1: show, <=0: hide
lapply(seq_along(stackTraces), function(i) {
res <- stripOneStackTrace(stackTraces[[i]], i != 1, score)
score <<- res$score
toShow <- as.logical(res$trace)
if (values) {
as.character(stackTraces[[i]][toShow])
} else {
as.logical(toShow)
}
})
}
stripOneStackTrace <- function(stackTrace, truncateFloor, startingScore) {
prefix <- logical(0)
if (truncateFloor) {
indexOfFloor <- utils::tail(which(stackTrace == "..stacktracefloor.."), 1)
if (length(indexOfFloor)) {
stackTrace <- stackTrace[(indexOfFloor+1L):length(stackTrace)]
prefix <- rep_len(FALSE, indexOfFloor)
}
}
if (length(stackTrace) == 0) {
return(list(score = startingScore, character(0)))
}
score <- rep.int(0L, length(stackTrace))
score[stackTrace == "..stacktraceon.."] <- 1L
score[stackTrace == "..stacktraceoff.."] <- -1L
score <- startingScore + cumsum(score)
toShow <- score > 0 & !(stackTrace %in% c("..stacktraceon..", "..stacktraceoff..", "..stacktracefloor.."))
list(score = utils::tail(score, 1), trace = c(prefix, toShow))
}
# Given sys.parents() (which corresponds to sys.calls()), return a logical index
# that prunes each subtree so that only the final branch remains. The result,
# when applied to sys.calls(), is a linear list of calls without any "wrapper"
# functions like tryCatch, try, with, hybrid_chain, etc. While these are often
# part of the active call stack, they rarely are helpful when trying to identify
# a broken bit of code.
pruneStackTrace <- function(parents) {
# Detect nodes that are not the last child. This is necessary, but not
# sufficient; we also need to drop nodes that are the last child, but one of
# their ancestors is not.
is_dupe <- duplicated(parents, fromLast = TRUE)
# The index of the most recently seen node that was actually kept instead of
# dropped.
current_node <- 0
# Loop over the parent indices. Anything that is not parented by current_node
# (a.k.a. last-known-good node), or is a dupe, can be discarded. Anything that
# is kept becomes the new current_node.
include <- vapply(seq_along(parents), function(i) {
if (!is_dupe[[i]] && parents[[i]] == current_node) {
current_node <<- i
TRUE
} else {
FALSE
}
}, FUN.VALUE = logical(1))
include
}
dropTrivialFrames <- function(callnames) {
# Remove stop(), .handleSimpleError(), and h() calls from the end of
# the calls--they don't add any helpful information. But only remove
# the last *contiguous* block of them, and then, only if they are the
# last thing in the calls list.
hideable <- callnames %in% c(".handleSimpleError", "h", "base$wrapOnFulfilled")
# What's the last that *didn't* match stop/.handleSimpleError/h?
lastGoodCall <- max(which(!hideable))
toRemove <- length(callnames) - lastGoodCall
c(
rep_len(TRUE, length(callnames) - toRemove),
rep_len(FALSE, toRemove)
)
}
offsetSrcrefs <- function(calls, offset = TRUE) {
if (offset) {
srcrefs <- getSrcRefs(calls)
# Offset calls vs. srcrefs by 1 to make them more intuitive.
# E.g. for "foo [bar.R:10]", line 10 of bar.R will be part of
# the definition of foo().
srcrefs <- c(utils::tail(srcrefs, -1), list(NULL))
calls <- setSrcRefs(calls, srcrefs)
}
calls
}
#' @details \code{formatStackTrace} is similar to \code{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.
#' @rdname stacktrace
#' @export
formatStackTrace <- function(calls, indent = " ",
full = getOption("shiny.fullstacktrace", FALSE),
offset = getOption("shiny.stacktraceoffset", TRUE)) {
shinyDeprecated(NULL,
"extractStackTrace is deprecated. Please contact the Shiny team if you were using this functionality.",
version = "1.0.5")
st <- extractStackTrace(calls, full = full, offset = offset)
if (nrow(st) == 0) {
return(character(0))
}
width <- floor(log10(max(st$num))) + 1
paste0(
indent,
formatC(st$num, width = width),
": ",
mapply(paste0(st$call, st$loc), st$category, FUN = function(name, category) {
if (category == "pkg")
crayon::silver(name)
else if (category == "user")
crayon::blue$bold(name)
else
crayon::white(name)
})
)
}
getSrcRefs <- function(calls) {
lapply(calls, function(call) {
attr(call, "srcref", exact = TRUE)
})
}
setSrcRefs <- function(calls, srcrefs) {
mapply(function(call, srcref) {
structure(call, srcref = srcref)
}, calls, srcrefs)
}
stripStackTrace <- function(cond) {
conditionStackTrace(cond) <- NULL
}
#' @details \code{conditionStackTrace} and \code{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}).
#' @rdname stacktrace
#' @export
conditionStackTrace <- function(cond) {
attr(cond, "stack.trace", exact = TRUE)
}
#' @param value The stack trace value to assign to the condition.
#' @rdname stacktrace
#' @export
`conditionStackTrace<-` <- function(cond, value) {
attr(cond, "stack.trace") <- value
invisible(cond)
}
#' @details The two functions \code{..stacktraceon..} and
#' \code{..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
#' call inwards. Each ..stacktraceoff.. decrements the state by one, and each
#' ..stacktraceon.. increments the state by one. Any stack trace frame whose
#' value is less than 1 is hidden, and finally, the ..stacktraceon.. and
#' ..stacktraceoff.. calls themselves are hidden too.
#'
#' @rdname stacktrace
#' @export
..stacktraceon.. <- function(expr) expr
#' @rdname stacktrace
#' @export
..stacktraceoff.. <- function(expr) expr
..stacktracefloor.. <- function(expr) expr

157
R/diagnose.R Normal file
View File

@@ -0,0 +1,157 @@
# Analyze an R file for possible extra or missing commas. Returns FALSE if any
# problems detected, TRUE otherwise.
diagnoseCode <- function(path = NULL, text = NULL) {
if (!xor(is.null(path), is.null(text))) {
stop("Must specify `path` or `text`, but not both.")
}
if (!is.null(path)) {
tokens <- sourcetools::tokenize_file(path)
} else {
tokens <- sourcetools::tokenize_string(text)
}
find_scopes <- function(tokens) {
# Strip whitespace and comments
tokens <- tokens[!(tokens$type %in% c("whitespace", "comment")),]
# Replace various types of things with "value"
tokens$type[tokens$type %in% c("string", "number", "symbol", "keyword")] <- "value"
# Record types for close and open brace/bracket/parens, and commas
brace_idx <- tokens$value %in% c("(", ")", "{", "}", "[", "]", ",")
tokens$type[brace_idx] <- tokens$value[brace_idx]
# Stack-related function for recording scope. Starting scope is "{"
stack <- "{"
push <- function(x) {
stack <<- c(stack, x)
}
pop <- function() {
if (length(stack) == 1) {
# Stack underflow, but we need to keep going
return(NA_character_)
}
res <- stack[length(stack)]
stack <<- stack[-length(stack)]
res
}
peek <- function() {
stack[length(stack)]
}
# First, establish a scope for each token. For opening and closing
# braces/brackets/parens, the scope at that location is the *surrounding*
# scope, not the new scope created by the brace/bracket/paren.
for (i in seq_len(nrow(tokens))) {
value <- tokens$value[i]
tokens$scope[i] <- peek()
if (value %in% c("{", "(", "[")) {
push(value)
} else if (value == "}") {
if (!identical(pop(), "{"))
tokens$err[i] <- "unmatched_brace"
# For closing brace/paren/bracket, get the scope after popping
tokens$scope[i] <- peek()
} else if (value == ")") {
if (!identical(pop(), "("))
tokens$err[i] <- "unmatched_paren"
tokens$scope[i] <- peek()
} else if (value == "]") {
if (!identical(pop(), "["))
tokens$err[i] <- "unmatched_bracket"
tokens$scope[i] <- peek()
}
}
tokens
}
check_commas <- function(tokens) {
# Find extra and missing commas
tokens$err <- mapply(
tokens$type,
c("", tokens$type[-length(tokens$type)]),
c(tokens$type[-1], ""),
tokens$scope,
tokens$err,
SIMPLIFY = FALSE,
FUN = function(type, prevType, nextType, scope, err) {
# If an error was already found, just return it. This could have
# happened in the brace/paren/bracket matching phase.
if (!is.na(err)) {
return(err)
}
if (scope == "(") {
if (type == "," &&
(prevType == "(" || prevType == "," || nextType == ")"))
{
return("extra_comma")
}
if ((prevType == ")" && type == "value") ||
(prevType == "value" && type == "value")) {
return("missing_comma")
}
}
NA_character_
}
)
tokens
}
tokens$err <- NA_character_
tokens <- find_scopes(tokens)
tokens <- check_commas(tokens)
# No errors found
if (all(is.na(tokens$err))) {
return(TRUE)
}
# If we got here, errors were found; print messages.
if (!is.null(path)) {
lines <- readLines(path)
} else {
lines <- strsplit(text, "\n")[[1]]
}
# Print out the line of code with the error, and point to the column with
# the error.
show_code_error <- function(msg, lines, row, col) {
message(paste0(
msg, "\n",
row, ":", lines[row], "\n",
paste0(rep.int(" ", nchar(as.character(row)) + 1), collapse = ""),
gsub(perl = TRUE, "[^\\s]", " ", substr(lines[row], 1, col-1)), "^"
))
}
err_idx <- which(!is.na(tokens$err))
msg <- ""
for (i in err_idx) {
row <- tokens$row[i]
col <- tokens$column[i]
err <- tokens$err[i]
if (err == "missing_comma") {
show_code_error("Possible missing comma at:", lines, row, col)
} else if (err == "extra_comma") {
show_code_error("Possible extra comma at:", lines, row, col)
} else if (err == "unmatched_brace") {
show_code_error("Possible unmatched '}' at:", lines, row, col)
} else if (err == "unmatched_paren") {
show_code_error("Possible unmatched ')' at:", lines, row, col)
} else if (err == "unmatched_bracket") {
show_code_error("Possible unmatched ']' at:", lines, row, col)
}
}
return(FALSE)
}

View File

@@ -20,6 +20,18 @@
# form upload, i.e. traditional HTTP POST-based file upload) doesn't work with
# the websockets package's HTTP server at the moment.
# @description Returns a file's extension, with a leading dot, if one can be
# found. A valid extension contains only alphanumeric characters. If there is
# no extension, or if it contains non-alphanumeric characters, an empty
# string is returned.
# @param x character vector giving file paths.
# @return The extension of \code{x}, with a leading dot, if one was found.
# Otherwise, an empty character vector.
maybeGetExtension <- function(x) {
ext <- tools::file_ext(x)
ifelse(ext == "", ext, paste0(".", ext))
}
FileUploadOperation <- R6Class(
'FileUploadOperation',
portable = FALSE,
@@ -52,8 +64,9 @@ FileUploadOperation <- R6Class(
.currentFileInfo <<- file
.pendingFileInfos <<- tail(.pendingFileInfos, -1)
filename <- file.path(.dir, as.character(length(.files$name)))
row <- data.frame(name=file$name, size=file$size, type=file$type,
fileBasename <- basename(.currentFileInfo$name)
filename <- file.path(.dir, paste0(as.character(length(.files$name)), maybeGetExtension(fileBasename)))
row <- data.frame(name=fileBasename, size=file$size, type=file$type,
datapath=filename, stringsAsFactors=FALSE)
if (length(.files$name) == 0)
@@ -81,33 +94,47 @@ FileUploadOperation <- R6Class(
#' @include map.R
FileUploadContext <- R6Class(
'FileUploadContext',
portable = FALSE,
class = FALSE,
private = list(
basedir = character(0),
operations = 'Map',
ids = character(0) # Keep track of all ids used for file uploads
),
public = list(
.basedir = character(0),
.operations = 'Map',
initialize = function(dir=tempdir()) {
.basedir <<- dir
.operations <<- Map$new()
private$basedir <- dir
private$operations <- Map$new()
},
createUploadOperation = function(fileInfos) {
while (TRUE) {
id <- paste(as.raw(p_runif(12, min=0, max=0xFF)), collapse='')
dir <- file.path(.basedir, id)
id <- createUniqueId(12)
private$ids <- c(private$ids, id)
dir <- file.path(private$basedir, id)
if (!dir.create(dir))
next
op <- FileUploadOperation$new(self, id, dir, fileInfos)
.operations$set(id, op)
private$operations$set(id, op)
return(id)
}
},
getUploadOperation = function(jobId) {
.operations$get(jobId)
private$operations$get(jobId)
},
onJobFinished = function(jobId) {
.operations$remove(jobId)
private$operations$remove(jobId)
},
# Remove the directories containing file uploads; this is to be called when
# a session ends.
rmUploadDirs = function() {
# Make sure all_paths is underneath the tempdir()
if (!grepl(normalizePath(tempdir()), normalizePath(private$basedir), fixed = TRUE)) {
stop("Won't remove upload path ", private$basedir,
"because it is not under tempdir(): ", tempdir())
}
all_paths <- file.path(private$basedir, private$ids)
unlink(all_paths, recursive = TRUE)
}
)
)

75
R/font-awesome.R Normal file
View File

@@ -0,0 +1,75 @@
font_awesome_brands <- c(
"500px", "accessible-icon", "accusoft", "adn", "adversal",
"affiliatetheme", "algolia", "alipay", "amazon", "amazon-pay",
"amilia", "android", "angellist", "angrycreative", "angular",
"app-store", "app-store-ios", "apper", "apple", "apple-pay",
"asymmetrik", "audible", "autoprefixer", "avianex", "aviato",
"aws", "bandcamp", "behance", "behance-square", "bimobject",
"bitbucket", "bitcoin", "bity", "black-tie", "blackberry", "blogger",
"blogger-b", "bluetooth", "bluetooth-b", "btc", "buromobelexperte",
"buysellads", "cc-amazon-pay", "cc-amex", "cc-apple-pay", "cc-diners-club",
"cc-discover", "cc-jcb", "cc-mastercard", "cc-paypal", "cc-stripe",
"cc-visa", "centercode", "chrome", "cloudscale", "cloudsmith",
"cloudversify", "codepen", "codiepie", "connectdevelop", "contao",
"cpanel", "creative-commons", "creative-commons-by", "creative-commons-nc",
"creative-commons-nc-eu", "creative-commons-nc-jp", "creative-commons-nd",
"creative-commons-pd", "creative-commons-pd-alt", "creative-commons-remix",
"creative-commons-sa", "creative-commons-sampling", "creative-commons-sampling-plus",
"creative-commons-share", "css3", "css3-alt", "cuttlefish", "d-and-d",
"dashcube", "delicious", "deploydog", "deskpro", "deviantart",
"digg", "digital-ocean", "discord", "discourse", "dochub", "docker",
"draft2digital", "dribbble", "dribbble-square", "dropbox", "drupal",
"dyalog", "earlybirds", "ebay", "edge", "elementor", "ello",
"ember", "empire", "envira", "erlang", "ethereum", "etsy", "expeditedssl",
"facebook", "facebook-f", "facebook-messenger", "facebook-square",
"firefox", "first-order", "first-order-alt", "firstdraft", "flickr",
"flipboard", "fly", "font-awesome", "font-awesome-alt", "font-awesome-flag",
"font-awesome-logo-full", "fonticons", "fonticons-fi", "fort-awesome",
"fort-awesome-alt", "forumbee", "foursquare", "free-code-camp",
"freebsd", "fulcrum", "galactic-republic", "galactic-senate",
"get-pocket", "gg", "gg-circle", "git", "git-square", "github",
"github-alt", "github-square", "gitkraken", "gitlab", "gitter",
"glide", "glide-g", "gofore", "goodreads", "goodreads-g", "google",
"google-drive", "google-play", "google-plus", "google-plus-g",
"google-plus-square", "google-wallet", "gratipay", "grav", "gripfire",
"grunt", "gulp", "hacker-news", "hacker-news-square", "hackerrank",
"hips", "hire-a-helper", "hooli", "hornbill", "hotjar", "houzz",
"html5", "hubspot", "imdb", "instagram", "internet-explorer",
"ioxhost", "itunes", "itunes-note", "java", "jedi-order", "jenkins",
"joget", "joomla", "js", "js-square", "jsfiddle", "kaggle", "keybase",
"keycdn", "kickstarter", "kickstarter-k", "korvue", "laravel",
"lastfm", "lastfm-square", "leanpub", "less", "line", "linkedin",
"linkedin-in", "linode", "linux", "lyft", "magento", "mailchimp",
"mandalorian", "markdown", "mastodon", "maxcdn", "medapps", "medium",
"medium-m", "medrt", "meetup", "megaport", "microsoft", "mix",
"mixcloud", "mizuni", "modx", "monero", "napster", "neos", "nimblr",
"nintendo-switch", "node", "node-js", "npm", "ns8", "nutritionix",
"odnoklassniki", "odnoklassniki-square", "old-republic", "opencart",
"openid", "opera", "optin-monster", "osi", "page4", "pagelines",
"palfed", "patreon", "paypal", "periscope", "phabricator", "phoenix-framework",
"phoenix-squadron", "php", "pied-piper", "pied-piper-alt", "pied-piper-hat",
"pied-piper-pp", "pinterest", "pinterest-p", "pinterest-square",
"playstation", "product-hunt", "pushed", "python", "qq", "quinscape",
"quora", "r-project", "ravelry", "react", "readme", "rebel",
"red-river", "reddit", "reddit-alien", "reddit-square", "rendact",
"renren", "replyd", "researchgate", "resolving", "rev", "rocketchat",
"rockrms", "safari", "sass", "schlix", "scribd", "searchengin",
"sellcast", "sellsy", "servicestack", "shirtsinbulk", "shopware",
"simplybuilt", "sistrix", "sith", "skyatlas", "skype", "slack",
"slack-hash", "slideshare", "snapchat", "snapchat-ghost", "snapchat-square",
"soundcloud", "speakap", "spotify", "squarespace", "stack-exchange",
"stack-overflow", "staylinked", "steam", "steam-square", "steam-symbol",
"sticker-mule", "strava", "stripe", "stripe-s", "studiovinari",
"stumbleupon", "stumbleupon-circle", "superpowers", "supple",
"teamspeak", "telegram", "telegram-plane", "tencent-weibo", "the-red-yeti",
"themeco", "themeisle", "trade-federation", "trello", "tripadvisor",
"tumblr", "tumblr-square", "twitch", "twitter", "twitter-square",
"typo3", "uber", "uikit", "uniregistry", "untappd", "usb", "ussunnah",
"vaadin", "viacoin", "viadeo", "viadeo-square", "viber", "vimeo",
"vimeo-square", "vimeo-v", "vine", "vk", "vnv", "vuejs", "weebly",
"weibo", "weixin", "whatsapp", "whatsapp-square", "whmcs", "wikipedia-w",
"windows", "wix", "wolf-pack-battalion", "wordpress", "wordpress-simple",
"wpbeginner", "wpexplorer", "wpforms", "xbox", "xing", "xing-square",
"y-combinator", "yahoo", "yandex", "yandex-international", "yelp",
"yoast", "youtube", "youtube-square", "zhihu"
)

View File

@@ -5,5 +5,19 @@
# 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(reinitializeSeed())
withPrivateSeed(set.seed(NULL))
}
.onAttach <- function(libname, pkgname) {
# Check for htmlwidgets version, if installed. As of Shiny 0.12.0 and
# htmlwidgets 0.4, both packages switched from RJSONIO to jsonlite. Because of
# this change, Shiny 0.12.0 will work only with htmlwidgets >= 0.4, and vice
# versa.
if (system.file(package = "htmlwidgets") != "" &&
utils::packageVersion("htmlwidgets") < "0.4") {
packageStartupMessage(
"This version of Shiny is designed to work with htmlwidgets >= 0.4. ",
"Please upgrade your version of htmlwidgets."
)
}
}

571
R/graph.R
View File

@@ -1,7 +1,58 @@
writeReactLog <- function(file=stdout()) {
cat(RJSONIO::toJSON(.graphStack$as_list(), 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
@@ -24,80 +75,494 @@ writeReactLog <- function(file=stdout()) {
#'
#' 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 \code{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 \code{reactlogShow()} explicitly.
#'
#' For security and performance reasons, do not enable
#' \code{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.
#'
#' @name reactlog
NULL
#' @describeIn reactlog Return a list of reactive information. Can be used in conjunction with
#' \code{reactlog::\link[reactlog]{reactlog_show}} to later display the reactlog graph.
#' @export
showReactLog <- function() {
browseURL(renderReactLog())
reactlog <- function() {
rLog$asList()
}
renderReactLog <- function() {
templateFile <- system.file('www/reactive-graph.html', package='shiny')
html <- paste(readLines(templateFile, warn=FALSE), collapse='\r\n')
tc <- textConnection(NULL, 'w')
on.exit(close(tc))
writeReactLog(tc)
cat('\n', file=tc)
flush(tc)
html <- sub('__DATA__', paste(textConnectionValue(tc), collapse='\r\n'), html, fixed=TRUE)
file <- tempfile(fileext = '.html')
writeLines(html, file)
return(file)
#' @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 \code{\link{reactlogShow}}
#' @export
# legacy purposes
showReactLog <- function(time = TRUE) {
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()
}
.graphAppend <- function(logEntry, domain = getDefaultReactiveDomain()) {
if (isTRUE(getOption('shiny.reactlog')))
.graphStack$push(logEntry)
# 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]]
if (!is.null(domain)) {
domain$reactlog(logEntry)
reactlog_info <- suggests_pkgs[grepl("reactlog", suggests_pkgs)]
if (length(reactlog_info) == 0) {
stop("reactlog can not be found in shiny DESCRIPTION file")
}
reactlog_info <- sub("^[^\\(]*\\(", "", reactlog_info)
reactlog_info <- sub("\\)[^\\)]*$", "", reactlog_info)
reactlog_info <- sub("^[>= ]*", "", reactlog_info)
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=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) {
.graphAppend(list(action='exit', id=id))
}
asList = function() {
ret <- self$logStack$as_list()
attr(ret, "version") <- "1"
ret
},
.graphValueChange <- function(label, value) {
.graphAppend(list(
action = 'valueChange',
id = label,
value = paste(capture.output(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) {
output <- try(silent = TRUE, {
utils::capture.output(utils::str(value))
})
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")

95
R/history.R Normal file
View File

@@ -0,0 +1,95 @@
#' @include reactive-domains.R
NULL
#' @include reactives.R
NULL
#' Get the query string / hash component from the URL
#'
#' Two user friendly wrappers for getting the query string and the hash
#' component from the app's URL.
#'
#' These can be particularly useful if you want to display different content
#' depending on the values in the query string / hash (e.g. instead of basing
#' 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
#' 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
#' 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).
#'
#' @seealso \code{\link{updateQueryString}}
#'
#' @examples
#' ## Only run this example in interactive R sessions
#' if (interactive()) {
#'
#' ## App 1: getQueryString
#' ## Printing the value of the query string
#' ## (Use the back and forward buttons to see how the browser
#' ## keeps a record of each state)
#' shinyApp(
#' ui = fluidPage(
#' textInput("txt", "Enter new query string"),
#' helpText("Format: ?param1=val1&param2=val2"),
#' actionButton("go", "Update"),
#' hr(),
#' verbatimTextOutput("query")
#' ),
#' server = function(input, output, session) {
#' observeEvent(input$go, {
#' updateQueryString(input$txt, mode = "push")
#' })
#' output$query <- renderText({
#' query <- getQueryString()
#' queryText <- paste(names(query), query,
#' sep = "=", collapse=", ")
#' paste("Your query string is:\n", queryText)
#' })
#' }
#' )
#'
#' ## App 2: getUrlHash
#' ## Printing the value of the URL hash
#' ## (Use the back and forward buttons to see how the browser
#' ## keeps a record of each state)
#' shinyApp(
#' ui = fluidPage(
#' textInput("txt", "Enter new hash"),
#' helpText("Format: #hash"),
#' actionButton("go", "Update"),
#' hr(),
#' verbatimTextOutput("hash")
#' ),
#' server = function(input, output, session) {
#' observeEvent(input$go, {
#' updateQueryString(input$txt, mode = "push")
#' })
#' output$hash <- renderText({
#' hash <- getUrlHash()
#' paste("Your hash is:\n", hash)
#' })
#' }
#' )
#' }
#' @export
getQueryString <- function(session = getDefaultReactiveDomain()) {
parseQueryString(session$clientData$url_search)
}
#' @rdname getQueryString
#' @export
getUrlHash <- function(session = getDefaultReactiveDomain()) {
session$clientData$url_hash
}

View File

@@ -6,13 +6,18 @@
#' URL.
#'
#' @param dependency A single HTML dependency object, created using
#' \code{\link{htmlDependency}}. If the \code{src} value is named, then
#' \code{href} and/or \code{file} names must be present.
#' \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
#' 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
#' cases.
#'
#' @return A single HTML dependency object that has an \code{href}-named element
#' in its \code{src}.
#' @export
createWebDependency <- function(dependency) {
createWebDependency <- function(dependency, scrubFile = TRUE) {
if (is.null(dependency))
return(NULL)
@@ -25,5 +30,27 @@ createWebDependency <- function(dependency) {
dependency$src$href <- prefix
}
# Don't leak local file path to client
if (scrubFile)
dependency$src$file <- NULL
return(dependency)
}
# Given a Shiny tag object, process singletons and dependencies. Returns a list
# with rendered HTML and dependency objects.
processDeps <- function(tags, session) {
ui <- takeSingletons(tags, session$singletons, desingleton=FALSE)$ui
ui <- surroundSingletons(ui)
dependencies <- lapply(
resolveDependencies(findDependencies(ui)),
createWebDependency
)
names(dependencies) <- NULL
list(
html = doRenderTags(ui),
deps = dependencies
)
}

View File

@@ -4,4 +4,5 @@
#' @export tag tagAppendAttributes tagAppendChild tagAppendChildren tagList tags tagSetChildren withTags
#' @export validateCssUnit
#' @export knit_print.html knit_print.shiny.tag knit_print.shiny.tag.list
#' @export htmlTemplate suppressDependencies
NULL

139
R/image-interact-opts.R Normal file
View File

@@ -0,0 +1,139 @@
#' 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}}.
#'
#' @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 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.
#' @export
clickOpts <- function(id = NULL, clip = TRUE) {
if (is.null(id))
stop("id must not be NULL")
list(
id = id,
clip = clip
)
}
#' 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}}.
#'
#' @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}.
#' @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.
#' @param delay Maximum delay (in ms) between a pair clicks for them to be
#' counted as a double-click.
#' @export
dblclickOpts <- function(id = NULL, clip = TRUE, delay = 400) {
if (is.null(id))
stop("id must not be NULL")
list(
id = id,
clip = clip,
delay = delay
)
}
#' 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}}.
#'
#' @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 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
#' while the cursor is moving, and wait until the cursor has been at rest for
#' \code{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
#' value will stop changing when the cursor exits the plotting area.
#' @export
hoverOpts <- function(id = NULL, delay = 300,
delayType = c("debounce", "throttle"), clip = TRUE,
nullOutside = TRUE) {
if (is.null(id))
stop("id must not be NULL")
list(
id = id,
delay = delay,
delayType = match.arg(delayType),
clip = clip,
nullOutside = nullOutside
)
}
#' 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}}.
#'
#' @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}
#' value; brushing one image or plot will cause any other brushes with the
#' same \code{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
#' while the cursor is moving, and wait until the cursor has been at rest for
#' \code{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"},
#' 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
#' the plot is updated.
#' @export
brushOpts <- function(id = NULL, fill = "#9cf", stroke = "#036",
opacity = 0.25, delay = 300,
delayType = c("debounce", "throttle"), clip = TRUE,
direction = c("xy", "x", "y"),
resetOnNew = FALSE) {
if (is.null(id))
stop("id must not be NULL")
list(
id = id,
fill = fill,
stroke = stroke,
opacity = opacity,
delay = delay,
delayType = match.arg(delayType),
clip = clip,
direction = match.arg(direction),
resetOnNew = resetOnNew
)
}

496
R/image-interact.R Normal file
View File

@@ -0,0 +1,496 @@
#' 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}}.
#'
#' 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.
#'
#' The \code{xvar}, \code{yvar}, \code{panelvar1}, and \code{panelvar2}
#' arguments specify which columns in the data correspond to the x variable, y
#' variable, and panel variables of the plot. For example, if your plot is
#' \code{plot(x=cars$speed, y=cars$dist)}, and your brush is named
#' \code{"cars_brush"}, then you would use \code{brushedPoints(cars,
#' input$cars_brush, "speed", "dist")}.
#'
#' For plots created with ggplot2, it should not be necessary to specify the
#' column names; that information will already be contained in the brush,
#' provided that variables are in the original data, and not computed. For
#' example, with \code{ggplot(cars, aes(x=speed, y=dist)) + geom_point()}, you
#' could use \code{brushedPoints(cars, input$cars_brush)}. If, however, you use
#' a computed column, like \code{ggplot(cars, aes(x=speed/2, y=dist)) +
#' geom_point()}, then it will not be able to automatically extract column names
#' and filter on them. If you want to use this function to filter data, it is
#' recommended that you not use computed columns; instead, modify the data
#' first, and then make the plot with "raw" columns in the modified data.
#'
#' If a specified x or y column is a factor, then it will be coerced to an
#' integer vector. If it is a character vector, then it will be coerced to a
#' factor and then integer vector. This means that the brush will be considered
#' to cover a given character/factor value when it covers the center value.
#'
#' If the brush is operating in just the x or y directions (e.g., with
#' \code{brushOpts(direction = "x")}, then this function will filter out points
#' using just the x or y variable, whichever is appropriate.
#'
#' @param brush The data from a brush, such as \code{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
#' 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
#' 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}).
#'
#' @seealso \code{\link{plotOutput}} for example usage.
#' @export
brushedPoints <- function(df, brush, xvar = NULL, yvar = NULL,
panelvar1 = NULL, panelvar2 = NULL,
allRows = FALSE) {
if (is.null(brush)) {
if (allRows)
df$selected_ <- FALSE
else
df <- df[0, , drop = FALSE]
return(df)
}
if (is.null(brush$xmin)) {
stop("brushedPoints requires a brush object with xmin, xmax, ymin, and ymax.")
}
# Which direction(s) the brush is selecting over. Direction can be 'x', 'y',
# or 'xy'.
use_x <- grepl("x", brush$direction)
use_y <- grepl("y", brush$direction)
# Try to extract vars from brush object
xvar <- xvar %OR% brush$mapping$x
yvar <- yvar %OR% brush$mapping$y
panelvar1 <- panelvar1 %OR% brush$mapping$panelvar1
panelvar2 <- panelvar2 %OR% brush$mapping$panelvar2
# Filter out x and y values
keep_rows <- rep(TRUE, nrow(df))
if (use_x) {
if (is.null(xvar))
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)
}
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)
}
# Find which rows are matches for the panel vars (if present)
if (!is.null(panelvar1))
keep_rows <- keep_rows & panelMatch(brush$panelvar1, df[[panelvar1]])
if (!is.null(panelvar2))
keep_rows <- keep_rows & panelMatch(brush$panelvar2, df[[panelvar2]])
if (allRows) {
df$selected_ <- keep_rows
df
} else {
df[keep_rows, , drop = FALSE]
}
}
# The `brush` data structure will look something like the examples below.
# For base graphics, `mapping` is empty, and there are no panelvars:
# List of 8
# $ xmin : num 3.73
# $ xmax : num 4.22
# $ ymin : num 13.9
# $ ymax : num 19.8
# $ coords_css:List of 4
# ..$ xmin: int 260
# ..$ xmax: int 298
# ..$ ymin: num 112
# ..$ ymax: num 205
# $ coords_img:List of 4
# ..$ xmin: int 325
# ..$ xmax: num 372
# ..$ ymin: num 140
# ..$ ymax: num 257
# $ img_css_ratio:List of 2
# ..$ x: num 1.25
# ..$ y: num 1.25
# $ mapping: Named list()
# $ domain :List of 4
# ..$ left : num 1.36
# ..$ right : num 5.58
# ..$ bottom: num 9.46
# ..$ top : num 34.8
# $ range :List of 4
# ..$ left : num 58
# ..$ right : num 429
# ..$ bottom: num 226
# ..$ top : num 58
# $ log :List of 2
# ..$ x: NULL
# ..$ y: NULL
# $ direction: chr "y"
#
# For ggplot2, the mapping vars usually will be included, and if faceting is
# used, they will be listed as panelvars:
# List of 10
# $ xmin : num 3.18
# $ xmax : num 3.78
# $ ymin : num 17.1
# $ ymax : num 20.4
# $ panelvar1: int 6
# $ panelvar2: int 0
# $ coords_css:List of 4
# ..$ xmin: int 260
# ..$ xmax: int 298
# ..$ ymin: num 112
# ..$ ymax: num 205
# $ coords_img:List of 4
# ..$ xmin: int 325
# ..$ xmax: num 372
# ..$ ymin: num 140
# ..$ ymax: num 257
# $ img_css_ratio:List of 2
# ..$ x: num 1.25
# ..$ y: num 1.25
# $ mapping :List of 4
# ..$ x : chr "wt"
# ..$ y : chr "mpg"
# ..$ panelvar1: chr "cyl"
# ..$ panelvar2: chr "am"
# $ domain :List of 4
# ..$ left : num 1.32
# ..$ right : num 5.62
# ..$ bottom: num 9.22
# ..$ top : num 35.1
# $ range :List of 4
# ..$ left : num 172
# ..$ right : num 300
# ..$ bottom: num 144
# ..$ top : num 28.5
# $ log :List of 2
# ..$ x: NULL
# ..$ y: NULL
# $ direction: chr "y"
#'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
#'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
#'be sorted by distance to the mouse event.
#'
#'The \code{xvar}, \code{yvar}, \code{panelvar1}, and \code{panelvar2} arguments
#'specify which columns in the data correspond to the x variable, y variable,
#'and panel variables of the plot. For example, if your plot is
#'\code{plot(x=cars$speed, y=cars$dist)}, and your click variable is named
#'\code{"cars_click"}, then you would use \code{nearPoints(cars,
#'input$cars_brush, "speed", "dist")}.
#'
#'@inheritParams brushedPoints
#'@param coordinfo The data from a mouse event, such as \code{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
#' 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
#' 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}).
#'
#'@seealso \code{\link{plotOutput}} for more examples.
#'
#' @examples
#' \dontrun{
#' # Note that in practice, these examples would need to go in reactives
#' # or observers.
#'
#' # This would select all points within 5 pixels of the click
#' nearPoints(mtcars, input$plot_click)
#'
#' # Select just the nearest point within 10 pixels of the click
#' nearPoints(mtcars, input$plot_click, threshold = 10, maxpoints = 1)
#'
#' }
#'@export
nearPoints <- function(df, coordinfo, xvar = NULL, yvar = NULL,
panelvar1 = NULL, panelvar2 = NULL,
threshold = 5, maxpoints = NULL, addDist = FALSE,
allRows = FALSE) {
if (is.null(coordinfo)) {
if (addDist)
df$dist_ <- NA_real_
if (allRows)
df$selected_ <- FALSE
else
df <- df[0, , drop = FALSE]
return(df)
}
if (is.null(coordinfo$x)) {
stop("nearPoints requires a click/hover/double-click object with x and y values.")
}
# Try to extract vars from coordinfo object
xvar <- xvar %OR% coordinfo$mapping$x
yvar <- yvar %OR% coordinfo$mapping$y
panelvar1 <- panelvar1 %OR% coordinfo$mapping$panelvar1
panelvar2 <- panelvar2 %OR% coordinfo$mapping$panelvar2
if (is.null(xvar))
stop("nearPoints: not able to automatically infer `xvar` from coordinfo")
if (is.null(yvar))
stop("nearPoints: not able to automatically infer `yvar` from coordinfo")
if (!(xvar %in% names(df)))
stop("nearPoints: `xvar` ('", xvar ,"') not in names of input")
if (!(yvar %in% names(df)))
stop("nearPoints: `yvar` ('", yvar ,"') not in names of input")
# Extract data values from the data frame
x <- asNumber(df[[xvar]])
y <- asNumber(df[[yvar]])
# Get the coordinates of the point (in img pixel coordinates)
point_img <- coordinfo$coords_img
# Get coordinates of data points (in img pixel coordinates)
data_img <- scaleCoords(x, y, coordinfo)
# Get x/y distances (in css coordinates)
dist_css <- list(
x = (data_img$x - point_img$x) / coordinfo$img_css_ratio$x,
y = (data_img$y - point_img$y) / coordinfo$img_css_ratio$y
)
# Distances of data points to the target point, in css pixels.
dists <- sqrt(dist_css$x^2 + dist_css$y^2)
if (addDist)
df$dist_ <- dists
keep_rows <- (dists <= threshold)
# Find which rows are matches for the panel vars (if present)
if (!is.null(panelvar1))
keep_rows <- keep_rows & panelMatch(coordinfo$panelvar1, df[[panelvar1]])
if (!is.null(panelvar2))
keep_rows <- keep_rows & panelMatch(coordinfo$panelvar2, df[[panelvar2]])
# Track the indices to keep
keep_idx <- which(keep_rows)
# Order by distance
dists <- dists[keep_idx]
keep_idx <- keep_idx[order(dists)]
# Keep max number of rows
if (!is.null(maxpoints) && length(keep_idx) > maxpoints) {
keep_idx <- keep_idx[seq_len(maxpoints)]
}
if (allRows) {
# Add selected_ column if needed
df$selected_ <- FALSE
df$selected_[keep_idx] <- TRUE
} else {
# If we don't keep all rows, return just the selected rows, sorted by
# distance.
df <- df[keep_idx, , drop = FALSE]
}
df
}
# The coordinfo data structure will look something like the examples below.
# For base graphics, `mapping` is empty, and there are no panelvars:
# List of 7
# $ x : num 4.37
# $ y : num 12
# $ coords_css:List of 2
# ..$ x: int 286
# ..$ y: int 192
# $ coords_img:List of 2
# ..$ x: num 358
# ..$ y: int 240
# $ img_css_ratio:List of 2
# ..$ x: num 1.25
# ..$ y: num 1.25
# $ mapping : Named list()
# $ domain :List of 4
# ..$ left : num 1.36
# ..$ right : num 5.58
# ..$ bottom: num 9.46
# ..$ top : num 34.8
# $ range :List of 4
# ..$ left : num 58
# ..$ right : num 429
# ..$ bottom: num 226
# ..$ top : num 58
# $ log :List of 2
# ..$ x: NULL
# ..$ y: NULL
# $ .nonce : num 0.343
#
# For ggplot2, the mapping vars usually will be included, and if faceting is
# used, they will be listed as panelvars:
# List of 9
# $ x : num 3.78
# $ y : num 17.1
# $ coords_css:List of 2
# ..$ x: int 286
# ..$ y: int 192
# $ coords_img:List of 2
# ..$ x: num 358
# ..$ y: int 240
# $ img_css_ratio:List of 2
# ..$ x: num 1.25
# ..$ y: num 1.25
# $ panelvar1 : int 6
# $ panelvar2 : int 0
# $ mapping :List of 4
# ..$ x : chr "wt"
# ..$ y : chr "mpg"
# ..$ panelvar1: chr "cyl"
# ..$ panelvar2: chr "am"
# $ domain :List of 4
# ..$ left : num 1.32
# ..$ right : num 5.62
# ..$ bottom: num 9.22
# ..$ top : num 35.1
# $ range :List of 4
# ..$ left : num 172
# ..$ right : num 300
# ..$ bottom: num 144
# ..$ top : num 28.5
# $ log :List of 2
# ..$ x: NULL
# ..$ y: NULL
# $ .nonce : num 0.603
# 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) {
if (is.character(x)) x <- as.factor(x)
if (is.factor(x)) x <- as.integer(x)
as.numeric(x)
}
# Given a panelvar value and a vector x, return logical vector indicating which
# items match the panelvar value. Because the panelvar value is always a
# string but the vector could be numeric, it might be necessary to coerce the
# panelvar to a number before comparing to the vector.
panelMatch <- function(search_value, x) {
if (is.numeric(x)) search_value <- as.numeric(search_value)
x == search_value
}
# ----------------------------------------------------------------------------
# Scaling functions
# These functions have direct analogs in Javascript code, except these are
# vectorized for x and y.
# Map a value x from a domain to a range. If clip is true, clip it to the
# range.
mapLinear <- function(x, domainMin, domainMax, rangeMin, rangeMax, clip = TRUE) {
factor <- (rangeMax - rangeMin) / (domainMax - domainMin)
val <- x - domainMin
newval <- (val * factor) + rangeMin
if (clip) {
maxval <- max(rangeMax, rangeMin)
minval <- min(rangeMax, rangeMin)
newval[newval > maxval] <- maxval
newval[newval < minval] <- minval
}
newval
}
# Scale val from domain to range. If logbase is present, use log scaling.
scale1D <- function(val, domainMin, domainMax, rangeMin, rangeMax,
logbase = NULL, clip = TRUE) {
if (!is.null(logbase))
val <- log(val, logbase)
mapLinear(val, domainMin, domainMax, rangeMin, rangeMax, clip)
}
# Inverse scale val, from range to domain. If logbase is present, use inverse
# log (power) transformation.
scaleInv1D <- function(val, domainMin, domainMax, rangeMin, rangeMax,
logbase = NULL, clip = TRUE) {
res <- mapLinear(val, rangeMin, rangeMax, domainMin, domainMax, clip)
if (!is.null(logbase))
res <- logbase ^ res
res
}
# Scale x and y coordinates from domain to range, using information in
# scaleinfo. scaleinfo must contain items $domain, $range, and $log. The
# scaleinfo object corresponds to one element from the coordmap object generated
# by getPrevPlotCoordmap or getGgplotCoordmap; it is the scaling information for
# one panel in a plot.
scaleCoords <- function(x, y, scaleinfo) {
if (is.null(scaleinfo))
return(NULL)
domain <- scaleinfo$domain
range <- scaleinfo$range
log <- scaleinfo$log
list(
x = scale1D(x, domain$left, domain$right, range$left, range$right, log$x),
y = scale1D(y, domain$bottom, domain$top, range$bottom, range$top, log$y)
)
}
# Inverse scale x and y coordinates from range to domain, using information in
# scaleinfo.
scaleInvCoords <- function(x, y, scaleinfo) {
if (is.null(scaleinfo))
return(NULL)
domain <- scaleinfo$domain
range <- scaleinfo$range
log <- scaleinfo$log
list(
x = scaleInv1D(x, domain$left, domain$right, range$left, range$right, log$x),
y = scaleInv1D(y, domain$bottom, domain$top, range$bottom, range$top, log$y)
)
}

View File

@@ -1,3 +1,33 @@
startPNG <- function(filename, width, height, res, ...) {
# If quartz is available, use png() (which will default to quartz).
# Otherwise, if the Cairo package is installed, use CairoPNG().
# Finally, if neither quartz nor Cairo, use png().
if (capabilities("aqua")) {
pngfun <- grDevices::png
} else if ((getOption('shiny.usecairo') %OR% TRUE) &&
nchar(system.file(package = "Cairo"))) {
pngfun <- Cairo::CairoPNG
} else {
pngfun <- grDevices::png
}
pngfun(filename=filename, width=width, height=height, res=res, ...)
# Call plot.new() so that even if no plotting operations are performed at
# least we have a blank background. N.B. we need to set the margin to 0
# temporarily before plot.new() because when the plot size is small (e.g.
# 200x50), we will get an error "figure margin too large", which is triggered
# by plot.new() with the default (large) margin. However, this does not
# guarantee user's code in func() will not trigger the error -- they may have
# to set par(mar = smaller_value) before they draw base graphics.
op <- graphics::par(mar = rep(0, 4))
tryCatch(
graphics::plot.new(),
finally = graphics::par(op)
)
grDevices::dev.cur()
}
#' Run a plotting function and save the output as a PNG
#'
#' This function returns the name of the PNG file that it generates. In
@@ -21,38 +51,51 @@
#' @param width Width in pixels.
#' @param height Height in pixels.
#' @param res Resolution in pixels per inch. This value is passed to
#' \code{\link{png}}. Note that this affects the resolution of PNG rendering in
#' \code{\link[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}}.
#' These can be used to set the width, height, background color, etc.
#'
#' @export
plotPNG <- function(func, filename=tempfile(fileext='.png'),
width=400, height=400, res=72, ...) {
# If quartz is available, use png() (which will default to quartz).
# Otherwise, if the Cairo package is installed, use CairoPNG().
# Finally, if neither quartz nor Cairo, use png().
if (capabilities("aqua")) {
pngfun <- png
} else if ((getOption('shiny.usecairo') %OR% TRUE) &&
nchar(system.file(package = "Cairo"))) {
pngfun <- Cairo::CairoPNG
} else {
pngfun <- png
}
pngfun(filename=filename, width=width, height=height, res=res, ...)
# Call plot.new() so that even if no plotting operations are performed at
# least we have a blank background. N.B. we need to set the margin to 0
# temporarily before plot.new() because when the plot size is small (e.g.
# 200x50), we will get an error "figure margin too large", which is triggered
# by plot.new() with the default (large) margin. However, this does not
# guarantee user's code in func() will not trigger the error -- they may have
# to set par(mar = smaller_value) before they draw base graphics.
op <- par(mar = rep(0, 4))
tryCatch(plot.new(), finally = par(op))
dv <- dev.cur()
tryCatch(shinyCallingHandlers(func()), finally = dev.off(dv))
dv <- startPNG(filename, width, height, res, ...)
on.exit(grDevices::dev.off(dv), add = TRUE)
func()
filename
}
#' @importFrom grDevices dev.set dev.cur
createGraphicsDevicePromiseDomain <- function(which = dev.cur()) {
force(which)
promises::new_promise_domain(
wrapOnFulfilled = function(onFulfilled) {
force(onFulfilled)
function(...) {
old <- dev.cur()
dev.set(which)
on.exit(dev.set(old))
onFulfilled(...)
}
},
wrapOnRejected = function(onRejected) {
force(onRejected)
function(...) {
old <- dev.cur()
dev.set(which)
on.exit(dev.set(old))
onRejected(...)
}
},
wrapSync = function(expr) {
old <- dev.cur()
dev.set(which)
on.exit(dev.set(old))
force(expr)
}
)
}

86
R/input-action.R Normal file
View File

@@ -0,0 +1,86 @@
#' Action button/link
#'
#' Creates an action button or link whose value is initially zero, and increments by one
#' each time it is pressed.
#'
#' @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 ... Named attributes to be applied to the button or link.
#'
#' @family input elements
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' sliderInput("obs", "Number of observations", 0, 1000, 500),
#' actionButton("goButton", "Go!"),
#' plotOutput("distPlot")
#' )
#'
#' server <- function(input, output) {
#' output$distPlot <- renderPlot({
#' # Take a dependency on input$goButton. This will run once initially,
#' # because the value changes from NULL to 0.
#' input$goButton
#'
#' # Use isolate() to avoid dependency on input$obs
#' dist <- isolate(rnorm(input$obs))
#' hist(dist)
#' })
#' }
#'
#' shinyApp(ui, server)
#'
#' }
#'
#' @seealso \code{\link{observeEvent}} and \code{\link{eventReactive}}
#' @export
actionButton <- function(inputId, label, icon = NULL, width = NULL, ...) {
value <- restoreInput(id = inputId, default = NULL)
tags$button(id=inputId,
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
type="button",
class="btn btn-default action-button",
`data-val` = value,
list(validateIcon(icon), label),
...
)
}
#' @rdname actionButton
#' @export
actionLink <- function(inputId, label, icon = NULL, ...) {
value <- restoreInput(id = inputId, default = NULL)
tags$a(id=inputId,
href="#",
class="action-button",
`data-val` = value,
list(validateIcon(icon), label),
...
)
}
# Check that the icon parameter is valid:
# 1) Check if the user wants to actually add an icon:
# -- if icon=NULL, it means leave the icon unchanged
# -- if icon=character(0), it means don't add an icon or, more usefully,
# remove the previous icon
# 2) If so, check that the icon has the right format (this does not check whether
# it is a *real* icon - currently that would require a massive cross reference
# with the "font-awesome" and the "glyphicon" libraries)
validateIcon <- function(icon) {
if (is.null(icon) || identical(icon, character(0))) {
return(icon)
} else if (inherits(icon, "shiny.tag") && icon$name == "i") {
return(icon)
} else {
stop("Invalid icon. Use Shiny's 'icon()' function to generate a valid icon")
}
}

40
R/input-checkbox.R Normal file
View File

@@ -0,0 +1,40 @@
#' Checkbox Input Control
#'
#' Create a checkbox that can be used to specify logical values.
#'
#' @inheritParams textInput
#' @param value Initial value (\code{TRUE} or \code{FALSE}).
#' @return A checkbox control that can be added to a UI definition.
#'
#' @family input elements
#' @seealso \code{\link{checkboxGroupInput}}, \code{\link{updateCheckboxInput}}
#'
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' checkboxInput("somevalue", "Some value", FALSE),
#' verbatimTextOutput("value")
#' )
#' server <- function(input, output) {
#' output$value <- renderText({ input$somevalue })
#' }
#' shinyApp(ui, server)
#' }
#' @export
checkboxInput <- function(inputId, label, value = FALSE, width = NULL) {
value <- restoreInput(id = inputId, default = value)
inputTag <- tags$input(id = inputId, type="checkbox")
if (!is.null(value) && value)
inputTag$attribs$checked <- "checked"
div(class = "form-group shiny-input-container",
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
div(class = "checkbox",
tags$label(inputTag, tags$span(label))
)
)
}

100
R/input-checkboxgroup.R Normal file
View File

@@ -0,0 +1,100 @@
#' Checkbox Group Input Control
#'
#' Create a group of checkboxes that can be used to toggle multiple choices
#' independently. The server will receive the input as a character vector of the
#' selected values.
#'
#' @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}
#' 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 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 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 (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}}
#'
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' checkboxGroupInput("variable", "Variables to show:",
#' c("Cylinders" = "cyl",
#' "Transmission" = "am",
#' "Gears" = "gear")),
#' tableOutput("data")
#' )
#'
#' server <- function(input, output, session) {
#' output$data <- renderTable({
#' mtcars[, c("mpg", input$variable), drop = FALSE]
#' }, rownames = TRUE)
#' }
#'
#' shinyApp(ui, server)
#'
#' ui <- fluidPage(
#' checkboxGroupInput("icons", "Choose icons:",
#' choiceNames =
#' list(icon("calendar"), icon("bed"),
#' icon("cog"), icon("bug")),
#' choiceValues =
#' list("calendar", "bed", "cog", "bug")
#' ),
#' textOutput("txt")
#' )
#'
#' server <- function(input, output, session) {
#' output$txt <- renderText({
#' icons <- paste(input$icons, collapse = ", ")
#' paste("You chose", icons)
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @export
checkboxGroupInput <- function(inputId, label, choices = NULL, selected = NULL,
inline = FALSE, width = NULL, choiceNames = NULL, choiceValues = NULL) {
# keep backward compatibility with Shiny < 1.0.1 (see #1649)
if (is.null(choices) && is.null(choiceNames) && is.null(choiceValues)) {
choices <- character(0)
}
args <- normalizeChoicesArgs(choices, choiceNames, choiceValues)
selected <- restoreInput(id = inputId, default = selected)
# default value if it's not specified
if (!is.null(selected)) selected <- as.character(selected)
options <- generateOptions(inputId, selected, inline,
'checkbox', args$choiceNames, args$choiceValues)
divClass <- "form-group shiny-input-checkboxgroup shiny-input-container"
if (inline)
divClass <- paste(divClass, "shiny-input-container-inline")
# return label and select tag
tags$div(id = inputId,
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
class = divClass,
controlLabel(inputId, label),
options
)
}

142
R/input-date.R Normal file
View File

@@ -0,0 +1,142 @@
#' Create date input
#'
#' Creates a text input which, when clicked on, brings up a calendar that
#' the user can click on to select dates.
#'
#' The date \code{format} string specifies how the date will be displayed in
#' the browser. It allows the following values:
#'
#' \itemize{
#' \item \code{yy} Year without century (12)
#' \item \code{yyyy} Year with century (2012)
#' \item \code{mm} Month number, with leading zero (01-12)
#' \item \code{m} Month number, without leading zero (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
#' }
#'
#' @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
#' in the client's time zone.
#' @param min The minimum allowed date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#' @param max The maximum allowed date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#' @param format The format of the date to display in the browser. Defaults to
#' \code{"yyyy-mm-dd"}.
#' @param startview The date range shown when the input object is first clicked.
#' Can be "month" (the default), "year", or "decade".
#' @param weekstart Which day is the start of the week. Should be an integer
#' from 0 (Sunday) to 6 (Saturday).
#' @param language The language used for month and day names. Default is "en".
#' Other valid values include "ar", "az", "bg", "bs", "ca", "cs", "cy", "da",
#' "de", "el", "en-AU", "en-GB", "eo", "es", "et", "eu", "fa", "fi", "fo",
#' "fr-CH", "fr", "gl", "he", "hr", "hu", "hy", "id", "is", "it-CH", "it",
#' "ja", "ka", "kh", "kk", "ko", "kr", "lt", "lv", "me", "mk", "mn", "ms",
#' "nb", "nl-BE", "nl", "no", "pl", "pt-BR", "pt", "ro", "rs-latin", "rs",
#' "ru", "sk", "sl", "sq", "sr-latin", "sr", "sv", "sw", "th", "tr", "uk",
#' "vi", "zh-CN", and "zh-TW".
#' @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.
#' @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}}
#'
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' dateInput("date1", "Date:", value = "2012-02-29"),
#'
#' # Default value is the date in client's time zone
#' dateInput("date2", "Date:"),
#'
#' # value is always yyyy-mm-dd, even if the display format is different
#' dateInput("date3", "Date:", value = "2012-02-29", format = "mm/dd/yy"),
#'
#' # Pass in a Date object
#' dateInput("date4", "Date:", value = Sys.Date()-10),
#'
#' # Use different language and different first day of week
#' dateInput("date5", "Date:",
#' language = "ru",
#' weekstart = 1),
#'
#' # Start with decade view instead of default month view
#' dateInput("date6", "Date:",
#' startview = "decade"),
#'
#' # 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"))
#' )
#'
#' shinyApp(ui, server = function(input, output) { })
#' }
#' @export
dateInput <- function(inputId, label, value = NULL, min = NULL, max = NULL,
format = "yyyy-mm-dd", startview = "month", weekstart = 0,
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 <- restoreInput(id = inputId, default = value)
tags$div(id = inputId,
class = "shiny-date-input form-group shiny-input-container",
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
controlLabel(inputId, label),
tags$input(type = "text",
class = "form-control",
`data-date-language` = language,
`data-date-week-start` = weekstart,
`data-date-format` = format,
`data-date-start-view` = startview,
`data-min-date` = min,
`data-max-date` = max,
`data-initial-date` = value,
`data-date-autoclose` = if (autoclose) "true" else "false",
`data-date-dates-disabled` =
# Ensure NULL is not sent as `{}` but as 'null'
jsonlite::toJSON(datesdisabled, null = 'null'),
`data-date-days-of-week-disabled` =
jsonlite::toJSON(daysofweekdisabled, null = 'null')
),
datePickerDependency
)
}
datePickerDependency <- htmlDependency(
"bootstrap-datepicker", "1.6.4", c(href = "shared/datepicker"),
script = "js/bootstrap-datepicker.min.js",
stylesheet = "css/bootstrap-datepicker3.min.css",
# Need to enable noConflict mode. See #1346.
head = "<script>
(function() {
var datepicker = $.fn.datepicker.noConflict();
$.fn.bsDatepicker = datepicker;
})();
</script>"
)

127
R/input-daterange.R Normal file
View File

@@ -0,0 +1,127 @@
#' Create date range input
#'
#' Creates a pair of text inputs which, when clicked on, bring up calendars that
#' the user can click on to select dates.
#'
#' The date \code{format} string specifies how the date will be displayed in
#' the browser. It allows the following values:
#'
#' \itemize{
#' \item \code{yy} Year without century (12)
#' \item \code{yyyy} Year with century (2012)
#' \item \code{mm} Month number, with leading zero (01-12)
#' \item \code{m} Month number, without leading zero (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
#' }
#'
#' @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
#' date in the client's time zone.
#' @param end The initial end date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format. If NULL (the default), will use the current
#' date in the client's time zone.
#' @param separator String to display between the start and end input boxes.
#'
#' @family input elements
#' @seealso \code{\link{dateInput}}, \code{\link{updateDateRangeInput}}
#'
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' dateRangeInput("daterange1", "Date range:",
#' start = "2001-01-01",
#' end = "2010-12-31"),
#'
#' # Default start and end is the current date in the client's time zone
#' dateRangeInput("daterange2", "Date range:"),
#'
#' # start and end are always specified in yyyy-mm-dd, even if the display
#' # format is different
#' dateRangeInput("daterange3", "Date range:",
#' start = "2001-01-01",
#' end = "2010-12-31",
#' min = "2001-01-01",
#' max = "2012-12-21",
#' format = "mm/dd/yy",
#' separator = " - "),
#'
#' # Pass in Date objects
#' dateRangeInput("daterange4", "Date range:",
#' start = Sys.Date()-10,
#' end = Sys.Date()+10),
#'
#' # Use different language and different first day of week
#' dateRangeInput("daterange5", "Date range:",
#' language = "de",
#' weekstart = 1),
#'
#' # Start with decade view instead of default month view
#' dateRangeInput("daterange6", "Date range:",
#' startview = "decade")
#' )
#'
#' shinyApp(ui, server = function(input, output) { })
#' }
#' @export
dateRangeInput <- function(inputId, label, start = NULL, end = NULL,
min = NULL, max = NULL, format = "yyyy-mm-dd", startview = "month",
weekstart = 0, language = "en", separator = " to ", 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")
restored <- restoreInput(id = inputId, default = list(start, end))
start <- restored[[1]]
end <- restored[[2]]
attachDependencies(
div(id = inputId,
class = "shiny-date-range-input form-group shiny-input-container",
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
controlLabel(inputId, label),
# input-daterange class is needed for dropdown behavior
div(class = "input-daterange input-group",
tags$input(
class = "input-sm form-control",
type = "text",
`data-date-language` = language,
`data-date-week-start` = weekstart,
`data-date-format` = format,
`data-date-start-view` = startview,
`data-min-date` = min,
`data-max-date` = max,
`data-initial-date` = start,
`data-date-autoclose` = if (autoclose) "true" else "false"
),
span(class = "input-group-addon", separator),
tags$input(
class = "input-sm form-control",
type = "text",
`data-date-language` = language,
`data-date-week-start` = weekstart,
`data-date-format` = format,
`data-date-start-view` = startview,
`data-min-date` = min,
`data-max-date` = max,
`data-initial-date` = end,
`data-date-autoclose` = if (autoclose) "true" else "false"
)
)
),
datePickerDependency
)
}

126
R/input-file.R Normal file
View File

@@ -0,0 +1,126 @@
#' File Upload Control
#'
#' Create a file upload control that can be used to upload one or more files.
#'
#' Whenever a file upload completes, the corresponding input variable is set
#' 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
#' (see
#' \code{datapath} column).}
#' \item{\code{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
#' uploaded. This file may be deleted if the user performs another upload
#' operation.}
#' }
#'
#' @family input elements
#'
#' @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.}
#' @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
#' object.
#' @param placeholder The text to show before a file has been uploaded.
#'
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' sidebarLayout(
#' sidebarPanel(
#' fileInput("file1", "Choose CSV File",
#' accept = c(
#' "text/csv",
#' "text/comma-separated-values,text/plain",
#' ".csv")
#' ),
#' tags$hr(),
#' checkboxInput("header", "Header", TRUE)
#' ),
#' mainPanel(
#' tableOutput("contents")
#' )
#' )
#' )
#'
#' server <- function(input, output) {
#' output$contents <- renderTable({
#' # input$file1 will be NULL initially. After the user selects
#' # and uploads a file, it will be a data frame with 'name',
#' # 'size', 'type', and 'datapath' columns. The 'datapath'
#' # column will contain the local filenames where the data can
#' # be found.
#' inFile <- input$file1
#'
#' if (is.null(inFile))
#' return(NULL)
#'
#' read.csv(inFile$datapath, header = input$header)
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @export
fileInput <- function(inputId, label, multiple = FALSE, accept = NULL,
width = NULL, buttonLabel = "Browse...", placeholder = "No file selected") {
restoredValue <- restoreInput(id = inputId, default = NULL)
# Catch potential edge case - ensure that it's either NULL or a data frame.
if (!is.null(restoredValue) && !is.data.frame(restoredValue)) {
warning("Restored value for ", inputId, " has incorrect format.")
restoredValue <- NULL
}
if (!is.null(restoredValue)) {
restoredValue <- toJSON(restoredValue, strict_atomic = FALSE)
}
inputTag <- tags$input(
id = inputId,
name = inputId,
type = "file",
style = "display: none;",
`data-restore` = restoredValue
)
if (multiple)
inputTag$attribs$multiple <- "multiple"
if (length(accept) > 0)
inputTag$attribs$accept <- paste(accept, collapse=',')
div(class = "form-group shiny-input-container",
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
label %AND% tags$label(label),
div(class = "input-group",
tags$label(class = "input-group-btn",
span(class = "btn btn-default btn-file",
buttonLabel,
inputTag
)
),
tags$input(type = "text", class = "form-control",
placeholder = placeholder, readonly = "readonly"
)
),
tags$div(
id=paste(inputId, "_progress", sep=""),
class="progress progress-striped active shiny-file-input-progress",
tags$div(class="progress-bar")
)
)
}

48
R/input-numeric.R Normal file
View File

@@ -0,0 +1,48 @@
#' Create a numeric input control
#'
#' Create an input control for entry of numeric values
#'
#' @inheritParams textInput
#' @param min Minimum allowed value
#' @param max Maximum allowed value
#' @param step Interval to use when stepping between min and max
#' @return A numeric input control that can be added to a UI definition.
#'
#' @family input elements
#' @seealso \code{\link{updateNumericInput}}
#'
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' numericInput("obs", "Observations:", 10, min = 1, max = 100),
#' verbatimTextOutput("value")
#' )
#' server <- function(input, output) {
#' output$value <- renderText({ input$obs })
#' }
#' shinyApp(ui, server)
#' }
#' @export
numericInput <- function(inputId, label, value, min = NA, max = NA, step = NA,
width = NULL) {
value <- restoreInput(id = inputId, default = value)
# build input tag
inputTag <- tags$input(id = inputId, type = "number", class="form-control",
value = formatNoSci(value))
if (!is.na(min))
inputTag$attribs$min = min
if (!is.na(max))
inputTag$attribs$max = max
if (!is.na(step))
inputTag$attribs$step = step
div(class = "form-group shiny-input-container",
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
label %AND% tags$label(label, `for` = inputId),
inputTag
)
}

37
R/input-password.R Normal file
View File

@@ -0,0 +1,37 @@
#' Create a password input control
#'
#' Create an password control for entry of passwords.
#'
#' @inheritParams textInput
#' @return A text input control that can be added to a UI definition.
#'
#' @family input elements
#' @seealso \code{\link{updateTextInput}}
#'
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' passwordInput("password", "Password:"),
#' actionButton("go", "Go"),
#' verbatimTextOutput("value")
#' )
#' server <- function(input, output) {
#' output$value <- renderText({
#' req(input$go)
#' isolate(input$password)
#' })
#' }
#' shinyApp(ui, server)
#' }
#' @export
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),
tags$input(id = inputId, type="password", class="form-control", value=value,
placeholder = placeholder)
)
}

108
R/input-radiobuttons.R Normal file
View File

@@ -0,0 +1,108 @@
#' Create radio buttons
#'
#' 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
#' to return to that state once they've made a selection. Instead, consider
#' having the first of your choices be \code{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}
#' 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)
#' @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
#' 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
#' (tag objects, icons, HTML code, ...), instead of just simple text. See
#' Examples.
#'
#' @family input elements
#' @seealso \code{\link{updateRadioButtons}}
#'
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' radioButtons("dist", "Distribution type:",
#' c("Normal" = "norm",
#' "Uniform" = "unif",
#' "Log-normal" = "lnorm",
#' "Exponential" = "exp")),
#' plotOutput("distPlot")
#' )
#'
#' server <- function(input, output) {
#' output$distPlot <- renderPlot({
#' dist <- switch(input$dist,
#' norm = rnorm,
#' unif = runif,
#' lnorm = rlnorm,
#' exp = rexp,
#' rnorm)
#'
#' hist(dist(500))
#' })
#' }
#'
#' shinyApp(ui, server)
#'
#' ui <- fluidPage(
#' radioButtons("rb", "Choose one:",
#' choiceNames = list(
#' icon("calendar"),
#' HTML("<p style='color:red;'>Red Text</p>"),
#' "Normal text"
#' ),
#' choiceValues = list(
#' "icon", "html", "text"
#' )),
#' textOutput("txt")
#' )
#'
#' server <- function(input, output) {
#' output$txt <- renderText({
#' paste("You chose", input$rb)
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @export
radioButtons <- function(inputId, label, choices = NULL, selected = NULL,
inline = FALSE, width = NULL, choiceNames = NULL, choiceValues = NULL) {
args <- normalizeChoicesArgs(choices, choiceNames, choiceValues)
selected <- restoreInput(id = inputId, default = selected)
# default value if it's not specified
selected <- if (is.null(selected)) args$choiceValues[[1]] else as.character(selected)
if (length(selected) > 1) stop("The 'selected' argument must be of length 1")
options <- generateOptions(inputId, selected, inline,
'radio', args$choiceNames, args$choiceValues)
divClass <- "form-group shiny-input-radiogroup shiny-input-container"
if (inline) divClass <- paste(divClass, "shiny-input-container-inline")
tags$div(id = inputId,
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
class = divClass,
controlLabel(inputId, label),
options
)
}

346
R/input-select.R Normal file
View File

@@ -0,0 +1,346 @@
#' Create a select list input control
#'
#' 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
#' JavaScript library \pkg{selectize.js}
#' (\url{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}.
#'
#' 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))}
#'
#' @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
#' 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
#' 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.
#' @return A select list control that can be added to a UI definition.
#'
#' @family input elements
#' @seealso \code{\link{updateSelectInput}} \code{\link{varSelectInput}}
#'
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' # basic example
#' shinyApp(
#' ui = fluidPage(
#' selectInput("variable", "Variable:",
#' c("Cylinders" = "cyl",
#' "Transmission" = "am",
#' "Gears" = "gear")),
#' tableOutput("data")
#' ),
#' server = function(input, output) {
#' output$data <- renderTable({
#' mtcars[, c("mpg", input$variable), drop = FALSE]
#' }, rownames = TRUE)
#' }
#' )
#'
#' # demoing optgroup support in the `choices` arg
#' shinyApp(
#' ui = fluidPage(
#' selectInput("state", "Choose a state:",
#' list(`East Coast` = list("NY", "NJ", "CT"),
#' `West Coast` = list("WA", "OR", "CA"),
#' `Midwest` = list("MN", "WI", "IA"))
#' ),
#' textOutput("result")
#' ),
#' server = function(input, output) {
#' output$result <- renderText({
#' paste("You chose", input$state)
#' })
#' }
#' )
#' }
#' @export
selectInput <- function(inputId, label, choices, selected = NULL,
multiple = FALSE, selectize = TRUE, width = NULL,
size = NULL) {
selected <- restoreInput(id = inputId, default = selected)
# resolve names
choices <- choicesWithNames(choices)
# default value if it's not specified
if (is.null(selected)) {
if (!multiple) selected <- firstChoice(choices)
} else selected <- as.character(selected)
if (!is.null(size) && selectize) {
stop("'size' argument is incompatible with 'selectize=TRUE'.")
}
# create select tag and add options
selectTag <- tags$select(
id = inputId,
class = if (!selectize) "form-control",
size = size,
selectOptions(choices, selected)
)
if (multiple)
selectTag$attribs$multiple <- "multiple"
# return label and select tag
res <- div(
class = "form-group shiny-input-container",
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
controlLabel(inputId, label),
div(selectTag)
)
if (!selectize) return(res)
selectizeIt(inputId, res, NULL, nonempty = !multiple && !("" %in% choices))
}
firstChoice <- function(choices) {
if (length(choices) == 0L) return()
choice <- choices[[1]]
if (is.list(choice)) firstChoice(choice) else choice
}
# Create tags for each of the options; use <optgroup> if necessary.
# This returns a HTML string instead of tags, because of the 'selected'
# attribute.
selectOptions <- function(choices, selected = NULL) {
html <- mapply(choices, names(choices), FUN = function(choice, label) {
if (is.list(choice)) {
# If sub-list, create an optgroup and recurse into the sublist
sprintf(
'<optgroup label="%s">\n%s\n</optgroup>',
htmlEscape(label, TRUE),
selectOptions(choice, selected)
)
} else {
# If single item, just return option string
sprintf(
'<option value="%s"%s>%s</option>',
htmlEscape(choice, TRUE),
if (choice %in% selected) ' selected' else '',
htmlEscape(label)
)
}
})
HTML(paste(html, collapse = '\n'))
}
# need <optgroup> when choices contains sub-lists
needOptgroup <- function(choices) {
any(vapply(choices, is.list, logical(1)))
}
#' @rdname selectInput
#' @param ... Arguments passed to \code{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 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
#' 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
#' 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)}.
#' @export
selectizeInput <- function(inputId, ..., options = NULL, width = NULL) {
selectizeIt(
inputId,
selectInput(inputId, ..., selectize = FALSE, width = width),
options
)
}
# given a select input and its id, selectize it
selectizeIt <- function(inputId, select, options, nonempty = FALSE) {
res <- checkAsIs(options)
selectizeDep <- htmlDependency(
"selectize", "0.11.2", c(href = "shared/selectize"),
stylesheet = "css/selectize.bootstrap3.css",
head = format(tagList(
HTML('<!--[if lt IE 9]>'),
tags$script(src = 'shared/selectize/js/es5-shim.min.js'),
HTML('<![endif]-->'),
tags$script(src = 'shared/selectize/js/selectize.min.js')
))
)
if ('drag_drop' %in% options$plugins) {
selectizeDep <- list(selectizeDep, htmlDependency(
'jqueryui', '1.12.1', c(href = 'shared/jqueryui'),
script = 'jquery-ui.min.js'
))
}
# Insert script on same level as <select> tag
select$children[[2]] <- tagAppendChild(
select$children[[2]],
tags$script(
type = 'application/json',
`data-for` = inputId, `data-nonempty` = if (nonempty) '',
`data-eval` = if (length(res$eval)) HTML(toJSON(res$eval)),
if (length(res$options)) HTML(toJSON(res$options)) else '{}'
)
)
attachDependencies(select, selectizeDep)
}
#' Select variables from a data frame
#'
#' 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:
#' \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
#' 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)]])}.
#' }
#'
#' By default, \code{varSelectInput()} and \code{selectizeInput()} use the
#' JavaScript library \pkg{selectize.js}
#' (\url{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}.
#'
#' @inheritParams selectInput
#' @param data A data frame. Used to retrieve the column names as choices for a \code{\link{selectInput}}
#' @return A variable select list control that can be added to a UI definition.
#'
#' @family input elements
#' @seealso \code{\link{updateSelectInput}}
#' @examples
#'
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' library(ggplot2)
#'
#' # single selection
#' shinyApp(
#' ui = fluidPage(
#' varSelectInput("variable", "Variable:", mtcars),
#' plotOutput("data")
#' ),
#' server = function(input, output) {
#' output$data <- renderPlot({
#' ggplot(mtcars, aes(!!input$variable)) + geom_histogram()
#' })
#' }
#' )
#'
#'
#' # multiple selections
#' \dontrun{
#' shinyApp(
#' ui = fluidPage(
#' varSelectInput("variables", "Variable:", mtcars, multiple = TRUE),
#' tableOutput("data")
#' ),
#' server = function(input, output) {
#' output$data <- renderTable({
#' if (length(input$variables) == 0) return(mtcars)
#' mtcars %>% dplyr::select(!!!input$variables)
#' }, rownames = TRUE)
#' }
#' )}
#'
#' }
#' @export
varSelectInput <- function(
inputId, label, data, selected = NULL,
multiple = FALSE, selectize = TRUE, width = NULL,
size = NULL
) {
# no place holders
choices <- colnames(data)
selectInputVal <- selectInput(
inputId = inputId,
label = label,
choices = choices,
selected = selected,
multiple = multiple,
selectize = selectize,
width = width,
size = size
)
# set the select tag class to be "symbol"
selectClass <- selectInputVal$children[[2]]$children[[1]]$attribs$class
if (is.null(selectClass)) {
newClass <- "symbol"
} else {
newClass <- paste(selectClass, "symbol", sep = " ")
}
selectInputVal$children[[2]]$children[[1]]$attribs$class <- newClass
selectInputVal
}
#' @rdname varSelectInput
#' @param ... Arguments passed to \code{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 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
#' 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
#' 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)}.
#' @export
varSelectizeInput <- function(inputId, ..., options = NULL, width = NULL) {
selectizeIt(
inputId,
varSelectInput(inputId, ..., selectize = FALSE, width = width),
options
)
}

272
R/input-slider.R Normal file
View File

@@ -0,0 +1,272 @@
#' Slider Input Widget
#'
#' Constructs a slider widget to select a numeric value from a range.
#'
#' @inheritParams textInput
#' @param min The minimum value (inclusive) that can be selected.
#' @param max The maximum value (inclusive) that can be selected.
#' @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}.
#' @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
#' 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
#' 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 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
#' 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
#' 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"}).
#' @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
#' result in UTC time.
#' @inheritParams selectizeInput
#' @family input elements
#' @seealso \code{\link{updateSliderInput}}
#'
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#' options(device.ask.default = FALSE)
#'
#' ui <- fluidPage(
#' sliderInput("obs", "Number of observations:",
#' min = 0, max = 1000, value = 500
#' ),
#' plotOutput("distPlot")
#' )
#'
#' # Server logic
#' server <- function(input, output) {
#' output$distPlot <- renderPlot({
#' hist(rnorm(input$obs))
#' })
#' }
#'
#' # Complete app with UI and server components
#' shinyApp(ui, server)
#' }
#' @export
sliderInput <- function(inputId, label, min, max, value, step = NULL,
round = FALSE, format = NULL, locale = NULL,
ticks = TRUE, animate = FALSE, width = NULL, sep = ",",
pre = NULL, post = NULL, timeFormat = NULL,
timezone = NULL, dragRange = TRUE)
{
if (!missing(format)) {
shinyDeprecated(msg = "The `format` argument to sliderInput is deprecated. Use `sep`, `pre`, and `post` instead.",
version = "0.10.2.2")
}
if (!missing(locale)) {
shinyDeprecated(msg = "The `locale` argument to sliderInput is deprecated. Use `sep`, `pre`, and `post` instead.",
version = "0.10.2.2")
}
dataType <- getSliderType(min, max, value)
if (is.null(timeFormat)) {
timeFormat <- switch(dataType, date = "%F", datetime = "%F %T", number = NULL)
}
# Restore bookmarked values here, after doing the type checking, because the
# restored value will be a character vector instead of Date or POSIXct, and we can do
# the conversion to correct type next.
value <- restoreInput(id = inputId, default = value)
if (is.character(value)) {
# If we got here, the value was restored from a URL-encoded bookmark.
if (dataType == "date") {
value <- as.Date(value, format = "%Y-%m-%d")
} else if (dataType == "datetime") {
# Date-times will have a format like "2018-02-28T03:46:26Z"
value <- as.POSIXct(value, format = "%Y-%m-%dT%H:%M:%SZ", tz = "UTC")
}
}
step <- findStepSize(min, max, step)
if (dataType %in% c("date", "datetime")) {
# For Dates, this conversion uses midnight on that date in UTC
to_ms <- function(x) 1000 * as.numeric(as.POSIXct(x))
# Convert values to milliseconds since epoch (this is the value JS uses)
# Find step size in ms
step <- to_ms(max) - to_ms(max - step)
min <- to_ms(min)
max <- to_ms(max)
value <- to_ms(value)
}
range <- max - min
# Try to get a sane number of tick marks
if (ticks) {
n_steps <- range / step
# Make sure there are <= 10 steps.
# n_ticks can be a noninteger, which is good when the range is not an
# integer multiple of the step size, e.g., min=1, max=10, step=4
scale_factor <- ceiling(n_steps / 10)
n_ticks <- n_steps / scale_factor
} else {
n_ticks <- NULL
}
sliderProps <- dropNulls(list(
class = "js-range-slider",
id = inputId,
`data-type` = if (length(value) > 1) "double",
`data-min` = formatNoSci(min),
`data-max` = formatNoSci(max),
`data-from` = formatNoSci(value[1]),
`data-to` = if (length(value) > 1) formatNoSci(value[2]),
`data-step` = formatNoSci(step),
`data-grid` = ticks,
`data-grid-num` = n_ticks,
`data-grid-snap` = FALSE,
`data-prettify-separator` = sep,
`data-prettify-enabled` = (sep != ""),
`data-prefix` = pre,
`data-postfix` = post,
`data-keyboard` = TRUE,
# This value is only relevant for range sliders; for non-range sliders it
# causes problems since ion.RangeSlider 2.1.2 (issue #1605).
`data-drag-interval` = if (length(value) > 1) dragRange,
# The following are ignored by the ion.rangeSlider, but are used by Shiny.
`data-data-type` = dataType,
`data-time-format` = timeFormat,
`data-timezone` = timezone
))
# Replace any TRUE and FALSE with "true" and "false"
sliderProps <- lapply(sliderProps, function(x) {
if (identical(x, TRUE)) "true"
else if (identical(x, FALSE)) "false"
else x
})
sliderTag <- div(class = "form-group shiny-input-container",
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
if (!is.null(label)) controlLabel(inputId, label),
do.call(tags$input, sliderProps)
)
# Add animation buttons
if (identical(animate, TRUE))
animate <- animationOptions()
if (!is.null(animate) && !identical(animate, FALSE)) {
if (is.null(animate$playButton))
animate$playButton <- icon('play', lib = 'glyphicon')
if (is.null(animate$pauseButton))
animate$pauseButton <- icon('pause', lib = 'glyphicon')
sliderTag <- tagAppendChild(
sliderTag,
tags$div(class='slider-animate-container',
tags$a(href='#',
class='slider-animate-button',
'data-target-id'=inputId,
'data-interval'=animate$interval,
'data-loop'=animate$loop,
span(class = 'play', animate$playButton),
span(class = 'pause', animate$pauseButton)
)
)
)
}
dep <- list(
htmlDependency("ionrangeslider", "2.1.6", c(href="shared/ionrangeslider"),
script = "js/ion.rangeSlider.min.js",
# ion.rangeSlider also needs normalize.css, which is already included in
# Bootstrap.
stylesheet = c("css/ion.rangeSlider.css",
"css/ion.rangeSlider.skinShiny.css")
),
htmlDependency("strftime", "0.9.2", c(href="shared/strftime"),
script = "strftime-min.js"
)
)
attachDependencies(sliderTag, dep)
}
hasDecimals <- function(value) {
truncatedValue <- round(value)
return (!identical(value, truncatedValue))
}
# If step is NULL, use heuristic to set the step size.
findStepSize <- function(min, max, step) {
if (!is.null(step)) return(step)
range <- max - min
# If short range or decimals, use continuous decimal with ~100 points
if (range < 2 || hasDecimals(min) || hasDecimals(max)) {
# Workaround for rounding errors (#1006): the intervals between the items
# returned by pretty() can have rounding errors. To avoid this, we'll use
# pretty() to find the min, max, and number of steps, and then use those
# values to calculate the step size.
pretty_steps <- pretty(c(min, max), n = 100)
n_steps <- length(pretty_steps) - 1
# Fix for #2061: Windows has low-significance digits (like 17 digits out)
# even at the boundaries of pretty()'s output. Use signif(digits = 10),
# which should be way way less significant than any data we'd want to keep.
# It might make sense to use signif(steps[2] - steps[1], 10) instead, but
# for now trying to make the minimal change.
signif(digits = 10, (max(pretty_steps) - min(pretty_steps)) / n_steps)
} else {
1
}
}
#' @rdname sliderInput
#'
#' @param interval The interval, in milliseconds, between each animation step.
#' @param loop \code{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.
#' @export
animationOptions <- function(interval=1000,
loop=FALSE,
playButton=NULL,
pauseButton=NULL) {
list(interval=interval,
loop=loop,
playButton=playButton,
pauseButton=pauseButton)
}

65
R/input-submit.R Normal file
View File

@@ -0,0 +1,65 @@
#' Create a submit button
#'
#' 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).
#'
#' Submit buttons are unusual Shiny inputs, and we recommend using
#' \code{\link{actionButton}} instead of \code{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}).
#'
#' 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,
#' 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.
#'
#' @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}}.
#' @return A submit button that can be added to a UI definition.
#'
#' @family input elements
#'
#' @examples
#' if (interactive()) {
#'
#' shinyApp(
#' ui = basicPage(
#' numericInput("num", label = "Make changes", value = 1),
#' submitButton("Update View", icon("refresh")),
#' helpText("When you click the button above, you should see",
#' "the output below update to reflect the value you",
#' "entered at the top:"),
#' verbatimTextOutput("value")
#' ),
#' server = function(input, output) {
#'
#' # submit buttons do not have a value of their own,
#' # they control when the app accesses values of other widgets.
#' # input$num is the value of the number widget.
#' output$value <- renderPrint({ input$num })
#' }
#' )
#' }
#' @export
submitButton <- function(text = "Apply Changes", icon = NULL, width = NULL) {
div(
tags$button(
type="submit",
class="btn btn-primary",
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
list(icon, text)
)
)
}

43
R/input-text.R Normal file
View File

@@ -0,0 +1,43 @@
#' Create a text input control
#'
#' 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 value Initial value.
#' @param width The width of the input, e.g. \code{'400px'}, or \code{'100\%'};
#' see \code{\link{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}}
#'
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' textInput("caption", "Caption", "Data Summary"),
#' verbatimTextOutput("value")
#' )
#' server <- function(input, output) {
#' output$value <- renderText({ input$caption })
#' }
#' shinyApp(ui, server)
#' }
#' @export
textInput <- function(inputId, label, value = "", width = NULL,
placeholder = NULL) {
value <- restoreInput(id = inputId, default = value)
div(class = "form-group shiny-input-container",
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
label %AND% tags$label(label, `for` = inputId),
tags$input(id = inputId, type="text", class="form-control", value=value,
placeholder = placeholder)
)
}

69
R/input-textarea.R Normal file
View File

@@ -0,0 +1,69 @@
#' Create a textarea input control
#'
#' 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 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.
#' @return A textarea input control that can be added to a UI definition.
#'
#' @family input elements
#' @seealso \code{\link{updateTextAreaInput}}
#'
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' textAreaInput("caption", "Caption", "Data Summary", width = "1000px"),
#' verbatimTextOutput("value")
#' )
#' server <- function(input, output) {
#' output$value <- renderText({ input$caption })
#' }
#' shinyApp(ui, server)
#'
#' }
#' @export
textAreaInput <- function(inputId, label, value = "", width = NULL, height = NULL,
cols = NULL, rows = NULL, placeholder = NULL, resize = NULL) {
value <- restoreInput(id = inputId, default = value)
if (!is.null(resize)) {
resize <- match.arg(resize, c("both", "none", "vertical", "horizontal"))
}
style <- paste(
if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
if (!is.null(height)) paste0("height: ", validateCssUnit(height), ";"),
if (!is.null(resize)) paste0("resize: ", resize, ";")
)
# Workaround for tag attribute=character(0) bug:
# https://github.com/rstudio/htmltools/issues/65
if (length(style) == 0) style <- NULL
div(class = "form-group shiny-input-container",
label %AND% tags$label(label, `for` = inputId),
tags$textarea(
id = inputId,
class = "form-control",
placeholder = placeholder,
style = style,
rows = rows,
cols = cols,
value
)
)
}

129
R/input-utils.R Normal file
View File

@@ -0,0 +1,129 @@
controlLabel <- function(controlName, label) {
label %AND% tags$label(class = "control-label", `for` = controlName, label)
}
# This function takes in either a list or vector for `choices` (and
# `choiceNames` and `choiceValues` are passed in as NULL) OR it takes
# in a list or vector for both `choiceNames` and `choiceValues` (and
# `choices` is passed as NULL) and returns a list of two elements:
# - `choiceNames` is a vector or list that holds the options names
# (each element can be arbitrary UI, or simple text)
# - `choiceValues` is a vector or list that holds the options values
# (each element must be simple text)
normalizeChoicesArgs <- function(choices, choiceNames, choiceValues,
mustExist = TRUE) {
# if-else to check that either choices OR (choiceNames + choiceValues)
# were correctly provided
if (is.null(choices)) {
if (is.null(choiceNames) || is.null(choiceValues)) {
if (mustExist) {
stop("Please specify a non-empty vector for `choices` (or, ",
"alternatively, for both `choiceNames` AND `choiceValues`).")
} else {
if (is.null(choiceNames) && is.null(choiceValues)) {
# this is useful when we call this function from `updateInputOptions()`
# in which case, all three `choices`, `choiceNames` and `choiceValues`
# may legitimately be NULL
return(list(choiceNames = NULL, choiceValues = NULL))
} else {
stop("One of `choiceNames` or `choiceValues` was set to ",
"NULL, but either both or none should be NULL.")
}
}
}
if (length(choiceNames) != length(choiceValues)) {
stop("`choiceNames` and `choiceValues` must have the same length.")
}
if (anyNamed(choiceNames) || anyNamed(choiceValues)) {
stop("`choiceNames` and `choiceValues` must not be named.")
}
} else {
if (!is.null(choiceNames) || !is.null(choiceValues)) {
warning("Using `choices` argument; ignoring `choiceNames` and `choiceValues`.")
}
choices <- choicesWithNames(choices) # resolve names if not specified
choiceNames <- names(choices)
choiceValues <- unname(choices)
}
return(list(choiceNames = as.list(choiceNames),
choiceValues = as.list(as.character(choiceValues))))
}
# generate options for radio buttons and checkbox groups (type = 'checkbox' or
# 'radio')
generateOptions <- function(inputId, selected, inline, type = 'checkbox',
choiceNames, choiceValues,
session = getDefaultReactiveDomain()) {
# generate a list of <input type=? [checked] />
options <- mapply(
choiceValues, choiceNames,
FUN = function(value, name) {
inputTag <- tags$input(
type = type, name = inputId, value = value
)
if (value %in% selected)
inputTag$attribs$checked <- "checked"
# in case, the options include UI code other than text
# (arbitrary HTML using the tags() function or equivalent)
pd <- processDeps(name, session)
# If inline, there's no wrapper div, and the label needs a class like
# checkbox-inline.
if (inline) {
tags$label(class = paste0(type, "-inline"), inputTag,
tags$span(pd$html, pd$deps))
} else {
tags$div(class = type, tags$label(inputTag,
tags$span(pd$html, pd$deps)))
}
},
SIMPLIFY = FALSE, USE.NAMES = FALSE
)
div(class = "shiny-options-group", options)
}
# Takes a vector or list, 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)
}
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
}

325
R/insert-tab.R Normal file
View File

@@ -0,0 +1,325 @@
#' 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}}.
#'
#' 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}.
#' When you want to append a tab (i.e. add a tab to the end of the
#' \code{tabsetPanel}), use \code{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).
#'
#' @param inputId The \code{id} of the \code{tabsetPanel} (or
#' \code{navlistPanel} or \code{navbarPage}) into which \code{tab} will
#' be inserted/removed.
#'
#' @param tab The item to be added (must be created with \code{tabPanel},
#' or with \code{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
#' you want to remove. See Details if you want to insert next to/remove
#' an entire \code{navbarMenu} instead.
#'
#' @param position Should \code{tab} be added before or after the
#' \code{target} tab?
#'
#' @param select Should \code{tab} be selected upon being inserted?
#'
#' @param session The shiny session within which to call this function.
#'
#' @seealso \code{\link{showTab}}
#'
#' @examples
#' ## Only run this example in interactive R sessions
#' if (interactive()) {
#'
#' # example app for inserting/removing a tab
#' ui <- fluidPage(
#' sidebarLayout(
#' sidebarPanel(
#' actionButton("add", "Add 'Dynamic' tab"),
#' actionButton("remove", "Remove 'Foo' tab")
#' ),
#' mainPanel(
#' tabsetPanel(id = "tabs",
#' tabPanel("Hello", "This is the hello tab"),
#' tabPanel("Foo", "This is the foo tab"),
#' tabPanel("Bar", "This is the bar tab")
#' )
#' )
#' )
#' )
#' server <- function(input, output, session) {
#' observeEvent(input$add, {
#' insertTab(inputId = "tabs",
#' tabPanel("Dynamic", "This a dynamically-added tab"),
#' target = "Bar"
#' )
#' })
#' observeEvent(input$remove, {
#' removeTab(inputId = "tabs", target = "Foo")
#' })
#' }
#'
#' shinyApp(ui, server)
#'
#'
#' # example app for prepending/appending a navbarMenu
#' ui <- navbarPage("Navbar page", id = "tabs",
#' tabPanel("Home",
#' actionButton("prepend", "Prepend a navbarMenu"),
#' actionButton("append", "Append a navbarMenu")
#' )
#' )
#' server <- function(input, output, session) {
#' observeEvent(input$prepend, {
#' id <- paste0("Dropdown", input$prepend, "p")
#' prependTab(inputId = "tabs",
#' navbarMenu(id,
#' tabPanel("Drop1", paste("Drop1 page from", id)),
#' tabPanel("Drop2", paste("Drop2 page from", id)),
#' "------",
#' "Header",
#' tabPanel("Drop3", paste("Drop3 page from", id))
#' )
#' )
#' })
#' observeEvent(input$append, {
#' id <- paste0("Dropdown", input$append, "a")
#' appendTab(inputId = "tabs",
#' navbarMenu(id,
#' tabPanel("Drop1", paste("Drop1 page from", id)),
#' tabPanel("Drop2", paste("Drop2 page from", id)),
#' "------",
#' "Header",
#' tabPanel("Drop3", paste("Drop3 page from", id))
#' )
#' )
#' })
#' }
#'
#' shinyApp(ui, server)
#'
#' }
#' @export
insertTab <- function(inputId, tab, target,
position = c("before", "after"), select = FALSE,
session = getDefaultReactiveDomain()) {
force(target)
force(select)
position <- match.arg(position)
inputId <- session$ns(inputId)
# Barbara -- August 2017
# Note: until now, the number of tabs in a tabsetPanel (or navbarPage
# or navlistPanel) was always fixed. So, an easy way to give an id to
# a tab was simply incrementing a counter. (Just like it was easy to
# give a random 4-digit number to identify the tabsetPanel). Since we
# can only know this in the client side, we'll just pass `id` and
# `tsid` (TabSetID) as dummy values that will be fixed in the JS code.
item <- buildTabItem("id", "tsid", TRUE, divTag = tab,
textFilter = if (is.character(tab)) navbarMenuTextFilter else NULL)
callback <- function() {
session$sendInsertTab(
inputId = inputId,
liTag = processDeps(item$liTag, session),
divTag = processDeps(item$divTag, session),
menuName = NULL,
target = target,
position = position,
select = select)
}
session$onFlush(callback, once = TRUE)
}
#' @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}
#' 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.
#'
#' @rdname insertTab
#' @export
prependTab <- function(inputId, tab, select = FALSE, menuName = NULL,
session = getDefaultReactiveDomain()) {
force(select)
force(menuName)
inputId <- session$ns(inputId)
item <- buildTabItem("id", "tsid", TRUE, divTag = tab,
textFilter = if (is.character(tab)) navbarMenuTextFilter else NULL)
callback <- function() {
session$sendInsertTab(
inputId = inputId,
liTag = processDeps(item$liTag, session),
divTag = processDeps(item$divTag, session),
menuName = menuName,
target = NULL,
position = "after",
select = select)
}
session$onFlush(callback, once = TRUE)
}
#' @rdname insertTab
#' @export
appendTab <- function(inputId, tab, select = FALSE, menuName = NULL,
session = getDefaultReactiveDomain()) {
force(select)
force(menuName)
inputId <- session$ns(inputId)
item <- buildTabItem("id", "tsid", TRUE, divTag = tab,
textFilter = if (is.character(tab)) navbarMenuTextFilter else NULL)
callback <- function() {
session$sendInsertTab(
inputId = inputId,
liTag = processDeps(item$liTag, session),
divTag = processDeps(item$divTag, session),
menuName = menuName,
target = NULL,
position = "before",
select = select)
}
session$onFlush(callback, once = TRUE)
}
#' @rdname insertTab
#' @export
removeTab <- function(inputId, target,
session = getDefaultReactiveDomain()) {
force(target)
inputId <- session$ns(inputId)
callback <- function() {
session$sendRemoveTab(
inputId = inputId,
target = target)
}
session$onFlush(callback, once = TRUE)
}
#' 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}}.
#'
#' 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).
#'
#' @param inputId The \code{id} of the \code{tabsetPanel} (or
#' \code{navlistPanel} or \code{navbarPage}) in which to find
#' \code{target}.
#'
#' @param target The \code{value} of the \code{tabPanel} to be
#' hidden/shown. See Details if you want to hide/show an entire
#' \code{navbarMenu} instead.
#'
#' @param select Should \code{target} be selected upon being shown?
#'
#' @param session The shiny session within which to call this function.
#'
#' @seealso \code{\link{insertTab}}
#'
#' @examples
#' ## Only run this example in interactive R sessions
#' if (interactive()) {
#'
#' ui <- navbarPage("Navbar page", id = "tabs",
#' tabPanel("Home",
#' actionButton("hideTab", "Hide 'Foo' tab"),
#' actionButton("showTab", "Show 'Foo' tab"),
#' actionButton("hideMenu", "Hide 'More' navbarMenu"),
#' actionButton("showMenu", "Show 'More' navbarMenu")
#' ),
#' tabPanel("Foo", "This is the foo tab"),
#' tabPanel("Bar", "This is the bar tab"),
#' navbarMenu("More",
#' tabPanel("Table", "Table page"),
#' tabPanel("About", "About page"),
#' "------",
#' "Even more!",
#' tabPanel("Email", "Email page")
#' )
#' )
#'
#' server <- function(input, output, session) {
#' observeEvent(input$hideTab, {
#' hideTab(inputId = "tabs", target = "Foo")
#' })
#'
#' observeEvent(input$showTab, {
#' showTab(inputId = "tabs", target = "Foo")
#' })
#'
#' observeEvent(input$hideMenu, {
#' hideTab(inputId = "tabs", target = "More")
#' })
#'
#' observeEvent(input$showMenu, {
#' showTab(inputId = "tabs", target = "More")
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#'
#' @export
showTab <- function(inputId, target, select = FALSE,
session = getDefaultReactiveDomain()) {
force(target)
if (select) updateTabsetPanel(session, inputId, selected = target)
inputId <- session$ns(inputId)
callback <- function() {
session$sendChangeTabVisibility(
inputId = inputId,
target = target,
type = "show"
)
}
session$onFlush(callback, once = TRUE)
}
#' @rdname showTab
#' @export
hideTab <- function(inputId, target,
session = getDefaultReactiveDomain()) {
force(target)
inputId <- session$ns(inputId)
callback <- function() {
session$sendChangeTabVisibility(
inputId = inputId,
target = target,
type = "hide"
)
}
session$onFlush(callback, once = TRUE)
}

174
R/insert-ui.R Normal file
View File

@@ -0,0 +1,174 @@
#' Insert UI objects
#'
#' Insert a UI object into the app.
#'
#' 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}
#' 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
#' 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}}.
#'
#' @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
#' 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
#' first child}
#' \item{\code{beforeEnd}}{Just inside the selector element, after its
#' last child (default)}
#' \item{\code{afterEnd}}{After the selector element itself}
#' }
#' Adapted from
#' \href{https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML}{here}.
#'
#' @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
#' 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
#' reference or remove it later on). If you want to insert raw html, use
#' \code{ui = HTML()}.
#'
#' @param multiple In case your selector matches more than one element,
#' \code{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
#' all outputs have been updated and all observers have been run (default).
#'
#' @param session The shiny session within which to call \code{insertUI}.
#'
#' @seealso \code{\link{removeUI}}
#'
#' @examples
#' ## Only run this example in interactive R sessions
#' if (interactive()) {
#' # Define UI
#' ui <- fluidPage(
#' actionButton("add", "Add UI")
#' )
#'
#' # Server logic
#' server <- function(input, output, session) {
#' observeEvent(input$add, {
#' insertUI(
#' selector = "#add",
#' where = "afterEnd",
#' ui = textInput(paste0("txt", input$add),
#' "Insert some text")
#' )
#' })
#' }
#'
#' # Complete app with UI and server components
#' shinyApp(ui, server)
#' }
#' @export
insertUI <- function(selector,
where = c("beforeBegin", "afterBegin", "beforeEnd", "afterEnd"),
ui,
multiple = FALSE,
immediate = FALSE,
session = getDefaultReactiveDomain()) {
force(selector)
force(ui)
force(session)
force(multiple)
if (missing(where)) where <- "beforeEnd"
where <- match.arg(where)
callback <- function() {
session$sendInsertUI(selector = selector,
multiple = multiple,
where = where,
content = processDeps(ui, session))
}
if (!immediate) session$onFlushed(callback, once = TRUE)
else callback()
}
#' Remove UI objects
#'
#' Remove a UI object from the app.
#'
#' This function allows you to remove any part of your UI. Once \code{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
#' 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
#' 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.
#' (Alternatively, you could also wrap the inputs/outputs that you want to be
#' able to remove easily in a \code{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
#' 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
#' all outputs have been updated and all observers have been run (default).
#'
#' @param session The shiny session within which to call \code{removeUI}.
#'
#' @seealso \code{\link{insertUI}}
#'
#' @examples
#' ## Only run this example in interactive R sessions
#' if (interactive()) {
#' # Define UI
#' ui <- fluidPage(
#' actionButton("rmv", "Remove UI"),
#' textInput("txt", "This is no longer useful")
#' )
#'
#' # Server logic
#' server <- function(input, output, session) {
#' observeEvent(input$rmv, {
#' removeUI(
#' selector = "div:has(> #txt)"
#' )
#' })
#' }
#'
#' # Complete app with UI and server components
#' shinyApp(ui, server)
#' }
#' @export
removeUI <- function(selector,
multiple = FALSE,
immediate = FALSE,
session = getDefaultReactiveDomain()) {
force(selector)
force(multiple)
force(session)
callback <- function() {
session$sendRemoveUI(selector = selector,
multiple = multiple)
}
if (!immediate) session$onFlushed(callback, once = TRUE)
else callback()
}

View File

@@ -53,7 +53,6 @@
#' over text). The default is \code{"auto"}, which is equivalent to
#' \code{ifelse(draggable, "move", "inherit")}.
#' @return An HTML element or list of elements.
#'
#' @export
absolutePanel <- function(...,
top = NULL, left = NULL, right = NULL, bottom = NULL,
@@ -80,9 +79,7 @@ absolutePanel <- function(...,
if (isTRUE(draggable)) {
divTag <- tagAppendAttributes(divTag, class='draggable')
return(tagList(
# IMPORTANT NOTE: If you update jqueryui, make sure you DON'T include the datepicker,
# as it collides with our bootstrap datepicker!
singleton(tags$head(tags$script(src='shared/jqueryui/1.10.4/jquery-ui.min.js'))),
singleton(tags$head(tags$script(src='shared/jqueryui/jquery-ui.min.js'))),
divTag,
tags$script('$(".draggable").draggable();')
))
@@ -97,8 +94,8 @@ fixedPanel <- function(...,
top = NULL, left = NULL, right = NULL, bottom = NULL,
width = NULL, height = NULL,
draggable = FALSE,
cursor = c('move', 'default', 'inherit')) {
cursor = c('auto', 'move', 'default', 'inherit')) {
absolutePanel(..., top=top, left=left, right=right, bottom=bottom,
width=width, height=height, draggable=draggable, cursor=cursor,
width=width, height=height, draggable=draggable, cursor=match.arg(cursor),
fixed=TRUE)
}

View File

@@ -23,6 +23,9 @@ Map <- R6Class(
env[[key]] <- value
value
},
mget = function(keys) {
base::mget(keys, env)
},
mset = function(...) {
args <- list(...)
if (length(args) == 0)

View File

@@ -2,17 +2,43 @@
NULL
reactLogHandler <- function(req) {
if (!identical(req$PATH_INFO, '/reactlog'))
return(NULL)
if (!isTRUE(getOption('shiny.reactlog'))) {
if (! rLog$isLogging()) {
return(NULL)
}
return(httpResponse(
status=200,
content=list(file=renderReactLog(), owned=TRUE)
))
if (identical(req$PATH_INFO, "/reactlog/mark")) {
sessionToken <- parseQueryString(req$QUERY_STRING)$s
shinysession <- appsByToken$get(sessionToken)
# 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) {
@@ -35,37 +61,7 @@ sessionHandler <- function(req) {
subreq$PATH_INFO <- subpath
subreq$SCRIPT_NAME <- paste(subreq$SCRIPT_NAME, matches[[1]][2], sep='')
return(shinysession$handleRequest(subreq))
}
dynamicHandler <- function(filePath, dependencyFiles=filePath) {
lastKnownTimestamps <- NA
metaHandler <- function(req) NULL
if (!file.exists(filePath))
return(metaHandler)
cacheContext <- CacheContext$new()
return (function(req) {
# Check if we need to rebuild
if (cacheContext$isDirty()) {
cacheContext$reset()
for (dep in dependencyFiles)
cacheContext$addDependencyFile(dep)
clearClients()
if (file.exists(filePath)) {
local({
cacheContext$with(function() {
sys.source(filePath, envir=new.env(parent=globalenv()), keep.source=TRUE)
})
})
}
metaHandler <<- joinHandlers(.globals$clients)
clearClients()
}
return(metaHandler(req))
withReactiveDomain(shinysession, {
shinysession$handleRequest(subreq)
})
}

View File

@@ -21,7 +21,7 @@ httpResponse <- function(status = 200,
# Make sure it's a list, not a vector
headers <- as.list(headers)
if (is.null(headers$`X-UA-Compatible`))
headers$`X-UA-Compatible` <- "chrome=1"
headers$`X-UA-Compatible` <- "IE=edge,chrome=1"
resp <- list(status = status, content_type = content_type, content = content,
headers = headers)
class(resp) <- 'httpResponse'
@@ -191,7 +191,7 @@ staticHandler <- function(root) {
if (!identical(req$REQUEST_METHOD, 'GET'))
return(NULL)
path <- req$PATH_INFO
path <- URLdecode(req$PATH_INFO)
if (is.null(path))
return(httpResponse(400, content="<h1>Bad Request</h1>"))
@@ -203,8 +203,7 @@ staticHandler <- function(root) {
if (is.null(abs.path))
return(NULL)
ext <- tools::file_ext(abs.path)
content.type <- getContentType(ext)
content.type <- getContentType(abs.path)
response.content <- readBin(abs.path, 'raw', n=file.info(abs.path)$size)
return(httpResponse(200, content.type, response.content))
})
@@ -300,9 +299,7 @@ HandlerManager <- R6Class("HandlerManager",
if (reqSize > maxSize) {
return(list(status = 413L,
headers = list(
'Content-Type' = 'text/plain'
),
headers = list('Content-Type' = 'text/plain'),
body = 'Maximum upload size exceeded'))
}
else {
@@ -311,49 +308,114 @@ HandlerManager <- R6Class("HandlerManager",
},
call = .httpServer(
function (req) {
return(handlers$invoke(req))
withCallingHandlers(withLogErrors(handlers$invoke(req)),
error = function(cond) {
sanitizeErrors <- getOption('shiny.sanitize.errors', FALSE)
if (inherits(cond, 'shiny.custom.error') || !sanitizeErrors) {
stop(cond$message, call. = FALSE)
} else {
stop(paste("An error has occurred. Check your logs or",
"contact the app author for clarification."),
call. = FALSE)
}
}
)
},
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')))
}
response <- handler(req)
if (is.null(response))
response <- httpResponse(404, content="<h1>Not Found</h1>")
if (inherits(response, "httpResponse")) {
headers <- as.list(response$headers)
headers$'Content-Type' <- response$content_type
response <- filter(req, response)
return(list(status=response$status,
body=response$content,
headers=headers))
} else {
# Assume it's a Rook-compatible response
return(response)
# Catch HEAD requests. For the purposes of handler functions, they
# should be treated like GET. The difference is that they shouldn't
# return a body in the http response.
head_request <- FALSE
if (identical(req$REQUEST_METHOD, "HEAD")) {
head_request <- TRUE
req$REQUEST_METHOD <- "GET"
}
response <- handler(req)
res <- hybrid_chain(response, function(response) {
if (is.null(response))
response <- httpResponse(404, content="<h1>Not Found</h1>")
if (inherits(response, "httpResponse")) {
headers <- as.list(response$headers)
headers$'Content-Type' <- response$content_type
response <- filter(req, response)
if (head_request) {
headers$`Content-Length` <- getResponseContentLength(response, deleteOwnedContent = TRUE)
return(list(
status = response$status,
body = "",
headers = headers
))
} else {
return(list(
status = response$status,
body = response$content,
headers = headers
))
}
} else {
# Assume it's a Rook-compatible response
return(response)
}
})
}
}
)
)
# Safely get the Content-Length of a Rook response, or NULL if the length cannot
# be determined for whatever reason (probably malformed response$content).
# If deleteOwnedContent is TRUE, then the function should delete response
# content that is of the form list(file=..., owned=TRUE).
getResponseContentLength <- function(response, deleteOwnedContent) {
force(deleteOwnedContent)
result <- if (is.character(response$content) && length(response$content) == 1) {
nchar(response$content, type = "bytes")
} else if (is.raw(response$content)) {
length(response$content)
} else if (is.list(response$content) && !is.null(response$content$file)) {
if (deleteOwnedContent && isTRUE(response$content$owned)) {
on.exit(unlink(response$content$file, recursive = FALSE, force = FALSE), add = TRUE)
}
file.info(response$content$file)$size
} else {
warning("HEAD request for unexpected content class ", class(response$content)[[1]])
NULL
}
if (is.na(result)) {
# Mostly for missing file case
return(NULL)
} else {
return(result)
}
}
#
# ## Next steps
#

183
R/modal.R Normal file
View File

@@ -0,0 +1,183 @@
#' 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}}.
#'
#' @param ui UI content to show in the modal.
#' @param session The \code{session} object passed to function given to
#' \code{shinyServer}.
#'
#' @seealso \code{\link{modalDialog}} for examples.
#' @export
showModal <- function(ui, session = getDefaultReactiveDomain()) {
res <- processDeps(ui, session)
session$sendModal("show",
list(
html = res$html,
deps = res$deps
)
)
}
#' @rdname showModal
#' @export
removeModal <- function(session = getDefaultReactiveDomain()) {
session$sendModal("remove", NULL)
}
#' Create a modal dialog UI
#'
#' This creates the UI for a modal dialog, using Bootstrap's modal class. Modals
#' are typically used for showing important messages, or for presenting UI that
#' requires input from the user, such as a username and password input.
#'
#' @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
#' clicking outside the dialog box, or be pressing the Escape key. If
#' \code{FALSE} (the default), the modal dialog can't be dismissed in those
#' ways; instead it must be dismissed by clicking on the dismiss button, or
#' from a call to \code{\link{removeModal}} on the server.
#' @param fade If \code{FALSE}, the modal dialog will have no fade-in animation
#' (it will simply appear rather than fade in to view).
#'
#' @examples
#' if (interactive()) {
#' # Display an important message that can be dismissed only by clicking the
#' # dismiss button.
#' shinyApp(
#' ui = basicPage(
#' actionButton("show", "Show modal dialog")
#' ),
#' server = function(input, output) {
#' observeEvent(input$show, {
#' showModal(modalDialog(
#' title = "Important message",
#' "This is an important message!"
#' ))
#' })
#' }
#' )
#'
#'
#' # Display a message that can be dismissed by clicking outside the modal dialog,
#' # or by pressing Esc.
#' shinyApp(
#' ui = basicPage(
#' actionButton("show", "Show modal dialog")
#' ),
#' server = function(input, output) {
#' observeEvent(input$show, {
#' showModal(modalDialog(
#' title = "Somewhat important message",
#' "This is a somewhat important message.",
#' easyClose = TRUE,
#' footer = NULL
#' ))
#' })
#' }
#' )
#'
#'
#' # Display a modal that requires valid input before continuing.
#' shinyApp(
#' ui = basicPage(
#' actionButton("show", "Show modal dialog"),
#' verbatimTextOutput("dataInfo")
#' ),
#'
#' server = function(input, output) {
#' # reactiveValues object for storing current data set.
#' vals <- reactiveValues(data = NULL)
#'
#' # Return the UI for a modal dialog with data selection input. If 'failed' is
#' # TRUE, then display a message that the previous value was invalid.
#' dataModal <- function(failed = FALSE) {
#' modalDialog(
#' textInput("dataset", "Choose data set",
#' placeholder = 'Try "mtcars" or "abc"'
#' ),
#' span('(Try the name of a valid data object like "mtcars", ',
#' 'then a name of a non-existent object like "abc")'),
#' if (failed)
#' div(tags$b("Invalid name of data object", style = "color: red;")),
#'
#' footer = tagList(
#' modalButton("Cancel"),
#' actionButton("ok", "OK")
#' )
#' )
#' }
#'
#' # Show modal when button is clicked.
#' observeEvent(input$show, {
#' showModal(dataModal())
#' })
#'
#' # When OK button is pressed, attempt to load the data set. If successful,
#' # remove the modal. If not show another modal, but this time with a failure
#' # message.
#' observeEvent(input$ok, {
#' # Check that data object exists and is data frame.
#' if (!is.null(input$dataset) && nzchar(input$dataset) &&
#' exists(input$dataset) && is.data.frame(get(input$dataset))) {
#' vals$data <- get(input$dataset)
#' removeModal()
#' } else {
#' showModal(dataModal(failed = TRUE))
#' }
#' })
#'
#' # Display information about selected data
#' output$dataInfo <- renderPrint({
#' if (is.null(vals$data))
#' "No data selected"
#' else
#' summary(vals$data)
#' })
#' }
#' )
#' }
#' @export
modalDialog <- function(..., title = NULL, footer = modalButton("Dismiss"),
size = c("m", "s", "l"), easyClose = FALSE, fade = TRUE) {
size <- match.arg(size)
cls <- if (fade) "modal fade" else "modal"
div(id = "shiny-modal", class = cls, tabindex = "-1",
`data-backdrop` = if (!easyClose) "static",
`data-keyboard` = if (!easyClose) "false",
div(
class = "modal-dialog",
class = switch(size, s = "modal-sm", m = NULL, l = "modal-lg"),
div(class = "modal-content",
if (!is.null(title)) div(class = "modal-header",
tags$h4(class = "modal-title", title)
),
div(class = "modal-body", ...),
if (!is.null(footer)) div(class = "modal-footer", footer)
)
),
tags$script("$('#shiny-modal').modal().focus();")
)
}
#' Create a button for a modal dialog
#'
#' When clicked, a \code{modalButton} will dismiss the modal dialog.
#'
#' @inheritParams actionButton
#' @seealso \code{\link{modalDialog}} for examples.
#' @export
modalButton <- function(label, icon = NULL) {
tags$button(type = "button", class = "btn btn-default",
`data-dismiss` = "modal", validateIcon(icon), label
)
}

67
R/modules.R Normal file
View File

@@ -0,0 +1,67 @@
# Creates an object whose $ and [[ pass through to the parent
# session, unless the name is matched in ..., in which case
# that value is returned instead. (See Decorator pattern.)
createSessionProxy <- function(parentSession, ...) {
e <- new.env(parent = emptyenv())
e$parent <- parentSession
e$overrides <- list(...)
structure(
e,
class = "session_proxy"
)
}
#' @export
`$.session_proxy` <- function(x, name) {
if (name %in% names(.subset2(x, "overrides")))
.subset2(x, "overrides")[[name]]
else
.subset2(x, "parent")[[name]]
}
#' @export
`[[.session_proxy` <- `$.session_proxy`
#' @export
`$<-.session_proxy` <- function(x, name, value) {
# this line allows users to write into session$userData
# (e.g. it allows something like `session$userData$x <- TRUE`,
# but not `session$userData <- TRUE`) from within a module
# without any hacks (see PR #1732)
if (identical(x[[name]], value)) return(x)
stop("Attempted to assign value on session proxy.")
}
`[[<-.session_proxy` <- `$<-.session_proxy`
#' Invoke a Shiny module
#'
#' 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.
#'
#' @param module A Shiny module server function
#' @param id An ID string that corresponds with the ID used to call the module's
#' UI function
#' @param ... Additional parameters to pass to module server function
#' @param session Session from which to make a child scope (the default should
#' almost always be used)
#'
#' @return The return value, if any, from executing the module server function
#' @seealso \url{http://shiny.rstudio.com/articles/modules.html}
#' @export
callModule <- function(module, id, ..., session = getDefaultReactiveDomain()) {
childScope <- session$makeScope(id)
withReactiveDomain(childScope, {
if (!is.function(module)) {
stop("module argument must be a function")
}
module(childScope$input, childScope$output, childScope, ...)
})
}

106
R/notifications.R Normal file
View File

@@ -0,0 +1,106 @@
#' Show or remove a notification
#'
#' These functions show and remove notifications in a Shiny application.
#'
#' @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}
#' 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
#' disappear.
#' @param closeButton If \code{TRUE}, display a button which will make the
#' notification disappear when clicked. If \code{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
#' 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
#' "default" (gray), "message" (blue), "warning" (yellow), or "error" (red).
#' @param session Session object to send notification to.
#'
#' @return An ID for the notification.
#'
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#' # Show a message when button is clicked
#' shinyApp(
#' ui = fluidPage(
#' actionButton("show", "Show")
#' ),
#' server = function(input, output) {
#' observeEvent(input$show, {
#' showNotification("Message text",
#' action = a(href = "javascript:location.reload();", "Reload page")
#' )
#' })
#' }
#' )
#'
#' # App with show and remove buttons
#' shinyApp(
#' ui = fluidPage(
#' actionButton("show", "Show"),
#' actionButton("remove", "Remove")
#' ),
#' server = function(input, output) {
#' # A queue of notification IDs
#' ids <- character(0)
#' # A counter
#' n <- 0
#'
#' observeEvent(input$show, {
#' # Save the ID for removal later
#' id <- showNotification(paste("Message", n), duration = NULL)
#' ids <<- c(ids, id)
#' n <<- n + 1
#' })
#'
#' observeEvent(input$remove, {
#' if (length(ids) > 0)
#' removeNotification(ids[1])
#' ids <<- ids[-1]
#' })
#' }
#' )
#' }
#' @export
showNotification <- function(ui, action = NULL, duration = 5,
closeButton = TRUE, id = NULL,
type = c("default", "message", "warning", "error"),
session = getDefaultReactiveDomain())
{
if (is.null(id))
id <- createUniqueId(8)
res <- processDeps(ui, session)
actionRes <- processDeps(action, session)
session$sendNotification("show",
list(
html = res$html,
action = actionRes$html,
deps = c(res$deps, actionRes$deps),
duration = if (!is.null(duration)) duration * 1000,
closeButton = closeButton,
id = id,
type = match.arg(type)
)
)
id
}
#' @rdname showNotification
#' @export
removeNotification <- function(id = NULL, session = getDefaultReactiveDomain()) {
if (is.null(id)) {
stop("id is required.")
}
session$sendNotification("remove", id)
id
}

View File

@@ -12,6 +12,14 @@
#' method is called. Calling \code{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
#' function.
#'
#' \strong{Methods}
#' \describe{
#' \item{\code{initialize(session, min = 0, max = 1)}}{
@@ -47,7 +55,10 @@
#' de-emphasized appearance relative to \code{message}.
#' @param value A numeric 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 style Progress display style. If \code{"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
#' (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.
@@ -55,11 +66,16 @@
#' progress bar.
#'
#' @examples
#' \dontrun{
#' # server.R
#' shinyServer(function(input, output, session) {
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' plotOutput("plot")
#' )
#'
#' server <- function(input, output, session) {
#' output$plot <- renderPlot({
#' progress <- shiny::Progress$new(session, min=1, max=15)
#' progress <- Progress$new(session, min=1, max=15)
#' on.exit(progress$close())
#'
#' progress$set(message = 'Calculation in progress',
@@ -71,7 +87,9 @@
#' }
#' plot(cars)
#' })
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @seealso \code{\link{withProgress}}
#' @format NULL
@@ -79,21 +97,24 @@
#' @export
Progress <- R6Class(
'Progress',
portable = TRUE,
public = list(
initialize = function(session = getDefaultReactiveDomain(), min = 0, max = 1) {
# A hacky check to make sure the session object is indeed a session object.
if (is.null(session$onFlush)) stop("'session' is not a session object.")
initialize = function(session = getDefaultReactiveDomain(),
min = 0, max = 1,
style = getShinyOption("progress.style", default = "notification"))
{
if (is.null(session$progressStack))
stop("'session' is not a ShinySession object.")
private$session <- session
private$id <- paste(as.character(as.raw(runif(8, min=0, max=255))), collapse='')
private$id <- createUniqueId(8)
private$min <- min
private$max <- max
private$value <- NULL
private$style <- match.arg(style, choices = c("notification", "old"))
private$closed <- FALSE
session$sendProgress('open', list(id = private$id))
session$sendProgress('open', list(id = private$id, style = private$style))
},
set = function(value = NULL, message = NULL, detail = NULL) {
@@ -102,25 +123,30 @@ Progress <- R6Class(
return()
}
if (is.null(value) || is.na(value)) {
if (is.null(value) || is.na(value))
value <- NULL
} else {
if (!is.null(value)) {
private$value <- value
# Normalize value to number between 0 and 1
value <- min(1, max(0, (value - private$min) / (private$max - private$min)))
}
private$value <- value
data <- dropNulls(list(
id = private$id,
message = message,
detail = detail,
value = value
value = value,
style = private$style
))
private$session$sendProgress('update', data)
private$session$sendProgress('update', data)
},
inc = function(amount = 0.1, message = NULL, detail = NULL) {
if (is.null(private$value))
private$value <- private$min
value <- min(private$value + amount, private$max)
self$set(value, message, detail)
},
@@ -137,17 +163,20 @@ Progress <- R6Class(
return()
}
private$session$sendProgress('close', list(id = private$id))
private$session$sendProgress('close',
list(id = private$id, style = private$style)
)
private$closed <- TRUE
}
),
private = list(
session = 'environment',
session = 'ShinySession',
id = character(0),
min = numeric(0),
max = numeric(0),
value = NULL,
style = character(0),
value = numeric(0),
closed = logical(0)
)
)
@@ -175,6 +204,14 @@ Progress <- R6Class(
#' is not common) or otherwise cannot be encapsulated by a single scope. In that
#' case, you can use the \code{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
#' function.
#'
#' @param session The Shiny session object, as provided by \code{shinyServer} to
#' the server function. The default is to automatically find the session by
#' using the current reactive domain.
@@ -195,14 +232,23 @@ Progress <- R6Class(
#' displayed to the user, or \code{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),
#' 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
#' (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}. \code{NULL} hides the
#' progress bar, if it is currently visible.
#' progress bar, relative to \code{min} and \code{max}.
#'
#' @examples
#' \dontrun{
#' # server.R
#' shinyServer(function(input, output) {
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#' options(device.ask.default = FALSE)
#'
#' ui <- fluidPage(
#' plotOutput("plot")
#' )
#'
#' server <- function(input, output) {
#' output$plot <- renderPlot({
#' withProgress(message = 'Calculation in progress',
#' detail = 'This may take a while...', value = 0, {
@@ -213,21 +259,30 @@ Progress <- R6Class(
#' })
#' plot(cars)
#' })
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @seealso \code{\link{Progress}}
#' @rdname withProgress
#' @export
withProgress <- function(expr, min = 0, max = 1,
value = min + (max - min) * 0.1,
message = NULL, detail = NULL,
session = getDefaultReactiveDomain(),
env = parent.frame(), quoted = FALSE) {
value = min + (max - min) * 0.1,
message = NULL, detail = NULL,
style = getShinyOption("progress.style", default = "notification"),
session = getDefaultReactiveDomain(),
env = parent.frame(), quoted = FALSE)
{
if (!quoted)
expr <- substitute(expr)
p <- Progress$new(session, min = min, max = max)
if (is.null(session$progressStack))
stop("'session' is not a ShinySession object.")
style <- match.arg(style, c("notification", "old"))
p <- Progress$new(session, min = min, max = max, style = style)
session$progressStack$push(p)
on.exit({
@@ -245,8 +300,8 @@ withProgress <- function(expr, min = 0, max = 1,
setProgress <- function(value = NULL, message = NULL, detail = NULL,
session = getDefaultReactiveDomain()) {
# A hacky check to make sure the session object is indeed a session object.
if (is.null(session$onFlush)) stop("'session' is not a session object.")
if (is.null(session$progressStack))
stop("'session' is not a ShinySession object.")
if (session$progressStack$size() == 0) {
warning('setProgress was called outside of withProgress; ignoring')
@@ -262,8 +317,8 @@ setProgress <- function(value = NULL, message = NULL, detail = NULL,
incProgress <- function(amount = 0.1, message = NULL, detail = NULL,
session = getDefaultReactiveDomain()) {
# A hacky check to make sure the session object is indeed a session object.
if (is.null(session$onFlush)) stop("'session' is not a session object.")
if (is.null(session$progressStack))
stop("'session' is not a ShinySession object.")
if (session$progressStack$size() == 0) {
warning('incProgress was called outside of withProgress; ignoring')

147
R/react.R
View File

@@ -1,40 +1,77 @@
processId <- local({
# pid is not sufficient to uniquely identify a process, because
# distributed futures span machines which could introduce pid
# collisions.
cached <- NULL
function() {
if (is.null(cached)) {
cached <<- digest::digest(list(
Sys.info(),
Sys.time()
))
}
# Sys.getpid() cannot be cached because forked children will
# then have the same processId as their parents.
paste(cached, Sys.getpid())
}
})
#' @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,
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
) {
id <<- id
.label <<- label
.domain <<- domain
.graphCreateContext(id, label, type, prevId, domain)
.pid <<- processId()
.reactId <<- reactId
.reactType <<- type
rLog$createContext(id, label, type, prevId, domain)
},
run = function(func) {
"Run the provided function under this context."
withReactiveDomain(.domain, {
env <- .getReactiveEnvironment()
.graphEnterContext(id)
tryCatch(
env$runWith(self, func),
finally = .graphExitContext(id)
)
promises::with_promise_domain(reactivePromiseDomain(), {
withReactiveDomain(.domain, {
env <- .getReactiveEnvironment()
rLog$enter(.reactId, id, .reactType, .domain)
on.exit(rLog$exit(.reactId, id, .reactType, .domain), add = TRUE)
env$runWith(self, func)
})
})
},
invalidate = function() {
"Invalidate this context. It will immediately call the callbacks
that have been registered with onInvalidate()."
if (!identical(.pid, processId())) {
stop("Reactive context was created in one process and invalidated from another")
}
if (.invalidated)
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()
})
@@ -45,6 +82,11 @@ Context <- R6Class(
"Register a function to be called when this context is invalidated.
If this context is already invalidated, the function is called
immediately."
if (!identical(.pid, processId())) {
stop("Reactive context was created in one process and accessed from another")
}
if (.invalidated)
func()
else
@@ -62,14 +104,9 @@ Context <- R6Class(
},
executeFlushCallbacks = function() {
"For internal use only."
lapply(.flushCallbacks, function(func) {
withCallingHandlers({
func()
}, warning = function(e) {
# TODO: Callbacks in app
}, error = function(e) {
# TODO: Callbacks in app
})
lapply(.flushCallbacks, function(flushCallback) {
flushCallback()
})
}
)
@@ -104,25 +141,36 @@ ReactiveEnvironment <- R6Class(
}
return(.currentContext)
},
runWith = function(ctx, func) {
runWith = function(ctx, contextFunc) {
old.ctx <- .currentContext
.currentContext <<- ctx
on.exit(.currentContext <<- old.ctx)
shinyCallingHandlers(func())
contextFunc()
},
addPendingFlush = function(ctx, priority) {
.pendingFlush$enqueue(ctx, priority)
},
hasPendingFlush = function() {
return(!.pendingFlush$isEmpty())
},
# Returns TRUE if anything was actually called
flush = function() {
# If nothing to flush, exit early
if (!hasPendingFlush()) return(invisible(FALSE))
# If already in a flush, don't start another one
if (.inFlush) return()
if (.inFlush) return(invisible(FALSE))
.inFlush <<- TRUE
on.exit(.inFlush <<- FALSE)
on.exit({
.inFlush <<- FALSE
rLog$idle(domain = NULL)
})
while (!.pendingFlush$isEmpty()) {
while (hasPendingFlush()) {
ctx <- .pendingFlush$dequeue()
ctx$executeFlushCallbacks()
}
invisible(TRUE)
}
)
)
@@ -136,9 +184,10 @@ ReactiveEnvironment <- R6Class(
}
})
# Causes any pending invalidations to run.
# Causes any pending invalidations to run. Returns TRUE if any invalidations
# were pending (i.e. if work was actually done).
flushReact <- function() {
.getReactiveEnvironment()$flush()
return(.getReactiveEnvironment()$flush())
}
# Retrieves the current reactive context, or errors if there is no reactive
@@ -146,15 +195,41 @@ 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)
force(ctx)
function(...) {
ctx$run(function() {
captureStackTraces(
func(...)
)
})
}
})
}
reactivePromiseDomain <- function() {
promises::new_promise_domain(
wrapOnFulfilled = function(onFulfilled) {
force(onFulfilled)
ctx <- getCurrentContext()
wrapForContext(onFulfilled, ctx)
},
wrapOnRejected = function(onRejected) {
force(onRejected)
ctx <- getCurrentContext()
wrapForContext(onRejected, ctx)
}
)
}

View File

@@ -42,11 +42,11 @@ NULL
#
## ------------------------------------------------------------------------
createMockDomain <- function() {
callbacks <- list()
callbacks <- Callbacks$new()
ended <- FALSE
domain <- new.env(parent = emptyenv())
domain$onEnded <- function(callback) {
callbacks <<- c(callbacks, callback)
return(callbacks$register(callback))
}
domain$isEnded <- function() {
ended
@@ -55,10 +55,12 @@ createMockDomain <- function() {
domain$end <- function() {
if (!ended) {
ended <<- TRUE
lapply(callbacks, do.call, list())
callbacks$invoke()
}
invisible()
}
domain$incrementBusyCount <- function() NULL
domain$decrementBusyCount <- function() NULL
return(domain)
}
@@ -93,11 +95,7 @@ getDefaultReactiveDomain <- function() {
#' @rdname domains
#' @export
withReactiveDomain <- function(domain, expr) {
oldValue <- .globals$domain
.globals$domain <- domain
on.exit(.globals$domain <- oldValue)
expr
promises::with_promise_domain(createVarPromiseDomain(.globals, "domain", domain), expr)
}
#

File diff suppressed because it is too large Load Diff

590
R/render-cached-plot.R Normal file
View File

@@ -0,0 +1,590 @@
#' Plot output with cached images
#'
#' 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
#' 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}}
#' 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
#' upstream reactives are invalidated. This will also trigger re-execution of
#' the plotting expression, \code{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
#' 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
#' penalty.
#'
#' If you face these issues with the cache key, you can work around them by
#' extracting out the important parts of the objects, and/or by converting them
#' 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.
#'
#' 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
#' will not have cache key collisions.
#'
#' @section Cache scoping:
#'
#' There are a number of different ways you may want to scope the cache. For
#' example, you may want each user session to have their own plot cache, or
#' you may want each run of the application to have a cache (shared among
#' possibly multiple simultaneous user sessions), or you may want to have a
#' 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
#' 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
#' 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
#' 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
#' in size, and will be stored stored in memory, using a
#' \code{\link{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
#' example, you may want to use a larger or smaller cache, share a cache
#' among multiple R processes, or you may want the cache to persist across
#' 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
#' global.R. For example, this will create a cache with 20 MB of space
#' instead of the default 10 MB:
#' \preformatted{
#' shinyOptions(cache = memoryCache(size = 20e6))
#' }
#'
#' 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:
#' \preformatted{
#' function(input, output, session) {
#' shinyOptions(cache = memoryCache(size = 20e6))
#'
#' output$plot <- renderCachedPlot(
#' ...,
#' cache = "session"
#' )
#' }
#' }
#'
#' 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
#' application-level shared cache by putting this at the top of your app.R,
#' server.R, or global.R:
#' \preformatted{
#' shinyOptions(cache = diskCache(file.path(dirname(tempdir()), "myapp-cache"))
#' }
#'
#' This will create a subdirectory in your system temp directory named
#' \code{myapp-cache} (replace \code{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.
#'
#' To have the cache persist even across multiple reboots, you can create the
#' cache in a location outside of the temp directory. For example, it could
#' be a subdirectory of the application:
#' \preformatted{
#' shinyOptions(cache = diskCache("./myapp-cache"))
#' }
#'
#' In this case, resetting the cache will have to be done manually, by deleting
#' 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}.
#'
#' @section Interactive plots:
#'
#' \code{renderCachedPlot} can be used to create interactive plots. See
#' \code{\link{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
#' 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
#' 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
#' information.
#'
#' @seealso See \code{\link{renderPlot}} for the regular, non-cached version of
#' this function. For more about configuring caches, see
#' \code{\link{memoryCache}} and \code{\link{diskCache}}.
#'
#'
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' # A basic example that uses the default app-scoped memory cache.
#' # The cache will be shared among all simultaneous users of the application.
#' shinyApp(
#' fluidPage(
#' sidebarLayout(
#' sidebarPanel(
#' sliderInput("n", "Number of points", 4, 32, value = 8, step = 4)
#' ),
#' mainPanel(plotOutput("plot"))
#' )
#' ),
#' function(input, output, session) {
#' output$plot <- renderCachedPlot({
#' Sys.sleep(2) # Add an artificial delay
#' seqn <- seq_len(input$n)
#' plot(mtcars$wt[seqn], mtcars$mpg[seqn],
#' xlim = range(mtcars$wt), ylim = range(mtcars$mpg))
#' },
#' cacheKeyExpr = { list(input$n) }
#' )
#' }
#' )
#'
#'
#'
#' # An example uses a data object shared across sessions. mydata() is part of
#' # the cache key, so when its value changes, plots that were previously
#' # stored in the cache will no longer be used (unless mydata() changes back
#' # to its previous value).
#' mydata <- reactiveVal(data.frame(x = rnorm(400), y = rnorm(400)))
#'
#' ui <- fluidPage(
#' sidebarLayout(
#' sidebarPanel(
#' sliderInput("n", "Number of points", 50, 400, 100, step = 50),
#' actionButton("newdata", "New data")
#' ),
#' mainPanel(
#' plotOutput("plot")
#' )
#' )
#' )
#'
#' server <- function(input, output, session) {
#' observeEvent(input$newdata, {
#' mydata(data.frame(x = rnorm(400), y = rnorm(400)))
#' })
#'
#' output$plot <- renderCachedPlot(
#' {
#' Sys.sleep(2)
#' d <- mydata()
#' seqn <- seq_len(input$n)
#' plot(d$x[seqn], d$y[seqn], xlim = range(d$x), ylim = range(d$y))
#' },
#' cacheKeyExpr = { list(input$n, mydata()) },
#' )
#' }
#'
#' shinyApp(ui, server)
#'
#'
#' # A basic application with two plots, where each plot in each session has
#' # a separate cache.
#' shinyApp(
#' fluidPage(
#' sidebarLayout(
#' sidebarPanel(
#' sliderInput("n", "Number of points", 4, 32, value = 8, step = 4)
#' ),
#' mainPanel(
#' plotOutput("plot1"),
#' plotOutput("plot2")
#' )
#' )
#' ),
#' function(input, output, session) {
#' output$plot1 <- renderCachedPlot({
#' Sys.sleep(2) # Add an artificial delay
#' seqn <- seq_len(input$n)
#' plot(mtcars$wt[seqn], mtcars$mpg[seqn],
#' xlim = range(mtcars$wt), ylim = range(mtcars$mpg))
#' },
#' cacheKeyExpr = { list(input$n) },
#' cache = memoryCache()
#' )
#' output$plot2 <- renderCachedPlot({
#' Sys.sleep(2) # Add an artificial delay
#' seqn <- seq_len(input$n)
#' plot(mtcars$wt[seqn], mtcars$mpg[seqn],
#' xlim = range(mtcars$wt), ylim = range(mtcars$mpg))
#' },
#' cacheKeyExpr = { list(input$n) },
#' cache = memoryCache()
#' )
#' }
#' )
#'
#' }
#'
#' \dontrun{
#' # At the top of app.R, this set the application-scoped cache to be a memory
#' # cache that is 20 MB in size, and where cached objects expire after one
#' # hour.
#' shinyOptions(cache = memoryCache(max_size = 20e6, max_age = 3600))
#'
#' # At the top of app.R, this set the application-scoped cache to be a disk
#' # cache that can be shared among multiple concurrent R processes, and is
#' # deleted when the system reboots.
#' shinyOptions(cache = diskCache(file.path(dirname(tempdir()), "myapp-cache"))
#'
#' # At the top of app.R, this set the application-scoped cache to be a disk
#' # cache that can be shared among multiple concurrent R processes, and
#' # persists on disk across reboots.
#' shinyOptions(cache = diskCache("./myapp-cache"))
#'
#' # At the top of the server function, this set the session-scoped cache to be
#' # a memory cache that is 5 MB in size.
#' server <- function(input, output, session) {
#' shinyOptions(cache = memoryCache(max_size = 5e6))
#'
#' output$plot <- renderCachedPlot(
#' ...,
#' cache = "session"
#' )
#' }
#'
#' }
#' @export
renderCachedPlot <- function(expr,
cacheKeyExpr,
sizePolicy = sizeGrowthRatio(width = 400, height = 400, growthRate = 1.2),
res = 72,
cache = "app",
...,
outputArgs = list()
) {
# This ..stacktraceon is matched by a ..stacktraceoff.. when plotFunc
# is called
installExprFunction(expr, "func", parent.frame(), quoted = FALSE, ..stacktraceon = TRUE)
# This is so that the expr doesn't re-execute by itself; it needs to be
# triggered by the cache key (or width/height) changing.
isolatedFunc <- function() isolate(func())
args <- list(...)
cacheKeyExpr <- substitute(cacheKeyExpr)
# The real cache key we'll use also includes width, height, res, pixelratio.
# This is just the part supplied by the user.
userCacheKey <- reactive(cacheKeyExpr, env = parent.frame(), quoted = TRUE, label = "userCacheKey")
ensureCacheSetup <- function() {
# For our purposes, cache objects must support these methods.
isCacheObject <- function(x) {
# Use tryCatch in case the object does not support `$`.
tryCatch(
is.function(x$get) && is.function(x$set),
error = function(e) FALSE
)
}
if (isCacheObject(cache)) {
# If `cache` is already a cache object, do nothing
return()
} else if (identical(cache, "app")) {
cache <<- getShinyOption("cache")
} else if (identical(cache, "session")) {
cache <<- session$cache
} else {
stop('`cache` must either be "app", "session", or a cache object with methods, `$get`, and `$set`.')
}
}
# The width and height of the plot to draw, given from sizePolicy. These
# values get filled by an observer below.
fitDims <- reactiveValues(width = NULL, height = NULL)
resizeObserver <- NULL
ensureResizeObserver <- function() {
if (!is.null(resizeObserver))
return()
# Given the actual width/height of the image in the browser, this gets the
# width/height from sizePolicy() and pushes those values into `fitDims`.
# It's done this way so that the `fitDims` only change (and cause
# invalidations) when the rendered image size changes, and not every time
# the browser's <img> tag changes size.
doResizeCheck <- function() {
width <- session$clientData[[paste0('output_', outputName, '_width')]]
height <- session$clientData[[paste0('output_', outputName, '_height')]]
if (is.null(width)) width <- 0
if (is.null(height)) height <- 0
rect <- sizePolicy(c(width, height))
fitDims$width <- rect[1]
fitDims$height <- rect[2]
}
# Run it once immediately, then set up the observer
isolate(doResizeCheck())
resizeObserver <<- observe(doResizeCheck())
}
# Vars to store session and output, so that they can be accessed from
# the plotObj() reactive.
session <- NULL
outputName <- NULL
drawReactive <- reactive(label = "plotObj", {
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
# renderFunc, which happens only if there's a cache miss.)
userCacheKey(),
function(userCacheKeyValue) {
# Get width/height, but don't depend on them.
isolate({
width <- fitDims$width
height <- fitDims$height
})
pixelratio <- session$clientData$pixelratio %OR% 1
do.call("drawPlot", c(
list(
name = outputName,
session = session,
func = isolatedFunc,
width = width,
height = height,
pixelratio = pixelratio,
res = res
),
args
))
},
catch = function(reason) {
# Non-isolating read. A common reason for errors in plotting is because
# the dimensions are too small. By taking a dependency on width/height,
# we can try again if the plot output element changes size.
fitDims$width
fitDims$height
# Propagate the error
stop(reason)
}
)
})
# This function is the one that's returned from renderPlot(), and gets
# wrapped in an observer when the output value is assigned.
renderFunc <- function(shinysession, name, ...) {
outputName <<- name
session <<- shinysession
ensureCacheSetup()
ensureResizeObserver()
hybrid_chain(
# This use of the userCacheKey() sets up the reactive dependency that
# causes plot re-draw events. These may involve pulling from the cache,
# replaying a display list, or re-executing user code.
userCacheKey(),
function(userCacheKeyResult) {
width <- fitDims$width
height <- fitDims$height
pixelratio <- session$clientData$pixelratio %OR% 1
key <- digest::digest(list(outputName, userCacheKeyResult, width, height, res, pixelratio), "xxhash64")
plotObj <- cache$get(key)
# First look in cache.
# Case 1. cache hit.
if (!is.key_missing(plotObj)) {
return(list(
cacheHit = TRUE,
key = key,
plotObj = plotObj,
width = width,
height = height,
pixelratio = pixelratio
))
}
# If not in cache, hybrid_chain call to drawReactive
#
# Two more possible cases:
# 2. drawReactive will re-execute and return a plot that's the
# correct size.
# 3. It will not re-execute, but it will return the previous value,
# which is the wrong size. It will include a valid display list
# which can be used by resizeSavedPlot.
hybrid_chain(
drawReactive(),
function(drawReactiveResult) {
# Pass along the key for caching in the next stage
list(
cacheHit = FALSE,
key = key,
plotObj = drawReactiveResult,
width = width,
height = height,
pixelratio = 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
))
# 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
})
}
)
}
# If renderPlot isn't going to adapt to the height of the div, then the
# div needs to adapt to the height of renderPlot. By default, plotOutput
# sets the height to 400px, so to make it adapt we need to override it
# with NULL.
outputFunc <- plotOutput
formals(outputFunc)['height'] <- list(NULL)
markRenderFunction(outputFunc, renderFunc, outputArgs = outputArgs)
}
#' Create a sizing function that grows at a given ratio
#'
#' Returns a function which takes a two-element vector representing an input
#' width and height, and returns a two-element vector of width and height. The
#' possible widths are the base width times the growthRate to any integer power.
#' For example, with a base width of 500 and growth rate of 1.25, the possible
#' widths include 320, 400, 500, 625, 782, and so on, both smaller and larger.
#' Sizes are rounded up to the next pixel. Heights are computed the same way as
#' widths.
#'
#' @param width,height Base width and height.
#' @param growthRate Growth rate multiplier.
#'
#' @seealso This is to be used with \code{\link{renderCachedPlot}}.
#'
#' @examples
#' f <- sizeGrowthRatio(500, 500, 1.25)
#' f(c(400, 400))
#' f(c(500, 500))
#' f(c(530, 550))
#' f(c(625, 700))
#'
#' @export
sizeGrowthRatio <- function(width = 400, height = 400, growthRate = 1.2) {
round_dim_up <- function(x, base, rate) {
power <- ceiling(log(x / base, rate))
ceiling(base * rate^power)
}
function(dims) {
if (length(dims) != 2) {
stop("dims must be a vector with two numbers, for width and height.")
}
c(
round_dim_up(dims[1], width, growthRate),
round_dim_up(dims[2], height, growthRate)
)
}
}

997
R/render-plot.R Normal file
View File

@@ -0,0 +1,997 @@
#' Plot Output
#'
#' Renders a reactive plot that is suitable for assigning to an \code{output}
#' slot.
#'
#' The corresponding HTML output tag should be \code{div} or \code{img} and have
#' the CSS class name \code{shiny-plot-output}.
#'
#' @section Interactive plots:
#'
#' With ggplot2 graphics, the code in \code{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
#' properly scaled to the data space.
#'
#' See \code{\link{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}}.
#'
#' @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 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
#' rendering in R; it won't change the actual ppi of the browser.
#' @param ... Arguments to be passed through to \code{\link[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
#' 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}.
#' 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}.
#' @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
#' interactive R Markdown document.
#' @export
renderPlot <- function(expr, width='auto', height='auto', res=72, ...,
env=parent.frame(), quoted=FALSE,
execOnResize=FALSE, outputArgs=list()
) {
# This ..stacktraceon is matched by a ..stacktraceoff.. when plotFunc
# is called
installExprFunction(expr, "func", env, quoted, ..stacktraceon = TRUE)
args <- list(...)
if (is.reactive(width))
widthWrapper <- width
else if (is.function(width))
widthWrapper <- reactive({ width() })
else
widthWrapper <- function() { width }
if (is.reactive(height))
heightWrapper <- height
else if (is.function(height))
heightWrapper <- reactive({ height() })
else
heightWrapper <- function() { height }
getDims <- function() {
width <- widthWrapper()
height <- heightWrapper()
# Note that these are reactive calls. A change to the width and height
# will inherently cause a reactive plot to redraw (unless width and
# height were explicitly specified).
if (width == 'auto')
width <- session$clientData[[paste0('output_', outputName, '_width')]]
if (height == 'auto')
height <- session$clientData[[paste0('output_', outputName, '_height')]]
list(width = width, height = height)
}
# Vars to store session and output, so that they can be accessed from
# the plotObj() reactive.
session <- NULL
outputName <- NULL
# Calls drawPlot, invoking the user-provided `func` (which may or may not
# return a promise). The idea is that the (cached) return value from this
# reactive can be used for varying width/heights, as it includes the
# displaylist, which is resolution independent.
drawReactive <- reactive(label = "plotObj", {
hybrid_chain(
{
# If !execOnResize, don't invalidate when width/height changes.
dims <- if (execOnResize) getDims() else isolate(getDims())
pixelratio <- session$clientData$pixelratio %OR% 1
do.call("drawPlot", c(
list(
name = outputName,
session = session,
func = func,
width = dims$width,
height = dims$height,
pixelratio = pixelratio,
res = res
), args))
},
catch = function(reason) {
# Non-isolating read. A common reason for errors in plotting is because
# the dimensions are too small. By taking a dependency on width/height,
# we can try again if the plot output element changes size.
getDims()
# Propagate the error
stop(reason)
}
)
})
# This function is the one that's returned from renderPlot(), and gets
# wrapped in an observer when the output value is assigned.
renderFunc <- function(shinysession, name, ...) {
outputName <<- name
session <<- shinysession
hybrid_chain(
drawReactive(),
function(result) {
dims <- getDims()
pixelratio <- session$clientData$pixelratio %OR% 1
result <- do.call("resizeSavedPlot", c(
list(name, shinysession, result, dims$width, dims$height, pixelratio, res),
args
))
result$img
}
)
}
# If renderPlot isn't going to adapt to the height of the div, then the
# div needs to adapt to the height of renderPlot. By default, plotOutput
# sets the height to 400px, so to make it adapt we need to override it
# with NULL.
outputFunc <- plotOutput
if (!identical(height, 'auto')) formals(outputFunc)['height'] <- list(NULL)
markRenderFunction(outputFunc, renderFunc, outputArgs = outputArgs)
}
resizeSavedPlot <- function(name, session, result, width, height, pixelratio, res, ...) {
if (result$img$width == width && result$img$height == height &&
result$pixelratio == pixelratio && result$res == res) {
return(result)
}
coordmap <- NULL
outfile <- plotPNG(function() {
grDevices::replayPlot(result$recordedPlot)
coordmap <<- getCoordmap(result$plotResult, width*pixelratio, height*pixelratio, res*pixelratio)
}, width = width*pixelratio, height = height*pixelratio, res = res*pixelratio, ...)
on.exit(unlink(outfile), add = TRUE)
result$img <- list(
src = session$fileUrl(name, outfile, contentType = "image/png"),
width = width,
height = height,
coordmap = coordmap,
error = attr(coordmap, "error", exact = TRUE)
)
result
}
drawPlot <- function(name, session, func, width, height, pixelratio, res, ...) {
# 1. Start PNG
# 2. Enable displaylist recording
# 3. Call user-defined func
# 4. Print/save result, if visible
# 5. Snapshot displaylist
# 6. Form coordmap
# 7. End PNG (in finally)
# 8. Form img tag
# 9. Return img, value, displaylist, coordmap
# 10. On error, take width and height dependency
outfile <- tempfile(fileext='.png') # If startPNG throws, this could leak. Shrug.
device <- startPNG(outfile, width*pixelratio, height*pixelratio, res = res*pixelratio, ...)
domain <- createGraphicsDevicePromiseDomain(device)
grDevices::dev.control(displaylist = "enable")
hybrid_chain(
hybrid_chain(
promises::with_promise_domain(domain, {
hybrid_chain(
func(),
function(value, .visible) {
if (.visible) {
# A modified version of print.ggplot which returns the built ggplot object
# as well as the gtable grob. This overrides the ggplot::print.ggplot
# method, but only within the context of renderPlot. The reason this needs
# to be a (pseudo) S3 method is so that, if an object has a class in
# addition to ggplot, and there's a print method for that class, that we
# won't override that method. https://github.com/rstudio/shiny/issues/841
print.ggplot <- custom_print.ggplot
# Use capture.output to squelch printing to the actual console; we
# are only interested in plot output
utils::capture.output({
# This ..stacktraceon.. negates the ..stacktraceoff.. that wraps
# the call to plotFunc. The value needs to be printed just in case
# it's an object that requires printing to generate plot output,
# similar to ggplot2. But for base graphics, it would already have
# been rendered when func was called above, and the print should
# have no effect.
result <- ..stacktraceon..(print(value))
# TODO jcheng 2017-04-11: Verify above ..stacktraceon..
})
result
} else {
# Not necessary, but I wanted to make it explicit
NULL
}
},
function(value) {
list(
plotResult = value,
recordedPlot = grDevices::recordPlot(),
coordmap = getCoordmap(value, width*pixelratio, height*pixelratio, res*pixelratio),
pixelratio = pixelratio,
res = res
)
}
)
}),
finally = function() {
grDevices::dev.off(device)
}
),
function(result) {
result$img <- dropNulls(list(
src = session$fileUrl(name, outfile, contentType='image/png'),
width = width,
height = height,
coordmap = result$coordmap,
# Get coordmap error message if present
error = attr(result$coordmap, "error", exact = TRUE)
))
result
},
finally = function() {
unlink(outfile)
}
)
}
# A modified version of print.ggplot which returns the built ggplot object
# as well as the gtable grob. This overrides the ggplot::print.ggplot
# method, but only within the context of renderPlot. The reason this needs
# to be a (pseudo) S3 method is so that, if an object has a class in
# addition to ggplot, and there's a print method for that class, that we
# won't override that method. https://github.com/rstudio/shiny/issues/841
custom_print.ggplot <- function(x) {
grid::grid.newpage()
build <- ggplot2::ggplot_build(x)
gtable <- ggplot2::ggplot_gtable(build)
grid::grid.draw(gtable)
structure(list(
build = build,
gtable = gtable
), class = "ggplot_build_gtable")
}
# The coordmap extraction functions below return something like the examples
# below. For base graphics:
# plot(mtcars$wt, mtcars$mpg)
# str(getPrevPlotCoordmap(400, 300))
# List of 2
# $ panels:List of 1
# ..$ :List of 4
# .. ..$ domain :List of 4
# .. .. ..$ left : num 1.36
# .. .. ..$ right : num 5.58
# .. .. ..$ bottom: num 9.46
# .. .. ..$ top : num 34.8
# .. ..$ range :List of 4
# .. .. ..$ left : num 65.6
# .. .. ..$ right : num 366
# .. .. ..$ bottom: num 238
# .. .. ..$ top : num 48.2
# .. ..$ log :List of 2
# .. .. ..$ x: NULL
# .. .. ..$ y: NULL
# .. ..$ mapping: Named list()
# $ dims :List of 2
# ..$ width : num 400
# ..$ height: num 300
#
# For ggplot2, first you need to define the print.ggplot function from inside
# renderPlot, then use it to print the plot:
# print.ggplot <- function(x) {
# grid::grid.newpage()
#
# build <- ggplot2::ggplot_build(x)
#
# gtable <- ggplot2::ggplot_gtable(build)
# grid::grid.draw(gtable)
#
# structure(list(
# build = build,
# gtable = gtable
# ), class = "ggplot_build_gtable")
# }
#
# p <- print(ggplot(mtcars, aes(wt, mpg)) + geom_point())
# str(getGgplotCoordmap(p, 400, 300, 72))
# List of 2
# $ panels:List of 1
# ..$ :List of 8
# .. ..$ panel : num 1
# .. ..$ row : num 1
# .. ..$ col : num 1
# .. ..$ panel_vars: Named list()
# .. ..$ 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
# .. ..$ mapping :List of 2
# .. .. ..$ x: chr "wt"
# .. .. ..$ y: chr "mpg"
# .. ..$ range :List of 4
# .. .. ..$ left : num 33.3
# .. .. ..$ right : num 355
# .. .. ..$ bottom: num 328
# .. .. ..$ top : num 5.48
# $ dims :List of 2
# ..$ width : num 400
# ..$ height: num 300
#
# 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))
# List of 2
# $ panels:List of 2
# ..$ :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
# .. ..$ 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
# .. ..$ mapping :List of 3
# .. .. ..$ x : chr "wt"
# .. .. ..$ y : chr "mpg"
# .. .. ..$ panelvar1: chr "am"
# .. ..$ range :List of 4
# .. .. ..$ left : num 33.3
# .. .. ..$ right : num 191
# .. .. ..$ bottom: num 328
# .. .. ..$ 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
# .. ..$ 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
# .. ..$ mapping :List of 3
# .. .. ..$ x : chr "wt"
# .. .. ..$ y : chr "mpg"
# .. .. ..$ panelvar1: chr "am"
# .. ..$ range :List of 4
# .. .. ..$ left : num 197
# .. .. ..$ right : num 355
# .. .. ..$ bottom: num 328
# .. .. ..$ top : num 23.1
# $ dims :List of 2
# ..$ width : num 400
# ..$ height: num 300
getCoordmap <- function(x, width, height, res) {
if (inherits(x, "ggplot_build_gtable")) {
getGgplotCoordmap(x, width, height, res)
} else {
getPrevPlotCoordmap(width, height)
}
}
# Get a coordmap for the previous plot made with base graphics.
# Requires width and height of output image, in pixels.
# Must be called before the graphics device is closed.
getPrevPlotCoordmap <- function(width, height) {
usrCoords <- graphics::par('usr')
usrBounds <- usrCoords
if (graphics::par('xlog')) {
usrBounds[c(1,2)] <- 10 ^ usrBounds[c(1,2)]
}
if (graphics::par('ylog')) {
usrBounds[c(3,4)] <- 10 ^ usrBounds[c(3,4)]
}
# Wrapped in double list because other types of plots can have multiple panels.
panel_info <- list(list(
# Bounds of the plot area, in data space
domain = list(
left = usrCoords[1],
right = usrCoords[2],
bottom = usrCoords[3],
top = usrCoords[4]
),
# The bounds of the plot area, in DOM pixels
range = list(
left = graphics::grconvertX(usrBounds[1], 'user', 'ndc') * width,
right = graphics::grconvertX(usrBounds[2], 'user', 'ndc') * width,
bottom = (1-graphics::grconvertY(usrBounds[3], 'user', 'ndc')) * height - 1,
top = (1-graphics::grconvertY(usrBounds[4], 'user', 'ndc')) * height - 1
),
log = list(
x = if (graphics::par('xlog')) 10 else NULL,
y = if (graphics::par('ylog')) 10 else NULL
),
# We can't extract the original variable names from a base graphic.
# `mapping` is an empty _named_ list, so that it is converted to an object
# (not an array) in JSON.
mapping = list(x = NULL)[0]
))
list(
panels = panel_info,
dims = list(
width = width,
height =height
)
)
}
# Given a ggplot_build_gtable object, return a coordmap for it.
getGgplotCoordmap <- function(p, width, height, res) {
if (!inherits(p, "ggplot_build_gtable"))
return(NULL)
tryCatch({
# Get info from built ggplot object
panel_info <- find_panel_info(p$build)
# Get ranges from gtable - it's possible for this to return more elements than
# info, because it calculates positions even for panels that aren't present.
# This can happen with facet_wrap.
ranges <- find_panel_ranges(p$gtable, res)
for (i in seq_along(panel_info)) {
panel_info[[i]]$range <- ranges[[i]]
}
return(
list(
panels = panel_info,
dims = list(
width = width,
height = height
)
)
)
}, error = function(e) {
# If there was an error extracting info from the ggplot object, just return
# a list with the error message.
return(structure(list(), error = e$message))
})
}
find_panel_info <- function(b) {
# Structure of ggplot objects changed after 2.1.0. After 2.2.1, there was a
# an API for extracting the necessary information.
ggplot_ver <- utils::packageVersion("ggplot2")
if (ggplot_ver > "2.2.1") {
find_panel_info_api(b)
} else if (ggplot_ver > "2.1.0") {
find_panel_info_non_api(b, ggplot_format = "new")
} else {
find_panel_info_non_api(b, ggplot_format = "old")
}
}
# This is for ggplot2>2.2.1, after an API was introduced for extracting
# information about the plot object.
find_panel_info_api <- function(b) {
# Given a built ggplot object, return x and y domains (data space coords) for
# each panel.
layout <- ggplot2::summarise_layout(b)
coord <- ggplot2::summarise_coord(b)
layers <- ggplot2::summarise_layers(b)
# Given x and y scale objects and a coord object, return a list that has
# the bases of log transformations for x and y, or NULL if it's not a
# log transform.
get_log_bases <- function(xscale, yscale, coord) {
# Given a transform object, find the log base; if the transform object is
# NULL, or if it's not a log transform, return NA.
get_log_base <- function(trans) {
if (!is.null(trans) && grepl("^log-", trans$name)) {
environment(trans$transform)$base
} else {
NA_real_
}
}
# First look for log base in scale, then coord; otherwise NULL.
list(
x = get_log_base(xscale$trans) %OR% coord$xlog %OR% NULL,
y = get_log_base(yscale$trans) %OR% coord$ylog %OR% NULL
)
}
# Given x/y min/max, and the x/y scale objects, create a list that
# represents the domain. Note that the x/y min/max should be taken from
# the layout summary table, not the scale objects.
get_domain <- function(xmin, xmax, ymin, ymax, xscale, yscale) {
is_reverse <- function(scale) {
identical(scale$trans$name, "reverse")
}
domain <- list(
left = xmin,
right = xmax,
bottom = ymin,
top = ymax
)
if (is_reverse(xscale)) {
domain$left <- -domain$left
domain$right <- -domain$right
}
if (is_reverse(yscale)) {
domain$top <- -domain$top
domain$bottom <- -domain$bottom
}
domain
}
# Rename the items in vars to have names like panelvar1, panelvar2.
rename_panel_vars <- function(vars) {
for (i in seq_along(vars)) {
names(vars)[i] <- paste0("panelvar", i)
}
vars
}
get_mappings <- function(layers, layout, coord) {
# For simplicity, we'll just use the mapping from the first layer of the
# ggplot object. The original uses quoted expressions; convert to
# character.
mapping <- layers$mapping[[1]]
# In ggplot2 <=2.2.1, the mappings are expressions. In later versions, they
# are quosures. `deparse(quo_squash(x))` will handle both cases.
# as.character results in unexpected behavior for expressions like `wt/2`,
# which is why we use deparse.
mapping <- lapply(mapping, function(x) deparse(rlang::quo_squash(x)))
# If either x or y is not present, give it a NULL entry.
mapping <- mergeVectors(list(x = NULL, y = NULL), mapping)
# The names (not values) of panel vars are the same across all panels,
# so just look at the first one. Also, the order of panel vars needs
# to be reversed.
vars <- rev(layout$vars[[1]])
for (i in seq_along(vars)) {
mapping[[paste0("panelvar", i)]] <- names(vars)[i]
}
if (isTRUE(coord$flip)) {
mapping[c("x", "y")] <- mapping[c("y", "x")]
}
mapping
}
# Mapping is constant across all panels, so get it here and reuse later.
mapping <- get_mappings(layers, layout, coord)
# If coord_flip is used, these need to be swapped
flip_xy <- function(layout) {
l <- layout
l$xscale <- layout$yscale
l$yscale <- layout$xscale
l$xmin <- layout$ymin
l$xmax <- layout$ymax
l$ymin <- layout$xmin
l$ymax <- layout$xmax
l
}
if (coord$flip) {
layout <- flip_xy(layout)
}
# Iterate over each row in the layout data frame
lapply(seq_len(nrow(layout)), function(i) {
# Slice out one row, use it as a list. The (former) list-cols are still
# in lists, so we need to unwrap them.
l <- as.list(layout[i, ])
l$vars <- l$vars[[1]]
l$xscale <- l$xscale[[1]]
l$yscale <- l$yscale[[1]]
list(
panel = as.numeric(l$panel),
row = l$row,
col = l$col,
# Rename panel vars. They must also be in reversed order.
panel_vars = rename_panel_vars(rev(l$vars)),
log = get_log_bases(l$xscale, l$yscale, coord),
domain = get_domain(l$xmin, l$xmax, l$ymin, l$ymax, l$xscale, l$yscale),
mapping = mapping
)
})
}
# This is for ggplot2<=2.2.1, before an API was introduced for extracting
# information about the plot object. The "old" format was used before 2.1.0.
# The "new" format was used after 2.1.0, up to 2.2.1. The reason these two
# formats are mixed together in a single function is historical, and it's not
# worthwhile to separate them at this point.
find_panel_info_non_api <- function(b, ggplot_format) {
# Given a single range object (representing the data domain) from a built
# ggplot object, return the domain.
find_panel_domain <- function(b, panel_num, scalex_num = 1, scaley_num = 1) {
if (ggplot_format == "new") {
range <- b$layout$panel_ranges[[panel_num]]
} else {
range <- b$panel$ranges[[panel_num]]
}
domain <- list(
left = range$x.range[1],
right = range$x.range[2],
bottom = range$y.range[1],
top = range$y.range[2]
)
# Check for reversed scales
if (ggplot_format == "new") {
xscale <- b$layout$panel_scales$x[[scalex_num]]
yscale <- b$layout$panel_scales$y[[scaley_num]]
} else {
xscale <- b$panel$x_scales[[scalex_num]]
yscale <- b$panel$y_scales[[scaley_num]]
}
if (!is.null(xscale$trans) && xscale$trans$name == "reverse") {
domain$left <- -domain$left
domain$right <- -domain$right
}
if (!is.null(yscale$trans) && yscale$trans$name == "reverse") {
domain$top <- -domain$top
domain$bottom <- -domain$bottom
}
domain
}
# Given built ggplot object, return object with the log base for x and y if
# there are log scales or coord transforms.
check_log_scales <- function(b, scalex_num = 1, scaley_num = 1) {
# Given a vector of transformation names like c("log-10", "identity"),
# return the first log base, like 10. If none are present, return NULL.
extract_log_base <- function(names) {
names <- names[grepl("^log-", names)]
if (length(names) == 0)
return(NULL)
names <- names[1]
as.numeric(sub("^log-", "", names))
}
# Look for log scales and log coord transforms. People shouldn't use both.
x_names <- character(0)
y_names <- character(0)
# Continuous scales have a trans; discrete ones don't
if (ggplot_format == "new") {
if (!is.null(b$layout$panel_scales$x[[scalex_num]]$trans))
x_names <- b$layout$panel_scales$x[[scalex_num]]$trans$name
if (!is.null(b$layout$panel_scales$y[[scaley_num]]$trans))
y_names <- b$layout$panel_scales$y[[scaley_num]]$trans$name
} else {
if (!is.null(b$panel$x_scales[[scalex_num]]$trans))
x_names <- b$panel$x_scales[[scalex_num]]$trans$name
if (!is.null(b$panel$y_scales[[scaley_num]]$trans))
y_names <- b$panel$y_scales[[scaley_num]]$trans$name
}
coords <- b$plot$coordinates
if (!is.null(coords$trans)) {
if (!is.null(coords$trans$x))
x_names <- c(x_names, coords$trans$x$name)
if (!is.null(coords$trans$y))
y_names <- c(y_names, coords$trans$y$name)
}
# Keep only scale/trans names that start with "log-"
x_names <- x_names[grepl("^log-", x_names)]
y_names <- y_names[grepl("^log-", y_names)]
# Extract the log base from the trans name -- a string like "log-10".
list(
x = extract_log_base(x_names),
y = extract_log_base(y_names)
)
}
# Given a built ggplot object, return a named list of variables mapped to x
# and y. This function will be called for each panel, but in practice the
# result is always the same across panels, so we'll cache the result.
mappings_cache <- NULL
find_plot_mappings <- function(b) {
if (!is.null(mappings_cache))
return(mappings_cache)
# lapply'ing as.character results in unexpected behavior for expressions
# like `wt/2`. This works better.
mappings <- as.list(as.character(b$plot$mapping))
# If x or y mapping is missing, look in each layer for mappings and return
# the first one.
missing_mappings <- setdiff(c("x", "y"), names(mappings))
if (length(missing_mappings) != 0) {
# Grab mappings for each layer
layer_mappings <- lapply(b$plot$layers, function(layer) {
lapply(layer$mapping, as.character)
})
# Get just the first x or y value in the combined list of plot and layer
# mappings.
mappings <- c(list(mappings), layer_mappings)
mappings <- Reduce(x = mappings, init = list(x = NULL, y = NULL),
function(init, m) {
# Can't use m$x/m$y; you get a partial match with xintercept/yintercept
if (is.null(init[["x"]]) && !is.null(m[["x"]])) init$x <- m[["x"]]
if (is.null(init[["y"]]) && !is.null(m[["y"]])) init$y <- m[["y"]]
init
}
)
}
# Look for CoordFlip
if (inherits(b$plot$coordinates, "CoordFlip")) {
mappings[c("x", "y")] <- mappings[c("y", "x")]
}
mappings_cache <<- mappings
mappings
}
if (ggplot_format == "new") {
layout <- b$layout$panel_layout
} else {
layout <- b$panel$layout
}
# Convert factor to numbers
layout$PANEL <- as.integer(as.character(layout$PANEL))
# Names of facets
facet_vars <- NULL
if (ggplot_format == "new") {
facet <- b$layout$facet
if (inherits(facet, "FacetGrid")) {
facet_vars <- vapply(c(facet$params$cols, facet$params$rows), as.character, character(1))
} else if (inherits(facet, "FacetWrap")) {
facet_vars <- vapply(facet$params$facets, as.character, character(1))
}
} else {
facet <- b$plot$facet
if (inherits(facet, "grid")) {
facet_vars <- vapply(c(facet$cols, facet$rows), as.character, character(1))
} else if (inherits(facet, "wrap")) {
facet_vars <- vapply(facet$facets, as.character, character(1))
}
}
# Iterate over each row in the layout data frame
lapply(seq_len(nrow(layout)), function(i) {
# Slice out one row
l <- layout[i, ]
scale_x <- l$SCALE_X
scale_y <- l$SCALE_Y
mapping <- find_plot_mappings(b)
# For each of the faceting variables, get the value of that variable in
# the current panel. Default to empty _named_ list so that it's sent as a
# JSON object, not array.
panel_vars <- list(a = NULL)[0]
for (i in seq_along(facet_vars)) {
var_name <- facet_vars[[i]]
vname <- paste0("panelvar", i)
mapping[[vname]] <- var_name
panel_vars[[vname]] <- l[[var_name]]
}
list(
panel = l$PANEL,
row = l$ROW,
col = l$COL,
panel_vars = panel_vars,
scale_x = scale_x,
scale_y = scale_x,
log = check_log_scales(b, scale_x, scale_y),
domain = find_panel_domain(b, l$PANEL, scale_x, scale_y),
mapping = mapping
)
})
}
# Given a gtable object, return the x and y ranges (in pixel dimensions)
find_panel_ranges <- function(g, res) {
# Given a vector of unit objects, return logical vector indicating which ones
# are "null" units. These units use the remaining available width/height --
# that is, the space not occupied by elements that have an absolute size.
is_null_unit <- function(x) {
# A vector of units can be either a list of individual units (a unit.list
# object), each with their own set of attributes, or an atomic vector with
# one set of attributes. ggplot2 switched from the former (in version
# 1.0.1) to the latter. We need to make sure that we get the correct
# result in both cases.
if (inherits(x, "unit.list")) {
# For ggplot2 <= 1.0.1
vapply(x, FUN.VALUE = logical(1), function(u) {
isTRUE(attr(u, "unit", exact = TRUE) == "null")
})
} else {
# For later versions of ggplot2
attr(x, "unit", exact = TRUE) == "null"
}
}
# Workaround for a bug in the quartz device. If you have a 400x400 image and
# run `convertWidth(unit(1, "npc"), "native")`, the result will depend on
# res setting of the device. If res=72, then it returns 400 (as expected),
# but if, e.g., res=96, it will return 300, which is incorrect.
devScaleFactor <- 1
if (grepl("quartz", names(grDevices::dev.cur()), fixed = TRUE)) {
devScaleFactor <- res / 72
}
# Convert a unit (or vector of units) to a numeric vector of pixel sizes
h_px <- function(x) {
devScaleFactor * grid::convertHeight(x, "native", valueOnly = TRUE)
}
w_px <- function(x) {
devScaleFactor * grid::convertWidth(x, "native", valueOnly = TRUE)
}
# Given a vector of relative sizes (in grid units), and a function for
# converting grid units to numeric pixels, return a list with: known pixel
# dimensions, scalable dimensions, and the overall space for the scalable
# objects.
find_size_info <- function(rel_sizes, unit_to_px) {
# Total pixels (in height or width)
total_px <- unit_to_px(grid::unit(1, "npc"))
# Calculate size of all panel(s) together. Panels (and only panels) have
# null size.
null_idx <- is_null_unit(rel_sizes)
# All the absolute heights. At this point, null heights are 0. We need to
# calculate them separately and add them in later.
px_sizes <- unit_to_px(rel_sizes)
# Mark the null heights as NA.
px_sizes[null_idx] <- NA_real_
# The plotting panels all are 'null' units.
null_sizes <- rep(NA_real_, length(rel_sizes))
null_sizes[null_idx] <- as.numeric(rel_sizes[null_idx])
# Total size allocated for panels is the total image size minus absolute
# (non-panel) elements.
panel_px_total <- total_px - sum(px_sizes, na.rm = TRUE)
# Size of a 1null unit
null_px <- abs(panel_px_total / sum(null_sizes, na.rm = TRUE))
# This returned list contains:
# * px_sizes: A vector of known pixel dimensions. The values that were
# null units will be assigned NA. The null units are ones that scale
# when the plotting area is resized.
# * null_sizes: A vector of the null units. All others will be assigned
# NA. The null units often are 1, but they may be any value, especially
# when using coord_fixed.
# * null_px: The size (in pixels) of a 1null unit.
# * null_px_scaled: The size (in pixels) of a 1null unit when scaled to
# fit a smaller dimension (used for plots with coord_fixed).
list(
px_sizes = abs(px_sizes),
null_sizes = null_sizes,
null_px = null_px,
null_px_scaled = null_px
)
}
# Given a size_info, return absolute pixel positions
size_info_to_px <- function(info) {
px_sizes <- info$px_sizes
null_idx <- !is.na(info$null_sizes)
px_sizes[null_idx] <- info$null_sizes[null_idx] * info$null_px_scaled
# If this direction is scaled down because of coord_fixed, we need to add an
# offset so that the pixel locations are centered.
offset <- (info$null_px - info$null_px_scaled) *
sum(info$null_sizes, na.rm = TRUE) / 2
# Get absolute pixel positions
cumsum(px_sizes) + offset
}
heights_info <- find_size_info(g$heights, h_px)
widths_info <- find_size_info(g$widths, w_px)
if (g$respect) {
# This is a plot with coord_fixed. The grid 'respect' option means to use
# the same pixel value for 1null, for width and height. We want the
# smaller of the two values -- that's what makes the plot fit in the
# viewport.
null_px_min <- min(heights_info$null_px, widths_info$null_px)
heights_info$null_px_scaled <- null_px_min
widths_info$null_px_scaled <- null_px_min
}
# Convert to absolute pixel positions
y_pos <- size_info_to_px(heights_info)
x_pos <- size_info_to_px(widths_info)
# Match up the pixel dimensions to panels
layout <- g$layout
# For panels:
# * For facet_wrap, they'll be named "panel-1", "panel-2", etc.
# * For no facet or facet_grid, they'll just be named "panel". For
# facet_grid, we need to re-order the layout table. Assume that panel
# numbers go from left to right, then next row.
# Assign a number to each panel, corresponding to PANEl in the built ggplot
# object.
layout <- layout[grepl("^panel", layout$name), ]
layout <- layout[order(layout$t, layout$l), ]
layout$panel <- seq_len(nrow(layout))
# Return list of lists, where each inner list has left, right, top, bottom
# values for a panel
lapply(seq_len(nrow(layout)), function(i) {
p <- layout[i, , drop = FALSE]
list(
left = x_pos[p$l - 1],
right = x_pos[p$r],
bottom = y_pos[p$b],
top = y_pos[p$t - 1]
)
})
}

228
R/render-table.R Normal file
View File

@@ -0,0 +1,228 @@
#' Table Output
#'
#' Creates a reactive table that is suitable for assigning to an \code{output}
#' slot.
#'
#' The corresponding HTML output tag should be \code{div} and have the CSS
#' class name \code{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
#' 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 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}
#' 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
#' for that particular column, indicating that it should keep its default
#' alignment). If \code{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
#' 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
#' columns will be displayed in scientific format with a precision of
#' \code{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()})?
#' 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
#' used in an interactive R Markdown document.
#' @export
renderTable <- function(expr, striped = FALSE, hover = FALSE,
bordered = FALSE, spacing = c("s", "xs", "m", "l"),
width = "auto", align = NULL,
rownames = FALSE, colnames = TRUE,
digits = NULL, na = "NA", ...,
env = parent.frame(), quoted = FALSE,
outputArgs=list()) {
installExprFunction(expr, "func", env, quoted)
if (!is.function(spacing)) spacing <- match.arg(spacing)
# A small helper function to create a wrapper for an argument that was
# passed to renderTable()
createWrapper <- function(arg) {
if (is.function(arg)) wrapper <- arg
else wrapper <- function() arg
return(wrapper)
}
# Create wrappers for most arguments so that functions can also be passed
# in, rather than only literals (useful for shiny apps)
stripedWrapper <- createWrapper(striped)
hoverWrapper <- createWrapper(hover)
borderedWrapper <- createWrapper(bordered)
spacingWrapper <- createWrapper(spacing)
widthWrapper <- createWrapper(width)
alignWrapper <- createWrapper(align)
rownamesWrapper <- createWrapper(rownames)
colnamesWrapper <- createWrapper(colnames)
digitsWrapper <- createWrapper(digits)
naWrapper <- createWrapper(na)
dots <- list(...) ## used later (but defined here because of scoping)
createRenderFunction(
func,
function(data, session, name, ...) {
striped <- stripedWrapper()
hover <- hoverWrapper()
bordered <- borderedWrapper()
format <- c(striped = striped, hover = hover, bordered = bordered)
spacing <- spacingWrapper()
width <- widthWrapper()
align <- alignWrapper()
rownames <- rownamesWrapper()
colnames <- colnamesWrapper()
digits <- digitsWrapper()
na <- naWrapper()
spacing_choices <- c("s", "xs", "m", "l")
if (!(spacing %in% spacing_choices)) {
stop(paste("`spacing` must be one of",
paste0("'", spacing_choices, "'", collapse=", ")))
}
# For css styling
classNames <- paste0("table shiny-table",
paste0(" table-", names(format)[format], collapse = "" ),
paste0(" spacing-", spacing))
data <- as.data.frame(data)
# Return NULL if no data is provided
if (is.null(data) ||
(is.data.frame(data) && nrow(data) == 0 && ncol(data) == 0))
return(NULL)
# Separate the ... args to pass to xtable() vs print.xtable()
xtable_argnames <- setdiff(names(formals(xtable)), c("x", "..."))
xtable_args <- dots[intersect(names(dots), xtable_argnames)]
non_xtable_args <- dots[setdiff(names(dots), xtable_argnames)]
# By default, numbers are right-aligned and everything else is left-aligned.
defaultAlignment <- function(col) {
if (is.numeric(col)) "r" else "l"
}
# Figure out column alignment
## Case 1: default alignment
if (is.null(align) || align == "?") {
names <- defaultAlignment(attr(data, "row.names"))
cols <- paste(vapply(data, defaultAlignment, character(1)), collapse = "")
cols <- paste0(names, cols)
} else {
## Case 2: user-specified alignment
num_cols <- if (rownames) nchar(align) else nchar(align)+1
valid <- !grepl("[^lcr\\?]", align)
if (num_cols == ncol(data)+1 && valid) {
cols <- if (rownames) align else paste0("r", align)
defaults <- grep("\\?", strsplit(cols,"")[[1]])
if (length(defaults) != 0) {
vals <- vapply(data[,defaults-1], defaultAlignment, character(1))
for (i in seq_len(length(defaults))) {
substr(cols, defaults[i], defaults[i]) <- vals[i]
}
}
} else if (nchar(align) == 1 && valid) {
cols <- paste0(rep(align, ncol(data)+1), collapse="")
} else {
stop("`align` must contain only the characters `l`, `c`, `r` and/or `?` and",
"have length either equal to 1 or to the total number of columns")
}
}
# Call xtable with its (updated) args
xtable_args <- c(xtable_args, align = cols, digits = digits)
xtable_res <- do.call(xtable, c(list(data), xtable_args))
# Set up print args
print_args <- list(
x = xtable_res,
type = 'html',
include.rownames = {
if ("include.rownames" %in% names(dots)) dots$include.rownames
else rownames
},
include.colnames = {
if ("include.colnames" %in% names(dots)) dots$include.colnames
else colnames
},
NA.string = {
if ("NA.string" %in% names(dots)) dots$NA.string
else na
},
html.table.attributes =
paste0({
if ("html.table.attributes" %in% names(dots)) dots$html.table.attributes
else ""
}, " ",
"class = '", htmlEscape(classNames, TRUE), "' ",
"style = 'width:", validateCssUnit(width), ";'"),
comment = {
if ("comment" %in% names(dots)) dots$comment
else FALSE
}
)
print_args <- c(print_args, non_xtable_args)
print_args <- print_args[unique(names(print_args))]
# Capture the raw html table returned by print.xtable(), and store it in
# a variable for further processing
tab <- paste(utils::capture.output(do.call(print, print_args)),collapse = "\n")
# Add extra class to cells with NA value, to be able to style them separately
tab <- gsub(paste(">", na, "<"), paste(" class='NA'>", na, "<"), tab)
# All further processing concerns the table headers, so we don't need to run
# any of this if colnames=FALSE
if (colnames) {
# Make sure that the final html table has a proper header (not included
# in the print.xtable() default)
tab <- sub("<tr>", "<thead> <tr>", tab)
tab <- sub("</tr>", "</tr> </thead> <tbody>", tab)
tab <- sub("</table>$", "</tbody> </table>", tab)
# Update the `cols` string (which stores the alignment of each column) so
# that it only includes the alignment for the table variables (and not
# for the row.names)
cols <- if (rownames) cols else substr(cols, 2, nchar(cols))
# Create a vector whose i-th entry corresponds to the i-th table variable
# alignment (substituting "l" by "left", "c" by "center" and "r" by "right")
cols <- strsplit(cols, "")[[1]]
cols[cols == "l"] <- "left"
cols[cols == "r"] <- "right"
cols[cols == "c"] <- "center"
# Align each header accordingly (this guarantees that each header and its
# corresponding column have the same alignment)
for (i in seq_len(length(cols))) {
tab <- sub("<th>", paste0("<th style='text-align: ", cols[i], ";'>"), tab)
}
}
return(tab)
},
tableOutput, outputArgs
)
}

View File

@@ -14,18 +14,22 @@
#' @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}
#' (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}.
#' @export
#' @examples
#' \donttest{
#' runUrl('https://github.com/rstudio/shiny_example/archive/master.tar.gz')
#' ## Only run this example in interactive R sessions
#' if (interactive()) {
#' runUrl('https://github.com/rstudio/shiny_example/archive/master.tar.gz')
#'
#' # Can run an app from a subdirectory in the archive
#' runUrl("https://github.com/rstudio/shiny_example/archive/master.zip",
#' subdir = "inst/shinyapp/")
#' # Can run an app from a subdirectory in the archive
#' runUrl("https://github.com/rstudio/shiny_example/archive/master.zip",
#' subdir = "inst/shinyapp/")
#' }
runUrl <- function(url, filetype = NULL, subdir = NULL, ...) {
runUrl <- function(url, filetype = NULL, subdir = NULL, destdir = NULL, ...) {
if (!is.null(subdir) && ".." %in% strsplit(subdir, '/')[[1]])
stop("'..' not allowed in subdir")
@@ -43,8 +47,14 @@ runUrl <- function(url, filetype = NULL, subdir = NULL, ...) {
stop("Unknown file extension.")
message("Downloading ", url)
filePath <- tempfile('shinyapp', fileext=fileext)
fileDir <- tempfile('shinyapp')
if (is.null(destdir)) {
filePath <- tempfile('shinyapp', fileext = fileext)
fileDir <- tempfile('shinyapp')
} else {
fileDir <- destdir
filePath <- paste(destdir, fileext)
}
dir.create(fileDir, showWarnings = FALSE)
if (download(url, filePath, mode = "wb", quiet = TRUE) != 0)
stop("Failed to download URL ", url)
@@ -61,13 +71,16 @@ runUrl <- function(url, filetype = NULL, subdir = NULL, ...) {
untar2(filePath, exdir = fileDir)
} else if (fileext == ".zip") {
first <- as.character(unzip(filePath, list=TRUE)$Name)[1]
unzip(filePath, exdir = fileDir)
first <- as.character(utils::unzip(filePath, list=TRUE)$Name)[1]
utils::unzip(filePath, exdir = fileDir)
}
if(is.null(destdir)){
on.exit(unlink(fileDir, recursive = TRUE), add = TRUE)
}
on.exit(unlink(fileDir, recursive = TRUE), add = TRUE)
appdir <- file.path(fileDir, first)
if (!file_test('-d', appdir)) appdir <- dirname(appdir)
if (!utils::file_test('-d', appdir)) appdir <- dirname(appdir)
if (!is.null(subdir)) appdir <- file.path(appdir, subdir)
runApp(appdir, ...)
@@ -80,15 +93,16 @@ runUrl <- function(url, filetype = NULL, subdir = NULL, ...) {
#' all valid values.
#' @export
#' @examples
#' \donttest{
#' runGist(3239667)
#' runGist("https://gist.github.com/jcheng5/3239667")
#' ## Only run this example in interactive R sessions
#' if (interactive()) {
#' runGist(3239667)
#' runGist("https://gist.github.com/jcheng5/3239667")
#'
#' # Old URL format without username
#' runGist("https://gist.github.com/3239667")
#' # Old URL format without username
#' runGist("https://gist.github.com/3239667")
#' }
#'
runGist <- function(gist, ...) {
runGist <- function(gist, destdir = NULL, ...) {
gistUrl <- if (is.numeric(gist) || grepl('^[0-9a-f]+$', gist)) {
sprintf('https://gist.github.com/%s/download', gist)
@@ -98,7 +112,7 @@ runGist <- function(gist, ...) {
stop('Unrecognized gist identifier format')
}
runUrl(gistUrl, filetype=".tar.gz", ...)
runUrl(gistUrl, filetype = ".zip", destdir = destdir, ...)
}
@@ -110,15 +124,16 @@ runGist <- function(gist, ...) {
#' Defaults to \code{"master"}.
#' @export
#' @examples
#' \donttest{
#' runGitHub("shiny_example", "rstudio")
#' # or runGitHub("rstudio/shiny_example")
#' ## Only run this example in interactive R sessions
#' if (interactive()) {
#' runGitHub("shiny_example", "rstudio")
#' # or runGitHub("rstudio/shiny_example")
#'
#' # Can run an app from a subdirectory in the repo
#' runGitHub("shiny_example", "rstudio", subdir = "inst/shinyapp/")
#' # Can run an app from a subdirectory in the repo
#' runGitHub("shiny_example", "rstudio", subdir = "inst/shinyapp/")
#' }
runGitHub <- function(repo, username = getOption("github.user"),
ref = "master", subdir = NULL, ...) {
ref = "master", subdir = NULL, destdir = NULL, ...) {
if (grepl('/', repo)) {
res <- strsplit(repo, '/')[[1]]
@@ -130,5 +145,5 @@ runGitHub <- function(repo, username = getOption("github.user"),
url <- paste("https://github.com/", username, "/", repo, "/archive/",
ref, ".tar.gz", sep = "")
runUrl(url, subdir=subdir, ...)
runUrl(url, subdir = subdir, destdir = destdir, ...)
}

91
R/serializers.R Normal file
View File

@@ -0,0 +1,91 @@
#' Add a function for serializing an input before bookmarking application state
#'
#' @param inputId Name of the input value.
#' @param fun A function that takes the input value and returns a modified
#' value. The returned value will be used for the test snapshot.
#' @param session A Shiny session object.
#'
#' @keywords internal
#' @export
setSerializer <- function(inputId, fun, session = getDefaultReactiveDomain()) {
if (is.null(session)) {
stop("setSerializer() needs a session object.")
}
input_impl <- .subset2(session$input, "impl")
input_impl$setMeta(inputId, "shiny.serializer", fun)
}
# For most types of values, simply return the value unchanged.
serializerDefault <- function(value, stateDir) {
value
}
serializerFileInput <- function(value, stateDir = NULL) {
# File inputs can be serialized only if there's a stateDir
if (is.null(stateDir)) {
return(serializerUnserializable())
}
# value is a data frame. When persisting files, we need to copy the file to
# the persistent dir and then strip the original path before saving.
newpaths <- file.path(stateDir, basename(value$datapath))
file.copy(value$datapath, newpaths, overwrite = TRUE)
value$datapath <- basename(newpaths)
value
}
# Return a sentinel value that represents "unserializable". This is applied to
# for example, passwords and actionButtons.
serializerUnserializable <- function(value, stateDir) {
structure(
list(),
serializable = FALSE
)
}
# Is this an "unserializable" sentinel value?
isUnserializable <- function(x) {
identical(
attr(x, "serializable", exact = TRUE),
FALSE
)
}
# Given a reactiveValues object and optional directory for saving state, apply
# serializer function to each of the values, and return a list of the returned
# values. This function passes stateDir to the serializer functions, so if
# stateDir is non-NULL, it can have a side effect of writing values to disk (in
# stateDir).
serializeReactiveValues <- function(values, exclude, stateDir = NULL) {
impl <- .subset2(values, "impl")
# Get named list where keys and values are the names of inputs; we'll retrieve
# actual values later.
vals <- isolate(impl$names())
vals <- setdiff(vals, exclude)
names(vals) <- vals
# Get values and apply serializer functions
vals <- lapply(vals, function(name) {
val <- impl$get(name)
# Get the serializer function for this input value. If none specified, use
# the default.
serializer_fun <- impl$getMeta(name, "shiny.serializer")
if (is.null(serializer_fun))
serializer_fun <- serializerDefault
# Apply serializer function.
serializer_fun(val, stateDir)
})
# Filter out any values that were marked as unserializable.
vals <- Filter(Negate(isUnserializable), vals)
vals
}

241
R/server-input-handlers.R Normal file
View File

@@ -0,0 +1,241 @@
# Create a map for input handlers and register the defaults.
inputHandlers <- Map$new()
#' Register an Input Handler
#'
#' 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.
#'
#' 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
#' 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}.
#'
#' 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
#' 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
#' 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 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
#' a handler defined.
#' @examples
#' \dontrun{
#' # Register an input handler which rounds a input number to the nearest integer
#' registerInputHandler("mypackage.validint", function(x, shinysession, name) {
#' if (is.null(x)) return(NA)
#' round(x)
#' })
#'
#' ## On the Javascript side, the associated input binding must have a corresponding getType method:
#' getType: function(el) {
#' return "mypackage.validint";
#' }
#'
#' }
#' @seealso \code{\link{removeInputHandler}}
#' @export
registerInputHandler <- function(type, fun, force=FALSE){
if (inputHandlers$containsKey(type) && !force){
stop("There is already an input handler for type: ", type)
}
inputHandlers$set(type, fun)
}
#' Deregister an Input Handler
#'
#' Removes an Input Handler. Rather than using the previously specified handler
#' 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}}
#' @export
removeInputHandler <- function(type){
inputHandlers$remove(type)
}
# Apply input handler to a single input value
applyInputHandler <- function(name, val, shinysession) {
splitName <- strsplit(name, ':')[[1]]
if (length(splitName) > 1) {
if (!inputHandlers$containsKey(splitName[[2]])) {
# No input handler registered for this type
stop("No handler registered for type ", name)
}
inputName <- splitName[[1]]
# Get the function for processing this type of input
inputHandler <- inputHandlers$get(splitName[[2]])
return(inputHandler(val, shinysession, inputName))
} else if (is.list(val) && is.null(names(val))) {
return(unlist(val, recursive = TRUE))
} else {
return(val)
}
}
#' Apply input handlers to raw input values
#'
#' The purpose of this function is to make it possible for external packages to
#' test Shiny inputs. It takes a named list of raw input values, applies input
#' handlers to those values, and then returns a named list of the processed
#' values.
#'
#' The raw input values should be in a named list. Some values may have names
#' like \code{"x:shiny.date"}. This function would apply the \code{"shiny.date"}
#' input handler to the value, and then rename the result to \code{"x"}, in the
#' output.
#'
#' @param inputs A named list of input values.
#' @param shinysession A Shiny session object.
#'
#' @seealso registerInputHandler
#' @keywords internal
applyInputHandlers <- function(inputs, shinysession = getDefaultReactiveDomain()) {
inputs <- mapply(applyInputHandler, names(inputs), inputs,
MoreArgs = list(shinysession = shinysession),
SIMPLIFY = FALSE)
# Convert names like "button1:shiny.action" to "button1"
names(inputs) <- vapply(
names(inputs),
function(name) { strsplit(name, ":")[[1]][1] },
FUN.VALUE = character(1)
)
inputs
}
# Takes a list-of-lists and returns a matrix. The lists
# must all be the same length. NULL is replaced by NA.
registerInputHandler("shiny.matrix", function(data, ...) {
if (length(data) == 0)
return(matrix(nrow=0, ncol=0))
m <- matrix(unlist(lapply(data, function(x) {
sapply(x, function(y) {
ifelse(is.null(y), NA, y)
})
})), nrow = length(data[[1]]), ncol = length(data))
return(m)
})
registerInputHandler("shiny.number", function(val, ...){
ifelse(is.null(val), NA, val)
})
registerInputHandler("shiny.password", function(val, shinysession, name) {
# Mark passwords as not serializable
setSerializer(name, serializerUnserializable)
val
})
registerInputHandler("shiny.date", function(val, ...){
# First replace NULLs with NA, then convert to Date vector
datelist <- ifelse(lapply(val, is.null), NA, val)
res <- NULL
tryCatch({
res <- as.Date(unlist(datelist))
},
error = function(e) {
# It's possible for client to send a string like "99999-01-01", which
# as.Date can't handle.
warning(e$message)
res <<- as.Date(rep(NA, length(datelist)))
}
)
res
})
registerInputHandler("shiny.datetime", function(val, ...){
# First replace NULLs with NA, then convert to POSIXct vector
times <- lapply(val, function(x) {
if (is.null(x)) NA
else x
})
as.POSIXct(unlist(times), origin = "1970-01-01", tz = "UTC")
})
registerInputHandler("shiny.action", function(val, shinysession, name) {
# mark up the action button value with a special class so we can recognize it later
class(val) <- c(class(val), "shinyActionButtonValue")
val
})
registerInputHandler("shiny.file", function(val, shinysession, name) {
# This function is only used when restoring a Shiny fileInput. When a file is
# uploaded the usual way, it takes a different code path and won't hit this
# function.
if (is.null(val))
return(NULL)
# The data will be a named list of lists; convert to a data frame.
val <- as.data.frame(lapply(val, unlist), stringsAsFactors = FALSE)
# `val$datapath` should be a filename without a path, for security reasons.
if (basename(val$datapath) != val$datapath) {
stop("Invalid '/' found in file input path.")
}
# Prepend the persistent dir
oldfile <- file.path(getCurrentRestoreContext()$dir, val$datapath)
# Copy the original file to a new temp dir, so that a restored session can't
# modify the original.
newdir <- file.path(tempdir(), createUniqueId(12))
dir.create(newdir)
val$datapath <- file.path(newdir, val$datapath)
file.copy(oldfile, val$datapath)
# Need to mark this input value with the correct serializer. When a file is
# uploaded the usual way (instead of being restored), this occurs in
# session$`@uploadEnd`.
setSerializer(name, serializerFileInput)
snapshotPreprocessInput(name, snapshotPreprocessorFileInput)
val
})
# to be used with !!!answer
registerInputHandler("shiny.symbolList", function(val, ...) {
if (is.null(val)) {
list()
} else {
lapply(val, as.symbol)
}
})
# to be used with !!answer
registerInputHandler("shiny.symbol", function(val, ...) {
if (is.null(val) || identical(val, "")) {
NULL
} else {
as.symbol(val)
}
})

File diff suppressed because it is too large Load Diff

83
R/shiny-options.R Normal file
View File

@@ -0,0 +1,83 @@
.globals$options <- list()
#' @param name Name of an option to get.
#' @param default Value to be returned if the option is not currently set.
#' @rdname shinyOptions
#' @export
getShinyOption <- function(name, default = NULL) {
# Make sure to use named (not numeric) indexing
name <- as.character(name)
if (name %in% names(.globals$options))
.globals$options[[name]]
else
default
}
#' 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.
#'
#' 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
#' 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}.
#'
#' @examples
#' \dontrun{
#' shinyOptions(myOption = 10)
#' getShinyOption("myOption")
#' }
#' @export
shinyOptions <- function(...) {
newOpts <- list(...)
if (length(newOpts) > 0) {
.globals$options <- dropNulls(mergeVectors(.globals$options, newOpts))
invisible(.globals$options)
} else {
.globals$options
}
}
# Eval an expression with a new option set
withLocalOptions <- function(expr) {
oldOptionSet <- .globals$options
on.exit(.globals$options <- oldOptionSet)
expr
}
# Get specific shiny options and put them in a list, reset those shiny options,
# and then return the options list. This should be during the creation of a
# shiny app object, which happens before another option frame is added to the
# options stack (the new option frame is added when the app is run). This
# function "consumes" the options when the shinyApp object is created, so the
# options won't affect another app that is created later.
consumeAppOptions <- function() {
options <- list(
appDir = getwd(),
bookmarkStore = getShinyOption("bookmarkStore")
)
shinyOptions(appDir = NULL, bookmarkStore = NULL)
options
}
# Do the inverse of consumeAppOptions. This should be called once the app is
# started.
unconsumeAppOptions <- function(options) {
if (!is.null(options)) {
do.call(shinyOptions, options)
}
}

2059
R/shiny.R

File diff suppressed because it is too large Load Diff

View File

@@ -14,85 +14,63 @@ NULL
#' # now we can just write "static" content without withMathJax()
#' div("more math here $$\\sqrt{2}$$")
withMathJax <- function(...) {
path <- 'https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML'
path <- 'https://mathjax.rstudio.com/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML'
tagList(
tags$head(
singleton(tags$script(src = path, type = 'text/javascript'))
),
...,
tags$script(HTML('MathJax.Hub.Queue(["Typeset", MathJax.Hub]);'))
tags$script(HTML('if (window.MathJax) MathJax.Hub.Queue(["Typeset", MathJax.Hub]);'))
)
}
renderPage <- function(ui, connection, showcase=0) {
renderPage <- function(ui, connection, showcase=0, testMode=FALSE) {
# If the ui is a NOT complete document (created by htmlTemplate()), then do some
# preprocessing and make sure it's a complete document.
if (!inherits(ui, "html_document")) {
if (showcase > 0)
ui <- showcaseUI(ui)
if (showcase > 0)
ui <- tagList(tags$head(showcaseHead()), ui)
# Wrap ui in body tag if it doesn't already have a single top-level body tag.
if (!(inherits(ui, "shiny.tag") && ui$name == "body"))
ui <- tags$body(ui)
result <- renderTags(ui)
deps <- c(
list(
htmlDependency("json2", "2014.02.04", c(href="shared"), script = "json2-min.js"),
htmlDependency("jquery", "1.11.0", c(href="shared"), script = "jquery.js"),
htmlDependency("shiny", packageVersion("shiny"), c(href="shared"),
script = "shiny.js", stylesheet = "shiny.css")
),
result$dependencies
)
deps <- resolveDependencies(deps)
deps <- lapply(deps, createWebDependency)
depStr <- paste(sapply(deps, function(dep) {
sprintf("%s[%s]", dep$name, dep$version)
}), collapse = ";")
depHtml <- renderDependencies(deps, "href")
# write preamble
writeLines(c('<!DOCTYPE html>',
'<html>',
'<head>',
' <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>',
sprintf(' <script type="application/shiny-singletons">%s</script>',
paste(result$singletons, collapse = ',')
),
sprintf(' <script type="application/html-dependencies">%s</script>',
depStr
),
depHtml
),
con = connection)
writeLines(c(result$head,
'</head>',
'<body>',
recursive=TRUE),
con = connection)
if (showcase > 0) {
# in showcase mode, emit containing elements and app HTML
writeLines(as.character(showcaseBody(result$html)),
con = connection)
} else {
# in normal mode, write UI html directly to connection
writeLines(result$html, con = connection)
# Put the body into the default template
ui <- htmlTemplate(
system.file("template", "default.html", package = "shiny"),
body = ui
)
}
# write end document
writeLines(c('</body>',
'</html>'),
con = connection)
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("shiny", utils::packageVersion("shiny"), c(href="shared"),
script = if (getOption("shiny.minified", TRUE)) "shiny.min.js" else "shiny.js",
stylesheet = "shiny.css")
)
if (testMode) {
# Add code injection listener if in test mode
shiny_deps[[length(shiny_deps) + 1]] <-
htmlDependency("shiny-testmode", utils::packageVersion("shiny"),
c(href="shared"), script = "shiny-testmode.js")
}
html <- renderDocument(ui, shiny_deps, processDep = createWebDependency)
writeUTF8(html, con = connection)
}
#' Create a Shiny UI handler
#'
#' Historically this function was used in ui.R files to register a user
#' interface with Shiny. It is no longer required; simply ensure that the last
#' expression to be returned from ui.R is a user interface. This function is
#' kept for backwards compatibility with older applications. It returns the
#' value that is passed to it.
#' interface with Shiny. It is no longer required as of Shiny 0.10; simply
#' ensure that the last expression to be returned from ui.R is a user interface.
#' This function is kept for backwards compatibility with older applications. It
#' returns the value that is passed to it.
#'
#' @param ui A user interace definition
#' @return The user interface definition, without modifications or side effects.
#'
#' @export
shinyUI <- function(ui) {
.globals$ui <- list(ui)
@@ -110,7 +88,7 @@ uiHttpHandler <- function(ui, uiPattern = "^/$") {
if (!isTRUE(grepl(uiPattern, req$PATH_INFO)))
return(NULL)
textConn <- textConnection(NULL, "w")
textConn <- file(open = "w+")
on.exit(close(textConn))
showcaseMode <- .globals$showcaseDefault
@@ -119,19 +97,41 @@ uiHttpHandler <- function(ui, uiPattern = "^/$") {
if (!is.null(mode))
showcaseMode <- mode
}
uiValue <- if (is.function(ui)) {
if (length(formals(ui)) > 0)
ui(req)
else
ui()
testMode <- .globals$testMode %OR% FALSE
# Create a restore context using query string
bookmarkStore <- getShinyOption("bookmarkStore", default = "disable")
if (bookmarkStore == "disable") {
# If bookmarking is disabled, use empty context
restoreContext <- RestoreContext$new()
} else {
ui
restoreContext <- RestoreContext$new(req$QUERY_STRING)
}
withRestoreContext(restoreContext, {
uiValue <- NULL
if (is.function(ui)) {
if (length(formals(ui)) > 0) {
# No corresponding ..stacktraceoff.., this is pure user code
uiValue <- ..stacktraceon..(ui(req))
} else {
# No corresponding ..stacktraceoff.., this is pure user code
uiValue <- ..stacktraceon..(ui())
}
} else {
if (getCurrentRestoreContext()$active) {
warning("Trying to restore saved app state, but UI code must be a function for this to work! See ?enableBookmarking")
}
uiValue <- ui
}
})
if (is.null(uiValue))
return(NULL)
renderPage(uiValue, textConn, showcaseMode)
html <- paste(textConnectionValue(textConn), collapse='\n')
renderPage(uiValue, textConn, showcaseMode, testMode)
html <- paste(readLines(textConn, encoding = 'UTF-8'), collapse='\n')
return(httpResponse(200, content=enc2utf8(html)))
}
}

View File

@@ -1,4 +1,4 @@
globalVariables('func')
utils::globalVariables('func')
#' Mark a function as a render function
#'
@@ -12,24 +12,121 @@ 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
#' 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.
#' @export
markRenderFunction <- function(uiFunc, renderFunc, outputArgs = list()) {
# a mutable object that keeps track of whether `useRenderFunction` has been
# executed (this usually only happens when rendering Shiny code snippets in
# an interactive R Markdown document); its initial value is FALSE
hasExecuted <- Mutable$new()
hasExecuted$set(FALSE)
origRenderFunc <- renderFunc
renderFunc <- function(...) {
# if the user provided something through `outputArgs` BUT the
# `useRenderFunction` was not executed, then outputArgs will be ignored,
# so throw a warning to let user know the correct usage
if (length(outputArgs) != 0 && !hasExecuted$get()) {
warning("Unused argument: outputArgs. The argument outputArgs is only ",
"meant to be used when embedding snippets of Shiny code in an ",
"R Markdown code chunk (using runtime: shiny). When running a ",
"full Shiny app, please set the output arguments directly in ",
"the corresponding output function of your UI code.")
# stop warning from happening again for the same object
hasExecuted$set(TRUE)
}
if (is.null(formals(origRenderFunc))) origRenderFunc()
else origRenderFunc(...)
}
structure(renderFunc,
class = c("shiny.render.function", "function"),
outputFunc = uiFunc,
outputArgs = outputArgs,
hasExecuted = hasExecuted)
}
#' 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},
#' 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
#' 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.
#'
#' @export
markRenderFunction <- function(uiFunc, renderFunc) {
structure(renderFunc,
class = c("shiny.render.function", "function"),
outputFunc = uiFunc)
createRenderFunction <- function(
func, transform = function(value, session, name, ...) value,
outputFunc = NULL, outputArgs = NULL
) {
renderFunc <- function(shinysession, name, ...) {
hybrid_chain(
func(),
function(value, .visible) {
transform(setVisible(value, .visible), shinysession, name, ...)
}
)
}
if (!is.null(outputFunc))
markRenderFunction(outputFunc, renderFunc, outputArgs = outputArgs)
else
renderFunc
}
useRenderFunction <- function(renderFunc, inline = FALSE) {
outputFunction <- attr(renderFunc, "outputFunc")
outputArgs <- attr(renderFunc, "outputArgs")
hasExecuted <- attr(renderFunc, "hasExecuted")
hasExecuted$set(TRUE)
for (arg in names(outputArgs)) {
if (!arg %in% names(formals(outputFunction))) {
stop(paste0("Unused argument: in 'outputArgs', '",
arg, "' is not an valid argument for ",
"the output function"))
outputArgs[[arg]] <- NULL
}
}
id <- createUniqueId(8, "out")
o <- getDefaultReactiveDomain()$output
if (!is.null(o))
if (!is.null(o)) {
o[[id]] <- renderFunc
if (is.logical(formals(outputFunction)[["inline"]])) {
outputFunction(id, inline = inline)
} else outputFunction(id)
# If there's a namespace, we must respect it
id <- getDefaultReactiveDomain()$ns(id)
}
# Make the id the first positional argument
outputArgs <- c(list(id), outputArgs)
if (is.logical(formals(outputFunction)[["inline"]]) && !("inline" %in% names(outputArgs))) {
outputArgs[["inline"]] <- inline
}
do.call(outputFunction, outputArgs)
}
#' @export
@@ -38,139 +135,32 @@ as.tags.shiny.render.function <- function(x, ..., inline = FALSE) {
useRenderFunction(x, inline = inline)
}
#' Plot Output
#' Mark a render function with attributes that will be used by the output
#'
#' Renders a reactive plot that is suitable for assigning to an \code{output}
#' slot.
#' @inheritParams markRenderFunction
#' @param snapshotExclude If TRUE, exclude the output from test snapshots.
#' @param snapshotPreprocess A function for preprocessing the value before
#' taking a test snapshot.
#'
#' The corresponding HTML output tag should be \code{div} or \code{img} and have
#' the CSS class name \code{shiny-plot-output}.
#'
#' @seealso For more details on how the plots are generated, and how to control
#' the output, see \code{\link{plotPNG}}.
#'
#' @param expr An expression that generates a plot.
#' @param width,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 res Resolution of resulting plot, in pixels per inch. This value is
#' passed to \code{\link{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}}.
#' 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
#' is useful if you want to save an expression in a variable.
#' @param func A function that generates a plot (deprecated; use \code{expr}
#' instead).
#'
#' @export
renderPlot <- function(expr, width='auto', height='auto', res=72, ...,
env=parent.frame(), quoted=FALSE, func=NULL) {
if (!is.null(func)) {
shinyDeprecated(msg="renderPlot: argument 'func' is deprecated. Please use 'expr' instead.")
} else {
installExprFunction(expr, "func", env, quoted)
#' @keywords internal
markOutputAttrs <- function(renderFunc, snapshotExclude = NULL,
snapshotPreprocess = NULL)
{
# Add the outputAttrs attribute if necessary
if (is.null(attr(renderFunc, "outputAttrs", TRUE))) {
attr(renderFunc, "outputAttrs") <- list()
}
args <- list(...)
if (!is.null(snapshotExclude)) {
attr(renderFunc, "outputAttrs")$snapshotExclude <- snapshotExclude
}
if (is.function(width))
widthWrapper <- reactive({ width() })
else
widthWrapper <- NULL
if (!is.null(snapshotPreprocess)) {
attr(renderFunc, "outputAttrs")$snapshotPreprocess <- snapshotPreprocess
}
if (is.function(height))
heightWrapper <- reactive({ height() })
else
heightWrapper <- NULL
# If renderPlot isn't going to adapt to the height of the div, then the
# div needs to adapt to the height of renderPlot. By default, plotOutput
# sets the height to 400px, so to make it adapt we need to override it
# with NULL.
outputFunc <- plotOutput
if (!identical(height, 'auto')) formals(outputFunc)['height'] <- list(NULL)
return(markRenderFunction(outputFunc, function(shinysession, name, ...) {
if (!is.null(widthWrapper))
width <- widthWrapper()
if (!is.null(heightWrapper))
height <- heightWrapper()
# Note that these are reactive calls. A change to the width and height
# will inherently cause a reactive plot to redraw (unless width and
# height were explicitly specified).
prefix <- 'output_'
if (width == 'auto')
width <- shinysession$clientData[[paste(prefix, name, '_width', sep='')]];
if (height == 'auto')
height <- shinysession$clientData[[paste(prefix, name, '_height', sep='')]];
if (is.null(width) || is.null(height) || width <= 0 || height <= 0)
return(NULL)
# Resolution multiplier
pixelratio <- shinysession$clientData$pixelratio
if (is.null(pixelratio))
pixelratio <- 1
coordmap <- NULL
plotFunc <- function() {
# Actually perform the plotting
result <- withVisible(func())
if (result$visible) {
# Use capture.output to squelch printing to the actual console; we
# are only interested in plot output
capture.output(print(result$value))
}
# Now capture some graphics device info before we close it
usrCoords <- par('usr')
usrBounds <- usrCoords
if (par('xlog')) {
usrBounds[c(1,2)] <- 10 ^ usrBounds[c(1,2)]
}
if (par('ylog')) {
usrBounds[c(3,4)] <- 10 ^ usrBounds[c(3,4)]
}
coordmap <<- list(
usr = c(
left = usrCoords[1],
right = usrCoords[2],
bottom = usrCoords[3],
top = usrCoords[4]
),
# The bounds of the plot area, in DOM pixels
bounds = c(
left = grconvertX(usrBounds[1], 'user', 'nfc') * width,
right = grconvertX(usrBounds[2], 'user', 'nfc') * width,
bottom = (1-grconvertY(usrBounds[3], 'user', 'nfc')) * height,
top = (1-grconvertY(usrBounds[4], 'user', 'nfc')) * height
),
log = c(
x = par('xlog'),
y = par('ylog')
),
pixelratio = pixelratio
)
}
outfile <- do.call(plotPNG, c(plotFunc, width=width*pixelratio,
height=height*pixelratio, res=res*pixelratio, args))
on.exit(unlink(outfile))
# Return a list of attributes for the img
return(list(
src=shinysession$fileUrl(name, outfile, contentType='image/png'),
width=width, height=height, coordmap=coordmap
))
}))
renderFunc
}
#' Image file output
@@ -204,13 +194,24 @@ renderPlot <- function(expr, width='auto', height='auto', res=72, ...,
#' 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}.
#'
#' @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
#' interactive R Markdown document.
#' @export
#'
#' @examples
#' \dontrun{
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#' options(device.ask.default = FALSE)
#'
#' shinyServer(function(input, output, clientData) {
#' ui <- fluidPage(
#' sliderInput("n", "Number of observations", 2, 1000, 500),
#' plotOutput("plot1"),
#' plotOutput("plot2"),
#' plotOutput("plot3")
#' )
#'
#' server <- function(input, output, session) {
#'
#' # A plot of fixed size
#' output$plot1 <- renderImage({
@@ -232,14 +233,14 @@ renderPlot <- function(expr, width='auto', height='auto', res=72, ...,
#' output$plot2 <- renderImage({
#' # Read plot2's width and height. These are reactive values, so this
#' # expression will re-run whenever these values change.
#' width <- clientData$output_plot2_width
#' height <- clientData$output_plot2_height
#' width <- session$clientData$output_plot2_width
#' height <- session$clientData$output_plot2_height
#'
#' # A temp file to save the output.
#' outfile <- tempfile(fileext='.png')
#'
#' png(outfile, width=width, height=height)
#' hist(rnorm(input$obs))
#' hist(rnorm(input$n))
#' dev.off()
#'
#' # Return a list containing the filename
@@ -250,6 +251,8 @@ renderPlot <- function(expr, width='auto', height='auto', res=72, ...,
#' }, deleteFile = TRUE)
#'
#' # Send a pre-rendered image, and don't delete the image after sending it
#' # NOTE: For this example to work, it would require files in a subdirectory
#' # named images/
#' output$plot3 <- renderImage({
#' # When input$n is 1, filename is ./images/image1.jpeg
#' filename <- normalizePath(file.path('./images',
@@ -258,88 +261,41 @@ renderPlot <- function(expr, width='auto', height='auto', res=72, ...,
#' # Return a list containing the filename
#' list(src = filename)
#' }, deleteFile = FALSE)
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
renderImage <- function(expr, env=parent.frame(), quoted=FALSE,
deleteFile=TRUE) {
deleteFile=TRUE, outputArgs=list()) {
installExprFunction(expr, "func", env, quoted)
return(markRenderFunction(imageOutput, function(shinysession, name, ...) {
imageinfo <- func()
# Should the file be deleted after being sent? If .deleteFile not set or if
# TRUE, then delete; otherwise don't delete.
if (deleteFile) {
on.exit(unlink(imageinfo$src))
}
createRenderFunction(func,
transform = function(imageinfo, session, name, ...) {
# Should the file be deleted after being sent? If .deleteFile not set or if
# TRUE, then delete; otherwise don't delete.
if (deleteFile) {
on.exit(unlink(imageinfo$src))
}
# If contentType not specified, autodetect based on extension
if (is.null(imageinfo$contentType)) {
contentType <- getContentType(sub('^.*\\.', '', basename(imageinfo$src)))
} else {
contentType <- imageinfo$contentType
}
# If contentType not specified, autodetect based on extension
contentType <- imageinfo$contentType %OR% getContentType(imageinfo$src)
# Extra values are everything in imageinfo except 'src' and 'contentType'
extra_attr <- imageinfo[!names(imageinfo) %in% c('src', 'contentType')]
# Extra values are everything in imageinfo except 'src' and 'contentType'
extra_attr <- imageinfo[!names(imageinfo) %in% c('src', 'contentType')]
# Return a list with src, and other img attributes
c(src = shinysession$fileUrl(name, file=imageinfo$src, contentType=contentType),
extra_attr)
}))
# Return a list with src, and other img attributes
c(src = session$fileUrl(name, file=imageinfo$src, contentType=contentType),
extra_attr)
},
imageOutput, outputArgs)
}
#' Table Output
#'
#' Creates a reactive table that is suitable for assigning to an \code{output}
#' slot.
#'
#' The corresponding HTML output tag should be \code{div} and have the CSS class
#' name \code{shiny-html-output}.
#'
#' @param expr An expression that returns an R object that can be used with
#' \code{\link[xtable]{xtable}}.
#' @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()})? This
#' is useful if you want to save an expression in a variable.
#' @param func A function that returns an R object that can be used with
#' \code{\link[xtable]{xtable}} (deprecated; use \code{expr} instead).
#'
#' @export
renderTable <- function(expr, ..., env=parent.frame(), quoted=FALSE, func=NULL) {
if (!is.null(func)) {
shinyDeprecated(msg="renderTable: argument 'func' is deprecated. Please use 'expr' instead.")
} else {
installExprFunction(expr, "func", env, quoted)
}
markRenderFunction(tableOutput, function() {
classNames <- getOption('shiny.table.class') %OR% 'data table table-bordered table-condensed'
data <- func()
if (is.null(data) || identical(data, data.frame()))
return("")
return(paste(
capture.output(
print(xtable(data, ...),
type='html',
html.table.attributes=paste('class="',
htmlEscape(classNames, TRUE),
'"',
sep=''), ...)),
collapse="\n"))
})
}
#' Printable Output
#'
#' Makes a reactive version of the given function that captures any printed
#' output, and also captures its printable result (unless
#' \code{\link{invisible}}), into a string. The resulting function is suitable
#' \code{\link[base]{invisible}}), into a string. The resulting function is suitable
#' for assigning to an \code{output} slot.
#'
#' The corresponding HTML output tag can be anything (though \code{pre} is
@@ -351,34 +307,92 @@ renderTable <- function(expr, ..., env=parent.frame(), quoted=FALSE, func=NULL)
#'
#' 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{invisible}()}.
#' To display nothing, make your function return \code{\link[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 func A function that may print output and/or return a printable R
#' object (deprecated; use \code{expr} instead).
#' @param width The value for \code{\link{options}('width')}.
#' is useful if you want to save an expression in a variable.
#' @param width The value for \code{\link[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
#' in an interactive R Markdown document.
#' @seealso \code{\link{renderText}} for displaying the value returned from a
#' function, instead of the printed output.
#'
#' @example res/text-example.R
#'
#' @export
renderPrint <- function(expr, env = parent.frame(), quoted = FALSE, func = NULL,
width = getOption('width')) {
if (!is.null(func)) {
shinyDeprecated(msg="renderPrint: argument 'func' is deprecated. Please use 'expr' instead.")
} else {
installExprFunction(expr, "func", env, quoted)
renderPrint <- function(expr, env = parent.frame(), quoted = FALSE,
width = getOption('width'), outputArgs=list()) {
installExprFunction(expr, "func", env, quoted)
# Set a promise domain that sets the console width
# and captures output
# op <- options(width = width)
# on.exit(options(op), add = TRUE)
renderFunc <- function(shinysession, name, ...) {
domain <- createRenderPrintPromiseDomain(width)
hybrid_chain(
{
promises::with_promise_domain(domain, func())
},
function(value, .visible) {
if (.visible) {
cat(file = domain$conn, paste(utils::capture.output(value, append = TRUE), collapse = "\n"))
}
res <- paste(readLines(domain$conn, warn = FALSE), collapse = "\n")
res
},
finally = function() {
close(domain$conn)
}
)
}
markRenderFunction(verbatimTextOutput, function() {
op <- options(width = width)
on.exit(options(op), add = TRUE)
paste(capture.output(func()), collapse = "\n")
})
markRenderFunction(verbatimTextOutput, renderFunc, outputArgs = outputArgs)
}
createRenderPrintPromiseDomain <- function(width) {
f <- file()
promises::new_promise_domain(
wrapOnFulfilled = function(onFulfilled) {
force(onFulfilled)
function(...) {
op <- options(width = width)
on.exit(options(op), add = TRUE)
sink(f, append = TRUE)
on.exit(sink(NULL), add = TRUE)
onFulfilled(...)
}
},
wrapOnRejected = function(onRejected) {
force(onRejected)
function(...) {
op <- options(width = width)
on.exit(options(op), add = TRUE)
sink(f, append = TRUE)
on.exit(sink(NULL), add = TRUE)
onRejected(...)
}
},
wrapSync = function(expr) {
op <- options(width = width)
on.exit(options(op), add = TRUE)
sink(f, append = TRUE)
on.exit(sink(NULL), add = TRUE)
force(expr)
},
conn = f
)
}
#' Text Output
@@ -399,32 +413,31 @@ renderPrint <- function(expr, env = parent.frame(), quoted = FALSE, func = NULL,
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' is useful if you want to save an expression in a variable.
#' @param func A function that returns an R object that can be used as an
#' argument to \code{cat}.(deprecated; use \code{expr} instead).
#' @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
#' interactive R Markdown document.
#'
#' @seealso \code{\link{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, func=NULL) {
if (!is.null(func)) {
shinyDeprecated(msg="renderText: argument 'func' is deprecated. Please use 'expr' instead.")
} else {
installExprFunction(expr, "func", env, quoted)
}
renderText <- function(expr, env=parent.frame(), quoted=FALSE,
outputArgs=list()) {
installExprFunction(expr, "func", env, quoted)
markRenderFunction(textOutput, function() {
value <- func()
return(paste(capture.output(cat(value)), collapse="\n"))
})
createRenderFunction(
func,
function(value, session, name, ...) {
paste(utils::capture.output(cat(value)), collapse="\n")
},
textOutput, outputArgs
)
}
#' UI Output
#'
#' \bold{Experimental feature.} Makes a reactive version of a function that
#' generates HTML using the Shiny UI library.
#' 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}}).
@@ -434,46 +447,45 @@ renderText <- function(expr, env=parent.frame(), quoted=FALSE, func=NULL) {
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' is useful if you want to save an expression in a variable.
#' @param func A function that returns a Shiny tag object, \code{\link{HTML}},
#' or a list of such objects (deprecated; use \code{expr} instead).
#'
#' @seealso conditionalPanel
#' @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
#' interactive R Markdown document.
#'
#' @seealso \code{\link{uiOutput}}
#' @export
#' @examples
#' \dontrun{
#' output$moreControls <- renderUI({
#' list(
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' uiOutput("moreControls")
#' )
#'
#' server <- function(input, output) {
#' output$moreControls <- renderUI({
#' tagList(
#' sliderInput("n", "N", 1, 1000, 500),
#' textInput("label", "Label")
#' )
#' })
#' }
renderUI <- function(expr, env=parent.frame(), quoted=FALSE, func=NULL) {
if (!is.null(func)) {
shinyDeprecated(msg="renderUI: argument 'func' is deprecated. Please use 'expr' instead.")
} else {
installExprFunction(expr, "func", env, quoted)
}
#' shinyApp(ui, server)
#' }
#'
renderUI <- function(expr, env=parent.frame(), quoted=FALSE,
outputArgs=list()) {
installExprFunction(expr, "func", env, quoted)
markRenderFunction(uiOutput, function(shinysession, name, ...) {
result <- func()
if (is.null(result) || length(result) == 0)
return(NULL)
createRenderFunction(
func,
function(result, shinysession, name, ...) {
if (is.null(result) || length(result) == 0)
return(NULL)
result <- takeSingletons(result, shinysession$singletons, desingleton=FALSE)$ui
result <- surroundSingletons(result)
dependencies <- lapply(resolveDependencies(findDependencies(result)),
createWebDependency)
names(dependencies) <- NULL
# renderTags returns a list with head, singletons, and html
output <- list(
html = doRenderTags(result),
deps = dependencies
)
return(output)
})
processDeps(result, shinysession)
},
uiOutput, outputArgs
)
}
#' File Downloads
@@ -499,28 +511,42 @@ renderUI <- function(expr, env=parent.frame(), quoted=FALSE, func=NULL) {
#' 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.
#' @param outputArgs A list of arguments to be passed through to the implicit
#' call to \code{\link{downloadButton}} when \code{downloadHandler} is used
#' in an interactive R Markdown document.
#'
#' @examples
#' \dontrun{
#' # In server.R:
#' output$downloadData <- downloadHandler(
#' filename = function() {
#' paste('data-', Sys.Date(), '.csv', sep='')
#' },
#' content = function(file) {
#' write.csv(data, file)
#' }
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' downloadLink("downloadData", "Download")
#' )
#'
#' # In ui.R:
#' downloadLink('downloadData', 'Download')
#' server <- function(input, output) {
#' # Our dataset
#' data <- mtcars
#'
#' output$downloadData <- downloadHandler(
#' filename = function() {
#' paste("data-", Sys.Date(), ".csv", sep="")
#' },
#' content = function(file) {
#' write.csv(data, file)
#' }
#' )
#' }
#'
#' shinyApp(ui, server)
#' }
#' @export
downloadHandler <- function(filename, content, contentType=NA) {
return(markRenderFunction(downloadButton, function(shinysession, name, ...) {
downloadHandler <- function(filename, content, contentType=NA, outputArgs=list()) {
renderFunc <- function(shinysession, name, ...) {
shinysession$registerDownload(name, filename, contentType, content)
}))
}
snapshotExclude(
markRenderFunction(downloadButton, renderFunc, outputArgs = outputArgs)
)
}
#' Table output with the JavaScript library DataTables
@@ -531,10 +557,12 @@ downloadHandler <- function(filename, content, contentType=NA) {
#' the server infrastructure.
#'
#' For the \code{options} argument, the character elements that have the class
#' \code{"AsIs"} (usually returned from \code{\link{I}()}) will be evaluated in
#' \code{"AsIs"} (usually returned from \code{\link[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.
#' 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
#' 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,
#' or a function to return such a list.
@@ -543,41 +571,107 @@ downloadHandler <- function(filename, content, contentType=NA) {
#' @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.
#' 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')}.
#' @param outputArgs A list of arguments to be passed through to the implicit
#' call to \code{\link{dataTableOutput}} when \code{renderDataTable} is used
#' in an interactive R Markdown document.
#'
#' @references \url{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
#' 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).
#' @export
#' @inheritParams renderPlot
#' @examples # pass a callback function to DataTables using I()
#' renderDataTable(iris,
#' options = list(
#' pageLength = 5,
#' initComplete = I("function(settings, json) {alert('Done.');}")
#' @examples
#' ## Only run this example in interactive R sessions
#' if (interactive()) {
#' # pass a callback function to DataTables using I()
#' shinyApp(
#' ui = fluidPage(
#' fluidRow(
#' column(12,
#' dataTableOutput('table')
#' )
#' )
#' ),
#' server = function(input, output) {
#' output$table <- renderDataTable(iris,
#' options = list(
#' pageLength = 5,
#' initComplete = I("function(settings, json) {alert('Done.');}")
#' )
#' )
#' }
#' )
#' )
#' }
renderDataTable <- function(expr, options = NULL, searchDelay = 500,
callback = 'function(oTable) {}',
env = parent.frame(), quoted = FALSE) {
callback = 'function(oTable) {}', escape = TRUE,
env = parent.frame(), quoted = FALSE,
outputArgs=list()) {
installExprFunction(expr, "func", env, quoted)
markRenderFunction(dataTableOutput, function(shinysession, name, ...) {
renderFunc <- function(shinysession, name, ...) {
if (is.function(options)) options <- options()
options <- checkDT9(options)
res <- checkAsIs(options)
data <- func()
if (length(dim(data)) != 2) return() # expects a rectangular data object
action <- shinysession$registerDataObj(name, data, dataTablesJSON)
list(
colnames = colnames(data), action = action, options = res$options,
evalOptions = if (length(res$eval)) I(res$eval), searchDelay = searchDelay,
callback = paste(callback, collapse = '\n')
hybrid_chain(
func(),
function(data) {
if (length(dim(data)) != 2) return() # expects a rectangular data object
if (is.data.frame(data)) data <- as.data.frame(data)
action <- shinysession$registerDataObj(name, data, dataTablesJSON)
colnames <- colnames(data)
# if escape is column names, turn names to numeric indices
if (is.character(escape)) {
escape <- stats::setNames(seq_len(ncol(data)), colnames)[escape]
if (any(is.na(escape)))
stop("Some column names in the 'escape' argument not found in data")
}
colnames[escape] <- htmlEscape(colnames[escape])
if (!is.logical(escape)) {
if (!is.numeric(escape))
stop("'escape' must be TRUE, FALSE, or a numeric vector, or column names")
escape <- paste(escape, collapse = ',')
}
list(
colnames = colnames, action = action, options = res$options,
evalOptions = if (length(res$eval)) I(res$eval), searchDelay = searchDelay,
callback = paste(callback, collapse = '\n'), escape = escape
)
}
)
}
renderFunc <- markRenderFunction(dataTableOutput, renderFunc, outputArgs = outputArgs)
renderFunc <- snapshotPreprocessOutput(renderFunc, function(value) {
# Remove the action field so that it's not saved in test snapshots. It
# contains a value that changes every time an app is run, and shouldn't be
# stored for test snapshots. It will be something like:
# "session/e0d14d3fe97f672f9655a127f2a1e079/dataobj/table?w=&nonce=7f5d6d54e22450a3"
value$action <- NULL
value
})
renderFunc
}
# a data frame containing the DataTables 1.9 and 1.10 names
DT10Names <- function() {
rbind(
read.table(system.file('www/shared/datatables/upgrade1.10.txt', package = 'shiny'),
stringsAsFactors = FALSE),
utils::read.table(
system.file('www/shared/datatables/upgrade1.10.txt', package = 'shiny'),
stringsAsFactors = FALSE
),
c('aoColumns', 'Removed') # looks like an omission on the upgrade guide
)
}
@@ -595,7 +689,7 @@ checkDT9 <- function(options) {
'and DataTables 1.10.x uses different parameter names with 1.9.x. ',
'Please follow the upgrade guide https://datatables.net/upgrade/1.10-convert',
' to change your DataTables parameter names:\n\n',
paste(formatUL(nms[i]), collapse = '\n'), '\n', sep = ''
paste(utils::formatUL(nms[i]), collapse = '\n'), '\n', sep = ''
)
j <- gsub('[.].*', '', DT10[, 1]) %in% nms
# I cannot help you upgrade automatically in these cases, so I have to stop
@@ -604,7 +698,7 @@ checkDT9 <- function(options) {
nms10 <- DT10[match(nms[i], DT10[, 1]), 2]
if (any(nms10 == 'Removed')) stop(
"These parameters have been removed in DataTables 1.10.x:\n\n",
paste(formatUL(nms[i][nms10 == 'Removed']), collapse = '\n'),
paste(utils::formatUL(nms[i][nms10 == 'Removed']), collapse = '\n'),
"\n\n", msg
)
names(options)[i] <- nms10

View File

@@ -18,7 +18,8 @@ licenseLink <- function(licenseName) {
"Artistic-2.0" = "http://www.r-project.org/Licenses/Artistic-2.0",
"BSD_2_clause" = "http://www.r-project.org/Licenses/BSD_2_clause",
"BSD_3_clause" = "http://www.r-project.org/Licenses/BSD_3_clause",
"MIT" = "http://www.r-project.org/Licenses/MIT")
"MIT" = "http://www.r-project.org/Licenses/MIT",
"CC-BY-SA-4.0" = "https://www.r-project.org/Licenses/CC-BY-SA-4.0")
if (exists(licenseName, where = licenses)) {
tags$a(href=licenses[[licenseName]], licenseName)
} else {
@@ -31,12 +32,10 @@ licenseLink <- function(licenseName) {
showcaseHead <- function() {
deps <- list(
htmlDependency("jqueryui", "1.10.4", c(href="shared/jqueryui/1.10.4"),
htmlDependency("jqueryui", "1.12.1", c(href="shared/jqueryui"),
script = "jquery-ui.min.js"),
htmlDependency("showdown", "0.3.1", c(href="shared/showdown/compressed"),
script = "showdown.js"),
htmlDependency("font-awesome", "4.0.3", c(href="shared/font-awesome"),
stylesheet = "css/font-awesome.min.css"),
htmlDependency("highlight.js", "6.2", c(href="shared/highlight"),
script = "highlight.pack.js")
)
@@ -62,7 +61,7 @@ showcaseHead <- function() {
appMetadata <- function(desc) {
cols <- colnames(desc)
if ("Title" %in% cols)
with(tags, h4(class="muted shiny-showcase-apptitle", desc[1,"Title"],
with(tags, h4(class="text-muted shiny-showcase-apptitle", desc[1,"Title"],
if ("Author" %in% cols) small(
br(), "by",
if ("AuthorUrl" %in% cols)
@@ -79,37 +78,82 @@ appMetadata <- function(desc) {
else ""
}
navTabsHelper <- function(files, prefix = "") {
lapply(files, function(file) {
with(tags,
li(class=if (tolower(file) %in% c("app.r", "server.r")) "active" else "",
a(href=paste("#", gsub(".", "_", file, fixed=TRUE), "_code", sep=""),
"data-toggle"="tab", paste0(prefix, file)))
)
})
}
navTabsDropdown <- function(files) {
if (length(files) > 0) {
with(tags,
li(role="presentation", class="dropdown",
a(class="dropdown-toggle", `data-toggle`="dropdown", href="#",
role="button", `aria-haspopup`="true", `aria-expanded`="false",
"www", span(class="caret")
),
ul(class="dropdown-menu", navTabsHelper(files))
)
)
}
}
tabContentHelper <- function(files, path, language) {
lapply(files, function(file) {
with(tags,
div(class=paste("tab-pane",
if (tolower(file) %in% c("app.r", "server.r")) " active"
else "",
sep=""),
id=paste(gsub(".", "_", file, fixed=TRUE),
"_code", sep=""),
pre(class="shiny-code",
# we need to prevent the indentation of <code> ... </code>
HTML(format(tags$code(
class=paste0("language-", language),
paste(readUTF8(file.path.ci(path, file)), collapse="\n")
), indent = FALSE))))
)
})
}
# Returns tags containing the application's code in Bootstrap-style tabs in
# showcase mode.
showcaseCodeTabs <- function(codeLicense) {
rFiles <- list.files(pattern = "\\.[rR]$")
wwwFiles <- list()
if (isTRUE(.globals$IncludeWWW)) {
path <- file.path(getwd(), "www")
wwwFiles$jsFiles <- list.files(path, pattern = "\\.js$")
wwwFiles$cssFiles <- list.files(path, pattern = "\\.css$")
wwwFiles$htmlFiles <- list.files(path, pattern = "\\.html$")
}
with(tags, div(id="showcase-code-tabs",
a(id="showcase-code-position-toggle",
class="btn btn-default btn-small",
class="btn btn-default btn-sm",
onclick="toggleCodePosition()",
i(class="fa fa-level-up", "show with app")),
icon("level-up"),
"show with app"),
ul(class="nav nav-tabs",
lapply(rFiles, function(rFile) {
li(class=if (tolower(rFile) %in% c("app.r", "server.r")) "active" else "",
a(href=paste("#", gsub(".", "_", rFile, fixed=TRUE),
"_code", sep=""),
"data-toggle"="tab", rFile))
})),
navTabsHelper(rFiles),
navTabsDropdown(unlist(wwwFiles))
),
div(class="tab-content", id="showcase-code-content",
lapply(rFiles, function(rFile) {
div(class=paste("tab-pane",
if (tolower(rFile) %in% c("app.r", "server.r")) " active"
else "",
sep=""),
id=paste(gsub(".", "_", rFile, fixed=TRUE),
"_code", sep=""),
pre(class="shiny-code",
# we need to prevent the indentation of <code> ... </code>
HTML(format(tags$code(
class="language-r",
paste(readUTF8(file.path.ci(getwd(), rFile)), collapse="\n")
), indent = FALSE))))
})),
tabContentHelper(rFiles, path = getwd(), language = "r"),
tabContentHelper(wwwFiles$jsFiles,
path = paste0(getwd(), "/www"),
language = "javascript"),
tabContentHelper(wwwFiles$cssFiles,
path = paste0(getwd(), "/www"),
language = "css"),
tabContentHelper(wwwFiles$htmlFiles,
path = paste0(getwd(), "/www"),
language = "xml")
),
codeLicense))
}
@@ -128,17 +172,17 @@ showcaseAppInfo <- function() {
with(tags,
div(class="container-fluid shiny-code-container well",
id="showcase-well",
div(class="row-fluid",
div(class="row",
if (hasDesc || hasReadme) {
div(id="showcase-app-metadata", class="span4",
div(id="showcase-app-metadata", class="col-sm-4",
if (hasDesc) appMetadata(desc) else "",
if (hasReadme) div(id="readme-md"))
} else "",
div(id="showcase-code-inline",
class=if (hasReadme || hasDesc) "span8" else "span10 offset1",
class=if (hasReadme || hasDesc) "col-sm-8" else "col-sm-10 col-sm-offset-1",
showcaseCodeTabs(
if (hasDesc && "License" %in% colnames(desc)) {
small(class="showcase-code-license muted",
small(class="showcase-code-license text-muted",
"Code license: ",
licenseLink(desc[1,"License"]))
} else "")))))
@@ -151,7 +195,7 @@ showcaseBody <- function(htmlBody) {
table(id="showcase-app-code",
tr(td(id="showcase-app-container",
class="showcase-app-container-expanded",
HTML(htmlBody),
htmlBody,
td(id="showcase-sxs-code",
class="showcase-sxs-code-collapsed")))),
showcaseAppInfo()))
@@ -162,3 +206,20 @@ setShowcaseDefault <- function(showcaseDefault) {
.globals$showcaseDefault <- showcaseDefault
.globals$showcaseOverride <- as.logical(showcaseDefault)
}
# Given a UI tag/tagList, wrap it in appropriate tags for showcase mode.
showcaseUI <- function(ui) {
# If top-level tag is a body, replace its children with children wrapped in
# showcase stuff.
if (inherits(ui, "shiny.tag") && ui$name == "body") {
ui$children <- showcaseUI(ui$children)
return(ui)
}
tagList(
tags$head(showcaseHead()),
showcaseBody(ui)
)
}

View File

@@ -1,142 +0,0 @@
hasDecimals <- function(value) {
truncatedValue <- round(value)
return (!identical(value, truncatedValue))
}
#' @rdname sliderInput
#'
#' @param interval The interval, in milliseconds, between each animation step.
#' @param loop \code{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.
#'
#' @export
animationOptions <- function(interval=1000,
loop=FALSE,
playButton=NULL,
pauseButton=NULL) {
list(interval=interval,
loop=loop,
playButton=playButton,
pauseButton=pauseButton)
}
# Create a new slider control (list of slider input element and the script
# tag used to configure it). This is a lower level control that should
# be wrapped in an "input" construct (e.g. sliderInput in bootstrap.R)
#
# this is a wrapper for: https://github.com/egorkhmelev/jslider
# (www/shared/slider contains js, css, and img dependencies)
slider <- function(inputId, min, max, value, step = NULL, ...,
round=FALSE, format='#,##0.#####', locale='us',
ticks=TRUE, animate=FALSE, width=NULL) {
# validate inputId
inputId <- as.character(inputId)
if (!is.character(inputId))
stop("inputId not specified")
# validate numeric inputs
if (!is.numeric(value) || !is.numeric(min) || !is.numeric(max))
stop("min, max, and value must all be numeric values")
else if (min(value) < min)
stop(paste("slider initial value", value,
"is less than the specified minimum"))
else if (max(value) > max)
stop(paste("slider initial value", value,
"is greater than the specified maximum"))
else if (min > max)
stop(paste("slider maximum is greater than minimum"))
else if (!is.null(step)) {
if (!is.numeric(step))
stop("step is not a numeric value")
if (step > (max - min))
stop("step is greater than range")
}
# step
range <- max - min
if (is.null(step)) {
# short range or decimals means continuous decimal
if (range < 2 || hasDecimals(min) || hasDecimals(max))
step <- range / 250 # ~ one step per pixel
else
step = 1
}
# Default state is to not have ticks
if (identical(ticks, TRUE)) {
# Automatic ticks
tickCount <- (range / step) + 1
if (tickCount <= 26)
ticks <- paste(rep('|', floor(tickCount)), collapse=';')
else {
ticks <- NULL
# # This is a smarter auto-tick algorithm, but to be truly useful
# # we need jslider to be able to space ticks irregularly
# tickSize <- 10^(floor(log10(range/0.39)))
# if ((range / tickSize) == floor(range / tickSize)) {
# ticks <- paste(rep('|', (range / tickSize) + 1), collapse=';')
# }
# else {
# ticks <- NULL
# }
}
}
else if (is.numeric(ticks) && length(ticks) == 1) {
# Use n ticks
ticks <- paste(rep('|', ticks), collapse=';')
}
else if (length(ticks) > 1 && (is.numeric(ticks) || is.character(ticks))) {
# Explicit ticks
ticks <- paste(ticks, collapse=';')
}
else {
ticks <- NULL
}
# build slider
dep <- htmlDependency("jslider", "1", c(href="shared/slider"),
script = "js/jquery.slider.min.js",
stylesheet = "css/jquery.slider.min.css"
)
sliderFragment <- list(
attachDependencies(
tags$input(
id=inputId, type="slider",
name=inputId, value=paste(value, collapse=';'), class="jslider",
'data-from'=min, 'data-to'=max, 'data-step'=step,
'data-skin'='plastic', 'data-round'=round, 'data-locale'=locale,
'data-format'=format, 'data-scale'=ticks,
'data-smooth'=FALSE,
'data-width'=validateCssUnit(width)
),
dep
)
)
if (identical(animate, TRUE))
animate <- animationOptions()
if (!is.null(animate) && !identical(animate, FALSE)) {
if (is.null(animate$playButton))
animate$playButton <- 'Play'
if (is.null(animate$pauseButton))
animate$pauseButton <- 'Pause'
sliderFragment[[length(sliderFragment)+1]] <-
tags$div(class='slider-animate-container',
tags$a(href='#',
class='slider-animate-button',
'data-target-id'=inputId,
'data-interval'=animate$interval,
'data-loop'=animate$loop,
tags$span(class='play', animate$playButton),
tags$span(class='pause', animate$pauseButton)))
}
return(tagList(sliderFragment))
}

44
R/snapshot.R Normal file
View File

@@ -0,0 +1,44 @@
#' Mark an output to be excluded from test snapshots
#'
#' @param x A reactive which will be assigned to an output.
#'
#' @export
snapshotExclude <- function(x) {
markOutputAttrs(x, snapshotExclude = TRUE)
}
#' Add a function for preprocessing an output before taking a test snapshot
#'
#' @param x A reactive which will be assigned to an output.
#' @param fun A function that takes the output value as an input and returns a
#' modified value. The returned value will be used for the test snapshot.
#'
#' @export
snapshotPreprocessOutput <- function(x, fun) {
markOutputAttrs(x, snapshotPreprocess = fun)
}
#' Add a function for preprocessing an input before taking a test snapshot
#'
#' @param inputId Name of the input value.
#' @param fun A function that takes the input value and returns a modified
#' value. The returned value will be used for the test snapshot.
#' @param session A Shiny session object.
#'
#' @export
snapshotPreprocessInput <- function(inputId, fun, session = getDefaultReactiveDomain()) {
if (is.null(session)) {
stop("snapshotPreprocessInput() needs a session object.")
}
input_impl <- .subset2(session$input, "impl")
input_impl$setMeta(inputId, "shiny.snapshot.preprocess", fun)
}
# Strip out file path from fileInput value
snapshotPreprocessorFileInput <- function(value) {
value$datapath <- basename(value$datapath)
value
}

View File

@@ -46,7 +46,7 @@ untar2 <- function(tarfile, files = NULL, list = FALSE, exdir = ".")
mydir.create <- function(path, ...) {
## for Windows' sake
path <- sub("[\\/]$", "", path)
if(file_test("-d", path)) return()
if(utils::file_test("-d", path)) return()
if(!dir.create(path, showWarnings = TRUE, recursive = TRUE, ...))
stop(gettextf("failed to create directory %s", sQuote(path)),
domain = NA)

61
R/test-export.R Normal file
View File

@@ -0,0 +1,61 @@
#' Register expressions for export in test mode
#'
#' This function registers expressions that will be evaluated when a test export
#' 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}.
#'
#' @param quoted_ Are the expression quoted? Default is \code{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
#' be captured and evaluated when snapshot URL is visited.
#' @examples
#' ## Only run this example in interactive R sessions
#' if (interactive()) {
#'
#' options(shiny.testmode = TRUE)
#'
#' # This application shows the test snapshot URL; clicking on it will
#' # fetch the input, output, and exported values in JSON format.
#' shinyApp(
#' ui = basicPage(
#' h4("Snapshot URL: "),
#' uiOutput("url"),
#' h4("Current values:"),
#' verbatimTextOutput("values"),
#' actionButton("inc", "Increment x")
#' ),
#'
#' server = function(input, output, session) {
#' vals <- reactiveValues(x = 1)
#' y <- reactive({ vals$x + 1 })
#'
#' observeEvent(input$inc, {
#' vals$x <<- vals$x + 1
#' })
#'
#' exportTestValues(
#' x = vals$x,
#' y = y()
#' )
#'
#' output$url <- renderUI({
#' url <- session$getTestSnapshotUrl(format="json")
#' a(href = url, url)
#' })
#'
#' output$values <- renderText({
#' paste0("vals$x: ", vals$x, "\ny: ", y())
#' })
#' }
#' )
#' }
#' @export
exportTestValues <- function(..., quoted_ = FALSE, env_ = parent.frame(),
session_ = getDefaultReactiveDomain())
{
session_$exportTestValues(..., quoted_ = quoted_, env_ = env_)
}

View File

@@ -22,6 +22,11 @@ TimerCallbacks <- R6Class(
.times <<- data.frame()
},
schedule = function(millis, func) {
# If args could fail to evaluate, let's make them do that before
# we change any state
force(millis)
force(func)
id <- .nextId
.nextId <<- .nextId + 1L
@@ -37,6 +42,17 @@ TimerCallbacks <- R6Class(
return(id)
},
unschedule = function(id) {
toRemoveIndices <- .times$id %in% id
toRemoveIds <- .times[toRemoveIndices, "id", drop = TRUE]
if (length(toRemoveIds) > 0) {
.times <<- .times[!toRemoveIndices,]
for (toRemoveId in as.character(toRemoveIds)) {
.funcs$remove(toRemoveId)
}
}
return(id %in% toRemoveIds)
},
timeToNextEvent = function() {
if (dim(.times)[1] == 0)
return(Inf)
@@ -56,7 +72,7 @@ TimerCallbacks <- R6Class(
},
executeElapsed = function() {
elapsed <- takeElapsed()
if (length(elapsed) == 0)
if (nrow(elapsed) == 0)
return(FALSE)
for (id in elapsed$id) {
@@ -71,3 +87,12 @@ TimerCallbacks <- R6Class(
)
timerCallbacks <- TimerCallbacks$new()
scheduleTask <- function(millis, callback) {
cancelled <- FALSE
id <- timerCallbacks$schedule(millis, callback)
function() {
invisible(timerCallbacks$unschedule(id))
}
}

View File

@@ -2,13 +2,21 @@
#'
#' @template update-input
#' @param value The value to set for the input object.
#' @param placeholder The placeholder to set for the input object.
#'
#' @seealso \code{\link{textInput}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' sliderInput("controller", "Controller", 0, 20, 10),
#' textInput("inText", "Input text"),
#' textInput("inText2", "Input text 2")
#' )
#'
#' server <- function(input, output, session) {
#' observe({
#' # We'll use the input$controller variable multiple times, so save it as x
#' # for convenience.
@@ -22,14 +30,54 @@
#' label = paste("New label", x),
#' value = paste("New text", x))
#' })
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @export
updateTextInput <- function(session, inputId, label = NULL, value = NULL) {
message <- dropNulls(list(label=label, value=value))
updateTextInput <- function(session, inputId, label = NULL, value = NULL, placeholder = NULL) {
message <- dropNulls(list(label=label, value=value, placeholder=placeholder))
session$sendInputMessage(inputId, message)
}
#' Change the value of a textarea input on the client
#'
#' @template update-input
#' @inheritParams updateTextInput
#'
#' @seealso \code{\link{textAreaInput}}
#'
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' sliderInput("controller", "Controller", 0, 20, 10),
#' textAreaInput("inText", "Input textarea"),
#' textAreaInput("inText2", "Input textarea 2")
#' )
#'
#' server <- function(input, output, session) {
#' observe({
#' # We'll use the input$controller variable multiple times, so save it as x
#' # for convenience.
#' x <- input$controller
#'
#' # This will change the value of input$inText, based on x
#' updateTextAreaInput(session, "inText", value = paste("New text", x))
#'
#' # Can also set the label, this time for input$inText2
#' updateTextAreaInput(session, "inText2",
#' label = paste("New label", x),
#' value = paste("New text", x))
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @export
updateTextAreaInput <- updateTextInput
#' Change the value of a checkbox input on the client
#'
@@ -39,58 +87,90 @@ updateTextInput <- function(session, inputId, label = NULL, value = NULL) {
#' @seealso \code{\link{checkboxInput}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' sliderInput("controller", "Controller", 0, 1, 0, step = 1),
#' checkboxInput("inCheckbox", "Input checkbox")
#' )
#'
#' server <- function(input, output, session) {
#' observe({
#' # TRUE if input$controller is even, FALSE otherwise.
#' x_even <- input$controller %% 2 == 0
#' # TRUE if input$controller is odd, FALSE if even.
#' x_even <- input$controller %% 2 == 1
#'
#' updateCheckboxInput(session, "inCheckbox", value = x_even)
#' })
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @export
updateCheckboxInput <- updateTextInput
updateCheckboxInput <- function(session, inputId, label = NULL, value = NULL) {
message <- dropNulls(list(label=label, value=value))
session$sendInputMessage(inputId, message)
}
#' Change the value of a slider input on the client
#' Change the label or icon of an action button on the client
#'
#' @template update-input
#' @param value The value to set for the input object.
#' @param icon The icon to set for the input object. To remove the
#' current icon, use \code{icon=character(0)}.
#'
#' @seealso \code{\link{sliderInput}}
#' @seealso \code{\link{actionButton}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' actionButton("update", "Update other buttons"),
#' br(),
#' actionButton("goButton", "Go"),
#' br(),
#' actionButton("goButton2", "Go 2", icon = icon("area-chart")),
#' br(),
#' actionButton("goButton3", "Go 3")
#' )
#'
#' server <- function(input, output, session) {
#' observe({
#' # We'll use the input$controller variable multiple times, so save it as x
#' # for convenience.
#' x <- input$controller
#' req(input$update)
#'
#' # Similar to number and text. only label and value can be set for slider
#' updateSliderInput(session, "inSlider",
#' label = paste("Slider label", x),
#' value = x)
#' # Updates goButton's label and icon
#' updateActionButton(session, "goButton",
#' label = "New label",
#' icon = icon("calendar"))
#'
#' # For sliders that pick out a range, pass in a vector of 2 values.
#' updateSliderInput(session, "inSlider2", value = c(x-1, x+1))
#' # Leaves goButton2's label unchaged and
#' # removes its icon
#' updateActionButton(session, "goButton2",
#' icon = character(0))
#'
#' # An NA means to not change that value (the low or high one)
#' updateSliderInput(session, "inSlider3", value = c(NA, x+2))
#' # Leaves goButton3's icon, if it exists,
#' # unchaged and changes its label
#' updateActionButton(session, "goButton3",
#' label = "New label 3")
#' })
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @export
updateSliderInput <- updateTextInput
updateActionButton <- function(session, inputId, label = NULL, icon = NULL) {
if (!is.null(icon)) icon <- as.character(validateIcon(icon))
message <- dropNulls(list(label=label, icon=icon))
session$sendInputMessage(inputId, message)
}
#' Change the value of a date input on the client
#'
#' @template update-input
#' @param value The desired date value. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#' \code{yyyy-mm-dd} format. Supply \code{NA} to clear the date.
#' @param min The minimum allowed date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#' @param max The maximum allowed date. Either a Date object, or a string in
@@ -99,32 +179,44 @@ updateSliderInput <- updateTextInput
#' @seealso \code{\link{dateInput}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' sliderInput("n", "Day of month", 1, 30, 10),
#' dateInput("inDate", "Input date")
#' )
#'
#' server <- function(input, output, session) {
#' observe({
#' # We'll use the input$controller variable multiple times, so save it as x
#' # for convenience.
#' x <- input$controller
#'
#' date <- as.Date(paste0("2013-04-", input$n))
#' updateDateInput(session, "inDate",
#' label = paste("Date label", x),
#' value = paste("2013-04-", x, sep=""),
#' min = paste("2013-04-", x-1, sep=""),
#' max = paste("2013-04-", x+1, sep="")
#' label = paste("Date label", input$n),
#' value = date,
#' min = date - 3,
#' max = date + 3
#' )
#' })
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @export
updateDateInput <- function(session, inputId, label = NULL, value = NULL,
min = NULL, max = NULL) {
# If value is a date object, convert it to a string with yyyy-mm-dd format
# Same for min and max
if (inherits(value, "Date")) value <- format(value, "%Y-%m-%d")
if (inherits(min, "Date")) min <- format(min, "%Y-%m-%d")
if (inherits(max, "Date")) max <- format(max, "%Y-%m-%d")
# 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)
message <- dropNulls(list(label=label, value=value, min=min, max=max))
session$sendInputMessage(inputId, message)
@@ -135,9 +227,9 @@ 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.
#' \code{yyyy-mm-dd} format. Supplying \code{NA} clears the start date.
#' @param end The end date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#' \code{yyyy-mm-dd} format. Supplying \code{NA} clears the end date.
#' @param min The minimum allowed date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#' @param max The maximum allowed date. Either a Date object, or a string in
@@ -146,20 +238,29 @@ updateDateInput <- function(session, inputId, label = NULL, value = NULL,
#' @seealso \code{\link{dateRangeInput}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' sliderInput("n", "Day of month", 1, 30, 10),
#' dateRangeInput("inDateRange", "Input date range")
#' )
#'
#' server <- function(input, output, session) {
#' observe({
#' # We'll use the input$controller variable multiple times, so save it as x
#' # for convenience.
#' x <- input$controller
#' date <- as.Date(paste0("2013-04-", input$n))
#'
#' updateDateRangeInput(session, "inDateRange",
#' label = paste("Date range label", x),
#' start = paste("2013-01-", x, sep=""))
#' end = paste("2013-12-", x, sep=""))
#' label = paste("Date range label", input$n),
#' start = date - 1,
#' end = date + 1,
#' min = date - 5,
#' max = date + 5
#' )
#' })
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @export
updateDateRangeInput <- function(session, inputId, label = NULL,
@@ -174,7 +275,7 @@ updateDateRangeInput <- function(session, inputId, label = NULL,
message <- dropNulls(list(
label = label,
value = c(start, end),
value = dropNulls(list(start = start, end = end)),
min = min,
max = max
))
@@ -194,22 +295,31 @@ updateDateRangeInput <- function(session, inputId, label = NULL,
#' \code{\link{navbarPage}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' observe({
#' # TRUE if input$controller is even, FALSE otherwise.
#' x_even <- input$controller %% 2 == 0
#' ui <- fluidPage(sidebarLayout(
#' sidebarPanel(
#' sliderInput("controller", "Controller", 1, 3, 1)
#' ),
#' mainPanel(
#' tabsetPanel(id = "inTabset",
#' tabPanel(title = "Panel 1", value = "panel1", "Panel 1 content"),
#' tabPanel(title = "Panel 2", value = "panel2", "Panel 2 content"),
#' tabPanel(title = "Panel 3", value = "panel3", "Panel 3 content")
#' )
#' )
#' ))
#'
#' # Change the selected tab.
#' # Note that the tabset container must have been created with an 'id' argument
#' if (x_even) {
#' updateTabsetPanel(session, "inTabset", selected = "panel2")
#' } else {
#' updateTabsetPanel(session, "inTabset", selected = "panel1")
#' }
#' server <- function(input, output, session) {
#' observeEvent(input$controller, {
#' updateTabsetPanel(session, "inTabset",
#' selected = paste0("panel", input$controller)
#' )
#' })
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @export
updateTabsetPanel <- function(session, inputId, selected = NULL) {
@@ -217,6 +327,13 @@ updateTabsetPanel <- function(session, inputId, selected = NULL) {
session$sendInputMessage(inputId, message)
}
#' @rdname updateTabsetPanel
#' @export
updateNavbarPage <- updateTabsetPanel
#' @rdname updateTabsetPanel
#' @export
updateNavlistPanel <- updateTabsetPanel
#' Change the value of a number input on the client
#'
@@ -229,10 +346,18 @@ updateTabsetPanel <- function(session, inputId, selected = NULL) {
#' @seealso \code{\link{numericInput}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' observe({
#' ui <- fluidPage(
#' sliderInput("controller", "Controller", 0, 20, 10),
#' numericInput("inNumber", "Input number", 0),
#' numericInput("inNumber2", "Input number 2", 0)
#' )
#'
#' server <- function(input, output, session) {
#'
#' observeEvent(input$controller, {
#' # We'll use the input$controller variable multiple times, so save it as x
#' # for convenience.
#' x <- input$controller
@@ -243,7 +368,9 @@ updateTabsetPanel <- function(session, inputId, selected = NULL) {
#' label = paste("Number label ", x),
#' value = x, min = x-10, max = x+10, step = 5)
#' })
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @export
updateNumericInput <- function(session, inputId, label = NULL, value = NULL,
@@ -256,18 +383,93 @@ updateNumericInput <- function(session, inputId, label = NULL, value = NULL,
session$sendInputMessage(inputId, message)
}
#' Update Slider Input Widget
#'
#' Change the value of a slider input on the client.
#'
#' @template update-input
#' @param value The value to set for the input object.
#' @param min Minimum value.
#' @param max Maximum value.
#' @param step Step size.
#' @param timeFormat Date and POSIXt formatting.
#' @param timezone The timezone offset for POSIXt objects.
#'
#' @seealso \code{\link{sliderInput}}
#'
#' @examples
#' ## Only run this example in interactive R sessions
#' if (interactive()) {
#' shinyApp(
#' ui = fluidPage(
#' sidebarLayout(
#' sidebarPanel(
#' p("The first slider controls the second"),
#' sliderInput("control", "Controller:", min=0, max=20, value=10,
#' step=1),
#' sliderInput("receive", "Receiver:", min=0, max=20, value=10,
#' step=1)
#' ),
#' mainPanel()
#' )
#' ),
#' server = function(input, output, session) {
#' observe({
#' val <- input$control
#' # Control the value, min, max, and step.
#' # Step size is 2 when input value is even; 1 when value is odd.
#' updateSliderInput(session, "receive", value = val,
#' min = floor(val/2), max = val+4, step = (val+1)%%2 + 1)
#' })
#' }
#' )
#' }
#' @export
updateSliderInput <- function(session, inputId, label = NULL, value = NULL,
min = NULL, max = NULL, step = NULL, timeFormat = NULL, timezone = NULL)
{
dataType <- getSliderType(min, max, value)
if (is.null(timeFormat)) {
timeFormat <- switch(dataType, date = "%F", datetime = "%F %T", number = NULL)
}
if (dataType == "date" || dataType == "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)
if (!is.null(value)) value <- to_ms(value)
}
message <- dropNulls(list(
label = label,
value = formatNoSci(value),
min = formatNoSci(min),
max = formatNoSci(max),
step = formatNoSci(step),
`data-type` = dataType,
`time-format` = timeFormat,
timezone = timezone
))
session$sendInputMessage(inputId, message)
}
updateInputOptions <- function(session, inputId, label = NULL, choices = NULL,
selected = NULL, inline = FALSE,
type = 'checkbox') {
selected = NULL, inline = FALSE, type = NULL,
choiceNames = NULL, choiceValues = NULL) {
if (is.null(type)) stop("Please specify the type ('checkbox' or 'radio')")
choices <- choicesWithNames(choices)
if (!is.null(selected))
selected <- validateSelected(selected, choices, inputId)
args <- normalizeChoicesArgs(choices, choiceNames, choiceValues, mustExist = FALSE)
options <- if (length(choices))
if (!is.null(selected)) selected <- as.character(selected)
options <- if (!is.null(args$choiceValues)) {
format(tagList(
generateOptions(inputId, choices, selected, inline, type = type)
generateOptions(session$ns(inputId), selected, inline, type,
args$choiceNames, args$choiceValues)
))
}
message <- dropNulls(list(label = label, options = options, value = selected))
@@ -282,37 +484,42 @@ updateInputOptions <- function(session, inputId, label = NULL, choices = NULL,
#' @seealso \code{\link{checkboxGroupInput}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' p("The first checkbox group controls the second"),
#' checkboxGroupInput("inCheckboxGroup", "Input checkbox",
#' c("Item A", "Item B", "Item C")),
#' checkboxGroupInput("inCheckboxGroup2", "Input checkbox 2",
#' c("Item A", "Item B", "Item C"))
#' )
#'
#' server <- function(input, output, session) {
#' observe({
#' # We'll use the input$controller variable multiple times, so save it as x
#' # for convenience.
#' x <- input$controller
#' x <- input$inCheckboxGroup
#'
#' # Create a list of new options, where the name of the items is something
#' # like 'option label x 1', and the values are 'option-x-1'.
#' cb_options <- list()
#' cb_options[[sprintf("option label %d 1", x)]] <- sprintf("option-%d-1", x)
#' cb_options[[sprintf("option label %d 2", x)]] <- sprintf("option-%d-2", x)
#'
#' # Change values for input$inCheckboxGroup
#' updateCheckboxGroupInput(session, "inCheckboxGroup", choices = cb_options)
#' # Can use character(0) to remove all choices
#' if (is.null(x))
#' x <- character(0)
#'
#' # Can also set the label and select items
#' updateCheckboxGroupInput(session, "inCheckboxGroup2",
#' label = paste("checkboxgroup label", x),
#' choices = cb_options,
#' selected = sprintf("option-%d-2", x)
#' label = paste("Checkboxgroup label", length(x)),
#' choices = x,
#' selected = x
#' )
#' })
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @export
updateCheckboxGroupInput <- function(session, inputId, label = NULL,
choices = NULL, selected = NULL,
inline = FALSE) {
updateInputOptions(session, inputId, label, choices, selected, inline)
choices = NULL, selected = NULL, inline = FALSE,
choiceNames = NULL, choiceValues = NULL) {
updateInputOptions(session, inputId, label, choices, selected,
inline, "checkbox", choiceNames, choiceValues)
}
@@ -324,36 +531,43 @@ updateCheckboxGroupInput <- function(session, inputId, label = NULL,
#' @seealso \code{\link{radioButtons}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' p("The first radio button group controls the second"),
#' radioButtons("inRadioButtons", "Input radio buttons",
#' c("Item A", "Item B", "Item C")),
#' radioButtons("inRadioButtons2", "Input radio buttons 2",
#' c("Item A", "Item B", "Item C"))
#' )
#'
#' server <- function(input, output, session) {
#' observe({
#' # We'll use the input$controller variable multiple times, so save it as x
#' # for convenience.
#' x <- input$controller
#' x <- input$inRadioButtons
#'
#' r_options <- list()
#' r_options[[sprintf("option label %d 1", x)]] <- sprintf("option-%d-1", x)
#' r_options[[sprintf("option label %d 2", x)]] <- sprintf("option-%d-2", x)
#'
#' # Change values for input$inRadio
#' updateRadioButtons(session, "inRadio", choices = r_options)
#'
#' # Can also set the label and select an item
#' updateRadioButtons(session, "inRadio2",
#' label = paste("Radio label", x),
#' choices = r_options,
#' selected = sprintf("option-%d-2", x)
#' # Can also set the label and select items
#' updateRadioButtons(session, "inRadioButtons2",
#' label = paste("radioButtons label", x),
#' choices = x,
#' selected = x
#' )
#' })
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @export
updateRadioButtons <- function(session, inputId, label = NULL, choices = NULL,
selected = NULL, inline = FALSE) {
selected = NULL, inline = FALSE,
choiceNames = NULL, choiceValues = NULL) {
# you must select at least one radio button
if (is.null(selected) && !is.null(choices)) selected <- choices[[1]]
updateInputOptions(session, inputId, label, choices, selected, inline, type = 'radio')
if (is.null(selected)) {
if (!is.null(choices)) selected <- choices[[1]]
else if (!is.null(choiceValues)) selected <- choiceValues[[1]]
}
updateInputOptions(session, inputId, label, choices, selected,
inline, 'radio', choiceNames, choiceValues)
}
@@ -362,43 +576,45 @@ updateRadioButtons <- function(session, inputId, label = NULL, choices = NULL,
#' @template update-input
#' @inheritParams selectInput
#'
#' @seealso \code{\link{selectInput}}
#' @seealso \code{\link{selectInput}} \code{\link{varSelectInput}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' p("The checkbox group controls the select input"),
#' checkboxGroupInput("inCheckboxGroup", "Input checkbox",
#' c("Item A", "Item B", "Item C")),
#' selectInput("inSelect", "Select input",
#' c("Item A", "Item B", "Item C"))
#' )
#'
#' server <- function(input, output, session) {
#' observe({
#' # We'll use the input$controller variable multiple times, so save it as x
#' # for convenience.
#' x <- input$controller
#' x <- input$inCheckboxGroup
#'
#' # Create a list of new options, where the name of the items is something
#' # like 'option label x 1', and the values are 'option-x-1'.
#' s_options <- list()
#' s_options[[sprintf("option label %d 1", x)]] <- sprintf("option-%d-1", x)
#' s_options[[sprintf("option label %d 2", x)]] <- sprintf("option-%d-2", x)
#' # Can use character(0) to remove all choices
#' if (is.null(x))
#' x <- character(0)
#'
#' # Change values for input$inSelect
#' updateSelectInput(session, "inSelect", choices = s_options)
#'
#' # Can also set the label and select an item (or more than one if it's a
#' # multi-select)
#' updateSelectInput(session, "inSelect2",
#' label = paste("Select label", x),
#' choices = s_options,
#' selected = sprintf("option-%d-2", x)
#' # Can also set the label and select items
#' updateSelectInput(session, "inSelect",
#' label = paste("Select input label", length(x)),
#' choices = x,
#' selected = tail(x, 1)
#' )
#' })
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @export
updateSelectInput <- function(session, inputId, label = NULL, choices = NULL,
selected = NULL) {
choices <- choicesWithNames(choices)
if (!is.null(selected))
selected <- validateSelected(selected, choices, inputId)
options <- if (length(choices)) selectOptions(choices, selected)
choices <- if (!is.null(choices)) choicesWithNames(choices)
if (!is.null(selected)) selected <- as.character(selected)
options <- if (!is.null(choices)) selectOptions(choices, selected)
message <- dropNulls(list(label = label, options = options, value = selected))
session$sendInputMessage(inputId, message)
}
@@ -417,7 +633,7 @@ updateSelectizeInput <- function(session, inputId, label = NULL, choices = NULL,
res <- checkAsIs(options)
cfg <- tags$script(
type = 'application/json',
`data-for` = inputId,
`data-for` = session$ns(inputId),
`data-eval` = if (length(res$eval)) HTML(toJSON(res$eval)),
HTML(toJSON(res$options))
)
@@ -426,55 +642,182 @@ updateSelectizeInput <- function(session, inputId, label = NULL, choices = NULL,
if (!server) {
return(updateSelectInput(session, inputId, label, choices, selected))
}
# in the server mode, the choices are not available before we type, so we
# cannot really pre-select any options, but here we insert the `selected`
# options into selectize forcibly
noOptGroup <- TRUE
if (is.list(choices)) {
# check if list is nested
for (i in seq_along(choices)) {
if (is.list(choices[[i]]) || length(choices[[i]]) > 1) {
noOptGroup <- FALSE
break()
}
}
}
# convert choices to a data frame so it returns [{label: , value: , optgroup: },...]
choices <- if (is.data.frame(choices)) {
# jcheng 2018/09/25: I don't think we ever said data frames were OK to pass
# to updateSelectInput, but one of the example apps does this and at least
# one user noticed when we broke it.
# https://github.com/rstudio/shiny/issues/2172
# https://github.com/rstudio/shiny/issues/2192
as.data.frame(choices, stringsAsFactors = FALSE)
} else if (is.atomic(choices) || noOptGroup) {
# fast path for vectors and flat lists
if (is.list(choices)) {
choices <- unlist(choices)
}
if (is.null(names(choices))) {
lab <- as.character(choices)
} else {
lab <- names(choices)
# replace empty names like: choices = c(a = 1, 2)
# in this case: names(choices) = c("a", "")
# with replacement below choices will be: lab = c("a", "2")
empty_names_indices <- lab == ""
lab[empty_names_indices] <- as.character(choices[empty_names_indices])
}
data.frame(label = lab, value = choices, stringsAsFactors = FALSE)
} else {
# slow path for nested lists/optgroups
list_names <- names(choices)
if (is.null(list_names)) {
list_names <- rep("", length(choices))
}
choice_list <- mapply(choices, list_names, FUN = function (choice, name) {
group <- ""
lab <- name
if (lab == "") lab <- as.character(choice)
if (is.list(choice) || length(choice) > 1) {
group <- rep(name, length(choice))
choice <- unlist(choice)
if (is.null(names(choice))) {
lab <- as.character(choice)
} else {
lab <- names(choice)
# replace empty names like: choices = c(a = 1, 2)
# in this case: names(choices) = c("a", "")
# with replacement below choices will be: lab = c("a", "2")
empty_names_indices <- lab == ""
lab[empty_names_indices] <- as.character(choice[empty_names_indices])
}
}
list(
label = lab,
value = as.character(choice),
# The name "optgroup" is because this is the default field where
# selectize will look for group IDs
optgroup = group
)
}, SIMPLIFY = FALSE)
extract_vector <- function(x, name) {
vecs <- lapply(x, `[[`, name)
do.call(c, vecs)
}
data.frame(
label = extract_vector(choice_list, "label"),
value = extract_vector(choice_list, "value"),
optgroup = extract_vector(choice_list, "optgroup"),
stringsAsFactors = FALSE, row.names = NULL
)
}
value <- unname(selected)
selected <- choicesWithNames(selected)
attr(choices, 'selected_value') <- value
message <- dropNulls(list(
label = label,
value = value,
selected = if (length(selected)) {
columnToRowData(list(label = names(selected), value = selected))
},
url = session$registerDataObj(inputId, choices, selectizeJSON)
))
session$sendInputMessage(inputId, message)
}
#' @rdname updateSelectInput
#' @inheritParams varSelectInput
#' @export
updateVarSelectInput <- function(session, inputId, label = NULL, data = NULL, selected = NULL) {
if (is.null(data)) {
choices <- NULL
} else {
choices <- colnames(data)
}
updateSelectInput(
session = session,
inputId = inputId,
label = label,
choices = choices,
selected = selected
)
}
#' @rdname updateSelectInput
#' @export
updateVarSelectizeInput <- function(session, inputId, label = NULL, data = NULL, selected = NULL, options = list(), server = FALSE) {
if (is.null(data)) {
choices <- NULL
} else {
choices <- colnames(data)
}
updateSelectizeInput(
session = session,
inputId = inputId,
label = label,
choices = choices,
selected = selected,
options = options,
server = server
)
}
selectizeJSON <- function(data, req) {
query <- parseQueryString(req$QUERY_STRING)
# extract the query variables, conjunction (and/or), search string, maximum options
var <- unlist(fromJSON(query$field, asText = TRUE))
cjn <- if (query$conju == 'and') all else any
var <- c(safeFromJSON(query$field))
# all keywords in lower-case, for case-insensitive matching
key <- unique(strsplit(tolower(query$query), '\\s+')[[1]])
if (identical(key, '')) key <- character(0)
mop <- query$maxop
# convert a single vector to a data frame so it returns {label: , value: }
# later in JSON; other objects return arbitrary JSON {x: , y: , foo: , ...}
data <- if (is.atomic(data)) {
data.frame(label = names(choicesWithNames(data)), value = data,
stringsAsFactors = FALSE)
} else as.data.frame(data, stringsAsFactors = FALSE)
if (identical(key, '')) key <- character(0)
mop <- as.numeric(query$maxop)
vfd <- query$value # the value field name
sel <- attr(data, 'selected_value', exact = TRUE)
# start searching for keywords in all specified columns
idx <- logical(nrow(data))
if (length(key)) for (v in var) {
matches <- do.call(
cbind,
lapply(key, function(k) {
grepl(k, tolower(as.character(data[[v]])), fixed = TRUE)
})
)
# merge column matches using OR, and match multiple keywords in one column
# using the conjunction setting (AND or OR)
idx <- idx | apply(matches, 1, cjn)
if (length(key)) {
for (v in var) {
matches <- do.call(
cbind,
lapply(key, function(k) {
grepl(k, tolower(as.character(data[[v]])), fixed = TRUE)
})
)
# merge column matches using OR, and match multiple keywords in one column
# using the conjunction setting (AND or OR)
matches <- rowSums(matches)
if (query$conju == 'and')
idx <- idx | (matches == length(key))
else
idx <- idx | matches
}
}
# only return the first n rows (n = maximum options in configuration)
idx <- head(which(idx), mop)
idx <- utils::head(if (length(key)) which(idx) else seq_along(idx), mop)
# make sure the selected value is in the data
if (length(sel)) {
i <- stats::na.omit(match(sel, data[, vfd]))
if (length(i)) idx <- sort(utils::head(unique(c(i, idx)), mop))
}
data <- data[idx, ]
httpResponse(200, 'application/json', toJSON(columnToRowData(data)))
res <- toJSON(columnToRowData(data))
httpResponse(200, 'application/json', enc2utf8(res))
}

1152
R/utils.R

File diff suppressed because it is too large Load Diff

View File

@@ -1,20 +1,24 @@
# Shiny
[![Build Status](https://travis-ci.org/rstudio/shiny.svg?branch=master)](https://travis-ci.org/rstudio/shiny)
*Travis:* [![Travis Build Status](https://travis-ci.org/rstudio/shiny.svg?branch=master)](https://travis-ci.org/rstudio/shiny)
*AppVeyor:* [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/rstudio/shiny?branch=master&svg=true)](https://ci.appveyor.com/project/rstudio/shiny)
Shiny is a new package from RStudio that makes it incredibly easy to build interactive web applications with R.
For an introduction and examples, visit the [Shiny Dev Center](http://shiny.rstudio.com/).
If you have general questions about using Shiny, please use the [RStudio Community website](https://community.rstudio.com). For bug reports, please use the [issue tracker](https://github.com/rstudio/shiny/issues).
## Features
* Build useful web applications with only a few lines of code&mdash;no JavaScript required.
* Shiny applications are automatically "live" in the same way that spreadsheets are live. Outputs change instantly as users modify inputs, without requiring a reload of the browser.
* Shiny user interfaces can be built entirely using R, or can be written directly in HTML, CSS, and JavaScript for more flexibility.
* Works in any R environment (Console R, Rgui for Windows or Mac, ESS, StatET, RStudio, etc.)
* Attractive default UI theme based on [Bootstrap](http://getbootstrap.com/2.3.2/).
* Works in any R environment (Console R, Rgui for Windows or Mac, ESS, StatET, RStudio, etc.).
* Attractive default UI theme based on [Bootstrap](http://getbootstrap.com/).
* A highly customizable slider widget with built-in support for animation.
* Pre-built output widgets for displaying plots, tables, and printed output of R objects.
* Prebuilt output widgets for displaying plots, tables, and printed output of R objects.
* Fast bidirectional communication between the web browser and R using the [httpuv](https://github.com/rstudio/httpuv) package.
* Uses a [reactive](http://en.wikipedia.org/wiki/Reactive_programming) programming model that eliminates messy event handling code, so you can focus on the code that really matters.
* Develop and redistribute your own Shiny widgets that other developers can easily drop into their own applications (coming soon!).
@@ -32,18 +36,33 @@ To install the latest development builds directly from GitHub, run this instead:
```r
if (!require("devtools"))
install.packages("devtools")
devtools::install_github("shiny", "rstudio")
devtools::install_github("rstudio/shiny")
```
## Getting Started
To learn more we highly recommend you check out the [Shiny Tutorial](http://shiny.rstudio.com/tutorial/). The tutorial explains the framework in-depth, walks you through building a simple application, and includes extensive annotated examples.
We hope you enjoy using Shiny. If you have general questions about using Shiny, please use the Shiny [mailing list](https://groups.google.com/forum/#!forum/shiny-discuss). For bug reports, please use the [issue tracker](https://github.com/rstudio/shiny/issues).
## Bootstrap 3 migration
Shiny versions 0.10.2.2 and below used the Bootstrap 2 web framework. After 0.10.2.2, Shiny switched to Bootstrap 3. For most users, the upgrade should be seamless. However, if you have have customized your HTML-generating code to use features specific to Bootstrap 2, you may need to update your code to work with Bootstrap 3.
If you do not wish to update your code at this time, you can use the [shinybootstrap2](https://github.com/rstudio/shinybootstrap2) package for backward compatibility.
If you prefer to install an older version of Shiny, you can do it using the devtools package:
```R
devtools::install_version("shiny", version = "0.10.2.2")
```
## Development notes
The Javascript code in Shiny is minified using tools that run on Node.js. See the tools/ directory for more information.
## Guidelines for contributing
We welcome contributions to the **shiny** package. Please see our [CONTRIBUTING.md](CONTRIBUTING.md) file for detailed guidelines of how to contribute.
## 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.

54
TODO-promises.md Normal file
View File

@@ -0,0 +1,54 @@
# Promises TODO
## Documentation
- [x] Motivation -- why should I care about async? Why shouldn't I (what are the limitations)?
- [x] High level technical overview
- [ ] Cookbook-style examples
- [ ] Top-down porting of a sync app to async
## Core API
- [x] Should as.promise() convert regular values to promises? Or throw?
- [x] If as.promise() doesn't convert regular values to promises, add promise_resolved(value) and promise_rejected(err) functions?
## later
- [ ] Add support for multiple event loops
- [x] Add timeout to run_now
## Error handling/debugging
- [ ] ..stacktraceon../..stacktraceoff.. and stack traces in general
- [x] long stack traces
- [x] require opt-in
- [ ] options(shiny.error) should work in promise handlers
- [x] Detect when reactives are used across process boundaries, and error
## Render functions
- [x] Non-async render functions should have their code all execute on the current tick. Otherwise order of execution will be surprising if they have side effects and explicit priorities.
- [x] Promise domains should maybe have an onExecute, for the "sync" part that kicks off async operations to also have wrapping behavior (like capturing output). Right now, I have to start off renderPrint with promise(~resolve(TRUE)) and then execute the user code in a then(), just to get the promise behavior. Same will be true when we tackle error handling (stack trace capture).
- [x] invisible() doesn't seem to be working correctly with renderPrint. .visible doesn't survive promise chaining, e.g. promise(~resolve(promise(~resolve(invisible("Hi"))))) %>% then(function(x, .visible) { cat(.visible) }) will print TRUE, not FALSE.
- [x] renderDataTable should support async
- [x] Support downloadHandler
- [ ] Support async filename?
- [x] Should prevent session from continuing until download completes (ref count)
## Flush lifecycle
- [x] While async operations are running in a session, hold off on any further processing of inputs and scheduled task items until all operations are complete.
- [x] Hold all outputs/errors until async operations are complete.
- [ ] Allow both sync and async outputs to be displayed before all outputs are done. (opt-in)
## Testing
- [x] App that tests that all built-in render functions support async
- [x] Apps that test flush lifecycle, including onFlushed(once = FALSE)
- [x] Apps that test invisible() behavior for renderPrint, both sync and async
- [x] Apps that ensure all render functions execute synchronous code before tick is over
- [x] App that tests async downloadHandler
- [x] App that verifies inputs/timers don't fire for a session while it has async operations pending
- [x] App that verifies req(FALSE), req(FALSE, cancelOutput = TRUE), validate/need, etc. all work in async
## External packages
- [x] DT
- [x] htmlwidgets: Don't require async-aware version of Shiny if not using async
- [x] Plotly
## Bugs
- [x] req(FALSE, cancelOutput = TRUE) shows grey (even without async)

49
appveyor.yml Normal file
View File

@@ -0,0 +1,49 @@
# DO NOT CHANGE the "init" and "install" sections below
# Download script file from GitHub
init:
ps: |
$ErrorActionPreference = "Stop"
Invoke-WebRequest http://raw.github.com/krlmlr/r-appveyor/master/scripts/appveyor-tool.ps1 -OutFile "..\appveyor-tool.ps1"
Import-Module '..\appveyor-tool.ps1'
install:
ps: Bootstrap
cache:
- C:\RLibrary
# Adapt as necessary starting from here
build_script:
- travis-tool.sh install_deps
test_script:
- travis-tool.sh run_tests
on_failure:
- 7z a failure.zip *.Rcheck\*
- appveyor PushArtifact failure.zip
artifacts:
- path: '*.Rcheck\**\*.log'
name: Logs
- path: '*.Rcheck\**\*.out'
name: Logs
- path: '*.Rcheck\**\*.fail'
name: Logs
- path: '*.Rcheck\**\*.Rout'
name: Logs
- path: '\*_*.tar.gz'
name: Bits
- path: '\*_*.zip'
name: Bits
environment:
global:
USE_RTOOLS: true

0
cran-comments.md Normal file
View File

View File

@@ -1,678 +0,0 @@
The shiny package is licensed to you under the GPLv3, the terms of
which are included below. The markdown pacakge includes other open
source software whose license terms can be found in the file NOTICE.
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View File

@@ -1,325 +0,0 @@
The shiny package inludes other open source software components. The following
is a list of these components (full copies of the license agreements used by
these components are included below):
- jQuery
- Bootstrap
- bootstrap-datepicker, from https://github.com/eternicode/bootstrap-datepicker
- selectize, from https://github.com/brianreavis/selectize.js
- es5-shim
- jslider
- DataTables
jQuery License
----------------------------------------------------------------------
Copyright (c) 2012 jQuery Foundation and other contributors,
http://jquery.com/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Bootstrap, bootstrap-datepicker, and selectize License
----------------------------------------------------------------------
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
es5-shim License
----------------------------------------------------------------------
The MIT License (MIT)
Copyright (C) 2009-2014 Kristopher Michael Kowal and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
jslider License
----------------------------------------------------------------------
The MIT License (MIT)
Copyright (c) 2012 Egor Khmelev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
DataTables License
----------------------------------------------------------------------
Copyright (c) 2008-2010, Allan Jardine
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Allan Jardine nor SpryMedia UK may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,4 +1,3 @@
This small Shiny application demonstrates Shiny's automatic UI updates. Move
the *Number of bins* slider and notice how the `renderPlot` expression is
automatically re-evaluated when its dependant, `input$bins`, changes,
causing a histogram with a new number of bins to be rendered.
This small Shiny application demonstrates Shiny's automatic UI updates.
Move the *Number of bins* slider and notice how the `renderPlot` expression is automatically re-evaluated when its dependant, `input$bins`, changes, causing a histogram with a new number of bins to be rendered.

View File

@@ -0,0 +1,59 @@
library(shiny)
# Define UI for app that draws a histogram ----
ui <- fluidPage(
# App title ----
titlePanel("Hello Shiny!"),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
# Input: Slider for the number of bins ----
sliderInput(inputId = "bins",
label = "Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Main panel for displaying outputs ----
mainPanel(
# Output: Histogram ----
plotOutput(outputId = "distPlot")
)
)
)
# Define server logic required to draw a histogram ----
server <- function(input, output) {
# Histogram of the Old Faithful Geyser Data ----
# with requested number of bins
# This expression that generates a histogram is wrapped in a call
# to renderPlot to indicate that:
#
# 1. It is "reactive" and therefore should be automatically
# re-executed when inputs (input$bins) change
# 2. Its output type is a plot
output$distPlot <- renderPlot({
x <- faithful$waiting
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x, breaks = bins, col = "#75AADB", border = "white",
xlab = "Waiting time to next eruption (in mins)",
main = "Histogram of waiting times")
})
}
# Create Shiny app ----
shinyApp(ui = ui, server = server)

View File

@@ -1,21 +0,0 @@
library(shiny)
# Define server logic required to draw a histogram
shinyServer(function(input, output) {
# Expression that generates a histogram. The expression is
# wrapped in a call to renderPlot to indicate that:
#
# 1) It is "reactive" and therefore should be automatically
# re-executed when inputs change
# 2) Its output type is a plot
output$distPlot <- renderPlot({
x <- faithful[, 2] # Old Faithful Geyser data
bins <- seq(min(x), max(x), length.out = input$bins + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
})

View File

@@ -1,24 +0,0 @@
library(shiny)
# Define UI for application that draws a histogram
shinyUI(fluidPage(
# Application title
titlePanel("Hello Shiny!"),
# Sidebar with a slider input for the number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
)
))

View File

@@ -1 +1 @@
This example demonstrates output of raw text from R using the `renderPrint` function in `server.R` and the `verbatimTextOutput` function in `ui.R`. In this case, a textual summary of the data is shown using R's built-in `summary` function.
This example demonstrates output of raw text from R using the `renderPrint` function in `server` and the `verbatimTextOutput` function in `ui`. In this case, a textual summary of the data is shown using R's built-in `summary` function.

View File

@@ -0,0 +1,64 @@
library(shiny)
# Define UI for dataset viewer app ----
ui <- fluidPage(
# App title ----
titlePanel("Shiny Text"),
# Sidebar layout with a input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
# Input: Selector for choosing dataset ----
selectInput(inputId = "dataset",
label = "Choose a dataset:",
choices = c("rock", "pressure", "cars")),
# Input: Numeric entry for number of obs to view ----
numericInput(inputId = "obs",
label = "Number of observations to view:",
value = 10)
),
# Main panel for displaying outputs ----
mainPanel(
# Output: Verbatim text for data summary ----
verbatimTextOutput("summary"),
# Output: HTML table with requested number of observations ----
tableOutput("view")
)
)
)
# Define server logic to summarize and view selected dataset ----
server <- function(input, output) {
# Return the requested dataset ----
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
# Generate a summary of the dataset ----
output$summary <- renderPrint({
dataset <- datasetInput()
summary(dataset)
})
# Show the first "n" observations ----
output$view <- renderTable({
head(datasetInput(), n = input$obs)
})
}
# Create Shiny app ----
shinyApp(ui = ui, server = server)

View File

@@ -1,26 +0,0 @@
library(shiny)
library(datasets)
# Define server logic required to summarize and view the selected
# dataset
shinyServer(function(input, output) {
# Return the requested dataset
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
# Generate a summary of the dataset
output$summary <- renderPrint({
dataset <- datasetInput()
summary(dataset)
})
# Show the first "n" observations
output$view <- renderTable({
head(datasetInput(), n = input$obs)
})
})

View File

@@ -1,27 +0,0 @@
library(shiny)
# Define UI for dataset viewer application
shinyUI(fluidPage(
# Application title
titlePanel("Shiny Text"),
# Sidebar with controls to select a dataset and specify the
# number of observations to view
sidebarLayout(
sidebarPanel(
selectInput("dataset", "Choose a dataset:",
choices = c("rock", "pressure", "cars")),
numericInput("obs", "Number of observations to view:", 10)
),
# Show a summary of the dataset and an HTML table with the
# requested number of observations
mainPanel(
verbatimTextOutput("summary"),
tableOutput("view")
)
)
))

View File

@@ -1,5 +1,5 @@
This example demonstrates a core feature of Shiny: **reactivity**. In `server.R`, a reactive called `datasetInput` is declared.
This example demonstrates a core feature of Shiny: **reactivity**. In the `server` function, a reactive called `datasetInput` is declared.
Notice that the reactive expression depends on the input expression `input$dataset`, and that it's used by both the output expression `output$summary` and `output$view`. Try changing the dataset (using *Choose a dataset*) while looking at the reactive and then at the outputs; you will see first the reactive and then its dependencies flash.
Notice that the reactive expression depends on the input expression `input$dataset`, and that it's used by two output expressions: `output$summary` and `output$view`. Try changing the dataset (using *Choose a dataset*) while looking at the reactive and then at the outputs; you will see first the reactive and then its dependencies flash.
Notice also that the reactive expression doesn't just update whenever anything changes--only the inputs it depends on will trigger an update. Change the "Caption" field and notice how only the `output$caption` expression is re-evaluated; the reactive and its dependents are left alone.

View File

@@ -0,0 +1,102 @@
library(shiny)
# Define UI for dataset viewer app ----
ui <- fluidPage(
# App title ----
titlePanel("Reactivity"),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
# Input: Text for providing a caption ----
# Note: Changes made to the caption in the textInput control
# are updated in the output area immediately as you type
textInput(inputId = "caption",
label = "Caption:",
value = "Data Summary"),
# Input: Selector for choosing dataset ----
selectInput(inputId = "dataset",
label = "Choose a dataset:",
choices = c("rock", "pressure", "cars")),
# Input: Numeric entry for number of obs to view ----
numericInput(inputId = "obs",
label = "Number of observations to view:",
value = 10)
),
# Main panel for displaying outputs ----
mainPanel(
# Output: Formatted text for caption ----
h3(textOutput("caption", container = span)),
# Output: Verbatim text for data summary ----
verbatimTextOutput("summary"),
# Output: HTML table with requested number of observations ----
tableOutput("view")
)
)
)
# Define server logic to summarize and view selected dataset ----
server <- function(input, output) {
# Return the requested dataset ----
# By declaring datasetInput as a reactive expression we ensure
# that:
#
# 1. It is only called when the inputs it depends on changes
# 2. The computation and result are shared by all the callers,
# i.e. it only executes a single time
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
# Create caption ----
# The output$caption is computed based on a reactive expression
# that returns input$caption. When the user changes the
# "caption" field:
#
# 1. This function is automatically called to recompute the output
# 2. New caption is pushed back to the browser for re-display
#
# Note that because the data-oriented reactive expressions
# below don't depend on input$caption, those expressions are
# NOT called when input$caption changes
output$caption <- renderText({
input$caption
})
# Generate a summary of the dataset ----
# The output$summary depends on the datasetInput reactive
# expression, so will be re-executed whenever datasetInput is
# invalidated, i.e. whenever the input$dataset changes
output$summary <- renderPrint({
dataset <- datasetInput()
summary(dataset)
})
# Show the first "n" observations ----
# The output$view depends on both the databaseInput reactive
# expression and input$obs, so it will be re-executed whenever
# input$dataset or input$obs is changed
output$view <- renderTable({
head(datasetInput(), n = input$obs)
})
}
# Create Shiny app ----
shinyApp(ui, server)

View File

@@ -1,53 +0,0 @@
library(shiny)
library(datasets)
# Define server logic required to summarize and view the selected
# dataset
shinyServer(function(input, output) {
# By declaring datasetInput as a reactive expression we ensure
# that:
#
# 1) It is only called when the inputs it depends on changes
# 2) The computation and result are shared by all the callers
# (it only executes a single time)
#
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
# The output$caption is computed based on a reactive expression
# that returns input$caption. When the user changes the
# "caption" field:
#
# 1) This function is automatically called to recompute the
# output
# 2) The new caption is pushed back to the browser for
# re-display
#
# Note that because the data-oriented reactive expressions
# below don't depend on input$caption, those expressions are
# NOT called when input$caption changes.
output$caption <- renderText({
input$caption
})
# The output$summary depends on the datasetInput reactive
# expression, so will be re-executed whenever datasetInput is
# invalidated
# (i.e. whenever the input$dataset changes)
output$summary <- renderPrint({
dataset <- datasetInput()
summary(dataset)
})
# The output$view depends on both the databaseInput reactive
# expression and input$obs, so will be re-executed whenever
# input$dataset or input$obs is changed.
output$view <- renderTable({
head(datasetInput(), n = input$obs)
})
})

View File

@@ -1,34 +0,0 @@
library(shiny)
# Define UI for dataset viewer application
shinyUI(fluidPage(
# Application title
titlePanel("Reactivity"),
# Sidebar with controls to provide a caption, select a dataset,
# and specify the number of observations to view. Note that
# changes made to the caption in the textInput control are
# updated in the output area immediately as you type
sidebarLayout(
sidebarPanel(
textInput("caption", "Caption:", "Data Summary"),
selectInput("dataset", "Choose a dataset:",
choices = c("rock", "pressure", "cars")),
numericInput("obs", "Number of observations to view:", 10)
),
# Show the caption, a summary of the dataset and an HTML
# table with the requested number of observations
mainPanel(
h3(textOutput("caption", container = span)),
verbatimTextOutput("summary"),
tableOutput("view")
)
)
))

View File

@@ -1,4 +1,4 @@
This example demonstrates the following concepts:
* **Global variables**: The `mpgData` variable is declared outside the `shinyServer` function. This makes it available anywhere inside `shinyServer`. The code in `server.R` outside `shinyServer` is only run once when the app starts up, so it can't contain user input.
* **Reactive expressions**: `formulaText` is a reactive expression. Note how it re-evaluates when the Variable field is changed, but not when the Show Outliers box is ticked.
- **Global variables**: The `mpgData` variable is declared outside of the `ui` and `server` function definitions. This makes it available anywhere inside `app.R`. The code in `app.R` outside of `ui` and `server` function definitions is only run once when the app starts up, so it can't contain user input.
- **Reactive expressions**: `formulaText` is a reactive expression. Note how it re-evaluates when the Variable field is changed, but not when the Show Outliers box is unchecked.

View File

@@ -0,0 +1,75 @@
library(shiny)
library(datasets)
# Data pre-processing ----
# Tweak the "am" variable to have nicer factor labels -- since this
# doesn't rely on any user inputs, we can do this once at startup
# and then use the value throughout the lifetime of the app
mpgData <- mtcars
mpgData$am <- factor(mpgData$am, labels = c("Automatic", "Manual"))
# Define UI for miles per gallon app ----
ui <- fluidPage(
# App title ----
titlePanel("Miles Per Gallon"),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
# Input: Selector for variable to plot against mpg ----
selectInput("variable", "Variable:",
c("Cylinders" = "cyl",
"Transmission" = "am",
"Gears" = "gear")),
# Input: Checkbox for whether outliers should be included ----
checkboxInput("outliers", "Show outliers", TRUE)
),
# Main panel for displaying outputs ----
mainPanel(
# Output: Formatted text for caption ----
h3(textOutput("caption")),
# Output: Plot of the requested variable against mpg ----
plotOutput("mpgPlot")
)
)
)
# Define server logic to plot various variables against mpg ----
server <- function(input, output) {
# Compute the formula text ----
# This is in a reactive expression since it is shared by the
# output$caption and output$mpgPlot functions
formulaText <- reactive({
paste("mpg ~", input$variable)
})
# Return the formula text for printing as a caption ----
output$caption <- renderText({
formulaText()
})
# Generate a plot of the requested variable against mpg ----
# and only exclude outliers if requested
output$mpgPlot <- renderPlot({
boxplot(as.formula(formulaText()),
data = mpgData,
outline = input$outliers,
col = "#75AADB", pch = 19)
})
}
# Create Shiny app ----
shinyApp(ui, server)

View File

@@ -1,34 +0,0 @@
library(shiny)
library(datasets)
# We tweak the "am" field to have nicer factor labels. Since
# this doesn't rely on any user inputs we can do this once at
# startup and then use the value throughout the lifetime of the
# application
mpgData <- mtcars
mpgData$am <- factor(mpgData$am, labels = c("Automatic", "Manual"))
# Define server logic required to plot various variables against
# mpg
shinyServer(function(input, output) {
# Compute the forumla text in a reactive expression since it is
# shared by the output$caption and output$mpgPlot functions
formulaText <- reactive({
paste("mpg ~", input$variable)
})
# Return the formula text for printing as a caption
output$caption <- renderText({
formulaText()
})
# Generate a plot of the requested variable against mpg and
# only include outliers if requested
output$mpgPlot <- renderPlot({
boxplot(as.formula(formulaText()),
data = mpgData,
outline = input$outliers)
})
})

View File

@@ -1,29 +0,0 @@
library(shiny)
# Define UI for miles per gallon application
shinyUI(fluidPage(
# Application title
titlePanel("Miles Per Gallon"),
# Sidebar with controls to select the variable to plot against
# mpg and to specify whether outliers should be included
sidebarLayout(
sidebarPanel(
selectInput("variable", "Variable:",
c("Cylinders" = "cyl",
"Transmission" = "am",
"Gears" = "gear")),
checkboxInput("outliers", "Show outliers", FALSE)
),
# Show the caption and plot of the requested variable against
# mpg
mainPanel(
h3(textOutput("caption")),
plotOutput("mpgPlot")
)
)
))

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