Compare commits

..

1007 Commits

Author SHA1 Message Date
Joe Cheng
417f7f7236 Clean up correctly after API caller leaves 2017-01-12 23:56:10 -05:00
Joe Cheng
c1d92c2767 API: Log errors; automatically print() ggplot2 2017-01-12 23:56:10 -05:00
Joe Cheng
3d2f677e2f Accept input via JSON POST 2017-01-12 23:56:10 -05:00
Joe Cheng
b15cc6cbc0 API parameters get parsed using JSON semantics
This gives us more accurate data types (numbers as ints/reals instead of strings)

Also adds serveRaw (raw vectors)
2017-01-12 23:56:10 -05:00
Joe Cheng
4fbb8a436c Import utils::write.csv 2017-01-12 23:56:10 -05:00
Joe Cheng
308b41b8e8 Improve result handling
- Arbitrary httpResponse can now be returned
- WebSocket API conn doesn't force everything to JSON
2017-01-12 23:56:10 -05:00
Joe Cheng
516d0cd2ca Squelch messages 2017-01-12 23:56:10 -05:00
Joe Cheng
f9d8217f90 Better API API :) 2017-01-12 23:56:10 -05:00
Joe Cheng
58ad213a6f Support plots 2017-01-12 23:56:10 -05:00
Joe Cheng
6ef5c7728e Fix shared secret check 2017-01-12 23:56:10 -05:00
Joe Cheng
58a5fe9a84 API prototype 2017-01-12 23:56:10 -05:00
Winston Chang
c633c8b7dd Update URL 2017-01-12 23:56:10 -05: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
509 changed files with 58594 additions and 27281 deletions

View File

@@ -6,6 +6,7 @@
^shiny\.cmd$
^run\.R$
^\.gitignore$
^smoketests$
^res$
^man-roxygen$
^\.travis\.yml$
@@ -14,3 +15,6 @@
^srcjs$
^CONTRIBUTING.md$
^cran-comments.md$
^.*\.o$
^appveyor\.yml$
^revdep$

1
.gitignore vendored
View File

@@ -8,3 +8,4 @@
/src-x86_64/
shinyapps/
README.html
.*.Rnb.cached

View File

@@ -1,10 +1,10 @@
language: r
warnings_are_errors: true
r_binary_packages:
- Rcpp
- cairo
- knitr
r:
- oldrel
- release
- devel
sudo: false
cache: packages
notifications:
email:

View File

@@ -1,10 +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](http://www.rstudio.com/wp-content/uploads/2014/06/RStudioIndividualContributorAgreement.pdf) or [corporate](http://www.rstudio.com/wp-content/uploads/2014/06/RStudioCorporateContributorAgreement.pdf) contributor agreement as appropriate. You can send the signed copy to jj@rstudio.com.
2. If the change is non-trivial, ensure that you have signed the [individual](http://www.rstudio.com/wp-content/uploads/2014/06/RStudioIndividualContributorAgreement.pdf) or [corporate](http://www.rstudio.com/wp-content/uploads/2014/06/RStudioCorporateContributorAgreement.pdf) contributor agreement as appropriate. You can send the signed copy to jj@rstudio.com. For trivial changes (like typo fixes), a contributor agreement is not needed.
3. Submit a [pull request](https://help.github.com/articles/using-pull-requests).
We'll try to be as responsive as possible in reviewing and accepting pull requests. We appreciate your contributions!
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,8 +1,7 @@
Package: shiny
Type: Package
Title: Web Application Framework for R
Version: 0.12.1
Date: 2015-06-11
Version: 1.0.0
Authors@R: c(
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@rstudio.com"),
person("Joe", "Cheng", role = "aut", email = "joe@rstudio.com"),
@@ -15,7 +14,7 @@ Authors@R: c(
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/1.10.4/AUTHORS.txt"),
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",
@@ -42,6 +41,8 @@ Authors@R: c(
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"),
@@ -59,35 +60,43 @@ Description: Makes it incredibly easy to build interactive web
beautiful, responsive, and powerful applications with minimal effort.
License: GPL-3 | file LICENSE
Depends:
R (>= 3.0.0)
R (>= 3.0.0),
methods
Imports:
utils,
httpuv (>= 1.3.2),
httpuv (>= 1.3.3),
mime (>= 0.3),
jsonlite (>= 0.9.16),
xtable,
digest,
htmltools (>= 0.2.6),
R6 (>= 2.0)
htmltools (>= 0.3.5),
R6 (>= 2.0),
sourcetools
Suggests:
datasets,
Cairo (>= 1.5-5),
testthat,
knitr (>= 1.6),
markdown,
ggplot2
rmarkdown,
ggplot2,
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'
'conditions.R'
'map.R'
'globals.R'
'utils.R'
'bootstrap.R'
'cache.R'
'diagnose.R'
'fileupload.R'
'stack.R'
'graph.R'
'hooks.R'
'html-deps.R'
@@ -95,22 +104,46 @@ Collate:
'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-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-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'
'tar.R'
'test-export.R'
'timer.R'
'update-input.R'
RoxygenNote: 5.0.1

28
LICENSE
View File

@@ -1,7 +1,7 @@
The shiny package as a whole is distributed under GPL-3 (GNU GENERAL PUBLIC
LICENSE version 3).
The shiny package inludes other open source software components. The following
The shiny package includes 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):
@@ -15,6 +15,7 @@ these components are included below):
- selectize.js, https://github.com/brianreavis/selectize.js
- es5-shim, https://github.com/es-shims/es5-shim
- ion.rangeSlider, https://github.com/IonDen/ion.rangeSlider
- strftime for Javascript, https://github.com/samsonjs/strftime
- DataTables, https://github.com/DataTables/DataTables
- showdown.js, https://github.com/showdownjs/showdown
- highlight.js, https://github.com/isagalaev/highlight.js
@@ -1051,6 +1052,31 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
strftime for Javascript License
----------------------------------------------------------------------
The MIT License (MIT)
Copyright © 2015 Sami Samhuri, http://samhuri.net <sami@samhuri.net>
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
----------------------------------------------------------------------

View File

@@ -1,16 +1,25 @@
# Generated by roxygen2 (4.1.1): do not edit by hand
# Generated by roxygen2: do not edit by hand
S3method("$",reactivevalues)
S3method("$",session_proxy)
S3method("$",shinyapi)
S3method("$",shinyoutput)
S3method("$<-",reactivevalues)
S3method("$<-",session_proxy)
S3method("$<-",shinyapi)
S3method("$<-",shinyoutput)
S3method("[",reactivevalues)
S3method("[",shinyapi)
S3method("[",shinyoutput)
S3method("[<-",reactivevalues)
S3method("[<-",shinyapi)
S3method("[<-",shinyoutput)
S3method("[[",reactivevalues)
S3method("[[",session_proxy)
S3method("[[",shinyapi)
S3method("[[",shinyoutput)
S3method("[[<-",reactivevalues)
S3method("[[<-",shinyapi)
S3method("[[<-",shinyoutput)
S3method("names<-",reactivevalues)
S3method(as.list,reactivevalues)
@@ -23,7 +32,11 @@ S3method(names,reactivevalues)
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)
@@ -33,36 +46,53 @@ export(addResourcePath)
export(animationOptions)
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(createWebDependency)
export(dataTableOutput)
export(dateInput)
export(dateRangeInput)
export(dblclickOpts)
export(debounce)
export(dialogViewer)
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(freezeReactiveValue)
export(getDefaultReactiveDomain)
export(getShinyOption)
export(h1)
export(h2)
export(h3)
@@ -74,6 +104,7 @@ export(helpText)
export(hoverOpts)
export(hr)
export(htmlOutput)
export(htmlTemplate)
export(icon)
export(imageOutput)
export(img)
@@ -84,14 +115,17 @@ export(includeMarkdown)
export(includeScript)
export(includeText)
export(inputPanel)
export(insertUI)
export(installExprFunction)
export(invalidateLater)
export(is.reactive)
export(is.reactivevalues)
export(is.shiny.appobj)
export(is.singleton)
export(isTruthy)
export(isolate)
export(knit_print.html)
export(knit_print.reactive)
export(knit_print.shiny.appobj)
export(knit_print.shiny.render.function)
export(knit_print.shiny.tag)
@@ -100,23 +134,36 @@ export(mainPanel)
export(makeReactiveBinding)
export(markRenderFunction)
export(maskReactiveContext)
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(outputOptions)
export(p)
export(pageWithSidebar)
export(paneViewer)
export(parseQueryString)
export(passwordInput)
export(plotOutput)
export(plotPNG)
export(pre)
export(printError)
export(printStackTrace)
export(radioButtons)
export(reactive)
export(reactiveFileReader)
@@ -131,6 +178,9 @@ export(reactiveValues)
export(reactiveValuesToList)
export(registerInputHandler)
export(removeInputHandler)
export(removeModal)
export(removeNotification)
export(removeUI)
export(renderDataTable)
export(renderImage)
export(renderPlot)
@@ -139,19 +189,34 @@ 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(serveCSV)
export(serveJSON)
export(servePlot)
export(serveRaw)
export(serveText)
export(serverInfo)
export(setBookmarkExclude)
export(setProgress)
export(shinyApp)
export(shinyAppDir)
export(shinyAppFile)
export(shinyOptions)
export(shinyServer)
export(shinyUI)
export(showBookmarkUrlModal)
export(showModal)
export(showNotification)
export(showReactLog)
export(sidebarLayout)
export(sidebarPanel)
@@ -162,6 +227,7 @@ export(splitLayout)
export(stopApp)
export(strong)
export(submitButton)
export(suppressDependencies)
export(tabPanel)
export(tableOutput)
export(tabsetPanel)
@@ -172,26 +238,35 @@ 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(urlModal)
export(validate)
export(validateCssUnit)
export(verbatimTextOutput)
export(verticalLayout)
export(wellPanel)
export(withLogErrors)
export(withMathJax)
export(withProgress)
export(withReactiveDomain)
@@ -200,5 +275,7 @@ import(R6)
import(digest)
import(htmltools)
import(httpuv)
import(methods)
import(mime)
import(xtable)
importFrom(utils,write.csv)

839
NEWS
View File

@@ -1,839 +0,0 @@
shiny 0.12.1
--------------------------------------------------------------------------------
* Fixed an issue where unbindAll() causes subsequent bindAll() to be ignored for
previously bound outputs. (#856)
* Undeprecate `dataTableOutput` and `renderDataTable`, which had been deprecated
in favor of the new DT package. The DT package is a bit too new and has a
slightly different API, we were too hasty in deprecating the existing Shiny
functions.
shiny 0.12.0
--------------------------------------------------------------------------------
* Switched from RJSONIO to jsonlite. This improves consistency and speed when
converting between R data structures and JSON. One notable change is that
POSIXt objects are now serialized to JSON in UTC8601 format (like
"2015-03-20T20:00:00Z"), instead of as seconds from the epoch).
* In addition to the existing support for clicking and hovering on plots
created by base graphics, added support for double-clicking and brushing.
(#769)
* Added support for clicking, hovering, double-clicking, and brushing for
plots created by ggplot2, including support for facets. (#802)
* Added `nearPoints` and `brushedPoints` functions for easily selecting rows of
data that are clicked/hovered, or brushed. (#802)
* Added `shiny.port` option. If this is option is set, `runApp()` will listen on
this port by default. (#756)
* `runUrl`, `runGist`, and `runGitHub` now can save downloaded applications,
with the `destdir` argument. (#688)
* Restored ability to set labels for `selectInput`. (#741)
* Travis continuous integration now uses Travis's native R support.
* Fixed encoding issue when the server receives data from the client browser.
(#742)
* The `session` object now has class `ShinySession`, making it easier to test
whether an object is indeed a session object. (#720, #746)
* Fix JavaScript error when an output appears in nested uiOutputs. (Thanks,
Gregory Zhang. #749)
* Eliminate delay on receiving new value when `updateSliderInput(value=...)` is
called.
* Updated to DataTables (Javascript library) 1.10.5.
* Fixed downloading of files that have no filename extension. (#575, #753)
* Fixed bug where nested UI outputs broke outputs. (#749, #750)
* Removed unneeded HTML ID attributes for `checkboxGroupInputs` and
`radioButtons`. (#684)
* Fixed bug where checkboxes were still active even after `Shiny.unbindAll()`
was called. (#206)
* The server side selectize input will load the first 1000 options by default
before users start to type and search in the box. (#823)
* renderDataTable() and dataTableOutput() have been deprecated in shiny and will
be removed in future versions of shiny. Please use the DT package instead:
http://rstudio.github.io/DT/ (#807)
shiny 0.11.1
--------------------------------------------------------------------------------
* Major client-side performance improvements for pages that have many
conditionalPanels, tabPanels, and plotOutputs. (#693, #717, #723)
* `tabPanel`s now use the `title` for `value` by default. This fixes a bug
in which an icon in the title caused problems with a conditionalPanel's test
condition. (#725, #728)
* `selectInput` now has a `size` argument to control the height of the input
box. (#729)
* `navbarPage` no longer includes a first row of extra whitespace when
`header=NULL`. (#722)
* `selectInput`s now use Bootstrap styling when `selectize=FALSE`. (#724)
* Better vertical spacing of label for checkbox groups and radio buttons.
* `selectInput` correctly uses width for both selectize and non-selectize
inputs. (#702)
* The wrapper tag generated by `htmlOutput` and `uiOutput` can now be any type
of HTML tag, instead of just span and div. Also, custom classes are now
allowed on the tag. (#704)
* Slider problems in IE 11 and Chrome on touchscreen-equipped Windows computers
have been fixed. (#700)
* Sliders now work correctly with draggable panels. (#711)
* Fixed arguments in `fixedPanel`. (#709)
* downloadHandler content callback functions are now invoked with a temp file
name that has the same extension as the final filename that will be used by
the download. This is to deal with the fact that some file writing functions
in R will auto-append the extension for their file type (pdf, zip).
shiny 0.11
--------------------------------------------------------------------------------
* Changed sliders from jquery-slider to ion.rangeSlider. These sliders have
an improved appearance, support updating more properties from the server,
and can be controlled with keyboard input.
* Switched from Bootstrap 2 to Bootstrap 3. For most users, this will work
seamlessly, but some users may need to use the shinybootstrap2 package for
backward compatibility.
* The UI of a Shiny app can now have a body tag. This is useful for CSS
templates that use classes on the body tag.
* `actionButton` and `actionLink` now pass their `...` arguments to the
underlying tag function. (#607)
* Added `observeEvent` and `eventReactive` functions for clearer, more concise
handling of `actionButton`, plot clicks, and other naturally-imperative
inputs.
* Errors that happen in reactives no longer prevent any remaining pending
observers from executing. It is also now possible for users to control how
errors are handled, with the 'shiny.observer.error' global option. (#603,
#604)
* Added an `escape` argument to `renderDataTable()` to escape the HTML entities
in the data table for security reasons. This might break tables from previous
versions of shiny that use raw HTML in the table content, and the old behavior
can be brought back by `escape = FALSE` if you are aware of the security
implications. (#627)
* Changed the URI encoding/decoding functions internally to use `encodeURI()`,
`encodeURIComponent()`, and `decodeURIComponent()` from the httpuv package
instead of `utils::URLencode()` and `utils::URLdecode()`. (#630)
* Shiny's web assets are now minified.
* The default reactive domain is now available in event handler functions. (#669)
* Password input fields can now be used, with `passwordInput()`. (#672)
shiny 0.10.2.2
--------------------------------------------------------------------------------
* Remove use of `rstudio::viewer` in a code example, for R CMD check.
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)
* Updated selectize.js to version 0.11.2. (#596)
* Added `position` parameter to `navbarPage`.
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!

1098
NEWS.md Normal file

File diff suppressed because it is too large Load Diff

162
R/app.R
View File

@@ -20,19 +20,29 @@
#' @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.
#' @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
#' ## Only run this example in interactive R sessions
#' if (interactive()) {
#' options(device.ask.default = FALSE)
#'
#' shinyApp(
#' ui = fluidPage(
#' numericInput("n", "n", 1),
@@ -59,10 +69,9 @@
#'
#' 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")
}
@@ -76,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"
)
}
@@ -91,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, "\"")
}
@@ -102,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()) {
@@ -124,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]]
}
@@ -147,7 +178,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]]
}
@@ -170,15 +201,21 @@ 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() {
setwd(oldwd)
monitorHandle()
monitorHandle <<- NULL
}
structure(
@@ -187,26 +224,76 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
serverFuncSource = serverFuncSource,
onStart = onStart,
onEnd = onEnd,
options = options),
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)
}
)
@@ -225,12 +312,16 @@ shinyAppDir_appR <- function(appDir, options=list()) {
fallbackWWWDir <- system.file("www-dir", package = "shiny")
oldwd <- NULL
monitorHandle <- NULL
onStart <- function() {
oldwd <<- getwd()
setwd(appDir)
monitorHandle <<- initAutoReloadMonitor(appDir)
}
onEnd <- function() {
setwd(oldwd)
monitorHandle()
monitorHandle <<- NULL
}
structure(
@@ -268,7 +359,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
@@ -283,7 +377,8 @@ 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)
@@ -302,7 +397,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
@@ -353,8 +461,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
@@ -378,3 +485,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)
}

1097
R/bookmark-state.R Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -31,7 +31,11 @@
#' @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!"),
@@ -52,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,
@@ -64,8 +80,10 @@
#' "3 offset 2"
#' )
#' )
#' ))
#' )
#'
#' shinyApp(ui, server = function(input, output) { })
#' }
#' @rdname fluidPage
#' @export
fluidPage <- function(..., title = NULL, responsive = NULL, theme = NULL) {
@@ -115,7 +133,10 @@ fluidRow <- function(...) {
#' @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,
@@ -125,7 +146,10 @@ fluidRow <- function(...) {
#' "3 offset 2"
#' )
#' )
#' ))
#' )
#'
#' shinyApp(ui, server = function(input, output) { })
#' }
#'
#' @rdname fixedPage
#' @export
@@ -160,24 +184,43 @@ 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) {
@@ -202,8 +245,14 @@ 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(
@@ -226,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!"),
@@ -248,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,
@@ -286,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) {
@@ -319,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()) {
@@ -346,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",
@@ -369,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"),
@@ -391,6 +476,8 @@ inputPanel <- function(...) {
#' plotOutput("plot2"),
#' plotOutput("plot3")
#' )
#' shinyApp(ui, server)
#' }
#' @export
splitLayout <- function(..., cellWidths = NULL, cellArgs = list()) {
@@ -417,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[1:length(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

334
R/conditions.R Normal file
View File

@@ -0,0 +1,334 @@
#' 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))
}
#' @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) {
withCallingHandlers(expr,
error = function(e) {
if (is.null(attr(e, "stack.trace", exact = TRUE))) {
calls <- sys.calls()
attr(e, "stack.trace") <- calls
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(
captureStackTraces(expr),
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)
invisible()
}
#' @rdname stacktrace
#' @export
printStackTrace <- function(cond,
full = getOption("shiny.fullstacktrace", FALSE),
offset = getOption("shiny.stacktraceoffset", TRUE)) {
stackTrace <- attr(cond, "stack.trace", exact = TRUE)
tryCatch(
if (!is.null(stackTrace)) {
message(paste0(
"Stack trace (innermost first):\n",
paste0(collapse = "\n",
formatStackTrace(stackTrace, full = full, offset = offset,
indent = " ")
)
))
} else {
message("No stack trace available")
},
error = function(cond) {
warning("Failed to write stack trace: ", cond)
}
)
invisible()
}
#' @details \code{extractStackTrace} takes a list of calls (e.g. as returned
#' from \code{conditionStackTrace(cond)}) and returns a data frame with one
#' row for each stack frame and the columns \code{num} (stack frame number),
#' \code{call} (a function name or similar), and \code{loc} (source file path
#' and line number, if available).
#' @rdname stacktrace
#' @export
extractStackTrace <- function(calls,
full = getOption("shiny.fullstacktrace", FALSE),
offset = getOption("shiny.stacktraceoffset", TRUE)) {
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.."))
}
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),
stringsAsFactors = FALSE
)
}
#' @details \code{formatStackTrace} is similar to \code{extractStackTrace}, but
#' it returns a preformatted character vector instead of a data frame.
#' @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)) {
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),
": ",
st$call,
st$loc
)
}
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

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

@@ -81,33 +81,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)
}
)
)

View File

@@ -1,5 +1,11 @@
writeReactLog <- function(file=stdout()) {
cat(toJSON(.graphStack$as_list(), pretty=TRUE), file=file)
writeReactLog <- function(file=stdout(), sessionToken = NULL) {
log <- .graphStack$as_list()
if (!is.null(sessionToken)) {
log <- Filter(function(x) {
is.null(x$session) || identical(x$session, sessionToken)
}, log)
}
cat(toJSON(log, pretty=TRUE), file=file)
}
#' Reactive Log Visualizer
@@ -35,28 +41,36 @@ writeReactLog <- function(file=stdout()) {
#' enabled, it's possible for any user of your app to see at least some
#' of the source code of your reactive expressions and observers.
#'
#' @param time A boolean that specifies whether or not to display the
#' time that each reactive.
#' @export
showReactLog <- function() {
browseURL(renderReactLog())
showReactLog <- function(time = TRUE) {
utils::browseURL(renderReactLog(time = as.logical(time)))
}
renderReactLog <- function() {
renderReactLog <- function(sessionToken = NULL, time = TRUE) {
templateFile <- system.file('www/reactive-graph.html', package='shiny')
html <- paste(readLines(templateFile, warn=FALSE), collapse='\r\n')
tc <- textConnection(NULL, 'w')
on.exit(close(tc))
writeReactLog(tc)
writeReactLog(tc, sessionToken)
cat('\n', file=tc)
flush(tc)
html <- sub('__DATA__', paste(textConnectionValue(tc), collapse='\r\n'), html, fixed=TRUE)
html <- sub('__TIME__', paste0('"', time, '"'), html, fixed=TRUE)
file <- tempfile(fileext = '.html')
writeLines(html, file)
return(file)
}
.graphAppend <- function(logEntry, domain = getDefaultReactiveDomain()) {
if (isTRUE(getOption('shiny.reactlog')))
.graphStack$push(logEntry)
if (isTRUE(getOption('shiny.reactlog'))) {
sessionToken <- if (is.null(domain)) NULL else domain$token
.graphStack$push(c(logEntry, list(
session = sessionToken,
time = as.numeric(Sys.time())
)))
}
if (!is.null(domain)) {
domain$reactlog(logEntry)
@@ -74,7 +88,7 @@ renderReactLog <- function() {
.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"),
srcref=as.vector(attr(label, "srcref")), srcfile=attr(label, "srcfile"),
type=type, prevId=prevId
), domain = domain)
}
@@ -91,7 +105,7 @@ renderReactLog <- function() {
.graphAppend(list(
action = 'valueChange',
id = label,
value = paste(capture.output(str(value)), collapse='\n')
value = paste(utils::capture.output(utils::str(value)), collapse='\n')
))
}

View File

@@ -6,7 +6,7 @@
#' URL.
#'
#' @param dependency A single HTML dependency object, created using
#' \code{\link{htmlDependency}}. If the \code{src} value is named, then
#' \code{\link[htmltools]{htmlDependency}}. If the \code{src} value is named, then
#' \code{href} and/or \code{file} names must be present.
#'
#' @return A single HTML dependency object that has an \code{href}-named element
@@ -27,3 +27,21 @@ createWebDependency <- function(dependency) {
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

View File

@@ -91,7 +91,10 @@ hoverOpts <- function(id = NULL, delay = 300,
#' \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}.
#' 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

View File

@@ -21,11 +21,10 @@
#' @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, ...) {
@@ -33,12 +32,12 @@ plotPNG <- function(func, filename=tempfile(fileext='.png'),
# Otherwise, if the Cairo package is installed, use CairoPNG().
# Finally, if neither quartz nor Cairo, use png().
if (capabilities("aqua")) {
pngfun <- png
pngfun <- grDevices::png
} else if ((getOption('shiny.usecairo') %OR% TRUE) &&
nchar(system.file(package = "Cairo"))) {
pngfun <- Cairo::CairoPNG
} else {
pngfun <- png
pngfun <- grDevices::png
}
pngfun(filename=filename, width=width, height=height, res=res, ...)
@@ -49,10 +48,15 @@ plotPNG <- function(func, filename=tempfile(fileext='.png'),
# 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))
op <- graphics::par(mar = rep(0, 4))
tryCatch(
graphics::plot.new(),
finally = graphics::par(op)
)
dv <- grDevices::dev.cur()
on.exit(grDevices::dev.off(dv), add = TRUE)
func()
filename
}

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))
)
)
}

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

@@ -0,0 +1,61 @@
#' 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.
#' @param selected The values that should be initially selected, if any.
#' @param inline If \code{TRUE}, render the choices inline (i.e. horizontally)
#' @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) {
#' output$data <- renderTable({
#' mtcars[, c("mpg", input$variable), drop = FALSE]
#' }, rownames = TRUE)
#' }
#'
#' shinyApp(ui, server)
#' }
#' @export
checkboxGroupInput <- function(inputId, label, choices, selected = NULL,
inline = FALSE, width = NULL) {
selected <- restoreInput(id = inputId, default = selected)
# resolve names
choices <- choicesWithNames(choices)
if (!is.null(selected))
selected <- validateSelected(selected, choices, inputId)
options <- generateOptions(inputId, choices, selected, inline)
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
)
}

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

@@ -0,0 +1,119 @@
#' 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".
#'
#' @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")
#' )
#'
#' 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) {
# 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")
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
),
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>"
)

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

@@ -0,0 +1,124 @@
#' 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) {
# 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-weekstart` = weekstart,
`data-date-format` = format,
`data-date-start-view` = startview,
`data-min-date` = min,
`data-max-date` = max,
`data-initial-date` = start
),
span(class = "input-group-addon", separator),
tags$input(
class = "input-sm form-control",
type = "text",
`data-date-language` = language,
`data-date-weekstart` = weekstart,
`data-date-format` = format,
`data-date-start-view` = startview,
`data-min-date` = min,
`data-max-date` = max,
`data-initial-date` = end
)
)
),
datePickerDependency
)
}

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

@@ -0,0 +1,123 @@
#' 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.
#'
#' @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) {
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",
"Browse...",
inputTag
)
),
tags$input(type = "text", class = "form-control",
placeholder = "No file selected", 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)
)
}

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

@@ -0,0 +1,78 @@
#' 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)
#' @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.
#'
#' @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)
#' }
#' @export
radioButtons <- function(inputId, label, choices, selected = NULL,
inline = FALSE, width = NULL) {
# resolve names
choices <- choicesWithNames(choices)
selected <- restoreInput(id = inputId, default = selected)
# default value if it's not specified
selected <- if (is.null(selected)) choices[[1]] else {
validateSelected(selected, choices, inputId)
}
if (length(selected) > 1) stop("The 'selected' argument must be of length 1")
options <- generateOptions(inputId, choices, selected, inline, type = 'radio')
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
)
}

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

@@ -0,0 +1,214 @@
#' 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/brianreavis/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}.
#'
#' 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}}
#'
#' @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` = c("NY", "NJ", "CT"),
#' `West Coast` = c("WA", "OR", "CA"),
#' `Midwest` = c("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 <- validateSelected(selected, choices, inputId)
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)
}

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

@@ -0,0 +1,262 @@
#' 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")
}
value <- restoreInput(id = inputId, default = value)
# 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)) {
step <- pretty(c(min, max), n = 100)
step[2] - step[1]
} else {
1
}
}
if (inherits(min, "Date")) {
if (!inherits(max, "Date") || !inherits(value, "Date"))
stop("`min`, `max`, and `value must all be Date or non-Date objects")
dataType <- "date"
if (is.null(timeFormat))
timeFormat <- "%F"
} else if (inherits(min, "POSIXt")) {
if (!inherits(max, "POSIXt") || !inherits(value, "POSIXt"))
stop("`min`, `max`, and `value must all be POSIXt or non-POSIXt objects")
dataType <- "datetime"
if (is.null(timeFormat))
timeFormat <- "%F %T"
} else {
dataType <- "number"
}
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-prefix` = pre,
`data-postfix` = post,
`data-keyboard` = TRUE,
`data-keyboard-step` = step / (max - min) * 100,
`data-drag-interval` = 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
))
if (sep == "") {
sliderProps$`data-prettify-enabled` <- "0"
} else {
sliderProps$`data-prettify-separator` <- sep
}
# 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.2", 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))
}
#' @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
)
)
}

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

@@ -0,0 +1,108 @@
controlLabel <- function(controlName, label) {
label %AND% tags$label(class = "control-label", `for` = controlName, label)
}
# Before shiny 0.9, `selected` refers to names/labels of `choices`; now it
# refers to values. Below is a function for backward compatibility. It also
# coerces the value to `character`.
validateSelected <- function(selected, choices, inputId) {
# this line accomplishes two tings:
# - coerces selected to character
# - drops name, otherwise toJSON() keeps it too
selected <- as.character(selected)
# if you are using optgroups, you're using shiny > 0.10.0, and you should
# already know that `selected` must be a value instead of a label
if (needOptgroup(choices)) return(selected)
if (is.list(choices)) choices <- unlist(choices)
nms <- names(choices)
# labels and values are identical, no need to validate
if (identical(nms, unname(choices))) return(selected)
# when selected labels instead of values
i <- (selected %in% nms) & !(selected %in% choices)
if (any(i)) {
warnFun <- if (all(i)) {
# replace names with values
selected <- unname(choices[selected])
warning
} else stop # stop when it is ambiguous (some labels == values)
warnFun("'selected' must be the values instead of names of 'choices' ",
"for the input '", inputId, "'")
}
selected
}
# generate options for radio buttons and checkbox groups (type = 'checkbox' or
# 'radio')
generateOptions <- function(inputId, choices, selected, inline, type = 'checkbox') {
# generate a list of <input type=? [checked] />
options <- mapply(
choices, names(choices),
FUN = function(value, name) {
inputTag <- tags$input(
type = type, name = inputId, value = value
)
if (value %in% selected)
inputTag$attribs$checked <- "checked"
# If inline, there's no wrapper div, and the label needs a class like
# checkbox-inline.
if (inline) {
tags$label(class = paste0(type, "-inline"), inputTag, tags$span(name))
} else {
tags$div(class = type,
tags$label(inputTag, tags$span(name))
)
}
},
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
}

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();')
))

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

@@ -9,9 +9,11 @@ reactLogHandler <- function(req) {
return(NULL)
}
sessionToken <- parseQueryString(req$QUERY_STRING)$s
return(httpResponse(
status=200,
content=list(file=renderReactLog(), owned=TRUE)
content=list(file=renderReactLog(sessionToken), owned=TRUE)
))
}
@@ -40,34 +42,228 @@ sessionHandler <- function(req) {
})
}
dynamicHandler <- function(filePath, dependencyFiles=filePath) {
lastKnownTimestamps <- NA
metaHandler <- function(req) NULL
apiHandler <- function(serverFuncSource) {
function(req) {
path <- req$PATH_INFO
if (is.null(path))
return(NULL)
if (!file.exists(filePath))
return(metaHandler)
matches <- regmatches(path, regexec('^/api/(.*)$', path))
if (length(matches[[1]]) == 0)
return(NULL)
cacheContext <- CacheContext$new()
apiName <- matches[[1]][2]
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()
sharedSecret <- getOption('shiny.sharedSecret')
if (!is.null(sharedSecret)
&& !identical(sharedSecret, req$HTTP_SHINY_SHARED_SECRET)) {
stop("Incorrect shared secret")
}
return(metaHandler(req))
})
if (!is.null(getOption("shiny.observer.error", NULL))) {
warning(
call. = FALSE,
"options(shiny.observer.error) is no longer supported; please unset it!"
)
stopApp()
}
# need to give a fake websocket to the session
ws <- list(
request = req,
sendMessage = function(...) {
#print(list(...))
}
)
# Accept JSON query string and/or JSON body as input values
inputVals <- c(
parseQueryStringJSON(req$QUERY_STRING),
parseJSONBody(req)
)
shinysession <- ShinySession$new(ws)
on.exit({
try({
# Clean up the session. Very important, so that observers
# and such don't hang around, and to let memory get gc'd.
shinysession$wsClosed()
appsByToken$remove(shinysession$token)
})
}, add = TRUE)
appsByToken$set(shinysession$token, shinysession)
shinysession$setShowcase(.globals$showcaseDefault)
serverFunc <- withReactiveDomain(NULL, serverFuncSource())
tryCatch({
withReactiveDomain(shinysession, {
shinysession$manageInputs(inputVals)
do.call(serverFunc, argsForServerFunc(serverFunc, shinysession))
result <- NULL
shinysession$enableApi(apiName, function(value) {
result <<- try(withLogErrors(value), silent = TRUE)
})
flushReact()
resultToResponse(result)
})
}, error = function(e) {
return(httpResponse(
status=500,
content=htmlEscape(conditionMessage(e))
))
})
}
}
apiWsHandler <- function(serverFuncSource) {
function(ws) {
path <- ws$request$PATH_INFO
if (is.null(path))
return(NULL)
matches <- regmatches(path, regexec('^/api/(.*)$', path))
if (length(matches[[1]]) == 0)
return(NULL)
apiName <- matches[[1]][2]
sharedSecret <- getOption('shiny.sharedSecret')
if (!is.null(sharedSecret)
&& !identical(sharedSecret, ws$request$HTTP_SHINY_SHARED_SECRET)) {
ws$close()
return(TRUE)
}
if (!is.null(getOption("shiny.observer.error", NULL))) {
warning(
call. = FALSE,
"options(shiny.observer.error) is no longer supported; please unset it!"
)
stopApp()
}
inputVals <- parseQueryStringJSON(ws$request$QUERY_STRING)
# Give a fake websocket to suppress messages from session
shinysession <- ShinySession$new(list(
request = ws$request,
sendMessage = function(...) {
#print(list(...))
}
))
appsByToken$set(shinysession$token, shinysession)
shinysession$setShowcase(.globals$showcaseDefault)
serverFunc <- withReactiveDomain(NULL, serverFuncSource())
tryCatch({
withReactiveDomain(shinysession, {
shinysession$manageInputs(inputVals)
do.call(serverFunc, argsForServerFunc(serverFunc, shinysession))
shinysession$enableApi(apiName, function(value) {
resp <- resultToResponse(value)
if (resp$status != 200L) {
warning("Error: ", responseToContent(resp))
ws$close()
} else {
content <- responseToContent(resp)
if (grepl("^image/", resp$content_type)) {
content <- paste0("data:", resp$content_type, ";base64,",
httpuv::rawToBase64(content))
}
try(ws$send(content), silent=TRUE)
}
})
flushReact()
})
}, error = function(e) {
ws$close()
})
ws$onClose(function() {
# Clean up the session. Very important, so that observers
# and such don't hang around, and to let memory get gc'd.
shinysession$wsClosed()
appsByToken$remove(shinysession$token)
})
# TODO: What to do on ws$onMessage?
}
}
parseJSONBody <- function(req) {
if (identical(req[["REQUEST_METHOD"]], "POST")) {
if (isTRUE(grepl(perl=TRUE, "^(text|application)/json(;\\s*charset\\s*=\\s*utf-8)?$", req[["HTTP_CONTENT_TYPE"]]))) {
tmp <- file("", "w+b")
on.exit(close(tmp))
input_file <- req[["rook.input"]]
while (TRUE) {
chunk <- input_file$read(8192L)
if (length(chunk) == 0)
break
writeBin(chunk, tmp)
}
return(jsonlite::fromJSON(tmp))
}
if (is.null(req[["HTTP_CONTENT_TYPE"]])) {
if (!is.null(req[["rook.input"]]) && length(req[["rook.input"]]$read(1L)) > 0) {
stop("Invalid POST request (body provided without content type)")
}
return()
}
stop("Invalid POST request (content type not supported)")
}
}
resultToResponse <- function(result) {
if (inherits(result, "httpResponse")) {
return(result)
} else if (inherits(result, "try-error")) {
return(httpResponse(
status=500,
content_type="text/plain",
content=conditionMessage(attr(result, "condition"))
))
} else if (!is.null(attr(result, "content.type"))) {
return(httpResponse(
status=200L,
content_type=attr(result, "content.type"),
content=result
))
} else {
return(httpResponse(
status=200L,
content_type="application/json",
content=toJSON(result, pretty=TRUE)
))
}
}
responseToContent <- function(result) {
ct <- result$content_type
textMode <- grepl("^text/", ct) || ct == "application/json" ||
grepl("^application/xml($|\\+)", ct)
# TODO: Make sure text is UTF-8
if ("file" %in% names(result$content)) {
filename <- result$content$file
if ("owned" %in% names(result$content) && result$content$owned) {
on.exit(unlink(filename), add = TRUE)
}
if (textMode)
return(paste(readLines(filename), collapse = "\n"))
else
return(readBin(filename, raw(), file.info(filename)$size))
} else {
if (textMode)
return(paste(result$content, collapse = "\n"))
else
return(result$content)
}
}

View File

@@ -299,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 {
@@ -310,7 +308,18 @@ 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')
),
@@ -332,6 +341,15 @@ HandlerManager <- R6Class("HandlerManager",
headers=list('Content-Type' = 'text/html')))
}
# 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)
if (is.null(response))
response <- httpResponse(404, content="<h1>Not Found</h1>")
@@ -341,9 +359,21 @@ HandlerManager <- R6Class("HandlerManager",
headers$'Content-Type' <- response$content_type
response <- filter(req, response)
return(list(status=response$status,
body=response$content,
headers=headers))
if (head_request) {
headers$`Content-Length` <- nchar(response$content, type = "bytes")
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)

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
)
}

62
R/modules.R Normal file
View File

@@ -0,0 +1,62 @@
# 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) {
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)}}{
@@ -48,6 +56,10 @@
#' @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 +67,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 +88,9 @@
#' }
#' plot(cars)
#' })
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @seealso \code{\link{withProgress}}
#' @format NULL
@@ -82,18 +101,22 @@ Progress <- R6Class(
portable = TRUE,
public = list(
initialize = function(session = getDefaultReactiveDomain(), min = 0, max = 1) {
if (!inherits(session, "ShinySession"))
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$style <- match.arg(style, choices = c("notification", "old"))
private$value <- NULL
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) {
@@ -115,7 +138,8 @@ Progress <- R6Class(
id = private$id,
message = message,
detail = detail,
value = value
value = value,
style = private$style
))
private$session$sendProgress('update', data)
@@ -141,7 +165,9 @@ 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
}
),
@@ -151,6 +177,7 @@ Progress <- R6Class(
id = character(0),
min = numeric(0),
max = numeric(0),
style = character(0),
value = NULL,
closed = logical(0)
)
@@ -179,6 +206,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.
@@ -199,14 +234,24 @@ 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.
#'
#' @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, {
@@ -217,24 +262,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)
if (!inherits(session, "ShinySession"))
if (is.null(session$progressStack))
stop("'session' is not a ShinySession object.")
p <- Progress$new(session, min = min, max = max)
style <- match.arg(style, c("notification", "old"))
p <- Progress$new(session, min = min, max = max, style = style)
session$progressStack$push(p)
on.exit({
@@ -252,7 +303,7 @@ withProgress <- function(expr, min = 0, max = 1,
setProgress <- function(value = NULL, message = NULL, detail = NULL,
session = getDefaultReactiveDomain()) {
if (!inherits(session, "ShinySession"))
if (is.null(session$progressStack))
stop("'session' is not a ShinySession object.")
if (session$progressStack$size() == 0) {
@@ -269,7 +320,7 @@ setProgress <- function(value = NULL, message = NULL, detail = NULL,
incProgress <- function(amount = 0.1, message = NULL, detail = NULL,
session = getDefaultReactiveDomain()) {
if (!inherits(session, "ShinySession"))
if (is.null(session$progressStack))
stop("'session' is not a ShinySession object.")
if (session$progressStack$size() == 0) {

View File

@@ -21,10 +21,8 @@ Context <- R6Class(
withReactiveDomain(.domain, {
env <- .getReactiveEnvironment()
.graphEnterContext(id)
tryCatch(
env$runWith(self, func),
finally = .graphExitContext(id)
)
on.exit(.graphExitContext(id), add = TRUE)
env$runWith(self, func)
})
},
invalidate = function() {
@@ -54,6 +52,9 @@ Context <- R6Class(
addPendingFlush = function(priority) {
"Tell the reactive environment that this context should be flushed the
next time flushReact() called."
if (!is.null(.domain)) {
.domain$incrementBusyCount()
}
.getReactiveEnvironment()$addPendingFlush(self, priority)
},
onFlush = function(func) {
@@ -62,8 +63,15 @@ Context <- R6Class(
},
executeFlushCallbacks = function() {
"For internal use only."
lapply(.flushCallbacks, function(func) {
func()
on.exit({
if (!is.null(.domain)) {
.domain$decrementBusyCount()
}
}, add = TRUE)
lapply(.flushCallbacks, function(flushCallback) {
flushCallback()
})
}
)
@@ -98,22 +106,25 @@ 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())
},
flush = function() {
# If already in a flush, don't start another one
if (.inFlush) return()
.inFlush <<- TRUE
on.exit(.inFlush <<- FALSE)
while (!.pendingFlush$isEmpty()) {
while (hasPendingFlush()) {
ctx <- .pendingFlush$dequeue()
ctx$executeFlushCallbacks()
}

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)
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,15 @@
#' 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}}.
@@ -19,36 +28,233 @@
#' 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
#' 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 func A function that generates a plot (deprecated; use \code{expr}
#' instead).
#'
#' @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, func=NULL) {
if (!is.null(func)) {
shinyDeprecated(msg="renderPlot: argument 'func' is deprecated. Please use 'expr' instead.")
} else {
installExprFunction(expr, "func", env, quoted)
}
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.function(width))
widthWrapper <- reactive({ width() })
else
widthWrapper <- NULL
widthWrapper <- function() { width }
if (is.function(height))
heightWrapper <- reactive({ height() })
else
heightWrapper <- NULL
heightWrapper <- function() { height }
# 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 <- 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")
}
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
# This function is the one that's returned from renderPlot(), and gets
# wrapped in an observer when the output value is assigned. The expression
# passed to renderPlot() is actually run in plotObj(); this function can only
# replay a plot if the width/height changes.
renderFunc <- function(shinysession, name, ...) {
session <<- shinysession
outputName <<- name
dims <- getDims()
if (is.null(dims$width) || is.null(dims$height) ||
dims$width <= 0 || dims$height <= 0) {
return(NULL)
}
# The reactive that runs the expr in renderPlot()
plotData <- plotObj()
img <- plotData$img
# If only the width/height have changed, simply replay the plot and make a
# new img.
if (dims$width != img$width || dims$height != img$height) {
pixelratio <- session$clientData$pixelratio %OR% 1
coordmap <- NULL
plotFunc <- function() {
..stacktraceon..(grDevices::replayPlot(plotData$recordedPlot))
# Coordmap must be recalculated after replaying plot, because pixel
# dimensions will have changed.
if (inherits(plotData$plotResult, "ggplot_build_gtable")) {
coordmap <<- getGgplotCoordmap(plotData$plotResult, pixelratio, res)
} else {
coordmap <<- getPrevPlotCoordmap(dims$width, dims$height)
}
}
outfile <- ..stacktraceoff..(
plotPNG(plotFunc, width = dims$width*pixelratio, height = dims$height*pixelratio,
res = res*pixelratio)
)
on.exit(unlink(outfile))
img <- dropNulls(list(
src = session$fileUrl(name, outfile, contentType='image/png'),
width = dims$width,
height = dims$height,
coordmap = coordmap,
# Get coordmap error message if present
error = attr(coordmap, "error", exact = TRUE)
))
}
img
}
plotObj <- reactive(label = "plotObj", {
if (execOnResize) {
dims <- getDims()
} else {
isolate({ dims <- getDims() })
}
if (is.null(dims$width) || is.null(dims$height) ||
dims$width <= 0 || dims$height <= 0) {
return(NULL)
}
# Resolution multiplier
pixelratio <- session$clientData$pixelratio %OR% 1
plotResult <- NULL
recordedPlot <- NULL
coordmap <- NULL
plotFunc <- function() {
success <-FALSE
tryCatch(
{
# This is necessary to enable displaylist recording
grDevices::dev.control(displaylist = "enable")
# Actually perform the plotting
result <- withVisible(func())
success <- TRUE
},
finally = {
if (!success) {
# If there was an error in making the plot, there's a good chance
# it's "Error in plot.new: figure margins too large". We need to
# take a reactive dependency on the width and height, so that the
# user's plotting code will re-execute when the plot is resized,
# instead of just replaying the previous plot (which errored).
getDims()
}
}
)
if (result$visible) {
# 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.
plotResult <<- ..stacktraceon..(print(result$value))
})
}
recordedPlot <<- grDevices::recordPlot()
if (inherits(plotResult, "ggplot_build_gtable")) {
coordmap <<- getGgplotCoordmap(plotResult, pixelratio, res)
} else {
coordmap <<- getPrevPlotCoordmap(dims$width, dims$height)
}
}
# This ..stacktraceoff.. is matched by the `func` function's
# wrapFunctionLabel(..stacktraceon=TRUE) call near the beginning of
# renderPlot, and by the ..stacktraceon.. in plotFunc where ggplot objects
# are printed
outfile <- ..stacktraceoff..(
do.call(plotPNG, c(plotFunc, width=dims$width*pixelratio,
height=dims$height*pixelratio, res=res*pixelratio, args))
)
on.exit(unlink(outfile))
list(
# img is the content that gets sent to the client.
img = dropNulls(list(
src = session$fileUrl(outputName, outfile, contentType='image/png'),
width = dims$width,
height = dims$height,
coordmap = coordmap,
# Get coordmap error message if present.
error = attr(coordmap, "error", exact = TRUE)
)),
# Returned value from expression in renderPlot() -- may be a printable
# object like ggplot2. Needed just in case we replayPlot and need to get
# a coordmap again.
plotResult = plotResult,
recordedPlot = recordedPlot
)
})
# 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
@@ -57,71 +263,7 @@ renderPlot <- function(expr, width='auto', height='auto', res=72, ...,
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())
coordmap <<- NULL
if (result$visible) {
# Use capture.output to squelch printing to the actual console; we
# are only interested in plot output
# Special case for ggplot objects - need to capture coordmap
if (inherits(result$value, "ggplot")) {
capture.output(coordmap <<- getGgplotCoordmap(result$value, pixelratio))
} else {
capture.output(print(result$value))
}
}
if (is.null(coordmap)) {
coordmap <<- getPrevPlotCoordmap(width, height)
}
}
outfile <- do.call(plotPNG, c(plotFunc, width=width*pixelratio,
height=height*pixelratio, res=res*pixelratio, args))
on.exit(unlink(outfile))
# A list of attributes for the img
res <- list(
src=shinysession$fileUrl(name, outfile, contentType='image/png'),
width=width, height=height, coordmap=coordmap
)
# Get error message if present (from attribute on the coordmap)
error <- attr(coordmap, "error", exact = TRUE)
if (!is.null(error)) {
res$error <- error
}
res
}))
markRenderFunction(outputFunc, renderFunc, outputArgs = outputArgs)
}
# The coordmap extraction functions below return something like the examples
@@ -237,12 +379,12 @@ renderPlot <- function(expr, width='auto', height='auto', res=72, ...,
# Requires width and height of output image, in pixels.
# Must be called before the graphics device is closed.
getPrevPlotCoordmap <- function(width, height) {
usrCoords <- par('usr')
usrCoords <- graphics::par('usr')
usrBounds <- usrCoords
if (par('xlog')) {
if (graphics::par('xlog')) {
usrBounds[c(1,2)] <- 10 ^ usrBounds[c(1,2)]
}
if (par('ylog')) {
if (graphics::par('ylog')) {
usrBounds[c(3,4)] <- 10 ^ usrBounds[c(3,4)]
}
@@ -257,14 +399,14 @@ getPrevPlotCoordmap <- function(width, height) {
),
# The bounds of the plot area, in DOM pixels
range = list(
left = grconvertX(usrBounds[1], 'user', 'nfc') * width,
right = grconvertX(usrBounds[2], 'user', 'nfc') * width,
bottom = (1-grconvertY(usrBounds[3], 'user', 'nfc')) * height - 1,
top = (1-grconvertY(usrBounds[4], 'user', 'nfc')) * height - 1
left = graphics::grconvertX(usrBounds[1], 'user', 'nfc') * width,
right = graphics::grconvertX(usrBounds[2], 'user', 'nfc') * width,
bottom = (1-graphics::grconvertY(usrBounds[3], 'user', 'nfc')) * height - 1,
top = (1-graphics::grconvertY(usrBounds[4], 'user', 'nfc')) * height - 1
),
log = list(
x = if (par('xlog')) 10 else NULL,
y = if (par('ylog')) 10 else NULL
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
@@ -273,41 +415,42 @@ getPrevPlotCoordmap <- function(width, height) {
))
}
# Print a ggplot object and return a coordmap for it.
getGgplotCoordmap <- function(p, pixelratio) {
if (!inherits(p, "ggplot"))
# Given a ggplot_build_gtable object, return a coordmap for it.
getGgplotCoordmap <- function(p, pixelratio, res) {
# Structure of ggplot objects changed after 2.1.0
new_ggplot <- (utils::packageVersion("ggplot2") > "2.1.0")
if (!inherits(p, "ggplot_build_gtable"))
return(NULL)
# A modified version of print.ggplot which returns the built ggplot object
# as well as the gtable grob.
print_ggplot <- function(x) {
grid::grid.newpage()
build <- ggplot2::ggplot_build(x)
gtable <- ggplot2::ggplot_gtable(build)
grid::grid.draw(gtable)
list(
build = build,
gtable = gtable
)
}
# Given a built ggplot object, return x and y domains (data space coords) for
# each panel.
find_panel_info <- function(b) {
layout <- b$panel$layout
if (new_ggplot) {
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 <- b$plot$facet
facet_vars <- NULL
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))
if (new_ggplot) {
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
@@ -349,8 +492,12 @@ getGgplotCoordmap <- function(p, pixelratio) {
# 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) {
range <- b$panel$ranges[[panel_num]]
res <- list(
if (new_ggplot) {
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],
@@ -358,19 +505,23 @@ getGgplotCoordmap <- function(p, pixelratio) {
)
# Check for reversed scales
xscale <- b$panel$x_scales[[scalex_num]]
yscale <- b$panel$y_scales[[scaley_num]]
if (new_ggplot) {
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") {
res$left <- -res$left
res$right <- -res$right
domain$left <- -domain$left
domain$right <- -domain$right
}
if (!is.null(yscale$trans) && yscale$trans$name == "reverse") {
res$top <- -res$top
res$bottom <- -res$bottom
domain$top <- -domain$top
domain$bottom <- -domain$bottom
}
res
domain
}
# Given built ggplot object, return object with the log base for x and y if
@@ -395,10 +546,18 @@ getGgplotCoordmap <- function(p, pixelratio) {
y_names <- character(0)
# Continuous scales have a trans; discrete ones don't
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
if (new_ggplot) {
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)) {
@@ -452,6 +611,11 @@ getGgplotCoordmap <- function(p, pixelratio) {
)
}
# Look for CoordFlip
if (inherits(b$plot$coordinates, "CoordFlip")) {
mappings[c("x", "y")] <- mappings[c("y", "x")]
}
mappings_cache <<- mappings
mappings
}
@@ -462,14 +626,38 @@ getGgplotCoordmap <- function(p, pixelratio) {
# 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) {
vapply(x, FUN.VALUE = logical(1), function(u) {
isTRUE(attr(u, "unit", exact = TRUE) == "null")
})
# 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) as.numeric(grid::convertHeight(x, "native"))
w_px <- function(x) as.numeric(grid::convertWidth(x, "native"))
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 numeric vector of
@@ -519,7 +707,7 @@ getGgplotCoordmap <- function(p, pixelratio) {
# the image has double size. In the latter case we don't have to scale the
# numbers down.
pix_ratio <- 1
if (!grepl("^quartz", names(dev.cur()))) {
if (!grepl("^quartz", names(grDevices::dev.cur()))) {
pix_ratio <- pixelratio
}
@@ -537,16 +725,14 @@ getGgplotCoordmap <- function(p, pixelratio) {
}
res <- print_ggplot(p)
tryCatch({
# Get info from built ggplot object
info <- find_panel_info(res$build)
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(res$gtable, pixelratio)
ranges <- find_panel_ranges(p$gtable, pixelratio)
for (i in seq_along(info)) {
info[[i]]$range <- ranges[[i]]

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

@@ -0,0 +1,223 @@
#' 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)
renderFunc <- function(shinysession, 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 <- func()
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), ";'"))
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)
}
# Main render function
markRenderFunction(tableOutput, renderFunc, outputArgs = outputArgs)
}

View File

@@ -22,7 +22,7 @@
#' @export
#' @examples
#' ## Only run this example in interactive R sessions
#' if (interactive()) {
#' if (interactive()) {
#' runUrl('https://github.com/rstudio/shiny_example/archive/master.tar.gz')
#'
#' # Can run an app from a subdirectory in the archive
@@ -71,8 +71,8 @@ runUrl <- function(url, filetype = NULL, subdir = NULL, destdir = 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)){
@@ -80,7 +80,7 @@ runUrl <- function(url, filetype = NULL, subdir = NULL, destdir = NULL, ...) {
}
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, ...)
@@ -112,7 +112,7 @@ runGist <- function(gist, destdir = NULL, ...) {
stop('Unrecognized gist identifier format')
}
runUrl(gistUrl, filetype = ".tar.gz", destdir = destdir, ...)
runUrl(gistUrl, filetype = ".zip", destdir = destdir, ...)
}

72
R/serializers.R Normal file
View File

@@ -0,0 +1,72 @@
# 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 <- impl$getMeta(name, "shiny.serializer")
if (is.null(serializer))
serializer <- serializerDefault
# Apply serializer function.
serializer(val, stateDir)
})
# Filter out any values that were marked as unserializable.
vals <- Filter(Negate(isUnserializable), vals)
vals
}

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

@@ -0,0 +1,220 @@
# 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
.subset2(shinysession$input, "impl")$setMeta(name, "shiny.serializer", 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`.
.subset2(shinysession$input, "impl")$setMeta(name, "shiny.serializer", serializerFileInput)
val
})

View File

@@ -1,110 +1,7 @@
#' @include globals.R
#' @include server-input-handlers.R
appsByToken <- Map$new()
# 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)
}
# 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.date", function(val, ...){
# First replace NULLs with NA, then convert to Date vector
datelist <- ifelse(lapply(val, is.null), NA, val)
as.Date(unlist(datelist))
})
registerInputHandler("shiny.action", function(val, ...) {
# mark up the action button value with a special class so we can recognize it later
class(val) <- c(class(val), "shinyActionButtonValue")
val
})
# Provide a character representation of the WS that can be used
# as a key in a Map.
wsToKey <- function(WS) {
@@ -137,7 +34,7 @@ registerClient <- function(client) {
#' JavaScript/CSS files available to their components.
#'
#' @param prefix The URL prefix (without slashes). Valid characters are a-z,
#' A-Z, 0-9, hyphen, period, and underscore; and must begin with a-z or A-Z.
#' A-Z, 0-9, hyphen, period, and underscore.
#' For example, a value of 'foo' means that any request paths that begin with
#' '/foo' will be mapped to the given directory.
#' @param directoryPath The directory that contains the static resources to be
@@ -152,11 +49,10 @@ registerClient <- function(client) {
#'
#' @examples
#' addResourcePath('datasets', system.file('data', package='datasets'))
#'
#' @export
addResourcePath <- function(prefix, directoryPath) {
prefix <- prefix[1]
if (!grepl('^[a-z][a-z0-9\\-_.]*$', prefix, ignore.case=TRUE, perl=TRUE)) {
if (!grepl('^[a-z0-9\\-_][a-z0-9\\-_.]*$', prefix, ignore.case=TRUE, perl=TRUE)) {
stop("addResourcePath called with invalid prefix; please see documentation")
}
@@ -244,7 +140,6 @@ resourcePathHandler <- function(req) {
#' })
#' }
#' }
#'
#' @export
shinyServer <- function(func) {
.globals$server <- list(func)
@@ -294,6 +189,7 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
appHandlers <- list(
http = joinHandlers(c(
sessionHandler,
apiHandler(serverFuncSource),
httpHandlers,
sys.www.root,
resourcePathHandler,
@@ -305,146 +201,147 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
return(TRUE)
}
if (grepl("^/api/", ws$request$PATH_INFO)) {
apiWsHandler(serverFuncSource)(ws)
return(TRUE)
}
if (!is.null(getOption("shiny.observer.error", NULL))) {
warning(
call. = FALSE,
"options(shiny.observer.error) is no longer supported; please unset it!"
)
stopApp()
}
shinysession <- ShinySession$new(ws)
appsByToken$set(shinysession$token, shinysession)
shinysession$setShowcase(.globals$showcaseDefault)
ws$onMessage(function(binary, msg) {
# To ease transition from websockets-based code. Should remove once we're stable.
if (is.character(msg))
msg <- charToRaw(msg)
messageHandler <- function(binary, msg) {
withReactiveDomain(shinysession, {
# To ease transition from websockets-based code. Should remove once we're stable.
if (is.character(msg))
msg <- charToRaw(msg)
if (isTRUE(getOption('shiny.trace'))) {
if (binary)
message("RECV ", '$$binary data$$')
else
message("RECV ", rawToChar(msg))
}
traceOption <- getOption('shiny.trace', FALSE)
if (isTRUE(traceOption) || traceOption == "recv") {
if (binary)
message("RECV ", '$$binary data$$')
else
message("RECV ", rawToChar(msg))
}
if (identical(charToRaw("\003\xe9"), msg))
return()
if (identical(charToRaw("\003\xe9"), msg))
return()
msg <- decodeMessage(msg)
msg <- decodeMessage(msg)
# Do our own list simplifying here. sapply/simplify2array give names to
# character vectors, which is rarely what we want.
if (!is.null(msg$data)) {
for (name in names(msg$data)) {
val <- msg$data[[name]]
splitName <- strsplit(name, ':')[[1]]
if (length(splitName) > 1) {
msg$data[[name]] <- NULL
if (!inputHandlers$containsKey(splitName[[2]])){
# No input handler registered for this type
stop("No handler registered for for type ", name)
}
msg$data[[ splitName[[1]] ]] <-
inputHandlers$get(splitName[[2]])(
val,
shinysession,
splitName[[1]] )
}
else if (is.list(val) && is.null(names(val))) {
val_flat <- unlist(val, recursive = TRUE)
if (is.null(val_flat)) {
# This is to assign NULL instead of deleting the item
msg$data[name] <- list(NULL)
} else {
msg$data[[name]] <- val_flat
}
# Set up a restore context from .clientdata_url_search before
# handling all the input values, because the restore context may be
# used by an input handler (like the one for "shiny.file"). This
# should only happen once, when the app starts.
if (is.null(shinysession$restoreContext)) {
bookmarkStore <- getShinyOption("bookmarkStore", default = "disable")
if (bookmarkStore == "disable") {
# If bookmarking is disabled, use empty context
shinysession$restoreContext <- RestoreContext$new()
} else {
# If there's bookmarked state, save it on the session object
shinysession$restoreContext <- RestoreContext$new(msg$data$.clientdata_url_search)
}
}
}
switch(
msg$method,
init = {
withRestoreContext(shinysession$restoreContext, {
serverFunc <- serverFuncSource()
if (!identicalFunctionBodies(serverFunc, appvars$server)) {
appvars$server <- serverFunc
if (!is.null(appvars$server))
{
# Tag this function as the Shiny server function. A debugger may use this
# tag to give this function special treatment.
# It's very important that it's appvars$server itself and NOT a copy that
# is invoked, otherwise new breakpoints won't be picked up.
attr(appvars$server, "shinyServerFunction") <- TRUE
registerDebugHook("server", appvars, "Server Function")
}
msg$data <- applyInputHandlers(msg$data)
switch(
msg$method,
init = {
serverFunc <- withReactiveDomain(NULL, serverFuncSource())
if (!identicalFunctionBodies(serverFunc, appvars$server)) {
appvars$server <- serverFunc
if (!is.null(appvars$server))
{
# Tag this function as the Shiny server function. A debugger may use this
# tag to give this function special treatment.
# It's very important that it's appvars$server itself and NOT a copy that
# is invoked, otherwise new breakpoints won't be picked up.
attr(appvars$server, "shinyServerFunction") <- TRUE
registerDebugHook("server", appvars, "Server Function")
}
}
# Check for switching into/out of showcase mode
if (.globals$showcaseOverride &&
exists(".clientdata_url_search", where = msg$data)) {
mode <- showcaseModeOfQuerystring(msg$data$.clientdata_url_search)
if (!is.null(mode))
shinysession$setShowcase(mode)
}
shinysession$manageInputs(msg$data)
# The client tells us what singletons were rendered into
# the initial page
if (!is.null(msg$data$.clientdata_singletons)) {
shinysession$singletons <- strsplit(
msg$data$.clientdata_singletons, ',')[[1]]
}
local({
args <- argsForServerFunc(serverFunc, shinysession)
withReactiveDomain(shinysession, {
do.call(
# No corresponding ..stacktraceoff; the server func is pure
# user code
wrapFunctionLabel(appvars$server, "server",
..stacktraceon = TRUE
),
args
)
})
})
},
update = {
shinysession$manageInputs(msg$data)
},
shinysession$dispatch(msg)
)
shinysession$manageHiddenOutputs()
if (exists(".shiny__stdout", globalenv()) &&
exists("HTTP_GUID", ws$request)) {
# safe to assume we're in shiny-server
shiny_stdout <- get(".shiny__stdout", globalenv())
# eNter a flushReact
writeLines(paste("_n_flushReact ", get("HTTP_GUID", ws$request),
" @ ", sprintf("%.3f", as.numeric(Sys.time())),
sep=""), con=shiny_stdout)
flush(shiny_stdout)
flushReact()
# eXit a flushReact
writeLines(paste("_x_flushReact ", get("HTTP_GUID", ws$request),
" @ ", sprintf("%.3f", as.numeric(Sys.time())),
sep=""), con=shiny_stdout)
flush(shiny_stdout)
} else {
flushReact()
}
# Check for switching into/out of showcase mode
if (.globals$showcaseOverride &&
exists(".clientdata_url_search", where = msg$data)) {
mode <- showcaseModeOfQuerystring(msg$data$.clientdata_url_search)
if (!is.null(mode))
shinysession$setShowcase(mode)
}
shinysession$manageInputs(msg$data)
# The client tells us what singletons were rendered into
# the initial page
if (!is.null(msg$data$.clientdata_singletons)) {
shinysession$singletons <<- strsplit(
msg$data$.clientdata_singletons, ',')[[1]]
}
local({
args <- list(
input=shinysession$input,
output=.createOutputWriter(shinysession))
# The clientData and session arguments are optional; check if
# each exists
if ('clientData' %in% names(formals(serverFunc)))
args$clientData <- shinysession$clientData
if ('session' %in% names(formals(serverFunc)))
args$session <- shinysession
withReactiveDomain(shinysession, {
do.call(appvars$server, args)
})
})
},
update = {
shinysession$manageInputs(msg$data)
},
shinysession$dispatch(msg)
)
shinysession$manageHiddenOutputs()
if (exists(".shiny__stdout", globalenv()) &&
exists("HTTP_GUID", ws$request)) {
# safe to assume we're in shiny-server
shiny_stdout <- get(".shiny__stdout", globalenv())
# eNter a flushReact
writeLines(paste("_n_flushReact ", get("HTTP_GUID", ws$request),
" @ ", sprintf("%.3f", as.numeric(Sys.time())),
sep=""), con=shiny_stdout)
flush(shiny_stdout)
flushReact()
# eXit a flushReact
writeLines(paste("_x_flushReact ", get("HTTP_GUID", ws$request),
" @ ", sprintf("%.3f", as.numeric(Sys.time())),
sep=""), con=shiny_stdout)
flush(shiny_stdout)
} else {
flushReact()
}
lapply(appsByToken$values(), function(shinysession) {
shinysession$flushOutput()
NULL
flushAllSessions()
})
})
}
ws$onMessage(function(binary, msg) {
# If unhandled errors occur, make sure they get properly logged
withLogErrors(messageHandler(binary, msg))
})
ws$onClose(function() {
@@ -458,6 +355,26 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
return(appHandlers)
}
# Determine what arguments should be passed to this serverFunc. All server funcs
# must take input and output, but clientData (obsolete) and session are
# optional.
argsForServerFunc <- function(serverFunc, session) {
args <- list(input = session$input, output = .createOutputWriter(session))
paramNames <- names(formals(serverFunc))
# The clientData and session arguments are optional; check if
# each exists
if ("clientData" %in% paramNames)
args$clientData <- session$clientData
if ("session" %in% paramNames)
args$session <- session
args
}
getEffectiveBody <- function(func) {
# Note: NULL values are OK. isS4(NULL) returns FALSE, body(NULL)
# returns NULL.
@@ -519,6 +436,12 @@ startApp <- function(appObj, port, host, quiet) {
message('\n', 'Listening on domain socket ', port)
}
mask <- attr(port, 'mask')
if (is.null(mask)) {
stop("`port` is not a valid domain socket (missing `mask` attribute). ",
"Note that if you're using the default `host` + `port` ",
"configuration (and not domain sockets), then `port` must ",
"be numeric, not a string.")
}
return(startPipeServer(port, mask, handlerManager$createHttpuvApp()))
}
}
@@ -532,10 +455,7 @@ serviceApp <- function() {
}
flushReact()
for (shinysession in appsByToken$values()) {
shinysession$flushOutput()
}
flushAllSessions()
}
# If this R session is interactive, then call service() with a short timeout
@@ -548,6 +468,9 @@ serviceApp <- function() {
.shinyServerMinVersion <- '0.3.4'
# Global flag that's TRUE whenever we're inside of the scope of a call to runApp
.globals$running <- FALSE
#' Run Shiny Application
#'
#' Runs a Shiny application. This function normally does not return; interrupt R
@@ -559,13 +482,16 @@ serviceApp <- function() {
#' clients to connect, use the value \code{"0.0.0.0"} instead (which was the
#' value that was hard-coded into Shiny in 0.8.0 and earlier).
#'
#' @param appDir The directory of the application. Should contain
#' \code{server.R}, plus, either \code{ui.R} or a \code{www} directory that
#' contains the file \code{index.html}. Alternately, instead of
#' \code{server.R} and \code{ui.R}, the directory may contain just
#' \code{app.R}. Defaults to the working directory. Instead of a directory,
#' this could be a list with \code{ui} and \code{server} components, or a
#' Shiny app object created by \code{\link{shinyApp}}.
#' @param appDir The application to run. Should be one of the following:
#' \itemize{
#' \item A directory containing \code{server.R}, plus, either \code{ui.R} or
#' a \code{www} directory that contains the file \code{index.html}.
#' \item A directory containing \code{app.R}.
#' \item An \code{.R} file containing a Shiny application, ending with an
#' expression that produces a Shiny app object.
#' \item A list with \code{ui} and \code{server} components.
#' \item A Shiny app object created by \code{\link{shinyApp}}.
#' }
#' @param port The TCP port that the application should listen on. If the
#' \code{port} is not specified, and the \code{shiny.port} option is set (with
#' \code{options(shiny.port = XX)}), then that port will be used. Otherwise,
@@ -586,6 +512,9 @@ serviceApp <- function() {
#' application. If set to \code{"normal"}, displays the application normally.
#' Defaults to \code{"auto"}, which displays the application in the mode given
#' in its \code{DESCRIPTION} file, if any.
#' @param test.mode Should the application be launched in test mode? This is
#' only used for recording or running automated tests. Defaults to the
#' \code{shiny.testmode} option, or FALSE if the option is not set.
#'
#' @examples
#' \dontrun{
@@ -598,6 +527,8 @@ serviceApp <- function() {
#'
#' ## Only run this example in interactive R sessions
#' if (interactive()) {
#' options(device.ask.default = FALSE)
#'
#' # Apps can be run without a server.r and ui.r file
#' runApp(list(
#' ui = bootstrapPage(
@@ -629,28 +560,82 @@ runApp <- function(appDir=getwd(),
interactive()),
host=getOption('shiny.host', '127.0.0.1'),
workerId="", quiet=FALSE,
display.mode=c("auto", "normal", "showcase")) {
display.mode=c("auto", "normal", "showcase"),
test.mode=getOption('shiny.testmode', FALSE)) {
on.exit({
handlerManager$clear()
}, add = TRUE)
if (.globals$running) {
stop("Can't call `runApp()` from within `runApp()`. If your ,",
"application code contains `runApp()`, please remove it.")
}
.globals$running <- TRUE
on.exit({
.globals$running <- FALSE
}, add = TRUE)
if (is.null(host) || is.na(host))
host <- '0.0.0.0'
# Enable per-app Shiny options
oldOptionSet <- .globals$options
on.exit({
.globals$options <- oldOptionSet
},add = TRUE)
# Make warnings print immediately
ops <- options(warn = 1)
# Set pool.scheduler to support pool package
ops <- options(warn = 1, pool.scheduler = scheduleTask)
on.exit(options(ops), add = TRUE)
appParts <- as.shiny.appobj(appDir)
# The lines below set some of the app's running options, which
# can be:
# - left unspeficied (in which case the arguments' default
# values from `runApp` kick in);
# - passed through `shinyApp`
# - passed through `runApp` (this function)
# - passed through both `shinyApp` and `runApp` (the latter
# takes precedence)
#
# Matrix of possibilities:
# | IN shinyApp | IN runApp | result | check |
# |-------------|-----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------|
# | no | no | use defaults | exhaust all possibilities: if it's missing (runApp does not specify); THEN if it's not in shinyApp appParts$options; THEN use defaults |
# | yes | no | use shinyApp | if it's missing (runApp does not specify); THEN if it's in shinyApp appParts$options; THEN use shinyApp |
# | no | yes | use runApp | if it's not missing (runApp specifies), use those |
# | yes | yes | use runApp | if it's not missing (runApp specifies), use those |
#
# I tried to make this as compact and intuitive as possible,
# given that there are four distinct possibilities to check
appOps <- appParts$options
findVal <- function(arg, default) {
if (arg %in% names(appOps)) appOps[[arg]] else default
}
if (missing(port))
port <- findVal("port", port)
if (missing(launch.browser))
launch.browser <- findVal("launch.browser", launch.browser)
if (missing(host))
host <- findVal("host", host)
if (missing(quiet))
quiet <- findVal("quiet", quiet)
if (missing(display.mode))
display.mode <- findVal("display.mode", display.mode)
if (missing(test.mode))
test.mode <- findVal("test.mode", test.mode)
if (is.null(host) || is.na(host)) host <- '0.0.0.0'
workerId(workerId)
if (nzchar(Sys.getenv('SHINY_PORT'))) {
if (inShinyServer()) {
# If SHINY_PORT is set, we're running under Shiny Server. Check the version
# to make sure it is compatible. Older versions of Shiny Server don't set
# SHINY_SERVER_VERSION, those will return "" which is considered less than
# any valid version.
ver <- Sys.getenv('SHINY_SERVER_VERSION')
if (compareVersion(ver, .shinyServerMinVersion) < 0) {
if (utils::compareVersion(ver, .shinyServerMinVersion) < 0) {
warning('Shiny Server v', .shinyServerMinVersion,
' or later is required; please upgrade!')
}
@@ -661,30 +646,58 @@ runApp <- function(appDir=getwd(),
# the display.mode parameter. The latter takes precedence.
setShowcaseDefault(0)
.globals$testMode <- test.mode
if (test.mode) {
message("Running application in test mode.")
}
# If appDir specifies a path, and display mode is specified in the
# DESCRIPTION file at that path, apply it here.
if (is.character(appDir)) {
desc <- file.path.ci(appDir, "DESCRIPTION")
# if appDir specifies a .R file (single-file Shiny app), look for the
# DESCRIPTION in the parent directory
desc <- file.path.ci(
if (tolower(tools::file_ext(appDir)) == "r")
dirname(appDir)
else
appDir, "DESCRIPTION")
if (file.exists(desc)) {
con <- file(desc, encoding = checkEncoding(desc))
on.exit(close(con), add = TRUE)
settings <- read.dcf(con)
if ("DisplayMode" %in% colnames(settings)) {
mode <- settings[1,"DisplayMode"]
mode <- settings[1, "DisplayMode"]
if (mode == "Showcase") {
setShowcaseDefault(1)
if ("IncludeWWW" %in% colnames(settings)) {
.globals$IncludeWWW <- as.logical(settings[1, "IncludeWWW"])
if (is.na(.globals$IncludeWWW)) {
stop("In your Description file, `IncludeWWW` ",
"must be set to `True` (default) or `False`")
}
} else {
.globals$IncludeWWW <- TRUE
}
}
}
}
}
## default is to show the .js, .css and .html files in the www directory
## (if not in showcase mode, this variable will simply be ignored)
if (is.null(.globals$IncludeWWW) || is.na(.globals$IncludeWWW)) {
.globals$IncludeWWW <- TRUE
}
# If display mode is specified as an argument, apply it (overriding the
# value specified in DESCRIPTION, if any).
display.mode <- match.arg(display.mode)
if (display.mode == "normal")
if (display.mode == "normal") {
setShowcaseDefault(0)
else if (display.mode == "showcase")
}
else if (display.mode == "showcase") {
setShowcaseDefault(1)
}
require(shiny)
@@ -705,7 +718,14 @@ runApp <- function(appDir=getwd(),
}
else {
# Try up to 20 random ports
port <- p_randomInt(3000, 8000)
while (TRUE) {
port <- p_randomInt(3000, 8000)
# Reject ports in this range that are considered unsafe by Chrome
# http://superuser.com/questions/188058/which-ports-are-considered-unsafe-on-chrome
if (!port %in% c(3659, 4045, 6000, 6665:6669)) {
break
}
}
}
# Test port to see if we can use it
@@ -718,7 +738,11 @@ runApp <- function(appDir=getwd(),
}
}
appParts <- as.shiny.appobj(appDir)
# Extract appOptions (which is a list) and store them as shinyOptions, for
# this app. (This is the only place we have to store settings that are
# accessible both the UI and server portion of the app.)
unconsumeAppOptions(appParts$appOptions)
# Set up the onEnd before we call onStart, so that it gets called even if an
# error happens in onStart.
if (!is.null(appParts$onEnd))
@@ -751,16 +775,31 @@ runApp <- function(appDir=getwd(),
callAppHook("onAppStop", appUrl)
}, add = TRUE)
.globals$reterror <- NULL
.globals$retval <- NULL
.globals$stopped <- FALSE
shinyCallingHandlers(
while (!.globals$stopped) {
serviceApp()
Sys.sleep(0.001)
}
# Top-level ..stacktraceoff..; matches with ..stacktraceon in observe(),
# reactive(), Callbacks$invoke(), and others
..stacktraceoff..(
captureStackTraces({
# If any observers were created before runApp was called, this will make
# sure they run once the app starts. (Issue #1013)
scheduleFlush()
while (!.globals$stopped) {
serviceApp()
Sys.sleep(0.001)
}
})
)
return(.globals$retval)
if (isTRUE(.globals$reterror)) {
stop(.globals$retval)
}
else if (.globals$retval$visible)
.globals$retval$value
else
invisible(.globals$retval$value)
}
#' Stop the currently running Shiny app
@@ -770,10 +809,26 @@ runApp <- function(appDir=getwd(),
#'
#' @param returnValue The value that should be returned from
#' \code{\link{runApp}}.
#'
#' @export
stopApp <- function(returnValue = NULL) {
.globals$retval <- returnValue
stopApp <- function(returnValue = invisible()) {
# reterror will indicate whether retval is an error (i.e. it should be passed
# to stop() when the serviceApp loop stops) or a regular value (in which case
# it should simply be returned with the appropriate visibility).
.globals$reterror <- FALSE
..stacktraceoff..(
tryCatch(
{
captureStackTraces(
.globals$retval <- withVisible(..stacktraceon..(force(returnValue)))
)
},
error = function(e) {
.globals$retval <- e
.globals$reterror <- TRUE
}
)
)
.globals$stopped <- TRUE
httpuv::interrupt()
}
@@ -836,3 +891,140 @@ runExample <- function(example=NA,
display.mode = display.mode)
}
}
#' Run a gadget
#'
#' Similar to \code{runApp}, but handles \code{input$cancel} automatically, and
#' if running in RStudio, defaults to viewing the app in the Viewer pane.
#'
#' @param app Either a Shiny app object as created by
#' \code{\link[=shiny]{shinyApp}} et al, or, a UI object.
#' @param server Ignored if \code{app} is a Shiny app object; otherwise, passed
#' along to \code{shinyApp} (i.e. \code{shinyApp(ui = app, server = server)}).
#' @param port See \code{\link[=shiny]{runApp}}.
#' @param viewer Specify where the gadget should be displayed--viewer pane,
#' dialog window, or external browser--by passing in a call to one of the
#' \code{\link{viewer}} functions.
#' @param stopOnCancel If \code{TRUE} (the default), then an \code{observeEvent}
#' is automatically created that handles \code{input$cancel} by calling
#' \code{stopApp()} with an error. Pass \code{FALSE} if you want to handle
#' \code{input$cancel} yourself.
#' @return The value returned by the gadget.
#'
#' @examples
#' \dontrun{
#' library(shiny)
#'
#' ui <- fillPage(...)
#'
#' server <- function(input, output, session) {
#' ...
#' }
#'
#' # Either pass ui/server as separate arguments...
#' runGadget(ui, server)
#'
#' # ...or as a single app object
#' runGadget(shinyApp(ui, server))
#' }
#' @export
runGadget <- function(app, server = NULL, port = getOption("shiny.port"),
viewer = paneViewer(), stopOnCancel = TRUE) {
if (!is.shiny.appobj(app)) {
app <- shinyApp(app, server)
}
if (isTRUE(stopOnCancel)) {
app <- decorateServerFunc(app, function(input, output, session) {
observeEvent(input$cancel, {
stopApp(stop("User cancel", call. = FALSE))
})
})
}
if (is.null(viewer)) {
viewer <- utils::browseURL
}
shiny::runApp(app, port = port, launch.browser = viewer)
}
# Add custom functionality to a Shiny app object's server func
decorateServerFunc <- function(appobj, serverFunc) {
origServerFuncSource <- appobj$serverFuncSource
appobj$serverFuncSource <- function() {
origServerFunc <- origServerFuncSource()
function(input, output, session) {
serverFunc(input, output, session)
# The clientData and session arguments are optional; check if
# each exists
args <- argsForServerFunc(origServerFunc, session)
do.call(origServerFunc, args)
}
}
appobj
}
#' Viewer options
#'
#' Use these functions to control where the gadget is displayed in RStudio (or
#' other R environments that emulate RStudio's viewer pane/dialog APIs). If
#' viewer APIs are not available in the current R environment, then the gadget
#' will be displayed in the system's default web browser (see
#' \code{\link[utils]{browseURL}}).
#'
#' @return A function that takes a single \code{url} parameter, suitable for
#' passing as the \code{viewer} argument of \code{\link{runGadget}}.
#'
#' @rdname viewer
#' @name viewer
NULL
#' @param minHeight The minimum height (in pixels) desired to show the gadget in
#' the viewer pane. If a positive number, resize the pane if necessary to show
#' at least that many pixels. If \code{NULL}, use the existing viewer pane
#' size. If \code{"maximize"}, use the maximum available vertical space.
#' @rdname viewer
#' @export
paneViewer <- function(minHeight = NULL) {
viewer <- getOption("viewer")
if (is.null(viewer)) {
utils::browseURL
} else {
function(url) {
viewer(url, minHeight)
}
}
}
#' @param dialogName The window title to display for the dialog.
#' @param width,height The desired dialog width/height, in pixels.
#' @rdname viewer
#' @export
dialogViewer <- function(dialogName, width = 600, height = 600) {
viewer <- getOption("shinygadgets.showdialog")
if (is.null(viewer)) {
utils::browseURL
} else {
function(url) {
viewer(dialogName, url, width = width, height = height)
}
}
}
#' @param browser See \code{\link[utils]{browseURL}}.
#' @rdname viewer
#' @export
browserViewer <- function(browser = getOption("browser")) {
function(url) {
utils::browseURL(url, browser = browser)
}
}
# Returns TRUE if we're running in Shiny Server or other hosting environment,
# otherwise returns FALSE.
inShinyServer <- function() {
nzchar(Sys.getenv('SHINY_PORT'))
}

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)
}
}

1141
R/shiny.R

File diff suppressed because it is too large Load Diff

View File

@@ -20,61 +20,46 @@ withMathJax <- function(...) {
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 <- showcaseUI(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)
# 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)
# Put the body into the default template
ui <- htmlTemplate(
system.file("template", "default.html", package = "shiny"),
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.min.js"),
htmlDependency("shiny", packageVersion("shiny"), c(href="shared"),
script = "shiny.min.js", stylesheet = "shiny.css")
),
result$dependencies
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("babel-polyfill", "6.7.2", c(href="shared"), script = "babel-polyfill.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")
)
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>',
recursive=TRUE),
con = connection)
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")
}
writeLines(result$html, con = connection)
# write end document
writeLines('</html>',
con = connection)
html <- renderDocument(ui, shiny_deps, processDep = createWebDependency)
writeUTF8(html, con = connection)
}
#' Create a Shiny UI handler
@@ -87,7 +72,6 @@ renderPage <- function(ui, connection, showcase=0) {
#'
#' @param ui A user interace definition
#' @return The user interface definition, without modifications or side effects.
#'
#' @export
shinyUI <- function(ui) {
.globals$ui <- list(ui)
@@ -105,7 +89,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
@@ -114,19 +98,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

@@ -12,24 +12,74 @@ 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) {
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)
class = c("shiny.render.function", "function"),
outputFunc = uiFunc,
outputArgs = outputArgs,
hasExecuted = hasExecuted)
}
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")
# Make the id the first positional argument
outputArgs <- c(list(id), outputArgs)
o <- getDefaultReactiveDomain()$output
if (!is.null(o))
o[[id]] <- renderFunc
if (is.logical(formals(outputFunction)[["inline"]])) {
outputFunction(id, inline = inline)
} else outputFunction(id)
if (is.logical(formals(outputFunction)[["inline"]]) && !("inline" %in% names(outputArgs))) {
outputArgs[["inline"]] <- inline
}
do.call(outputFunction, outputArgs)
}
#' @export
@@ -69,13 +119,24 @@ as.tags.shiny.render.function <- function(x, ..., inline = FALSE) {
#' 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({
@@ -97,14 +158,14 @@ as.tags.shiny.render.function <- function(x, ..., inline = FALSE) {
#' 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
@@ -115,6 +176,8 @@ as.tags.shiny.render.function <- function(x, ..., inline = FALSE) {
#' }, 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',
@@ -123,14 +186,15 @@ as.tags.shiny.render.function <- function(x, ..., inline = FALSE) {
#' # 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, ...) {
renderFunc <- 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.
@@ -147,60 +211,17 @@ renderImage <- function(expr, env=parent.frame(), quoted=FALSE,
# Return a list with src, and other img attributes
c(src = shinysession$fileUrl(name, file=imageinfo$src, contentType=contentType),
extra_attr)
}))
}
#' 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"))
})
markRenderFunction(imageOutput, renderFunc, outputArgs = outputArgs)
}
#' 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
@@ -212,34 +233,33 @@ 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)
markRenderFunction(verbatimTextOutput, function() {
renderFunc <- function(shinysession, name, ...) {
op <- options(width = width)
on.exit(options(op), add = TRUE)
paste(capture.output(func()), collapse = "\n")
})
paste(utils::capture.output(func()), collapse = "\n")
}
markRenderFunction(verbatimTextOutput, renderFunc, outputArgs = outputArgs)
}
#' Text Output
@@ -260,26 +280,25 @@ 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)
renderFunc <- function(shinysession, name, ...) {
value <- func()
return(paste(utils::capture.output(cat(value)), collapse="\n"))
}
markRenderFunction(textOutput, function() {
value <- func()
return(paste(capture.output(cat(value)), collapse="\n"))
})
markRenderFunction(textOutput, renderFunc, outputArgs = outputArgs)
}
#' UI Output
@@ -295,46 +314,136 @@ 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).
#' @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 conditionalPanel
#'
#' @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, ...) {
renderFunc <- function(shinysession, name, ...) {
result <- func()
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
processDeps(result, shinysession)
}
# renderTags returns a list with head, singletons, and html
output <- list(
html = doRenderTags(result),
deps = dependencies
markRenderFunction(uiOutput, renderFunc, outputArgs = outputArgs)
}
#' @export
serveJSON <- function(expr, env=parent.frame(), quoted=FALSE) {
installExprFunction(expr, "func", env, quoted)
function() {
structure(
toJSON(func(), pretty = TRUE),
content.type = "application/json"
)
}
}
return(output)
})
#' @export
servePlot <- function(expr, env=parent.frame(), quoted=FALSE,
defaultWidth = 600, defaultHeight = 400) {
if (!is.function(defaultWidth))
defaultWidth <- valueToFunc(defaultWidth)
if (!is.function(defaultHeight))
defaultHeight <- valueToFunc(defaultHeight)
installExprFunction(expr, "func", env, quoted)
function() {
input <- getDefaultReactiveDomain()$input
w <- if (!is.null(input$`plot-width`)) as.numeric(input$`plot-width`) else defaultWidth()
h <- if (!is.null(input$`plot-height`)) as.numeric(input$`plot-height`) else defaultHeight()
pngfile <- plotPNG(function() {
result <- withVisible(func())
if (result$visible) {
# Use capture.output to squelch printing to the actual console; we
# are only interested in plot output
utils::capture.output({
# 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.
print(result$value)
})
}
}, width = w, height = h)
structure(
list(file = pngfile, owned = TRUE),
content.type = "image/png"
)
}
}
#' @importFrom utils write.csv
#' @export
serveCSV <- function(expr, env=parent.frame(), quoted=FALSE, row.names=FALSE) {
installExprFunction(expr, "func", env, quoted)
function() {
tmp <- tempfile(".csv")
write.csv(func(), tmp, row.names=row.names)
structure(
list(file = tmp, owned = TRUE),
content.type = "text/csv"
)
}
}
#' @export
serveText <- function(expr, env=parent.frame(), quoted=FALSE) {
installExprFunction(expr, "func", env, quoted)
function() {
structure(
paste(func(), collapse = "\n"),
content.type = "text/plain"
)
}
}
#' @export
serveRaw <- function(expr, env=parent.frame(), quoted=FALSE, contentType) {
if (!is.function(contentType))
contentType <- valueToFunc(contentType)
installExprFunction(expr, "func", env, quoted)
function() {
bytes <- func()
if (!is.raw(bytes)) {
stop("serveRaw expects raw vector data")
}
structure(
bytes,
content.type = contentType()
)
}
}
#' File Downloads
@@ -360,28 +469,40 @@ 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)
}))
}
markRenderFunction(downloadButton, renderFunc, outputArgs = outputArgs)
}
#' Table output with the JavaScript library DataTables
@@ -392,7 +513,7 @@ 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. Note this only applies to the root-level elements of the
@@ -412,6 +533,10 @@ downloadHandler <- function(filename, content, contentType=NA) {
#' 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
@@ -446,10 +571,11 @@ downloadHandler <- function(filename, content, contentType=NA) {
#' }
renderDataTable <- function(expr, options = NULL, searchDelay = 500,
callback = 'function(oTable) {}', escape = TRUE,
env = parent.frame(), quoted = FALSE) {
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)
@@ -460,7 +586,7 @@ renderDataTable <- function(expr, options = NULL, searchDelay = 500,
colnames <- colnames(data)
# if escape is column names, turn names to numeric indices
if (is.character(escape)) {
escape <- setNames(seq_len(ncol(data)), colnames)[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")
}
@@ -475,14 +601,18 @@ renderDataTable <- function(expr, options = NULL, searchDelay = 500,
evalOptions = if (length(res$eval)) I(res$eval), searchDelay = searchDelay,
callback = paste(callback, collapse = '\n'), escape = escape
)
})
}
markRenderFunction(dataTableOutput, renderFunc, outputArgs = outputArgs)
}
# 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
)
}
@@ -500,7 +630,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
@@ -509,7 +639,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

@@ -31,7 +31,7 @@ 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"),
@@ -77,10 +77,60 @@ 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-sm",
@@ -88,27 +138,21 @@ showcaseCodeTabs <- function(codeLicense) {
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))
}
@@ -177,3 +221,4 @@ showcaseUI <- function(ui) {
showcaseBody(ui)
)
}

View File

@@ -1,26 +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)
}

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
@@ -56,7 +61,7 @@ TimerCallbacks <- R6Class(
},
executeElapsed = function() {
elapsed <- takeElapsed()
if (length(elapsed) == 0)
if (nrow(elapsed) == 0)
return(FALSE)
for (id in elapsed$id) {
@@ -71,3 +76,16 @@ TimerCallbacks <- R6Class(
)
timerCallbacks <- TimerCallbacks$new()
scheduleTask <- function(millis, callback) {
cancelled <- FALSE
timerCallbacks$schedule(millis, function() {
if (!cancelled)
callback()
})
function() {
cancelled <<- TRUE
callback <<- NULL # to allow for callback to be gc'ed
}
}

View File

@@ -6,9 +6,16 @@
#' @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,7 +29,9 @@
#' label = paste("New label", x),
#' value = paste("New text", x))
#' })
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @export
updateTextInput <- function(session, inputId, label = NULL, value = NULL) {
@@ -30,6 +39,44 @@ updateTextInput <- function(session, inputId, label = NULL, value = NULL) {
session$sendInputMessage(inputId, message)
}
#' Change the value of a textarea input on the client
#'
#' @template update-input
#' @param value The value to set for the input object.
#'
#' @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,26 +86,87 @@ 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
#' Change the label or icon of an action button on the client
#'
#' @template update-input
#' @param icon The icon to set for the input object. To remove the
#' current icon, use \code{icon=character(0)}.
#'
#' @seealso \code{\link{actionButton}}
#'
#' @examples
#' ## 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({
#' req(input$update)
#'
#' # Updates goButton's label and icon
#' updateActionButton(session, "goButton",
#' label = "New label",
#' icon = icon("calendar"))
#'
#' # Leaves goButton2's label unchaged and
#' # removes its icon
#' updateActionButton(session, "goButton2",
#' icon = character(0))
#'
#' # Leaves goButton3's icon, if it exists,
#' # unchaged and changes its label
#' updateActionButton(session, "goButton3",
#' label = "New label 3")
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @export
updateActionButton <- function(session, inputId, label = NULL, icon = NULL) {
if (!is.null(icon)) icon <- as.character(validateIcon(icon))
message <- dropNulls(list(label=label, icon=icon))
session$sendInputMessage(inputId, message)
}
#' 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
@@ -67,32 +175,44 @@ updateCheckboxInput <- 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)
@@ -103,9 +223,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
@@ -114,20 +234,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,
@@ -142,7 +271,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
))
@@ -162,22 +291,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) {
@@ -185,6 +323,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
#'
@@ -197,10 +342,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
@@ -211,7 +364,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,
@@ -242,9 +397,9 @@ updateNumericInput <- function(session, inputId, label = NULL, value = NULL,
#' sidebarLayout(
#' sidebarPanel(
#' p("The first slider controls the second"),
#' slider2Input("control", "Controller:", min=0, max=20, value=10,
#' sliderInput("control", "Controller:", min=0, max=20, value=10,
#' step=1),
#' slider2Input("receive", "Receiver:", min=0, max=20, value=10,
#' sliderInput("receive", "Receiver:", min=0, max=20, value=10,
#' step=1)
#' ),
#' mainPanel()
@@ -262,20 +417,53 @@ updateNumericInput <- function(session, inputId, label = NULL, value = NULL,
#' )
#' }
#' @export
updateSliderInput <- updateNumericInput
updateSliderInput <- function(session, inputId, label = NULL, value = NULL,
min = NULL, max = NULL, step = NULL)
{
# Make sure that value, min, max all have the same type, because we need
# special handling for dates and datetimes.
vals <- dropNulls(list(value, min, max))
type <- unique(lapply(vals, function(x) {
if (inherits(x, "Date")) "date"
else if (inherits(x, "POSIXt")) "datetime"
else "number"
}))
if (length(type) > 1) {
stop("Type mismatch for value, min, and max")
}
if ((length(type) == 1) && (type == "date" || type == "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)
))
session$sendInputMessage(inputId, message)
}
updateInputOptions <- function(session, inputId, label = NULL, choices = NULL,
selected = NULL, inline = FALSE,
type = 'checkbox') {
choices <- choicesWithNames(choices)
if (!is.null(choices))
choices <- choicesWithNames(choices)
if (!is.null(selected))
selected <- validateSelected(selected, choices, inputId)
selected <- validateSelected(selected, choices, session$ns(inputId))
options <- if (length(choices))
options <- if (!is.null(choices)) {
format(tagList(
generateOptions(inputId, choices, selected, inline, type = type)
generateOptions(session$ns(inputId), choices, selected, inline, type = type)
))
}
message <- dropNulls(list(label = label, options = options, value = selected))
@@ -290,31 +478,35 @@ 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,
@@ -332,29 +524,31 @@ 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,
@@ -373,32 +567,35 @@ updateRadioButtons <- function(session, inputId, label = NULL, choices = NULL,
#' @seealso \code{\link{selectInput}}
#'
#' @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,
@@ -425,7 +622,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))
)
@@ -435,13 +632,10 @@ updateSelectizeInput <- function(session, inputId, label = NULL, choices = NULL,
return(updateSelectInput(session, inputId, label, choices, selected))
}
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)
@@ -450,12 +644,14 @@ updateSelectizeInput <- function(session, inputId, label = NULL, choices = NULL,
selectizeJSON <- function(data, req) {
query <- parseQueryString(req$QUERY_STRING)
# extract the query variables, conjunction (and/or), search string, maximum options
var <- unlist(jsonlite::fromJSON(query$field))
var <- c(jsonlite::fromJSON(query$field))
cjn <- if (query$conju == 'and') all else any
# 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
mop <- as.numeric(query$maxop)
vfd <- query$value # the value field name
sel <- attr(data, 'selected_value', exact = TRUE)
# convert a single vector to a data frame so it returns {label: , value: }
# later in JSON; other objects return arbitrary JSON {x: , y: , foo: , ...}
@@ -478,7 +674,12 @@ selectizeJSON <- function(data, req) {
idx <- idx | apply(matches, 1, cjn)
}
# only return the first n rows (n = maximum options in configuration)
idx <- head(if (length(key)) which(idx) else seq_along(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, ]
res <- toJSON(columnToRowData(data))

775
R/utils.R

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,8 @@
# 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.
@@ -11,7 +13,7 @@ For an introduction and examples, visit the [Shiny Dev Center](http://shiny.rstu
* 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.)
* 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/).
* 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.
@@ -57,6 +59,10 @@ devtools::install_version("shiny", version = "0.10.2.2")
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:

45
appveyor.yml Normal file
View File

@@ -0,0 +1,45 @@
# 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

View File

@@ -1,7 +1,7 @@
library(shiny)
# Define server logic required to draw a histogram
shinyServer(function(input, output) {
function(input, output) {
# Expression that generates a histogram. The expression is
# wrapped in a call to renderPlot to indicate that:
@@ -18,4 +18,4 @@ shinyServer(function(input, output) {
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
})
}

View File

@@ -1,7 +1,7 @@
library(shiny)
# Define UI for application that draws a histogram
shinyUI(fluidPage(
fluidPage(
# Application title
titlePanel("Hello Shiny!"),
@@ -21,4 +21,4 @@ shinyUI(fluidPage(
plotOutput("distPlot")
)
)
))
)

View File

@@ -3,7 +3,7 @@ library(datasets)
# Define server logic required to summarize and view the selected
# dataset
shinyServer(function(input, output) {
function(input, output) {
# Return the requested dataset
datasetInput <- reactive({
@@ -23,4 +23,4 @@ shinyServer(function(input, output) {
output$view <- renderTable({
head(datasetInput(), n = input$obs)
})
})
}

View File

@@ -1,7 +1,7 @@
library(shiny)
# Define UI for dataset viewer application
shinyUI(fluidPage(
fluidPage(
# Application title
titlePanel("Shiny Text"),
@@ -24,4 +24,4 @@ shinyUI(fluidPage(
tableOutput("view")
)
)
))
)

View File

@@ -3,7 +3,7 @@ library(datasets)
# Define server logic required to summarize and view the selected
# dataset
shinyServer(function(input, output) {
function(input, output) {
# By declaring datasetInput as a reactive expression we ensure
# that:
@@ -50,4 +50,4 @@ shinyServer(function(input, output) {
output$view <- renderTable({
head(datasetInput(), n = input$obs)
})
})
}

View File

@@ -1,7 +1,7 @@
library(shiny)
# Define UI for dataset viewer application
shinyUI(fluidPage(
fluidPage(
# Application title
titlePanel("Reactivity"),
@@ -31,4 +31,4 @@ shinyUI(fluidPage(
tableOutput("view")
)
)
))
)

View File

@@ -11,7 +11,7 @@ mpgData$am <- factor(mpgData$am, labels = c("Automatic", "Manual"))
# Define server logic required to plot various variables against
# mpg
shinyServer(function(input, output) {
function(input, output) {
# Compute the formula text in a reactive expression since it is
# shared by the output$caption and output$mpgPlot functions
@@ -31,4 +31,4 @@ shinyServer(function(input, output) {
data = mpgData,
outline = input$outliers)
})
})
}

View File

@@ -1,7 +1,7 @@
library(shiny)
# Define UI for miles per gallon application
shinyUI(fluidPage(
fluidPage(
# Application title
titlePanel("Miles Per Gallon"),
@@ -26,4 +26,4 @@ shinyUI(fluidPage(
plotOutput("mpgPlot")
)
)
))
)

View File

@@ -1,7 +1,7 @@
library(shiny)
# Define server logic for slider examples
shinyServer(function(input, output) {
function(input, output) {
# Reactive expression to compose a data frame containing all of
# the values
@@ -26,4 +26,4 @@ shinyServer(function(input, output) {
output$values <- renderTable({
sliderValues()
})
})
}

View File

@@ -1,7 +1,7 @@
library(shiny)
# Define UI for slider demo application
shinyUI(fluidPage(
fluidPage(
# Application title
titlePanel("Sliders"),
@@ -40,4 +40,4 @@ shinyUI(fluidPage(
tableOutput("values")
)
)
))
)

View File

@@ -1,7 +1,7 @@
library(shiny)
# Define server logic for random distribution application
shinyServer(function(input, output) {
function(input, output) {
# Reactive expression to generate the requested distribution.
# This is called whenever the inputs change. The output
@@ -41,4 +41,4 @@ shinyServer(function(input, output) {
data.frame(x=data())
})
})
}

View File

@@ -1,7 +1,7 @@
library(shiny)
# Define UI for random distribution application
shinyUI(fluidPage(
fluidPage(
# Application title
titlePanel("Tabsets"),
@@ -35,4 +35,4 @@ shinyUI(fluidPage(
)
)
)
))
)

View File

@@ -1 +1 @@
This example demonstrates some additional widgets included in Shiny, such as `helpText` and `submitButton`. The latter is used to delay rendering output until the user explicitly requests it.
This example demonstrates some additional widgets included in Shiny, such as `helpText` and `actionButton`. The latter is used to delay rendering output until the user explicitly requests it (a construct which also introduces two important server functions, `eventReactive` and `isolate`).

View File

@@ -1,26 +1,32 @@
library(shiny)
library(datasets)
# Define server logic required to summarize and view the
# Define server logic required to summarize and view the
# selected dataset
shinyServer(function(input, output) {
# Return the requested dataset
datasetInput <- reactive({
function(input, output) {
# Return the requested dataset. Note that we use `eventReactive()`
# here, which takes a dependency on input$update (the action
# button), so that the output is only updated when the user
# clicks the button.
datasetInput <- eventReactive(input$update, {
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
}, ignoreNULL = FALSE)
# Generate a summary of the dataset
output$summary <- renderPrint({
dataset <- datasetInput()
summary(dataset)
})
# Show the first "n" observations
# Show the first "n" observations. The use of `isolate()` here
# is necessary because we don't want the table to update
# whenever input$obs changes (only when the user clicks the
# action button).
output$view <- renderTable({
head(datasetInput(), n = input$obs)
head(datasetInput(), n = isolate(input$obs))
})
})
}

View File

@@ -1,33 +1,33 @@
library(shiny)
# Define UI for dataset viewer application
shinyUI(fluidPage(
fluidPage(
# Application title.
titlePanel("More Widgets"),
# Sidebar with controls to select a dataset and specify the
# number of observations to view. The helpText function is
# also used to include clarifying text. Most notably, the
# inclusion of a submitButton defers the rendering of output
# inclusion of an actionButton defers the rendering of output
# until the user explicitly clicks the button (rather than
# doing it immediately when inputs change). This is useful if
# the computations required to render output are inordinately
# time-consuming.
sidebarLayout(
sidebarPanel(
selectInput("dataset", "Choose a dataset:",
selectInput("dataset", "Choose a dataset:",
choices = c("rock", "pressure", "cars")),
numericInput("obs", "Number of observations to view:", 10),
helpText("Note: while the data view will show only the specified",
"number of observations, the summary will still be based",
"on the full dataset."),
submitButton("Update View")
actionButton("update", "Update View")
),
# Show a summary of the dataset and an HTML table with the
# requested number of observations. Note the use of the h4
# function to provide an additional header above each output
@@ -35,9 +35,9 @@ shinyUI(fluidPage(
mainPanel(
h4("Summary"),
verbatimTextOutput("summary"),
h4("Observations"),
tableOutput("view")
)
)
))
)

View File

@@ -1,7 +1,7 @@
library(shiny)
# Define server logic for random distribution application
shinyServer(function(input, output) {
function(input, output) {
# Reactive expression to generate the requested distribution. This is
# called whenever the inputs change. The output expressions defined
@@ -39,4 +39,4 @@ shinyServer(function(input, output) {
data.frame(x=data())
})
})
}

View File

@@ -1,6 +1,6 @@
library(shiny)
shinyServer(function(input, output) {
function(input, output) {
output$contents <- renderTable({
# input$file1 will be NULL initially. After the user selects
@@ -17,4 +17,4 @@ shinyServer(function(input, output) {
read.csv(inFile$datapath, header=input$header, sep=input$sep,
quote=input$quote)
})
})
}

View File

@@ -1,6 +1,6 @@
library(shiny)
shinyUI(fluidPage(
fluidPage(
titlePanel("Uploading Files"),
sidebarLayout(
sidebarPanel(
@@ -25,4 +25,4 @@ shinyUI(fluidPage(
tableOutput('contents')
)
)
))
)

View File

@@ -1,4 +1,4 @@
shinyServer(function(input, output) {
function(input, output) {
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
@@ -18,4 +18,4 @@ shinyServer(function(input, output) {
write.csv(datasetInput(), file)
}
)
})
}

View File

@@ -1,4 +1,4 @@
shinyUI(fluidPage(
fluidPage(
titlePanel('Downloading Data'),
sidebarLayout(
sidebarPanel(
@@ -10,4 +10,4 @@ shinyUI(fluidPage(
tableOutput('table')
)
)
))
)

View File

@@ -1,6 +1,6 @@
shinyServer(function(input, output, session) {
function(input, output, session) {
output$currentTime <- renderText({
invalidateLater(1000, session)
paste("The current time is", Sys.time())
})
})
}

View File

@@ -1,3 +1,3 @@
shinyUI(fluidPage(
fluidPage(
textOutput("currentTime")
))
)

View File

@@ -5,6 +5,8 @@ sd_section("UI Layout",
"bootstrapPage",
"column",
"conditionalPanel",
"fillPage",
"fillRow",
"fixedPage",
"fluidPage",
"headerPanel",
@@ -42,7 +44,10 @@ sd_section("UI Inputs",
"sliderInput",
"submitButton",
"textInput",
"textAreaInput",
"passwordInput",
"modalButton",
"updateActionButton",
"updateCheckboxGroupInput",
"updateCheckboxInput",
"updateDateInput",
@@ -52,14 +57,15 @@ sd_section("UI Inputs",
"updateSelectInput",
"updateSliderInput",
"updateTabsetPanel",
"updateTextInput"
"updateTextInput",
"updateTextAreaInput",
"updateQueryString"
)
)
sd_section("UI Outputs",
"Functions for creating user interface elements that, in conjunction with rendering functions, display different kinds of output from your application.",
c(
"htmlOutput",
"imageOutput",
"plotOutput",
"outputOptions",
"tableOutput",
@@ -67,7 +73,11 @@ sd_section("UI Outputs",
"verbatimTextOutput",
"downloadButton",
"Progress",
"withProgress"
"withProgress",
"modalDialog",
"urlModal",
"showModal",
"showNotification"
)
)
sd_section("Interface builder functions",
@@ -79,7 +89,12 @@ sd_section("Interface builder functions",
"singleton",
"tag",
"validateCssUnit",
"withTags"
"withTags",
"htmlTemplate",
"bootstrapLib",
"suppressDependencies",
"insertUI",
"removeUI"
)
)
sd_section("Rendering functions",
@@ -100,23 +115,25 @@ sd_section("Rendering functions",
"reactiveUI"
)
)
sd_section("Reactive constructs",
sd_section("Reactive programming",
"A sub-library that provides reactive programming facilities for R.",
c(
"invalidateLater",
"is.reactivevalues",
"isolate",
"makeReactiveBinding",
"reactive",
"observe",
"observeEvent",
"reactive",
"reactiveValues",
"reactiveValuesToList",
"is.reactivevalues",
"isolate",
"invalidateLater",
"debounce",
"showReactLog",
"makeReactiveBinding",
"reactiveFileReader",
"reactivePoll",
"reactiveTimer",
"reactiveValues",
"reactiveValuesToList",
"domains",
"showReactLog"
"freezeReactiveValue"
)
)
sd_section("Boilerplate",
@@ -130,9 +147,22 @@ sd_section("Running",
"Functions that are used to run or stop Shiny applications.",
c(
"runApp",
"runGadget",
"runExample",
"runGadget",
"runUrl",
"stopApp"
"stopApp",
"viewer"
)
)
sd_section("Bookmarking state",
"Functions that are used for bookmarking and restoring state.",
c(
"bookmarkButton",
"enableBookmarking",
"setBookmarkExclude",
"showBookmarkUrlModal",
"onBookmark"
)
)
sd_section("Extending Shiny",
@@ -148,18 +178,43 @@ sd_section("Extending Shiny",
sd_section("Utility functions",
"Miscellaneous utilities that may be useful to advanced users or when extending Shiny.",
c(
"req",
"validate",
"session",
"shinyOptions",
"safeError",
"onFlush",
"restoreInput",
"applyInputHandlers",
"exprToFunction",
"installExprFunction",
"parseQueryString",
"plotPNG",
"exportTestValues",
"repeatable",
"shinyDeprecated",
"serverInfo",
"shiny-options"
)
)
sd_section("Plot interaction",
"Functions related to interactive plots",
c(
"brushedPoints",
"brushOpts",
"clickOpts",
"dblclickOpts",
"hoverOpts",
"nearPoints"
)
)
sd_section("Modules",
"Functions for modularizing Shiny apps",
c(
"NS",
"callModule"
)
)
sd_section("Embedding",
"Functions that are intended for third-party packages that embed Shiny applications.",
c(

View File

@@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<head>
{{ headContent() }}
</head>
{{ body }}
</html>

View File

@@ -1,35 +0,0 @@
context("staticdocs")
test_that("All man pages have an entry in staticdocs/index.r", {
if (!all(file.exists(c('../../inst/staticdocs', '../../man')))) {
# This test works only when run against a package directory
return()
}
# Known not to be indexed
known_unindexed <- c("shiny-package", "knitr_methods", "knitr_methods_htmltools")
indexed_topics <- local({
result <- character(0)
sd_section <- function(dummy1, dummy2, section_topics) {
result <<- c(result, section_topics)
}
source("../../inst/staticdocs/index.r", local = TRUE)
result
})
all_topics <- sub("\\.Rd", "", list.files("../../man", pattern = "*.Rd"))
# This test ensures that every documented topic is included in
# staticdocs/index.r, unless explicitly waived by specifying it
# in the known_unindexed variable above.
missing <- setdiff(sort(all_topics), sort(c(known_unindexed, indexed_topics)))
unknown <- setdiff(sort(c(known_unindexed, indexed_topics)), sort(all_topics))
expect_equal(length(missing), 0,
info = paste("Functions missing from index:\n",
paste(" ", missing, sep = "", collapse = "\n"),
sep = ""))
expect_equal(length(unknown), 0,
info = paste("Unrecognized functions in index.r:\n",
paste(" ", unknown, sep = "", collapse = "\n"),
sep = ""))
})

File diff suppressed because it is too large Load Diff

3
inst/www/shared/babel-polyfill.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,9 +1,8 @@
/*!
* Bootstrap v3.3.1 (http://getbootstrap.com)
* Copyright 2011-2014 Twitter, Inc.
* Bootstrap v3.3.7 (http://getbootstrap.com)
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
.btn-default,
.btn-primary,
.btn-success,
@@ -29,6 +28,27 @@
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
}
.btn-default.disabled,
.btn-primary.disabled,
.btn-success.disabled,
.btn-info.disabled,
.btn-warning.disabled,
.btn-danger.disabled,
.btn-default[disabled],
.btn-primary[disabled],
.btn-success[disabled],
.btn-info[disabled],
.btn-warning[disabled],
.btn-danger[disabled],
fieldset[disabled] .btn-default,
fieldset[disabled] .btn-primary,
fieldset[disabled] .btn-success,
fieldset[disabled] .btn-info,
fieldset[disabled] .btn-warning,
fieldset[disabled] .btn-danger {
-webkit-box-shadow: none;
box-shadow: none;
}
.btn-default .badge,
.btn-primary .badge,
.btn-success .badge,
@@ -63,8 +83,24 @@
background-color: #e0e0e0;
border-color: #dbdbdb;
}
.btn-default:disabled,
.btn-default[disabled] {
.btn-default.disabled,
.btn-default[disabled],
fieldset[disabled] .btn-default,
.btn-default.disabled:hover,
.btn-default[disabled]:hover,
fieldset[disabled] .btn-default:hover,
.btn-default.disabled:focus,
.btn-default[disabled]:focus,
fieldset[disabled] .btn-default:focus,
.btn-default.disabled.focus,
.btn-default[disabled].focus,
fieldset[disabled] .btn-default.focus,
.btn-default.disabled:active,
.btn-default[disabled]:active,
fieldset[disabled] .btn-default:active,
.btn-default.disabled.active,
.btn-default[disabled].active,
fieldset[disabled] .btn-default.active {
background-color: #e0e0e0;
background-image: none;
}
@@ -88,8 +124,24 @@
background-color: #265a88;
border-color: #245580;
}
.btn-primary:disabled,
.btn-primary[disabled] {
.btn-primary.disabled,
.btn-primary[disabled],
fieldset[disabled] .btn-primary,
.btn-primary.disabled:hover,
.btn-primary[disabled]:hover,
fieldset[disabled] .btn-primary:hover,
.btn-primary.disabled:focus,
.btn-primary[disabled]:focus,
fieldset[disabled] .btn-primary:focus,
.btn-primary.disabled.focus,
.btn-primary[disabled].focus,
fieldset[disabled] .btn-primary.focus,
.btn-primary.disabled:active,
.btn-primary[disabled]:active,
fieldset[disabled] .btn-primary:active,
.btn-primary.disabled.active,
.btn-primary[disabled].active,
fieldset[disabled] .btn-primary.active {
background-color: #265a88;
background-image: none;
}
@@ -113,8 +165,24 @@
background-color: #419641;
border-color: #3e8f3e;
}
.btn-success:disabled,
.btn-success[disabled] {
.btn-success.disabled,
.btn-success[disabled],
fieldset[disabled] .btn-success,
.btn-success.disabled:hover,
.btn-success[disabled]:hover,
fieldset[disabled] .btn-success:hover,
.btn-success.disabled:focus,
.btn-success[disabled]:focus,
fieldset[disabled] .btn-success:focus,
.btn-success.disabled.focus,
.btn-success[disabled].focus,
fieldset[disabled] .btn-success.focus,
.btn-success.disabled:active,
.btn-success[disabled]:active,
fieldset[disabled] .btn-success:active,
.btn-success.disabled.active,
.btn-success[disabled].active,
fieldset[disabled] .btn-success.active {
background-color: #419641;
background-image: none;
}
@@ -138,8 +206,24 @@
background-color: #2aabd2;
border-color: #28a4c9;
}
.btn-info:disabled,
.btn-info[disabled] {
.btn-info.disabled,
.btn-info[disabled],
fieldset[disabled] .btn-info,
.btn-info.disabled:hover,
.btn-info[disabled]:hover,
fieldset[disabled] .btn-info:hover,
.btn-info.disabled:focus,
.btn-info[disabled]:focus,
fieldset[disabled] .btn-info:focus,
.btn-info.disabled.focus,
.btn-info[disabled].focus,
fieldset[disabled] .btn-info.focus,
.btn-info.disabled:active,
.btn-info[disabled]:active,
fieldset[disabled] .btn-info:active,
.btn-info.disabled.active,
.btn-info[disabled].active,
fieldset[disabled] .btn-info.active {
background-color: #2aabd2;
background-image: none;
}
@@ -163,8 +247,24 @@
background-color: #eb9316;
border-color: #e38d13;
}
.btn-warning:disabled,
.btn-warning[disabled] {
.btn-warning.disabled,
.btn-warning[disabled],
fieldset[disabled] .btn-warning,
.btn-warning.disabled:hover,
.btn-warning[disabled]:hover,
fieldset[disabled] .btn-warning:hover,
.btn-warning.disabled:focus,
.btn-warning[disabled]:focus,
fieldset[disabled] .btn-warning:focus,
.btn-warning.disabled.focus,
.btn-warning[disabled].focus,
fieldset[disabled] .btn-warning.focus,
.btn-warning.disabled:active,
.btn-warning[disabled]:active,
fieldset[disabled] .btn-warning:active,
.btn-warning.disabled.active,
.btn-warning[disabled].active,
fieldset[disabled] .btn-warning.active {
background-color: #eb9316;
background-image: none;
}
@@ -188,8 +288,24 @@
background-color: #c12e2a;
border-color: #b92c28;
}
.btn-danger:disabled,
.btn-danger[disabled] {
.btn-danger.disabled,
.btn-danger[disabled],
fieldset[disabled] .btn-danger,
.btn-danger.disabled:hover,
.btn-danger[disabled]:hover,
fieldset[disabled] .btn-danger:hover,
.btn-danger.disabled:focus,
.btn-danger[disabled]:focus,
fieldset[disabled] .btn-danger:focus,
.btn-danger.disabled.focus,
.btn-danger[disabled].focus,
fieldset[disabled] .btn-danger.focus,
.btn-danger.disabled:active,
.btn-danger[disabled]:active,
fieldset[disabled] .btn-danger:active,
.btn-danger.disabled.active,
.btn-danger[disabled].active,
fieldset[disabled] .btn-danger.active {
background-color: #c12e2a;
background-image: none;
}
@@ -254,6 +370,7 @@
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-radius: 4px;
}
.navbar-inverse .navbar-nav > .open > a,
.navbar-inverse .navbar-nav > .active > a {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

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