Compare commits

...

318 Commits

Author SHA1 Message Date
Barbara Borges Ribeiro
e71e8db0a2 added NEWS entry 2016-10-25 14:23:01 +01:00
Barbara Borges Ribeiro
6cc2f875b0 fix tests 2016-10-25 14:17:02 +01:00
Barbara Borges Ribeiro
bcbc6b8b92 allow for Radio Buttons to have choice names using HTML 2016-10-25 13:47:32 +01: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
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
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
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
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
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
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
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
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
307 changed files with 28358 additions and 15290 deletions

View File

@@ -16,3 +16,5 @@
^CONTRIBUTING.md$
^cran-comments.md$
^.*\.o$
^appveyor\.yml$
^revdep$

View File

@@ -1,4 +1,8 @@
language: r
r:
- oldrel
- release
- devel
sudo: false
cache: packages

View File

@@ -1,8 +1,7 @@
Package: shiny
Type: Package
Title: Web Application Framework for R
Version: 0.13.2.9004
Date: 2016-02-17
Version: 0.14.1.9001
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",
@@ -83,9 +82,11 @@ Suggests:
ggplot2
URL: http://shiny.rstudio.com
BugReports: https://github.com/rstudio/shiny/issues
VignetteBuilder: knitr
Collate:
'app.R'
'bookmark-state-local.R'
'stack.R'
'bookmark-state.R'
'bootstrap-layout.R'
'conditions.R'
'map.R'
@@ -95,7 +96,6 @@ Collate:
'cache.R'
'diagnose.R'
'fileupload.R'
'stack.R'
'graph.R'
'hooks.R'
'html-deps.R'
@@ -116,6 +116,7 @@ Collate:
'input-slider.R'
'input-submit.R'
'input-text.R'
'input-textarea.R'
'input-utils.R'
'insert-ui.R'
'jqueryui.R'
@@ -132,8 +133,10 @@ Collate:
'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'

View File

@@ -11,6 +11,7 @@ S3method("[",shinyoutput)
S3method("[<-",reactivevalues)
S3method("[<-",shinyoutput)
S3method("[[",reactivevalues)
S3method("[[",session_proxy)
S3method("[[",shinyoutput)
S3method("[[<-",reactivevalues)
S3method("[[<-",shinyoutput)
@@ -37,8 +38,10 @@ export(actionButton)
export(actionLink)
export(addResourcePath)
export(animationOptions)
export(applyInputHandlers)
export(as.shiny.appobj)
export(basicPage)
export(bookmarkButton)
export(bootstrapLib)
export(bootstrapPage)
export(br)
@@ -46,7 +49,6 @@ export(browserViewer)
export(brushOpts)
export(brushedPoints)
export(callModule)
export(cancelOutput)
export(captureStackTraces)
export(checkboxGroupInput)
export(checkboxInput)
@@ -66,6 +68,7 @@ export(downloadButton)
export(downloadHandler)
export(downloadLink)
export(em)
export(enableBookmarking)
export(eventReactive)
export(exprToFunction)
export(extractStackTrace)
@@ -80,7 +83,9 @@ export(flowLayout)
export(fluidPage)
export(fluidRow)
export(formatStackTrace)
export(freezeReactiveValue)
export(getDefaultReactiveDomain)
export(getShinyOption)
export(h1)
export(h2)
export(h3)
@@ -110,6 +115,7 @@ 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)
@@ -132,7 +138,14 @@ 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)
@@ -170,6 +183,7 @@ export(renderText)
export(renderUI)
export(repeatable)
export(req)
export(restoreInput)
export(runApp)
export(runExample)
export(runGadget)
@@ -180,12 +194,15 @@ export(safeError)
export(selectInput)
export(selectizeInput)
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)
@@ -209,6 +226,7 @@ export(tagAppendChildren)
export(tagList)
export(tagSetChildren)
export(tags)
export(textAreaInput)
export(textInput)
export(textOutput)
export(titlePanel)
@@ -221,12 +239,15 @@ 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)

1047
NEWS

File diff suppressed because it is too large Load Diff

1026
NEWS.md Normal file

File diff suppressed because it is too large Load Diff

41
R/app.R
View File

@@ -27,6 +27,12 @@
#' 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.
#'
@@ -59,10 +65,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 +81,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"
)
}
@@ -113,7 +130,9 @@ shinyAppDir <- function(appDir, options=list()) {
#' @export
shinyAppFile <- function(appFile, options=list()) {
appFile <- normalizePath(appFile, mustWork = TRUE)
shinyAppDir_appR(basename(appFile), dirname(appFile), options = options)
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)
@@ -178,6 +197,8 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
}
}
shinyOptions(appDir = appDir)
oldwd <- NULL
monitorHandle <- NULL
onStart <- function() {
@@ -199,7 +220,8 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
serverFuncSource = serverFuncSource,
onStart = onStart,
onEnd = onEnd,
options = options),
options = options
),
class = "shiny.appobj"
)
}
@@ -209,13 +231,13 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
# ignored when checking extensions. If any changes are detected, all connected
# Shiny sessions are reloaded.
#
# Use option(shiny.autoreload = TRUE) to enable this behavior. Since monitoring
# 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:
# option(shiny.autoreload.pattern = glob2rx("ui.R"))
# options(shiny.autoreload.pattern = glob2rx("ui.R"))
#
# The return value is a function that halts monitoring when called.
initAutoReloadMonitor <- function(dir) {
@@ -252,7 +274,8 @@ initAutoReloadMonitor <- function(dir) {
# 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()) {
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
@@ -265,6 +288,8 @@ shinyAppDir_appR <- function(fileName, appDir, options=list()) {
if (!is.shiny.appobj(result))
stop("app.R did not return a shiny.appobj object.")
unconsumeAppOptions(result$appOptions)
return(result)
}
)

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

1113
R/bookmark-state.R Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -418,7 +418,6 @@ flowLayout <- function(..., cellArgs = list()) {
#' suitable for wrapping inputs.
#'
#' @param ... Input controls or other HTML elements.
#'
#' @export
inputPanel <- function(...) {
div(class = "shiny-input-panel",

View File

@@ -25,7 +25,6 @@ NULL
#' \code{\link{fluidPage}} function instead.
#'
#' @seealso \code{\link{fluidPage}}, \code{\link{fixedPage}}
#'
#' @export
bootstrapPage <- function(..., title = NULL, responsive = NULL, theme = NULL) {
@@ -61,7 +60,7 @@ bootstrapPage <- function(..., title = NULL, responsive = NULL, theme = NULL) {
#' @inheritParams bootstrapPage
#' @export
bootstrapLib <- function(theme = NULL) {
htmlDependency("bootstrap", "3.3.6",
htmlDependency("bootstrap", "3.3.7",
c(
href = "shared/bootstrap",
file = system.file("www/shared/bootstrap", package = "shiny")
@@ -153,7 +152,6 @@ basicPage <- function(...) {
#' div(style = "background-color: blue; width: 100%; height: 100%;")
#' )
#' )
#'
#' @export
fillPage <- function(..., padding = 0, title = NULL, bootstrap = TRUE,
theme = NULL) {
@@ -215,7 +213,6 @@ collapseSizes <- function(padding) {
#' plotOutput("distPlot")
#' )
#' )
#'
#' @export
pageWithSidebar <- function(headerPanel,
sidebarPanel,
@@ -338,6 +335,9 @@ navbarPage <- function(title,
if (inverse)
navbarClass <- paste(navbarClass, "navbar-inverse")
if (!is.null(id))
selected <- restoreInput(id = id, default = selected)
# build the tabset
tabs <- list(...)
tabset <- buildTabset(tabs, "nav navbar-nav", NULL, id, selected)
@@ -430,7 +430,6 @@ headerPanel <- function(title, windowTitle=title) {
#'
#' @param ... UI elements to include inside the panel.
#' @return The newly created panel.
#'
#' @export
wellPanel <- function(...) {
div(class="well", ...)
@@ -534,7 +533,6 @@ mainPanel <- function(..., width = 8) {
#' )
#' )
#' )
#'
#' @export
conditionalPanel <- function(condition, ...) {
div('data-display-if'=condition, ...)
@@ -635,9 +633,13 @@ tabsetPanel <- function(...,
version = "0.10.2.2")
}
if (!is.null(id))
selected <- restoreInput(id = id, default = selected)
# build the tabset
tabs <- list(...)
type <- match.arg(type)
tabset <- buildTabset(tabs, paste0("nav nav-", type), NULL, id, selected)
# create the content
@@ -700,6 +702,9 @@ navlistPanel <- function(...,
tags$li(class="navbar-brand", text)
}
if (!is.null(id))
selected <- restoreInput(id = id, default = selected)
# build the tabset
tabs <- list(...)
tabset <- buildTabset(tabs,
@@ -1495,7 +1500,6 @@ downloadLink <- function(outputId, label="Download", class=NULL) {
#' tabPanel("Summary", icon = icon("list-alt")),
#' tabPanel("Table", icon = icon("table"))
#' )
#'
#' @export
icon <- function(name, class = NULL, lib = "font-awesome") {
prefixes <- list(
@@ -1523,7 +1527,7 @@ icon <- function(name, class = NULL, lib = "font-awesome") {
# font-awesome needs an additional dependency (glyphicon is in bootstrap)
if (lib == "font-awesome") {
htmlDependencies(iconTag) <- htmlDependency(
"font-awesome", "4.5.0", c(href="shared/font-awesome"),
"font-awesome", "4.6.3", c(href="shared/font-awesome"),
stylesheet = "css/font-awesome.min.css"
)
}

View File

@@ -76,7 +76,7 @@ getCallNames <- function(calls) {
}
getLocs <- function(calls) {
sapply(calls, function(call) {
vapply(calls, function(call) {
srcref <- attr(call, "srcref", exact = TRUE)
if (!is.null(srcref)) {
srcfile <- attr(srcref, "srcfile", exact = TRUE)
@@ -86,7 +86,7 @@ getLocs <- function(calls) {
}
}
return("")
})
}, character(1))
}
#' @details \code{captureStackTraces} runs the given \code{expr} and if any

View File

@@ -94,7 +94,7 @@ FileUploadContext <- R6Class(
},
createUploadOperation = function(fileInfos) {
while (TRUE) {
id <- paste(as.raw(p_runif(12, min=0, max=0xFF)), collapse='')
id <- createUniqueId(12)
private$ids <- c(private$ids, id)
dir <- file.path(private$basedir, id)
if (!dir.create(dir))

View File

@@ -43,7 +43,6 @@ writeReactLog <- function(file=stdout(), sessionToken = NULL) {
#'
#' @param time A boolean that specifies whether or not to display the
#' time that each reactive.
#'
#' @export
showReactLog <- function(time = TRUE) {
utils::browseURL(renderReactLog(time = as.logical(time)))

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

View File

@@ -25,7 +25,6 @@
#' 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, ...) {

View File

@@ -37,13 +37,16 @@
#' }
#'
#' @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),
...
)
@@ -52,9 +55,12 @@ actionButton <- function(inputId, label, icon = NULL, width = NULL, ...) {
#' @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),
...
)

View File

@@ -24,6 +24,9 @@
#' }
#' @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"

View File

@@ -38,6 +38,8 @@
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))

View File

@@ -21,23 +21,26 @@
#'
#' @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.
#' \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 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 "bg", "ca", "cs", "da", "de", "el", "es", "fi",
#' "fr", "he", "hr", "hu", "id", "is", "it", "ja", "kr", "lt", "lv", "ms",
#' "nb", "nl", "pl", "pt", "pt-BR", "ro", "rs", "rs-latin", "ru", "sk", "sl",
#' "sv", "sw", "th", "tr", "uk", "zh-CN", and "zh-TW".
#' 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}}
@@ -60,7 +63,7 @@
#'
#' # Use different language and different first day of week
#' dateInput("date5", "Date:",
#' language = "de",
#' language = "ru",
#' weekstart = 1),
#'
#' # Start with decade view instead of default month view
@@ -81,29 +84,36 @@ dateInput <- function(inputId, label, value = NULL, min = NULL, max = NULL,
if (inherits(min, "Date")) min <- format(min, "%Y-%m-%d")
if (inherits(max, "Date")) max <- format(max, "%Y-%m-%d")
attachDependencies(
tags$div(id = inputId,
class = "shiny-date-input form-group shiny-input-container",
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
value <- restoreInput(id = inputId, default = value)
controlLabel(inputId, label),
tags$input(type = "text",
# datepicker class necessary for dropdown to display correctly
class = "form-control datepicker",
`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` = 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.0.2", c(href = "shared/datepicker"),
"bootstrap-datepicker", "1.6.4", c(href = "shared/datepicker"),
script = "js/bootstrap-datepicker.min.js",
stylesheet = "css/datepicker.css")
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>"
)

View File

@@ -82,6 +82,10 @@ dateRangeInput <- function(inputId, label, start = NULL, end = NULL,
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",

View File

@@ -72,16 +72,48 @@
fileInput <- function(inputId, label, multiple = FALSE, accept = NULL,
width = NULL) {
inputTag <- tags$input(id = inputId, name = inputId, type = "file")
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),
inputTag,
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",

View File

@@ -1,4 +1,3 @@
#' Create a numeric input control
#'
#' Create an input control for entry of numeric values
@@ -29,6 +28,8 @@
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))

View File

@@ -55,6 +55,8 @@ radioButtons <- function(inputId, label, choices, selected = 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)

View File

@@ -54,6 +54,9 @@
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)
@@ -169,7 +172,7 @@ selectizeIt <- function(inputId, select, options, nonempty = FALSE) {
if ('drag_drop' %in% options$plugins) {
selectizeDep <- list(selectizeDep, htmlDependency(
'jqueryui', '1.11.4', c(href = 'shared/jqueryui'),
'jqueryui', '1.12.1', c(href = 'shared/jqueryui'),
script = 'jquery-ui.min.js'
))
}

View File

@@ -85,6 +85,8 @@ sliderInput <- function(inputId, label, min, max, value, step = NULL,
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)
@@ -161,7 +163,6 @@ sliderInput <- function(inputId, label, min, max, value, step = NULL,
`data-grid` = ticks,
`data-grid-num` = n_ticks,
`data-grid-snap` = FALSE,
`data-prettify-separator` = sep,
`data-prefix` = pre,
`data-postfix` = post,
`data-keyboard` = TRUE,
@@ -173,6 +174,12 @@ sliderInput <- function(inputId, label, min, max, value, step = NULL,
`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"
@@ -242,7 +249,6 @@ hasDecimals <- function(value) {
#' 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,

View File

@@ -32,6 +32,8 @@
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),

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

View File

@@ -4,10 +4,13 @@ controlLabel <- function(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.
# refers to values. Below is a function for backward compatibility. It also
# coerces the value to `character`.
validateSelected <- function(selected, choices, inputId) {
# drop names, otherwise toJSON() keeps them too
selected <- unname(selected)
# 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)
@@ -48,10 +51,10 @@ generateOptions <- function(inputId, choices, selected, inline, type = 'checkbox
# 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))
tags$label(class = paste0(type, "-inline"), inputTag, tags$span(HTML(name)))
} else {
tags$div(class = type,
tags$label(inputTag, tags$span(name))
tags$label(inputTag, tags$span(HTML(name)))
)
}
},
@@ -63,7 +66,7 @@ generateOptions <- function(inputId, choices, selected, inline, type = 'checkbox
# Takes a vector or list, and adds names (same as the value) to any entries
# without names.
# 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,
@@ -79,7 +82,7 @@ choicesWithNames <- function(choices) {
if (is.list(val))
listify(val)
else if (length(val) == 1 && is.null(names(val)))
val
as.character(val)
else
makeNamed(as.list(val))
})

View File

@@ -17,11 +17,6 @@
#' will determine the element(s) relative to which you want to insert your
#' UI object.
#'
#' @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 where Where your UI object should go relative to the selector:
#' \describe{
#' \item{\code{beforeBegin}}{Before the selector element itself}
@@ -42,6 +37,11 @@
#' 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).
@@ -73,12 +73,11 @@
#' # Complete app with UI and server components
#' shinyApp(ui, server)
#' }
#'
#' @export
insertUI <- function(selector,
multiple = FALSE,
where = c("beforeBegin", "afterBegin", "beforeEnd", "afterEnd"),
ui,
multiple = FALSE,
immediate = FALSE,
session = getDefaultReactiveDomain()) {
@@ -155,7 +154,6 @@ insertUI <- function(selector,
#' # Complete app with UI and server components
#' shinyApp(ui, server)
#' }
#'
#' @export
removeUI <- function(selector,
multiple = FALSE,

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,8 +79,6 @@ 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/jquery-ui.min.js'))),
divTag,
tags$script('$(".draggable").draggable();')

View File

@@ -36,11 +36,15 @@ removeModal <- function(session = getDefaultReactiveDomain()) {
#' @param ... UI elements for the body of the modal dialog box.
#' @param title An optional title for the dialog.
#' @param footer UI for footer. Use \code{NULL} for no footer.
#' @param size One of \code{"s"} for small, \code{"m"} (the default) for medium,
#' or \code{"l"} for large.
#' @param easyClose If \code{TRUE}, the modal dialog can be dismissed by
#' 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()) {
@@ -80,7 +84,7 @@ removeModal <- function(session = getDefaultReactiveDomain()) {
#' )
#'
#'
# Display a modal that requires valid input before continuing.
#' # Display a modal that requires valid input before continuing.
#' shinyApp(
#' ui = basicPage(
#' actionButton("show", "Show modal dialog"),
@@ -120,7 +124,8 @@ removeModal <- function(session = getDefaultReactiveDomain()) {
#' # message.
#' observeEvent(input$ok, {
#' # Check that data object exists and is data frame.
#' if (exists(input$dataset) && is.data.frame(get(input$dataset))) {
#' if (!is.null(input$dataset) && nzchar(input$dataset) &&
#' exists(input$dataset) && is.data.frame(get(input$dataset))) {
#' vals$data <- get(input$dataset)
#' removeModal()
#' } else {
@@ -140,13 +145,18 @@ removeModal <- function(session = getDefaultReactiveDomain()) {
#' }
#' @export
modalDialog <- function(..., title = NULL, footer = modalButton("Dismiss"),
easyClose = FALSE) {
size = c("m", "s", "l"), easyClose = FALSE, fade = TRUE) {
div(id = "shiny-modal", class = "modal fade", tabindex = "-1",
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",
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)

View File

@@ -1,4 +1,4 @@
# Creates an object whose $ and $<- pass through to the parent
# 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, ...) {
@@ -14,18 +14,24 @@ createSessionProxy <- function(parentSession, ...) {
#' @export
`$.session_proxy` <- function(x, name) {
if (name %in% names(x[["overrides"]]))
x[["overrides"]][[name]]
if (name %in% names(.subset2(x, "overrides")))
.subset2(x, "overrides")[[name]]
else
x[["parent"]][[name]]
.subset2(x, "parent")[[name]]
}
#' @export
`[[.session_proxy` <- `$.session_proxy`
#' @export
`$<-.session_proxy` <- function(x, name, value) {
x[["parent"]][[name]] <- value
x
stop("Attempted to assign value on session proxy.")
}
`[[<-.session_proxy` <- `$<-.session_proxy`
#' Invoke a Shiny module
#'
#' Shiny's module feature lets you break complicated UI and server logic into
@@ -42,7 +48,6 @@ createSessionProxy <- function(parentSession, ...) {
#'
#' @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)

View File

@@ -75,7 +75,7 @@ showNotification <- function(ui, action = NULL, duration = 5,
{
if (is.null(id))
id <- randomID()
id <- createUniqueId(8)
res <- processDeps(ui, session)
actionRes <- processDeps(action, session)

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.
@@ -89,18 +101,22 @@ Progress <- R6Class(
portable = TRUE,
public = list(
initialize = function(session = getDefaultReactiveDomain(), min = 0, max = 1) {
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 <- randomID()
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) {
@@ -122,7 +138,8 @@ Progress <- R6Class(
id = private$id,
message = message,
detail = detail,
value = value
value = value,
style = private$style
))
private$session$sendProgress('update', data)
@@ -148,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
}
),
@@ -158,6 +177,7 @@ Progress <- R6Class(
id = character(0),
min = numeric(0),
max = numeric(0),
style = character(0),
value = NULL,
closed = logical(0)
)
@@ -186,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.
@@ -206,6 +234,10 @@ 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.
@@ -237,10 +269,12 @@ Progress <- R6Class(
#' @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)
@@ -248,7 +282,9 @@ withProgress <- function(expr, min = 0, max = 1,
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({

View File

@@ -115,13 +115,16 @@ ReactiveEnvironment <- R6Class(
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,7 +55,7 @@ createMockDomain <- function() {
domain$end <- function() {
if (!ended) {
ended <<- TRUE
lapply(callbacks, do.call, list())
callbacks$invoke()
}
invisible()
}

View File

@@ -47,6 +47,7 @@ ReactiveValues <- R6Class(
# For debug purposes
.label = character(0),
.values = 'environment',
.metadata = 'environment',
.dependents = 'environment',
# Dependents for the list of all names, including hidden
.namesDeps = 'Dependents',
@@ -60,32 +61,40 @@ ReactiveValues <- R6Class(
p_randomInt(1000, 10000),
sep="")
.values <<- new.env(parent=emptyenv())
.metadata <<- new.env(parent=emptyenv())
.dependents <<- new.env(parent=emptyenv())
.namesDeps <<- Dependents$new()
.allValuesDeps <<- Dependents$new()
.valuesDeps <<- Dependents$new()
},
get = function(key) {
# Register the "downstream" reactive which is accessing this value, so
# that we know to invalidate them when this value changes.
ctx <- .getReactiveEnvironment()$currentContext()
dep.key <- paste(key, ':', ctx$id, sep='')
if (!exists(dep.key, where=.dependents, inherits=FALSE)) {
if (!exists(dep.key, envir=.dependents, inherits=FALSE)) {
.graphDependsOn(ctx$id, sprintf('%s$%s', .label, key))
assign(dep.key, ctx, pos=.dependents, inherits=FALSE)
.dependents[[dep.key]] <- ctx
ctx$onInvalidate(function() {
rm(list=dep.key, pos=.dependents, inherits=FALSE)
rm(list=dep.key, envir=.dependents, inherits=FALSE)
})
}
if (!exists(key, where=.values, inherits=FALSE))
if (isFrozen(key))
reactiveStop()
if (!exists(key, envir=.values, inherits=FALSE))
NULL
else
base::get(key, pos=.values, inherits=FALSE)
.values[[key]]
},
set = function(key, value) {
hidden <- substr(key, 1, 1) == "."
if (exists(key, where=.values, inherits=FALSE)) {
if (identical(base::get(key, pos=.values, inherits=FALSE), value)) {
if (exists(key, envir=.values, inherits=FALSE)) {
if (identical(.values[[key]], value)) {
return(invisible())
}
}
@@ -98,14 +107,14 @@ ReactiveValues <- R6Class(
else
.valuesDeps$invalidate()
assign(key, value, pos=.values, inherits=FALSE)
.values[[key]] <- value
.graphValueChange(sprintf('names(%s)', .label), ls(.values, all.names=TRUE))
.graphValueChange(sprintf('%s (all)', .label), as.list(.values))
.graphValueChange(sprintf('%s$%s', .label, key), value)
dep.keys <- objects(
pos=.dependents,
envir=.dependents,
pattern=paste('^\\Q', key, ':', '\\E', '\\d+$', sep=''),
all.names=TRUE
)
@@ -118,18 +127,54 @@ ReactiveValues <- R6Class(
)
invisible()
},
mset = function(lst) {
lapply(base::names(lst),
function(name) {
self$set(name, lst[[name]])
})
},
names = function() {
.graphDependsOn(.getReactiveEnvironment()$currentContext()$id,
sprintf('names(%s)', .label))
.namesDeps$register()
return(ls(.values, all.names=TRUE))
},
# Get a metadata value. Does not trigger reactivity.
getMeta = function(key, metaKey) {
# Make sure to use named (not numeric) indexing into list.
metaKey <- as.character(metaKey)
.metadata[[key]][[metaKey]]
},
# Set a metadata value. Does not trigger reactivity.
setMeta = function(key, metaKey, value) {
# Make sure to use named (not numeric) indexing into list.
metaKey <- as.character(metaKey)
if (!exists(key, envir = .metadata, inherits = FALSE)) {
.metadata[[key]] <<- list()
}
.metadata[[key]][[metaKey]] <<- value
},
# Mark a value as frozen If accessed while frozen, a shiny.silent.error will
# be thrown.
freeze = function(key) {
setMeta(key, "frozen", TRUE)
},
thaw = function(key) {
setMeta(key, "frozen", NULL)
},
isFrozen = function(key) {
isTRUE(getMeta(key, "frozen"))
},
toList = function(all.names=FALSE) {
.graphDependsOn(.getReactiveEnvironment()$currentContext()$id,
sprintf('%s (all)', .label))
@@ -140,6 +185,7 @@ ReactiveValues <- R6Class(
return(as.list(.values, all.names=all.names))
},
.setLabel = function(label) {
.label <<- label
}
@@ -186,7 +232,6 @@ ReactiveValues <- R6Class(
#' these objects must be named.
#'
#' @seealso \code{\link{isolate}} and \code{\link{is.reactivevalues}}.
#'
#' @export
reactiveValues <- function(...) {
args <- list(...)
@@ -317,10 +362,25 @@ as.list.reactivevalues <- function(x, all.names=FALSE, ...) {
#' # isolate() can also be used when calling from outside a reactive context (e.g.
#' # at the console)
#' isolate(reactiveValuesToList(values))
#'
#' @export
reactiveValuesToList <- function(x, all.names=FALSE) {
.subset2(x, 'impl')$toList(all.names)
# Default case
res <- .subset2(x, 'impl')$toList(all.names)
prefix <- .subset2(x, 'ns')("")
# Special handling for namespaces
if (nzchar(prefix)) {
fullNames <- names(res)
# Filter out items that match namespace
fullNames <- fullNames[substring(fullNames, 1, nchar(prefix)) == prefix]
res <- res[fullNames]
# Remove namespace prefix
names(res) <- substring(fullNames, nchar(prefix) + 1)
}
res
}
# This function is needed because str() on a reactivevalues object will call
@@ -334,6 +394,67 @@ str.reactivevalues <- function(object, indent.str = " ", ...) {
utils::str(class(object))
}
#' Freeze a reactive value
#'
#' This freezes a reactive value. If the value is accessed while frozen, a
#' "silent" exception is raised and the operation is stopped. This is the same
#' thing that happens if \code{req(FALSE)} is called. The value is thawed
#' (un-frozen; accessing it will no longer raise an exception) when the current
#' reactive domain is flushed. In a Shiny application, this occurs after all of
#' the observers are executed.
#'
#' @param x A \code{\link{reactiveValues}} object (like \code{input}).
#' @param name The name of a value in the \code{\link{reactiveValues}} object.
#'
#' @seealso \code{\link{req}}
#' @examples
#' ## Only run this examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' selectInput("data", "Data Set", c("mtcars", "pressure")),
#' checkboxGroupInput("cols", "Columns (select 2)", character(0)),
#' plotOutput("plot")
#' )
#'
#' server <- function(input, output, session) {
#' observe({
#' data <- get(input$data)
#' # Sets a flag on input$cols to essentially do req(FALSE) if input$cols
#' # is accessed. Without this, an error will momentarily show whenever a
#' # new data set is selected.
#' freezeReactiveValue(input, "cols")
#' updateCheckboxGroupInput(session, "cols", choices = names(data))
#' })
#'
#' output$plot <- renderPlot({
#' # When a new data set is selected, input$cols will have been invalidated
#' # above, and this will essentially do the same as req(FALSE), causing
#' # this observer to stop and raise a silent exception.
#' cols <- input$cols
#' data <- get(input$data)
#'
#' if (length(cols) == 2) {
#' plot(data[[ cols[1] ]], data[[ cols[2] ]])
#' }
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
#' @export
freezeReactiveValue <- function(x, name) {
domain <- getDefaultReactiveDomain()
if (is.null(getDefaultReactiveDomain)) {
stop("freezeReactiveValue() must be called when a default reactive domain is active.")
}
domain$freezeValue(x, name)
invisible()
}
# Observable ----------------------------------------------------------------
Observable <- R6Class(
@@ -499,7 +620,6 @@ Observable <- R6Class(
#' isolate(reactiveB())
#' isolate(reactiveC())
#' isolate(reactiveD())
#'
#' @export
reactive <- function(x, env = parent.frame(), quoted = FALSE, label = NULL,
domain = getDefaultReactiveDomain(),
@@ -562,7 +682,7 @@ srcrefToLabel <- function(srcref, defaultLabel) {
#' @export
print.reactive <- function(x, ...) {
label <- attr(x, "observable")$.label
label <- attr(x, "observable", exact = TRUE)$.label
cat(label, "\n")
}
@@ -573,7 +693,7 @@ is.reactive <- function(x) inherits(x, "reactive")
# Return the number of times that a reactive expression or observer has been run
execCount <- function(x) {
if (is.reactive(x))
return(attr(x, "observable")$.execCount)
return(attr(x, "observable", exact = TRUE)$.execCount)
else if (inherits(x, 'Observer'))
return(x$.execCount)
else
@@ -591,12 +711,18 @@ Observer <- R6Class(
.domain = 'ANY',
.priority = numeric(0),
.autoDestroy = logical(0),
# A function that, when invoked, unsubscribes the autoDestroy
# listener (or NULL if autodestroy is disabled for this observer).
# We must unsubscribe when this observer is destroyed, or else
# the observer cannot be garbage collected until the session ends.
.autoDestroyHandle = 'ANY',
.invalidateCallbacks = list(),
.execCount = integer(0),
.onResume = 'function',
.suspended = logical(0),
.destroyed = logical(0),
.prevId = character(0),
.ctx = NULL,
initialize = function(observerFunc, label, suspended = FALSE, priority = 0,
domain = getDefaultReactiveDomain(),
@@ -611,15 +737,14 @@ registerDebugHook("observerFunc", environment(), label)
..stacktraceon..(observerFunc())
else
observerFunc(),
validation = function(e) {
# It's OK for a validation error to cause an observer to stop
# running
}
# It's OK for shiny.silent.error errors to cause an observer to stop running
shiny.silent.error = function(e) NULL
# validation = function(e) NULL,
# shiny.output.cancel = function(e) NULL
)
}
.label <<- label
.domain <<- domain
.autoDestroy <<- autoDestroy
.priority <<- normalizePriority(priority)
.execCount <<- 0L
.suspended <<- suspended
@@ -627,7 +752,9 @@ registerDebugHook("observerFunc", environment(), label)
.destroyed <<- FALSE
.prevId <<- ''
onReactiveDomainEnded(.domain, self$.onDomainEnded)
.autoDestroy <<- FALSE
.autoDestroyHandle <<- NULL
setAutoDestroy(autoDestroy)
# Defer the first running of this until flushReact is called
.createContext()$invalidate()
@@ -636,7 +763,23 @@ registerDebugHook("observerFunc", environment(), label)
ctx <- Context$new(.domain, .label, type='observer', prevId=.prevId)
.prevId <<- ctx$id
if (!is.null(.ctx)) {
# If this happens, something went wrong.
warning("Created a new context without invalidating previous context.")
}
# Store the context explicitly in the Observer object. This is necessary
# to make sure that when the observer is destroyed, it also gets
# invalidated. Otherwise the upstream reactive (on which the observer
# depends) will hold a (indirect) reference to this context until the
# reactive is invalidated, which may not happen immediately or at all.
# This can lead to a memory leak (#1253).
.ctx <<- ctx
ctx$onInvalidate(function() {
# Context is invalidated, so we don't need to store a reference to it
# anymore.
.ctx <<- NULL
lapply(.invalidateCallbacks, function(invalidateCallback) {
invalidateCallback()
NULL
@@ -689,11 +832,28 @@ registerDebugHook("observerFunc", environment(), label)
"Sets whether this observer should be automatically destroyed when its
domain (if any) ends. If autoDestroy is TRUE and the domain already
ended, then destroy() is called immediately."
if (.autoDestroy == autoDestroy) {
return(.autoDestroy)
}
oldValue <- .autoDestroy
.autoDestroy <<- autoDestroy
if (!is.null(.domain) && .domain$isEnded()) {
destroy()
if (autoDestroy) {
if (!.destroyed && !is.null(.domain)) { # Make sure to not try to destroy twice.
if (.domain$isEnded()) {
destroy()
} else {
.autoDestroyHandle <<- onReactiveDomainEnded(.domain, .onDomainEnded)
}
}
} else {
if (!is.null(.autoDestroyHandle))
.autoDestroyHandle()
.autoDestroyHandle <<- NULL
}
invisible(oldValue)
},
suspend = function() {
@@ -719,8 +879,21 @@ registerDebugHook("observerFunc", environment(), label)
"Prevents this observer from ever executing again (even if a flush has
already been scheduled)."
# Make sure to not try to destory twice.
if (.destroyed)
return()
suspend()
.destroyed <<- TRUE
if (!is.null(.autoDestroyHandle)) {
.autoDestroyHandle()
}
.autoDestroyHandle <<- NULL
if (!is.null(.ctx)) {
.ctx$invalidate()
}
},
.onDomainEnded = function() {
if (isTRUE(.autoDestroy)) {
@@ -749,8 +922,8 @@ registerDebugHook("observerFunc", environment(), label)
#' soon as their dependencies change, they schedule themselves to re-execute.
#'
#' Starting with Shiny 0.10.0, observers are automatically destroyed by default
#' when the \link[=domains]{domain} that owns them ends (e.g. when a Shiny session
#' ends).
#' when the \link[=domains]{domain} that owns them ends (e.g. when a Shiny
#' session ends).
#'
#' @param x An expression (quoted or unquoted). Any return value will be
#' ignored.
@@ -761,12 +934,13 @@ registerDebugHook("observerFunc", environment(), label)
#' This is useful when you want to use an expression that is stored in a
#' variable; to do so, it must be quoted with \code{quote()}.
#' @param label A label for the observer, useful for debugging.
#' @param suspended If \code{TRUE}, start the observer in a suspended state.
#' If \code{FALSE} (the default), start in a non-suspended state.
#' @param suspended If \code{TRUE}, start the observer in a suspended state. If
#' \code{FALSE} (the default), start in a non-suspended state.
#' @param priority An integer or numeric that controls the priority with which
#' this observer should be executed. An observer with a given priority level
#' will always execute sooner than all observers with a lower priority level.
#' Positive, negative, and zero values are allowed.
#' this observer should be executed. A higher value means higher priority: an
#' observer with a higher priority value will execute before all observers
#' with lower priority values. Positive, negative, and zero values are
#' allowed.
#' @param domain See \link{domains}.
#' @param autoDestroy If \code{TRUE} (the default), the observer will be
#' automatically destroyed when its domain (if any) ends.
@@ -825,7 +999,6 @@ registerDebugHook("observerFunc", environment(), label)
#' # In a normal Shiny app, the web client will trigger flush events. If you
#' # are at the console, you can force a flush with flushReact()
#' shiny:::flushReact()
#'
#' @export
observe <- function(x, env=parent.frame(), quoted=FALSE, label=NULL,
suspended=FALSE, priority=0,
@@ -865,9 +1038,9 @@ observe <- function(x, env=parent.frame(), quoted=FALSE, label=NULL,
#' }
#' @export
makeReactiveBinding <- function(symbol, env = parent.frame()) {
if (exists(symbol, where = env, inherits = FALSE)) {
initialValue <- get(symbol, pos = env, inherits = FALSE)
rm(list = symbol, pos = env, inherits = FALSE)
if (exists(symbol, envir = env, inherits = FALSE)) {
initialValue <- env[[symbol]]
rm(list = symbol, envir = env, inherits = FALSE)
}
else
initialValue <- NULL
@@ -975,7 +1148,6 @@ setAutoflush <- local({
#'
#' shinyApp(ui, server)
#' }
#'
#' @export
reactiveTimer <- function(intervalMs=1000, session = getDefaultReactiveDomain()) {
dependents <- Map$new()
@@ -1134,7 +1306,6 @@ coerceToFunc <- function(x) {
#' data()
#' })
#' }
#'
#' @export
reactivePoll <- function(intervalMillis, session, checkFunc, valueFunc) {
intervalMillis <- coerceToFunc(intervalMillis)
@@ -1212,7 +1383,6 @@ reactivePoll <- function(intervalMillis, session, checkFunc, valueFunc) {
#' })
#' }
#' }
#'
#' @export
reactiveFileReader <- function(intervalMillis, session, filePath, readFunc, ...) {
filePath <- coerceToFunc(filePath)
@@ -1300,7 +1470,6 @@ reactiveFileReader <- function(intervalMillis, session, filePath, readFunc, ...)
#'
#' # isolate also works if the reactive expression accesses values from the
#' # input object, like input$x
#'
#' @export
isolate <- function(expr) {
ctx <- Context$new(getDefaultReactiveDomain(), '[isolate]', type='isolate')
@@ -1322,7 +1491,6 @@ isolate <- function(expr) {
#' @return The value of \code{expr}.
#'
#' @seealso \code{\link{isolate}}
#'
#' @export
maskReactiveContext <- function(expr) {
.getReactiveEnvironment()$runWith(NULL, function() {
@@ -1449,7 +1617,6 @@ maskReactiveContext <- function(expr) {
#' }
#' shinyApp(ui=ui, server=server)
#' }
#'
#' @export
observeEvent <- function(eventExpr, handlerExpr,
event.env = parent.frame(), event.quoted = FALSE,

View File

@@ -165,9 +165,9 @@ renderPlot <- function(expr, width='auto', height='auto', res=72, ...,
plotObj <- reactive(label = "plotObj", {
if (execOnResize) {
isolate({ dims <- getDims() })
} else {
dims <- getDims()
} else {
isolate({ dims <- getDims() })
}
if (is.null(dims$width) || is.null(dims$height) ||
@@ -185,6 +185,9 @@ renderPlot <- function(expr, width='auto', height='auto', res=72, ...,
success <-FALSE
tryCatch(
{
# This is necessary to enable displaylist recording
grDevices::dev.control(displaylist = "enable")
# Actually perform the plotting
result <- withVisible(func())
success <- TRUE
@@ -415,23 +418,39 @@ getPrevPlotCoordmap <- function(width, height) {
# 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)
# 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
@@ -473,7 +492,11 @@ getGgplotCoordmap <- function(p, pixelratio, res) {
# 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]]
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],
@@ -482,9 +505,13 @@ getGgplotCoordmap <- function(p, pixelratio, res) {
)
# 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") {
domain$left <- -domain$left
domain$right <- -domain$right
@@ -519,10 +546,18 @@ getGgplotCoordmap <- function(p, pixelratio, res) {
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)) {
@@ -576,6 +611,11 @@ getGgplotCoordmap <- function(p, pixelratio, res) {
)
}
# Look for CoordFlip
if (inherits(b$plot$coordinates, "CoordFlip")) {
mappings[c("x", "y")] <- mappings[c("y", "x")]
}
mappings_cache <<- mappings
mappings
}

View File

@@ -46,7 +46,6 @@
#' @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"),
@@ -80,6 +79,8 @@ renderTable <- function(expr, striped = FALSE, hover = FALSE,
digitsWrapper <- createWrapper(digits)
naWrapper <- createWrapper(na)
dots <- list(...) ## used later (but defined here because of scoping)
renderFunc <- function(shinysession, name, ...) {
striped <- stripedWrapper()
hover <- hoverWrapper()
@@ -113,7 +114,6 @@ renderTable <- function(expr, striped = FALSE, hover = FALSE,
return(NULL)
# Separate the ... args to pass to xtable() vs print.xtable()
dots <- list(...)
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)]
@@ -156,15 +156,30 @@ renderTable <- function(expr, striped = FALSE, hover = FALSE,
# Set up print args
print_args <- list(
xtable_res,
x = xtable_res,
type = 'html',
include.rownames = rownames,
include.colnames = colnames,
NA.string = na,
html.table.attributes = paste0("class = '", htmlEscape(classNames, TRUE), "' ",
"style = 'width:", validateCssUnit(width), ";'"))
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

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
}

View File

@@ -71,6 +71,72 @@ removeInputHandler <- function(type){
inputHandlers$remove(type)
}
# Apply input handler to a single input value
applyInputHandler <- function(name, val) {
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.
#'
#' @seealso registerInputHandler
#'
#' @examples
#' applyInputHandlers(list(
#' "m1" = list(list(1, 2), list(3, 4)),
#' "m2:shiny.matrix" = list(list(1, 2), list(3, 4)),
#'
#' "d1" = "2016-01-01",
#' "d2:shiny.date" = "2016-01-01", # Date object
#'
#' "n1" = NULL,
#' "n2:shiny.number" = NULL # Converts to NA
#' ))
#' @export
applyInputHandlers <- function(inputs) {
inputs <- mapply(applyInputHandler, names(inputs), inputs, 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, ...) {
@@ -89,10 +155,29 @@ 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)
as.Date(unlist(datelist))
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, ...){
@@ -104,8 +189,41 @@ registerInputHandler("shiny.datetime", function(val, ...){
as.POSIXct(unlist(times), origin = "1970-01-01", tz = "UTC")
})
registerInputHandler("shiny.action", function(val, ...) {
registerInputHandler("shiny.action", function(val, shinysession, name) {
# mark up the action button value with a special class so we can recognize it later
class(val) <- c(class(val), "shinyActionButtonValue")
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

@@ -49,7 +49,6 @@ registerClient <- function(client) {
#'
#' @examples
#' addResourcePath('datasets', system.file('data', package='datasets'))
#'
#' @export
addResourcePath <- function(prefix, directoryPath) {
prefix <- prefix[1]
@@ -141,7 +140,6 @@ resourcePathHandler <- function(req) {
#' })
#' }
#' }
#'
#' @export
shinyServer <- function(func) {
.globals$server <- list(func)
@@ -232,121 +230,105 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
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 <- 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")
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)
}
# 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)
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]]
}
# 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)
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
)
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()
},
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())
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)
# 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()
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
# 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()
}
flushAllSessions()
})
})
}
@@ -447,6 +429,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()))
}
}
@@ -460,10 +448,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
@@ -565,17 +550,23 @@ runApp <- function(appDir=getwd(),
handlerManager$clear()
}, add = TRUE)
# Enable per-app Shiny options
oldOptionSet <- .globals$options
on.exit({
.globals$options <- oldOptionSet
},add = TRUE)
if (is.null(host) || is.na(host))
host <- '0.0.0.0'
# 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)
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
@@ -659,7 +650,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
@@ -673,6 +671,12 @@ 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))
@@ -735,7 +739,6 @@ runApp <- function(appDir=getwd(),
#'
#' @param returnValue The value that should be returned from
#' \code{\link{runApp}}.
#'
#' @export
stopApp <- function(returnValue = invisible()) {
# reterror will indicate whether retval is an error (i.e. it should be passed
@@ -854,7 +857,6 @@ runExample <- function(example=NA,
#' # ...or as a single app object
#' runGadget(shinyApp(ui, server))
#' }
#'
#' @export
runGadget <- function(app, server = NULL, port = getOption("shiny.port"),
viewer = paneViewer(), stopOnCancel = TRUE) {
@@ -950,3 +952,9 @@ browserViewer <- function(browser = getOption("browser")) {
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)
}
}

518
R/shiny.R
View File

@@ -53,10 +53,10 @@ NULL
#'
#' You can customize the file patterns Shiny will monitor by setting the
#' shiny.autoreload.pattern option. For example, to monitor only ui.R:
#' \code{option(shiny.autoreload.pattern = glob2rx("ui.R"))}
#' \code{options(shiny.autoreload.pattern = glob2rx("ui.R"))}
#'
#' The default polling interval is 500 milliseconds. You can change this
#' by setting e.g. \code{option(shiny.autoreload.interval = 2000)} (every
#' by setting e.g. \code{options(shiny.autoreload.interval = 2000)} (every
#' two seconds).}
#' \item{shiny.reactlog}{If \code{TRUE}, enable logging of reactive events,
#' which can be viewed later with the \code{\link{showReactLog}} function.
@@ -123,10 +123,14 @@ createUniqueId <- function(bytes, prefix = "", suffix = "") {
toJSON <- function(x, ..., dataframe = "columns", null = "null", na = "null",
auto_unbox = TRUE, digits = getOption("shiny.json.digits", 16),
use_signif = TRUE, force = TRUE, POSIXt = "ISO8601", UTC = TRUE,
rownames = FALSE, keep_vec_names = TRUE) {
rownames = FALSE, keep_vec_names = TRUE, strict_atomic = TRUE) {
if (strict_atomic) {
x <- I(x)
}
# I(x) is so that length-1 atomic vectors get put in [].
jsonlite::toJSON(I(x), dataframe = dataframe, null = null, na = na,
jsonlite::toJSON(x, dataframe = dataframe, null = null, na = na,
auto_unbox = auto_unbox, digits = digits, use_signif = use_signif,
force = force, POSIXt = POSIXt, UTC = UTC, rownames = rownames,
keep_vec_names = keep_vec_names, json_verbatim = TRUE, ...)
@@ -282,6 +286,12 @@ workerId <- local({
#' \code{addCustomMessageHandler} will be invoked each time
#' \code{sendCustomMessage} is called on the server.
#' }
#' \item{sendBinaryMessage(type, message)}{
#' Similar to \code{sendCustomMessage}, but the message must be a raw vector
#' and the registration method on the client is
#' \code{Shiny.addBinaryMessageHandler(type, function(message){...})}. The
#' message argument on the client will be a \href{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView}{DataView}.
#' }
#' \item{sendInputMessage(inputId, message)}{
#' Sends a message to an input on the session's client web page; if the input
#' is present and bound on the page at the time the message is received, then
@@ -290,6 +300,29 @@ workerId <- local({
#' from Shiny apps, but through friendlier wrapper functions like
#' \code{\link{updateTextInput}}.
#' }
#' \item{setBookmarkExclude(names)}{
#' Set input names to be excluded from bookmarking.
#' }
#' \item{getBookmarkExclude()}{
#' Returns the set of input names to be excluded from bookmarking.
#' }
#' \item{onBookmark(fun)}{
#' Registers a function that will be called just before bookmarking state.
#' }
#' \item{onBookmarked(fun)}{
#' Registers a function that will be called just after bookmarking state.
#' }
#' \item{onRestore(fun)}{
#' Registers a function that will be called when a session is restored, before
#' all other reactives, observers, and render functions are run.
#' }
#' \item{onRestored(fun)}{
#' Registers a function that will be called when a session is restored, after
#' all other reactives, observers, and render functions are run.
#' }
#' \item{doBookmark()}{
#' Do bookmarking and invoke the onBookmark and onBookmarked callback functions.
#' }
#'
#' @name session
NULL
@@ -316,7 +349,6 @@ NULL
#' @return If \code{id} is missing, returns a function that expects an id string
#' as its only argument and returns that id with the namespace prepended.
#' @seealso \url{http://shiny.rstudio.com/articles/modules.html}
#'
#' @export
NS <- function(namespace, id = NULL) {
if (missing(id)) {
@@ -332,6 +364,7 @@ NS <- function(namespace, id = NULL) {
#' @export
ns.sep <- "-"
#' @include utils.R
ShinySession <- R6Class(
'ShinySession',
@@ -345,7 +378,7 @@ ShinySession <- R6Class(
.outputs = list(), # Keeps track of all the output observer objects
.outputOptions = list(), # Options for each of the output observer objects
progressKeys = 'character',
showcase = 'ANY',
showcase = FALSE,
fileUploadContext = 'FileUploadContext',
.input = 'ANY', # Internal ReactiveValues object for normal input sent from client
.clientData = 'ANY', # Internal ReactiveValues object for other data sent from the client
@@ -354,6 +387,12 @@ ShinySession <- R6Class(
flushCallbacks = 'Callbacks',
flushedCallbacks = 'Callbacks',
inputReceivedCallbacks = 'Callbacks',
bookmarkCallbacks = 'Callbacks',
bookmarkedCallbacks = 'Callbacks',
restoreCallbacks = 'Callbacks',
restoredCallbacks = 'Callbacks',
bookmarkExclude = character(0), # Names of inputs to exclude from bookmarking
sendResponse = function(requestMsg, value) {
if (is.null(requestMsg$tag)) {
warning("Tried to send response for untagged message; method: ",
@@ -420,9 +459,99 @@ ShinySession <- R6Class(
# Clear file upload directories, if present
self$onSessionEnded(private$fileUploadContext$rmUploadDirs)
},
createBookmarkObservers = function() {
# This is to be called from the initialization. It registers observers
# for bookmarking to work.
# Get bookmarking config
store <- getShinyOption("bookmarkStore", default = "disable")
if (store == "disable")
return()
# Warn if trying to enable save-to-server bookmarking on a version of SS,
# SSP, or Connect that doesn't support it.
if (store == "server" && inShinyServer() &&
is.null(getShinyOption("save.interface")))
{
showNotification(
"This app tried to enable saved-to-server bookmarking, but it is not supported by the hosting environment.",
duration = NULL, type = "warning", session = self
)
return()
}
withReactiveDomain(self, {
# This observer fires when the bookmark button is clicked.
observeEvent(self$input[["._bookmark_"]], {
self$doBookmark()
})
# If there was an error initializing the current restore context, show
# notification in the client.
observe({
rc <- getCurrentRestoreContext()
if (!is.null(rc$initErrorMessage)) {
showNotification(
paste("Error in RestoreContext initialization:", rc$initErrorMessage),
duration = NULL, type = "error"
)
}
})
# Run the onRestore function at the beginning of the flush cycle, but after
# the server function has been executed.
observe({
if (private$restoreCallbacks$count() > 0) {
tryCatch(
withLogErrors(
isolate({
rc <- getCurrentRestoreContext()
if (rc$active) {
restoreState <- getCurrentRestoreContext()$asList()
private$restoreCallbacks$invoke(restoreState)
}
})
),
error = function(e) {
showNotification(
paste0("Error calling onRestore callback: ", e$message),
duration = NULL, type = "error"
)
}
)
}
}, priority = 1000000)
# Run the onRestored function after the flush cycle completes and information
# is sent to the client.
self$onFlushed(function() {
if (private$restoredCallbacks$count() > 0) {
tryCatch(
withLogErrors(
isolate({
rc <- getCurrentRestoreContext()
if (rc$active) {
restoreState <- getCurrentRestoreContext()$asList()
private$restoredCallbacks$invoke(restoreState)
}
})
),
error = function(e) {
msg <- paste0("Error calling onRestored callback: ", e$message)
showNotification(msg, duration = NULL, type = "error")
}
)
}
})
}) # withReactiveDomain
}
),
public = list(
restoreContext = NULL,
progressStack = 'Stack', # Stack of progress objects
input = 'reactivevalues', # Externally-usable S3 wrapper object for .input
output = 'ANY', # Externally-usable S3 wrapper object for .outputs
@@ -465,6 +594,12 @@ ShinySession <- R6Class(
private$.outputs <- list()
private$.outputOptions <- list()
private$bookmarkCallbacks <- Callbacks$new()
private$bookmarkedCallbacks <- Callbacks$new()
private$restoreCallbacks <- Callbacks$new()
private$restoredCallbacks <- Callbacks$new()
private$createBookmarkObservers()
private$registerSessionEndCallbacks()
if (!is.null(websocket$request$HTTP_SHINY_SERVER_CREDENTIALS)) {
@@ -487,10 +622,20 @@ ShinySession <- R6Class(
)
)
},
rootScope = function() {
self
},
makeScope = function(namespace) {
ns <- NS(namespace)
createSessionProxy(self,
# Private items for this scope. Can't be part of the scope object because
# `$<-.session_proxy` doesn't allow assignment on overidden names.
bookmarkCallbacks <- Callbacks$new()
restoreCallbacks <- Callbacks$new()
restoredCallbacks <- Callbacks$new()
bookmarkExclude <- character(0)
scope <- createSessionProxy(self,
input = .createReactiveValues(private$.input, readonly = TRUE, ns = ns),
output = .createOutputWriter(self, ns = ns),
sendInputMessage = function(inputId, message) {
@@ -502,12 +647,157 @@ ShinySession <- R6Class(
ns = ns,
makeScope = function(namespace) {
self$makeScope(ns(namespace))
},
setBookmarkExclude = function(names) {
bookmarkExclude <<- names
},
getBookmarkExclude = function() {
bookmarkExclude
},
onBookmark = function(fun) {
if (!is.function(fun) || length(fun) != 1) {
stop("`fun` must be a function that takes one argument")
}
bookmarkCallbacks$register(fun)
},
onBookmarked = function(fun) {
stop("onBookmarked() can't be used in a module.")
},
onRestore = function(fun) {
if (!is.function(fun) || length(fun) != 1) {
stop("`fun` must be a function that takes one argument")
}
restoreCallbacks$register(fun)
},
onRestored = function(fun) {
if (!is.function(fun) || length(fun) != 1) {
stop("`fun` must be a function that takes one argument")
}
restoredCallbacks$register(fun)
}
)
# Given a char vector, return a logical vector indicating which of those
# strings are names of things in the namespace.
filterNamespace <- function(x) {
nsString <- paste0(namespace, ns.sep)
substr(x, 1, nchar(nsString)) == nsString
}
# Given a char vector of namespaced names, return a char vector of corresponding
# names with namespace prefix removed.
unNamespace <- function(x) {
if (!all(filterNamespace(x))) {
stop("x contains strings(s) that do not have namespace prefix ", namespace)
}
nsString <- paste0(namespace, ns.sep)
substring(x, nchar(nsString) + 1)
}
# Given a restore state object (a list), return a modified version that's
# scoped to this namespace.
scopeRestoreState <- function(state) {
# State is a list. We need to copy and transform some things for the
# scope.
scopeState <- state
# `values` is an environment and we don't want to modify the original.
scopeState$values <- new.env(parent = emptyenv())
# Keep only inputs that are in the scope, and rename them
scopeState$input <- scopeState$input[filterNamespace(names(scopeState$input))]
names(scopeState$input) <- unNamespace(names(scopeState$input))
# Same for values. This is an environment so we have to handle a little
# differently.
origNames <- names(state$values)
origNames <- origNames[filterNamespace(origNames)]
lapply(origNames, function(origName) {
scopedName <- unNamespace(origName)
scopeState$values[[scopedName]] <- state$values[[origName]]
})
if (!is.null(state$dir)) {
dir <- file.path(state$dir, namespace)
if (dirExists(dir))
scopeState$dir <- dir
}
scopeState
}
# When scope is created, register these bookmarking callbacks on the main
# session object. They will invoke the scope's own callbacks, if any are
# present.
self$onBookmark(function(state) {
# Exit if no user-defined callbacks.
if (bookmarkCallbacks$count() == 0)
return()
scopeState <- ShinySaveState$new(scope$input, scope$getBookmarkExclude())
# Create subdir for this scope
if (!is.null(state$dir)) {
scopeState$dir <- file.path(state$dir, namespace)
res <- dir.create(scopeState$dir)
if (res == FALSE) {
stop("Error creating subdirectory for scope ", namespace)
}
}
# Invoke the callback on the scopeState object
bookmarkCallbacks$invoke(scopeState)
# Copy `values` from scopeState to state, adding namespace
if (length(scopeState$values) != 0) {
if (anyUnnamed(scopeState$values)) {
stop("All scope values in must be named.")
}
lapply(names(scopeState$values), function(origName) {
scopedName <- ns(origName)
state$values[[scopedName]] <- scopeState$values[[origName]]
})
}
})
self$onRestore(function(state) {
# Exit if no user-defined callbacks.
if (restoreCallbacks$count() == 0)
return()
scopeState <- scopeRestoreState(state)
# Invoke user callbacks
restoreCallbacks$invoke(scopeState)
})
self$onRestored(function(state) {
# Exit if no user-defined callbacks.
if (restoredCallbacks$count() == 0)
return()
scopeState <- scopeRestoreState(state)
# Invoke user callbacks
restoredCallbacks$invoke(scopeState)
})
scope
},
ns = function(id) {
NS(NULL, id)
},
# Freeze a value until the flush cycle completes
freezeValue = function(x, name) {
if (!is.reactivevalues(x))
stop("x must be a reactivevalues object")
impl <- .subset2(x, 'impl')
impl$freeze(name)
self$onFlushed(function() impl$thaw(name))
},
onSessionEnded = function(sessionEndedCallback) {
"Registers the given callback to be invoked when the session is closed
(i.e. the connection to the client has been severed). The return value
@@ -540,10 +830,7 @@ ShinySession <- R6Class(
# ..stacktraceon matches with the top-level ..stacktraceoff..
private$closedCallbacks$invoke(onError = printError, ..stacktraceon = TRUE)
flushReact()
lapply(appsByToken$values(), function(shinysession) {
shinysession$flushOutput()
NULL
})
flushAllSessions()
},
isClosed = function() {
return(self$closed)
@@ -668,21 +955,43 @@ ShinySession <- R6Class(
}
},
flushOutput = function() {
if (self$isClosed())
return()
# Return TRUE if there's any stuff to send to the client.
hasPendingUpdates <- function() {
# Even though progressKeys isn't sent to the client, we use it in this
# check. This is because if it is non-empty, sending `values` to the
# client tells it that the flushReact loop is finished, and the client
# then knows to stop showing progress.
return(
length(private$progressKeys) != 0 ||
length(private$invalidatedOutputValues) != 0 ||
length(private$invalidatedOutputErrors) != 0 ||
length(private$inputMessageQueue) != 0
)
}
# ..stacktraceon matches with the top-level ..stacktraceoff..
private$flushCallbacks$invoke(..stacktraceon = TRUE)
# ..stacktraceon matches with the top-level ..stacktraceoff..
on.exit(private$flushedCallbacks$invoke(..stacktraceon = TRUE))
if (length(private$progressKeys) == 0
&& length(private$invalidatedOutputValues) == 0
&& length(private$invalidatedOutputErrors) == 0
&& length(private$inputMessageQueue) == 0) {
# Schedule execution of onFlushed callbacks
on.exit({
# ..stacktraceon matches with the top-level ..stacktraceoff..
private$flushedCallbacks$invoke(..stacktraceon = TRUE)
# If one of the flushedCallbacks added anything to send to the client,
# or invalidated any observers, set up another flush cycle.
if (hasPendingUpdates() || .getReactiveEnvironment()$hasPendingFlush()) {
scheduleFlush()
}
})
if (!hasPendingUpdates()) {
return(invisible())
}
private$progressKeys <- character(0)
values <- private$invalidatedOutputValues
private$invalidatedOutputValues <- Map$new()
errors <- private$invalidatedOutputErrors
@@ -745,6 +1054,13 @@ ShinySession <- R6Class(
private$sendResponse(msg, value)
}
},
sendBinaryMessage = function(type, message) {
typeBytes <- charToRaw(type)
if (length(typeBytes) > 255) {
stop("'type' argument is too long")
}
private$write(c(as.raw(length(typeBytes)), typeBytes, message))
},
sendCustomMessage = function(type, message) {
data <- list()
data[[type]] <- message
@@ -778,9 +1094,92 @@ ShinySession <- R6Class(
return(dereg)
}
},
setBookmarkExclude = function(names) {
private$bookmarkExclude <- names
},
getBookmarkExclude = function() {
private$bookmarkExclude
},
onBookmark = function(fun) {
if (!is.function(fun) || length(fun) != 1) {
stop("`fun` must be a function that takes one argument")
}
private$bookmarkCallbacks$register(fun)
},
onBookmarked = function(fun) {
if (!is.function(fun) || length(fun) != 1) {
stop("`fun` must be a function that takes one argument")
}
private$bookmarkedCallbacks$register(fun)
},
onRestore = function(fun) {
if (!is.function(fun) || length(fun) != 1) {
stop("`fun` must be a function that takes one argument")
}
private$restoreCallbacks$register(fun)
},
onRestored = function(fun) {
if (!is.function(fun) || length(fun) != 1) {
stop("`fun` must be a function that takes one argument")
}
private$restoredCallbacks$register(fun)
},
doBookmark = function() {
# Get bookmarking store config
store <- getShinyOption("bookmarkStore", default = "disable")
if (store == "disable")
return()
tryCatch(
withLogErrors({
saveState <- ShinySaveState$new(
input = self$input,
exclude = self$getBookmarkExclude(),
onSave = function(state) {
private$bookmarkCallbacks$invoke(state)
}
)
if (store == "server") {
url <- saveShinySaveState(saveState)
} else if (store == "url") {
url <- encodeShinySaveState(saveState)
} else {
stop("Unknown store type: ", store)
}
clientData <- self$clientData
url <- paste0(
clientData$url_protocol, "//",
clientData$url_hostname,
if (nzchar(clientData$url_port)) paste0(":", clientData$url_port),
clientData$url_pathname,
"?", url
)
# If onBookmarked callback was provided, invoke it; if not call
# the default.
if (private$bookmarkedCallbacks$count() > 0) {
private$bookmarkedCallbacks$invoke(url)
} else {
showBookmarkUrlModal(url)
}
}),
error = function(e) {
msg <- paste0("Error bookmarking state: ", e$message)
showNotification(msg, duration = NULL, type = "error")
}
)
},
reactlog = function(logEntry) {
# Use sendCustomMessage instead of sendMessage, because the handler in
# shiny-showcase.js only has access to public API of the Shiny object.
if (private$showcase)
private$sendMessage(reactlog = logEntry)
self$sendCustomMessage("reactlog", logEntry)
},
reload = function() {
private$sendMessage(reload = TRUE)
@@ -803,6 +1202,9 @@ ShinySession <- R6Class(
)
)
},
updateQueryString = function(queryString) {
private$sendMessage(updateQueryString = list(queryString = queryString))
},
resetBrush = function(brushId) {
private$sendMessage(
resetBrush = list(
@@ -836,6 +1238,9 @@ ShinySession <- R6Class(
`@uploadEnd` = function(jobId, inputId) {
fileData <- private$fileUploadContext$getUploadOperation(jobId)$finish()
private$.input$set(inputId, fileData)
private$.input$setMeta(inputId, "shiny.serializer", serializerFileInput)
invisible()
},
# Provides a mechanism for handling direct HTTP requests that are posted
@@ -947,6 +1352,10 @@ ShinySession <- R6Class(
unlink(tmpdata)
stop(attr(result, "condition", exact = TRUE))
}
if (!file.exists(tmpdata)) {
# If no file was created, return a 404
return(httpResponse(404, content = "404 Not found"))
}
return(httpResponse(
200,
download$contentType %OR% getContentType(filename),
@@ -1017,10 +1426,11 @@ ShinySession <- R6Class(
registerDataObj = function(name, data, filterFunc) {
# abusing downloads at the moment
self$downloads$set(name, list(data = data, filter = filterFunc))
return(sprintf('session/%s/dataobj/%s?w=%s',
return(sprintf('session/%s/dataobj/%s?w=%s&nonce=%s',
URLencode(self$token, TRUE),
URLencode(name, TRUE),
workerId()))
workerId(),
URLencode(createUniqueId(8), TRUE)))
},
# This function suspends observers for hidden outputs and resumes observers
# for un-hidden outputs.
@@ -1186,10 +1596,72 @@ ShinySession <- R6Class(
#' @param ... Options to set for the output observer.
#' @export
outputOptions <- function(x, name, ...) {
if (!inherits(x, "shinyoutput"))
if (!inherits(x, "shinyoutput")) {
stop("x must be a shinyoutput object.")
}
name <- .subset2(x, 'ns')(name)
if (!missing(name)) {
name <- .subset2(x, 'ns')(name)
} else {
name <- NULL
}
.subset2(x, 'impl')$outputOptions(name, ...)
}
#' Add callbacks for Shiny session events
#'
#' These functions are for registering callbacks on Shiny session events.
#' \code{onFlush} registers a function that will be called before Shiny flushes
#' the reactive system. \code{onFlushed} registers a function that will be
#' called after Shiny flushes the reactive system. \code{onSessionEnded}
#' registers a function to be called after the client has disconnected.
#'
#' These functions should be called within the application's server function.
#'
#' All of these functions return a function which can be called with no
#' arguments to cancel the registration.
#'
#' @param fun A callback function.
#' @param once Should the function be run once, and then cleared, or should it
#' re-run each time the event occurs. (Only for \code{onFlush} and
#' \code{onFlushed}.)
#' @param session A shiny session object.
#'
#' @export
onFlush <- function(fun, once = TRUE, session = getDefaultReactiveDomain()) {
session$onFlush(fun, once = once)
}
#' @rdname onFlush
#' @export
onFlushed <- function(fun, once = TRUE, session = getDefaultReactiveDomain()) {
session$onFlushed(fun, once = once)
}
#' @rdname onFlush
#' @export
onSessionEnded <- function(fun, session = getDefaultReactiveDomain()) {
session$onSessionEnded(fun)
}
scheduleFlush <- function() {
timerCallbacks$schedule(0, function() {})
}
flushAllSessions <- function() {
lapply(appsByToken$values(), function(shinysession) {
tryCatch(
shinysession$flushOutput(),
stop = function(e) {
# If there are any uncaught errors that bubbled up to here, close the
# session.
shinysession$close()
}
)
NULL
})
}

View File

@@ -44,7 +44,7 @@ renderPage <- function(ui, connection, showcase=0) {
shiny_deps <- list(
htmlDependency("json2", "2014.02.04", c(href="shared"), script = "json2-min.js"),
htmlDependency("jquery", "1.11.3", c(href="shared"), script = "jquery.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",
@@ -64,7 +64,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)
@@ -91,17 +90,34 @@ uiHttpHandler <- function(ui, uiPattern = "^/$") {
if (!is.null(mode))
showcaseMode <- mode
}
uiValue <- if (is.function(ui)) {
if (length(formals(ui)) > 0) {
# No corresponding ..stacktraceoff.., this is pure user code
..stacktraceon..(ui(req))
} else {
# No corresponding ..stacktraceoff.., this is pure user code
..stacktraceon..(ui())
}
# 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)

View File

@@ -19,7 +19,6 @@ globalVariables('func')
#' dynamically generated UIs, such as those created by Shiny code snippets
#' embedded in R Markdown documents).
#' @return The \code{renderFunc} function, with annotations.
#'
#' @export
markRenderFunction <- function(uiFunc, renderFunc, outputArgs = list()) {
# a mutable object that keeps track of whether `useRenderFunction` has been
@@ -248,7 +247,6 @@ renderImage <- function(expr, env=parent.frame(), quoted=FALSE,
#' function, instead of the printed output.
#'
#' @example res/text-example.R
#'
#' @export
renderPrint <- function(expr, env = parent.frame(), quoted = FALSE,
width = getOption('width'), outputArgs=list()) {
@@ -289,7 +287,6 @@ renderPrint <- function(expr, env = parent.frame(), quoted = FALSE,
#' function, rather than the returned text value.
#'
#' @example res/text-example.R
#'
#' @export
renderText <- function(expr, env=parent.frame(), quoted=FALSE,
outputArgs=list()) {
@@ -321,7 +318,6 @@ renderText <- function(expr, env=parent.frame(), quoted=FALSE,
#' interactive R Markdown document.
#'
#' @seealso conditionalPanel
#'
#' @export
#' @examples
#' ## Only run examples in interactive R sessions

View File

@@ -31,7 +31,7 @@ licenseLink <- function(licenseName) {
showcaseHead <- function() {
deps <- list(
htmlDependency("jqueryui", "1.11.4", c(href="shared/jqueryui"),
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"),

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

@@ -39,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
#'
@@ -128,7 +166,7 @@ updateActionButton <- function(session, inputId, label = NULL, icon = NULL) {
#'
#' @template update-input
#' @param value The desired date value. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#' \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
@@ -141,21 +179,18 @@ updateActionButton <- function(session, inputId, label = NULL, icon = NULL) {
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' sliderInput("controller", "Controller", 1, 30, 10),
#' 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
#' )
#' })
#' }
@@ -166,11 +201,18 @@ updateActionButton <- function(session, inputId, label = NULL, icon = NULL) {
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)
@@ -181,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
@@ -196,20 +238,20 @@ updateDateInput <- function(session, inputId, label = NULL, value = NULL,
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' sliderInput("controller", "Controller", 1, 30, 10),
#' 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
#' )
#' })
#' }
@@ -229,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
))
@@ -415,11 +457,11 @@ updateInputOptions <- function(session, inputId, label = NULL, choices = NULL,
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 (!is.null(choices)) {
format(tagList(
generateOptions(inputId, choices, selected, inline, type = type)
generateOptions(session$ns(inputId), choices, selected, inline, type = type)
))
}

219
R/utils.R
View File

@@ -23,7 +23,6 @@ NULL
#' rnormA(3) # [1] 1.8285879 -0.7468041 -0.4639111
#' rnormA(5) # [1] 1.8285879 -0.7468041 -0.4639111 -1.6510126 -1.4686924
#' rnormB(5) # [1] -0.7946034 0.2568374 -0.6567597 1.2451387 -0.8375699
#'
#' @export
repeatable <- function(rngfunc, seed = stats::runif(1, 0, .Machine$integer.max)) {
force(seed)
@@ -120,16 +119,6 @@ p_randomInt <- function(...) {
withPrivateSeed(randomInt(...))
}
# Return a random hexadecimal string with `length` digits.
randomID <- function(length = 16) {
paste(sample(
c("0", "1", "2", "3", "4", "5", "6", "7", "8","9",
"a", "b", "c", "d", "e", "f"),
length,
replace = TRUE
), collapse = '')
}
isWholeNum <- function(x, tol = .Machine$double.eps^0.5) {
abs(x - round(x)) < tol
}
@@ -193,6 +182,21 @@ anyUnnamed <- function(x) {
any(!nzchar(nms))
}
# Given two named vectors, join them together, and keep only the last element
# with a given name in the resulting vector. If b has any elements with the same
# name as elements in a, the element in a is dropped. Also, if there are any
# duplicated names in a or b, only the last one with that name is kept.
mergeVectors <- function(a, b) {
if (anyUnnamed(a) || anyUnnamed(b)) {
stop("Vectors must be either NULL or have names for all elements")
}
x <- c(a, b)
drop_idx <- duplicated(names(x), fromLast = TRUE)
x[!drop_idx]
}
# Combine dir and (file)name into a file path. If a file already exists with a
# name differing only by case, then use it instead.
file.path.ci <- function(...) {
@@ -235,6 +239,12 @@ find.file.ci <- function(...) {
return(matches[1])
}
# The function base::dir.exists was added in R 3.2.0, but for backward
# compatibility we need to add this function
dirExists <- function(paths) {
file.exists(paths) & file.info(paths)$isdir
}
# Attempt to join a path and relative path, and turn the result into a
# (normalized) absolute path. The result will only be returned if it is an
# existing file/directory and is a descendant of dir.
@@ -400,7 +410,6 @@ makeFunction <- function(args = pairlist(), body, env = parent.frame()) {
#'
#' isolate(tripleA())
#' # "text, text, text"
#'
#' @export
exprToFunction <- function(expr, env=parent.frame(), quoted=FALSE) {
if (!quoted) {
@@ -434,7 +443,6 @@ exprToFunction <- function(expr, env=parent.frame(), quoted=FALSE) {
#' the name of the calling function.
#' @param wrappedWithLabel,..stacktraceon Advanced use only. For stack manipulation purposes; see
#' \code{\link{stacktrace}}.
#'
#' @export
installExprFunction <- function(expr, name, eval.env = parent.frame(2),
quoted = FALSE,
@@ -506,6 +514,8 @@ parseQueryString <- function(str, nested = FALSE) {
str <- substr(str, 2, nchar(str))
pairs <- strsplit(str, '&', fixed = TRUE)[[1]]
# Drop any empty items (if there's leading/trailing/consecutive '&' chars)
pairs <- pairs[pairs != ""]
pairs <- strsplit(pairs, '=', fixed = TRUE)
keys <- vapply(pairs, function(x) x[1], FUN.VALUE = character(1))
@@ -553,13 +563,7 @@ shinyCallingHandlers <- function(expr) {
return()
handle <- getOption('shiny.error')
if (is.function(handle)) {
if ("condition" %in% names(formals(handle))) {
handle(condition = e)
} else {
handle()
}
}
if (is.function(handle)) handle()
}
)
}
@@ -976,6 +980,62 @@ safeError <- function(error) {
error
}
#***********************************************************************#
#**** Keep this function internal for now, may chnage in the future ****#
#***********************************************************************#
# #' Propagate an error through Shiny, but catch it before it throws
# #'
# #' Throws a type of exception that is caught by observers. When such an
# #' exception is triggered, all reactive links are broken. So, essentially,
# #' \code{reactiveStop()} behaves just like \code{stop()}, except that
# #' instead of ending the session, it is silently swalowed by Shiny.
# #'
# #' This function should be used when you want to disrupt the reactive
# #' links in a reactive chain, but do not want to end the session. For
# #' example, this enables you to disallow certain inputs, but get back
# #' to business as usual when valid inputs are re-entered.
# #' \code{reactiveStop} is also called internally by Shiny to create
# #' special errors, such as the ones generated by \code{\link{validate}()},
# #' \code{\link{req}()} and \code{\link{cancelOutput}()}.
# #'
# #' @param message An optional error message.
# #' @param class An optional class to add to the error.
# #' @export
# #' @examples
# #' ## Note: the breaking of the reactive chain that happens in the app
# #' ## below (when input$txt = 'bad' and input$allowBad = 'FALSE') is
# #' ## easily visualized with `showReactLog()`
# #'
# #' ## Only run examples in interactive R sessions
# #' if (interactive()) {
# #'
# #' ui <- fluidPage(
# #' textInput('txt', 'Enter some text...'),
# #' selectInput('allowBad', 'Allow the string \'bad\'?',
# #' c('TRUE', 'FALSE'), selected = 'FALSE')
# #' )
# #'
# #' server <- function(input, output) {
# #' val <- reactive({
# #' if (!(as.logical(input$allowBad))) {
# #' if (identical(input$txt, "bad")) {
# #' reactiveStop()
# #' }
# #' }
## ' })
# #'
# #' observe({
# #' val()
# #' })
# #' }
# #'
# #' shinyApp(ui, server)
# #' }
# #' @export
reactiveStop <- function(message = "", class = NULL) {
stopWithCondition(c("shiny.silent.error", class), message)
}
#' Validate input values and other conditions
#'
#' For an output rendering function (e.g. \code{\link{renderPlot}()}), you may
@@ -1032,15 +1092,16 @@ safeError <- function(error) {
#' \code{shiny-output-error-} prepended to this value.
#' @export
#' @examples
#' # in ui.R
#' fluidPage(
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#'
#' ui <- fluidPage(
#' checkboxGroupInput('in1', 'Check some letters', choices = head(LETTERS)),
#' selectizeInput('in2', 'Select a state', choices = state.name),
#' plotOutput('plot')
#' )
#'
#' # in server.R
#' function(input, output) {
#' server <- function(input, output) {
#' output$plot <- renderPlot({
#' validate(
#' need(input$in1, 'Check at least one letter!'),
@@ -1049,6 +1110,10 @@ safeError <- function(error) {
#' plot(1:10, main = paste(c(input$in1, input$in2), collapse = ', '))
#' })
#' }
#'
#' shinyApp(ui, server)
#'
#' }
validate <- function(..., errorClass = character(0)) {
results <- sapply(list(...), function(x) {
# Detect NULL or NA
@@ -1069,8 +1134,7 @@ validate <- function(..., errorClass = character(0)) {
# There may be empty strings remaining; these are message-less failures that
# started as FALSE
results <- results[nzchar(results)]
stopWithCondition(c("validation", "shiny.silent.error", errorClass),
paste(results, collapse="\n"))
reactiveStop(paste(results, collapse="\n"), c(errorClass, "validation"))
}
#' @param expr An expression to test. The condition will pass if the expression
@@ -1164,20 +1228,67 @@ need <- function(expr, message = paste(label, "must be provided"), label) {
#'
#' \code{req(input$a != 0)}
#'
#' \strong{Using \code{req(FALSE)}}
#'
#' You can use \code{req(FALSE)} (i.e. no condition) if you've already performed
#' all the checks you needed to by that point and just want to stop the reactive
#' chain now. There is no advantange to this, except perhaps ease of readibility
#' if you have a complicated condition to check for (or perhaps if you'd like to
#' divide your condition into nested \code{if} statements).
#'
#' \strong{Using \code{cancelOutput = TRUE}}
#'
#' When \code{req(..., cancelOutput = TRUE)} is used, the "silent" exception is
#' also raised, but it is treated slightly differently if one or more outputs are
#' currently being evaluated. In those cases, the reactive chain does not proceed
#' or update, but the output(s) are left is whatever state they happen to be in
#' (whatever was their last valid state).
#'
#' Note that this is always going to be the case if
#' this is used inside an output context (e.g. \code{output$txt <- ...}). It may
#' or may not be the case if it is used inside a non-output context (e.g.
#' \code{\link{reactive}}, \code{\link{observe}} or \code{\link{observeEvent}})
#' -- depending on whether or not there is an \code{output$...} that is triggered
#' as a result of those calls. See the examples below for concrete scenarios.
#'
#' @param ... Values to check for truthiness.
#' @param cancelOutput If \code{TRUE} and an output is being evaluated, stop
#' processing as usual but instead of clearing the output, leave it in
#' whatever state it happens to be in.
#' @param x An expression whose truthiness value we want to determine
#' @return The first value that was passed in.
#'
#' @export
#' @examples
#' ## Only run examples in interactive R sessions
#' if (interactive()) {
#' ui <- fluidPage(
#' textInput('data', 'Enter a dataset from the "datasets" package', 'cars'),
#' p('(E.g. "cars", "mtcars", "pressure", "faithful")'), hr(),
#' tableOutput('tbl')
#' )
#'
#' server <- function(input, output) {
#' output$tbl <- renderTable({
#'
#' ## to require that the user types something, use: `req(input$data)`
#' ## but better: require that input$data is valid and leave the last
#' ## valid table up
#' req(exists(input$data, "package:datasets", inherits = FALSE),
#' cancelOutput = TRUE)
#'
#' head(get(input$data, "package:datasets", inherits = FALSE))
#' })
#' }
#'
#' shinyApp(ui, server)
#' }
req <- function(..., cancelOutput = FALSE) {
dotloop(function(item) {
if (!isTruthy(item)) {
if (isTRUE(cancelOutput)) {
cancelOutput()
} else {
stopWithCondition(c("validation", "shiny.silent.error"), "")
reactiveStop(class = "validation")
}
}
}, ...)
@@ -1188,20 +1299,44 @@ req <- function(..., cancelOutput = FALSE) {
invisible()
}
#' Cancel processing of the current output
#'
#' Signals an error that Shiny treats specially if an output is currently being
#' evaluated. Execution will stop, but rather than clearing the output (as
#' \code{\link{req}} does) or showing an error message (as \code{\link{stop}}
#' does), the output simply remains unchanged.
#'
#' If \code{cancelOutput} is called in any non-output context (like in an
#' \code{\link{observe}} or \code{\link{observeEvent}}), the effect is the same
#' as \code{\link{req}(FALSE)}.
#'
#' @export
#***********************************************************************#
#**** Keep this function internal for now, may chnage in the future ****#
#***********************************************************************#
# #' Cancel processing of the current output
# #'
# #' Signals an error that Shiny treats specially if an output is currently being
# #' evaluated. Execution will stop, but rather than clearing the output (as
# #' \code{\link{req}} does) or showing an error message (as \code{\link{stop}}
# #' does), the output simply remains unchanged.
# #'
# #' If \code{cancelOutput} is called in any non-output context (like in an
# #' \code{\link{observe}} or \code{\link{observeEvent}}), the effect is the same
# #' as \code{\link{req}(FALSE)}.
# #' @export
# #' @examples
# #' ## Only run examples in interactive R sessions
# #' if (interactive()) {
# #'
# #' # uncomment the desired line to experiment with cancelOutput() vs. req()
# #'
# #' ui <- fluidPage(
# #' textInput('txt', 'Enter text'),
# #' textOutput('check')
# #' )
# #'
# #' server <- function(input, output) {
# #' output$check <- renderText({
# #' # req(input$txt)
# #' if (input$txt == 'hi') return('hi')
# #' else if (input$txt == 'bye') return('bye')
# #' # else cancelOutput()
# #' })
# #' }
# #'
# #' shinyApp(ui, server)
# #' }
cancelOutput <- function() {
stopWithCondition(c("shiny.output.cancel", "shiny.silent.error"), "")
reactiveStop(class = "shiny.output.cancel")
}
# Execute a function against each element of ..., but only evaluate each element
@@ -1216,6 +1351,8 @@ dotloop <- function(fun_, ...) {
invisible()
}
#' @export
#' @rdname req
isTruthy <- function(x) {
if (inherits(x, 'try-error'))
return(FALSE)

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.

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,6 +0,0 @@
name: 01_hello
account: admin
server: localhost
bundleId: 1
url: http://localhost:3939/admin/01_hello/
when: 1436550957.65385

View File

@@ -44,7 +44,9 @@ sd_section("UI Inputs",
"sliderInput",
"submitButton",
"textInput",
"textAreaInput",
"passwordInput",
"modalButton",
"updateActionButton",
"updateCheckboxGroupInput",
"updateCheckboxInput",
@@ -55,7 +57,9 @@ sd_section("UI Inputs",
"updateSelectInput",
"updateSliderInput",
"updateTabsetPanel",
"updateTextInput"
"updateTextInput",
"updateTextAreaInput",
"updateQueryString"
)
)
sd_section("UI Outputs",
@@ -69,7 +73,11 @@ sd_section("UI Outputs",
"verbatimTextOutput",
"downloadButton",
"Progress",
"withProgress"
"withProgress",
"modalDialog",
"urlModal",
"showModal",
"showNotification"
)
)
sd_section("Interface builder functions",
@@ -84,7 +92,9 @@ sd_section("Interface builder functions",
"withTags",
"htmlTemplate",
"bootstrapLib",
"suppressDependencies"
"suppressDependencies",
"insertUI",
"removeUI"
)
)
sd_section("Rendering functions",
@@ -120,6 +130,7 @@ sd_section("Reactive constructs",
"reactiveTimer",
"reactiveValues",
"reactiveValuesToList",
"freezeReactiveValue",
"domains",
"showReactLog"
)
@@ -143,6 +154,16 @@ sd_section("Running",
"viewer"
)
)
sd_section("Bookmarking state",
"Functions that are used for bookmarking and restoring state.",
c(
"bookmarkButton",
"enableBookmarking",
"setBookmarkExclude",
"showBookmarkUrlModal",
"onBookmark"
)
)
sd_section("Extending Shiny",
"Functions that are intended to be called by third-party packages that extend Shiny.",
c(
@@ -157,9 +178,13 @@ sd_section("Utility functions",
"Miscellaneous utilities that may be useful to advanced users or when extending Shiny.",
c(
"req",
"cancelOutput",
"validate",
"session",
"shinyOptions",
"safeError",
"onFlush",
"restoreInput",
"applyInputHandlers",
"exprToFunction",
"installExprFunction",
"parseQueryString",

View File

@@ -66,7 +66,7 @@ svg {
}
.node path {
fill: white;
stroke: #777;
stroke: #999;
stroke-width: 7.5px;
transition: fill 0.75s ease;
}
@@ -83,6 +83,9 @@ svg {
.node.running path {
fill: #61B97E;
}
.node.fixed path {
stroke: #000;
}
#legend {
font-size: 22px;
position: fixed;
@@ -1094,7 +1097,13 @@ function update() {
.on('mouseout', function(d, i) {
$('#description').text('');
})
.call(force.drag);
.on('dblclick', function(d) {
d3.event.stopPropagation();
d3.select(this).classed('fixed', d.fixed = false);
})
.call(force.drag().on('dragstart', function(d) {
d3.select(this).classed('fixed', d.fixed = true);
}));
newG.append('path')
.attr('transform', 'scale(0.08)')
.attr('stroke', 'black')

View File

@@ -1,6 +1,6 @@
/*!
* Bootstrap v3.3.6 (http://getbootstrap.com)
* Copyright 2011-2015 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,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
/*!
* Bootstrap v3.3.6 (http://getbootstrap.com)
* Copyright 2011-2015 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)
*/
/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
@@ -1106,7 +1106,6 @@ a:focus {
text-decoration: underline;
}
a:focus {
outline: thin dotted;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
@@ -2537,7 +2536,6 @@ select[size] {
input[type="file"]:focus,
input[type="radio"]:focus,
input[type="checkbox"]:focus {
outline: thin dotted;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
@@ -3029,7 +3027,6 @@ select[multiple].input-lg {
.btn.focus,
.btn:active.focus,
.btn.active.focus {
outline: thin dotted;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
/*!
* Bootstrap v3.3.6 (http://getbootstrap.com)
* Copyright 2011-2015 Twitter, Inc.
* Bootstrap v3.3.7 (http://getbootstrap.com)
* Copyright 2011-2016 Twitter, Inc.
* Licensed under the MIT license
*/
@@ -11,16 +11,16 @@ if (typeof jQuery === 'undefined') {
+function ($) {
'use strict';
var version = $.fn.jquery.split(' ')[0].split('.')
if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 2)) {
throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3')
if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 3)) {
throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4')
}
}(jQuery);
/* ========================================================================
* Bootstrap: transition.js v3.3.6
* Bootstrap: transition.js v3.3.7
* http://getbootstrap.com/javascript/#transitions
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -77,10 +77,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: alert.js v3.3.6
* Bootstrap: alert.js v3.3.7
* http://getbootstrap.com/javascript/#alerts
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -96,7 +96,7 @@ if (typeof jQuery === 'undefined') {
$(el).on('click', dismiss, this.close)
}
Alert.VERSION = '3.3.6'
Alert.VERSION = '3.3.7'
Alert.TRANSITION_DURATION = 150
@@ -109,7 +109,7 @@ if (typeof jQuery === 'undefined') {
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
}
var $parent = $(selector)
var $parent = $(selector === '#' ? [] : selector)
if (e) e.preventDefault()
@@ -172,10 +172,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: button.js v3.3.6
* Bootstrap: button.js v3.3.7
* http://getbootstrap.com/javascript/#buttons
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -192,7 +192,7 @@ if (typeof jQuery === 'undefined') {
this.isLoading = false
}
Button.VERSION = '3.3.6'
Button.VERSION = '3.3.7'
Button.DEFAULTS = {
loadingText: 'loading...'
@@ -214,10 +214,10 @@ if (typeof jQuery === 'undefined') {
if (state == 'loadingText') {
this.isLoading = true
$el.addClass(d).attr(d, d)
$el.addClass(d).attr(d, d).prop(d, true)
} else if (this.isLoading) {
this.isLoading = false
$el.removeClass(d).removeAttr(d)
$el.removeClass(d).removeAttr(d).prop(d, false)
}
}, this), 0)
}
@@ -281,10 +281,15 @@ if (typeof jQuery === 'undefined') {
$(document)
.on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
var $btn = $(e.target)
if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
var $btn = $(e.target).closest('.btn')
Plugin.call($btn, 'toggle')
if (!($(e.target).is('input[type="radio"]') || $(e.target).is('input[type="checkbox"]'))) e.preventDefault()
if (!($(e.target).is('input[type="radio"], input[type="checkbox"]'))) {
// Prevent double click on radios, and the double selections (so cancellation) on checkboxes
e.preventDefault()
// The target component still receive the focus
if ($btn.is('input,button')) $btn.trigger('focus')
else $btn.find('input:visible,button:visible').first().trigger('focus')
}
})
.on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
$(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
@@ -293,10 +298,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: carousel.js v3.3.6
* Bootstrap: carousel.js v3.3.7
* http://getbootstrap.com/javascript/#carousel
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -324,7 +329,7 @@ if (typeof jQuery === 'undefined') {
.on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
}
Carousel.VERSION = '3.3.6'
Carousel.VERSION = '3.3.7'
Carousel.TRANSITION_DURATION = 600
@@ -531,13 +536,14 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: collapse.js v3.3.6
* Bootstrap: collapse.js v3.3.7
* http://getbootstrap.com/javascript/#collapse
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
/* jshint latedef: false */
+function ($) {
'use strict';
@@ -561,7 +567,7 @@ if (typeof jQuery === 'undefined') {
if (this.options.toggle) this.toggle()
}
Collapse.VERSION = '3.3.6'
Collapse.VERSION = '3.3.7'
Collapse.TRANSITION_DURATION = 350
@@ -743,10 +749,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: dropdown.js v3.3.6
* Bootstrap: dropdown.js v3.3.7
* http://getbootstrap.com/javascript/#dropdowns
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -763,7 +769,7 @@ if (typeof jQuery === 'undefined') {
$(element).on('click.bs.dropdown', this.toggle)
}
Dropdown.VERSION = '3.3.6'
Dropdown.VERSION = '3.3.7'
function getParent($this) {
var selector = $this.attr('data-target')
@@ -909,10 +915,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: modal.js v3.3.6
* Bootstrap: modal.js v3.3.7
* http://getbootstrap.com/javascript/#modals
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -943,7 +949,7 @@ if (typeof jQuery === 'undefined') {
}
}
Modal.VERSION = '3.3.6'
Modal.VERSION = '3.3.7'
Modal.TRANSITION_DURATION = 300
Modal.BACKDROP_TRANSITION_DURATION = 150
@@ -1050,7 +1056,9 @@ if (typeof jQuery === 'undefined') {
$(document)
.off('focusin.bs.modal') // guard against infinite focus loop
.on('focusin.bs.modal', $.proxy(function (e) {
if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
if (document !== e.target &&
this.$element[0] !== e.target &&
!this.$element.has(e.target).length) {
this.$element.trigger('focus')
}
}, this))
@@ -1247,11 +1255,11 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: tooltip.js v3.3.6
* Bootstrap: tooltip.js v3.3.7
* http://getbootstrap.com/javascript/#tooltip
* Inspired by the original jQuery.tipsy by Jason Frame
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -1274,7 +1282,7 @@ if (typeof jQuery === 'undefined') {
this.init('tooltip', element, options)
}
Tooltip.VERSION = '3.3.6'
Tooltip.VERSION = '3.3.7'
Tooltip.TRANSITION_DURATION = 150
@@ -1565,9 +1573,11 @@ if (typeof jQuery === 'undefined') {
function complete() {
if (that.hoverState != 'in') $tip.detach()
that.$element
.removeAttr('aria-describedby')
.trigger('hidden.bs.' + that.type)
if (that.$element) { // TODO: Check whether guarding this code with this `if` is really necessary.
that.$element
.removeAttr('aria-describedby')
.trigger('hidden.bs.' + that.type)
}
callback && callback()
}
@@ -1610,7 +1620,10 @@ if (typeof jQuery === 'undefined') {
// width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
}
var elOffset = isBody ? { top: 0, left: 0 } : $element.offset()
var isSvg = window.SVGElement && el instanceof window.SVGElement
// Avoid using $.offset() on SVGs since it gives incorrect results in jQuery 3.
// See https://github.com/twbs/bootstrap/issues/20280
var elOffset = isBody ? { top: 0, left: 0 } : (isSvg ? null : $element.offset())
var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
@@ -1726,6 +1739,7 @@ if (typeof jQuery === 'undefined') {
that.$tip = null
that.$arrow = null
that.$viewport = null
that.$element = null
})
}
@@ -1762,10 +1776,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: popover.js v3.3.6
* Bootstrap: popover.js v3.3.7
* http://getbootstrap.com/javascript/#popovers
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -1782,7 +1796,7 @@ if (typeof jQuery === 'undefined') {
if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
Popover.VERSION = '3.3.6'
Popover.VERSION = '3.3.7'
Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
placement: 'right',
@@ -1871,10 +1885,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: scrollspy.js v3.3.6
* Bootstrap: scrollspy.js v3.3.7
* http://getbootstrap.com/javascript/#scrollspy
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -1900,7 +1914,7 @@ if (typeof jQuery === 'undefined') {
this.process()
}
ScrollSpy.VERSION = '3.3.6'
ScrollSpy.VERSION = '3.3.7'
ScrollSpy.DEFAULTS = {
offset: 10
@@ -2044,10 +2058,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: tab.js v3.3.6
* Bootstrap: tab.js v3.3.7
* http://getbootstrap.com/javascript/#tabs
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -2064,7 +2078,7 @@ if (typeof jQuery === 'undefined') {
// jscs:enable requireDollarBeforejQueryAssignment
}
Tab.VERSION = '3.3.6'
Tab.VERSION = '3.3.7'
Tab.TRANSITION_DURATION = 150
@@ -2200,10 +2214,10 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: affix.js v3.3.6
* Bootstrap: affix.js v3.3.7
* http://getbootstrap.com/javascript/#affix
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -2229,7 +2243,7 @@ if (typeof jQuery === 'undefined') {
this.checkPosition()
}
Affix.VERSION = '3.3.6'
Affix.VERSION = '3.3.7'
Affix.RESET = 'affix affix-top affix-bottom'

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,678 @@
/*!
* Datepicker for Bootstrap v1.6.4 (https://github.com/eternicode/bootstrap-datepicker)
*
* Copyright 2012 Stefan Petre
* Improvements by Andrew Rowls
* Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/
.datepicker {
border-radius: 4px;
direction: ltr;
}
.datepicker-inline {
width: 220px;
}
.datepicker.datepicker-rtl {
direction: rtl;
}
.datepicker.datepicker-rtl table tr td span {
float: right;
}
.datepicker-dropdown {
top: 0;
left: 0;
padding: 4px;
}
.datepicker-dropdown:before {
content: '';
display: inline-block;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 7px solid rgba(0, 0, 0, 0.15);
border-top: 0;
border-bottom-color: rgba(0, 0, 0, 0.2);
position: absolute;
}
.datepicker-dropdown:after {
content: '';
display: inline-block;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-top: 0;
position: absolute;
}
.datepicker-dropdown.datepicker-orient-left:before {
left: 6px;
}
.datepicker-dropdown.datepicker-orient-left:after {
left: 7px;
}
.datepicker-dropdown.datepicker-orient-right:before {
right: 6px;
}
.datepicker-dropdown.datepicker-orient-right:after {
right: 7px;
}
.datepicker-dropdown.datepicker-orient-bottom:before {
top: -7px;
}
.datepicker-dropdown.datepicker-orient-bottom:after {
top: -6px;
}
.datepicker-dropdown.datepicker-orient-top:before {
bottom: -7px;
border-bottom: 0;
border-top: 7px solid rgba(0, 0, 0, 0.15);
}
.datepicker-dropdown.datepicker-orient-top:after {
bottom: -6px;
border-bottom: 0;
border-top: 6px solid #fff;
}
.datepicker table {
margin: 0;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.datepicker table tr td,
.datepicker table tr th {
text-align: center;
width: 30px;
height: 30px;
border-radius: 4px;
border: none;
}
.table-striped .datepicker table tr td,
.table-striped .datepicker table tr th {
background-color: transparent;
}
.datepicker table tr td.old,
.datepicker table tr td.new {
color: #777777;
}
.datepicker table tr td.day:hover,
.datepicker table tr td.focused {
background: #eeeeee;
cursor: pointer;
}
.datepicker table tr td.disabled,
.datepicker table tr td.disabled:hover {
background: none;
color: #777777;
cursor: default;
}
.datepicker table tr td.highlighted {
color: #000;
background-color: #d9edf7;
border-color: #85c5e5;
border-radius: 0;
}
.datepicker table tr td.highlighted:focus,
.datepicker table tr td.highlighted.focus {
color: #000;
background-color: #afd9ee;
border-color: #298fc2;
}
.datepicker table tr td.highlighted:hover {
color: #000;
background-color: #afd9ee;
border-color: #52addb;
}
.datepicker table tr td.highlighted:active,
.datepicker table tr td.highlighted.active {
color: #000;
background-color: #afd9ee;
border-color: #52addb;
}
.datepicker table tr td.highlighted:active:hover,
.datepicker table tr td.highlighted.active:hover,
.datepicker table tr td.highlighted:active:focus,
.datepicker table tr td.highlighted.active:focus,
.datepicker table tr td.highlighted:active.focus,
.datepicker table tr td.highlighted.active.focus {
color: #000;
background-color: #91cbe8;
border-color: #298fc2;
}
.datepicker table tr td.highlighted.disabled:hover,
.datepicker table tr td.highlighted[disabled]:hover,
fieldset[disabled] .datepicker table tr td.highlighted:hover,
.datepicker table tr td.highlighted.disabled:focus,
.datepicker table tr td.highlighted[disabled]:focus,
fieldset[disabled] .datepicker table tr td.highlighted:focus,
.datepicker table tr td.highlighted.disabled.focus,
.datepicker table tr td.highlighted[disabled].focus,
fieldset[disabled] .datepicker table tr td.highlighted.focus {
background-color: #d9edf7;
border-color: #85c5e5;
}
.datepicker table tr td.highlighted.focused {
background: #afd9ee;
}
.datepicker table tr td.highlighted.disabled,
.datepicker table tr td.highlighted.disabled:active {
background: #d9edf7;
color: #777777;
}
.datepicker table tr td.today {
color: #000;
background-color: #ffdb99;
border-color: #ffb733;
}
.datepicker table tr td.today:focus,
.datepicker table tr td.today.focus {
color: #000;
background-color: #ffc966;
border-color: #b37400;
}
.datepicker table tr td.today:hover {
color: #000;
background-color: #ffc966;
border-color: #f59e00;
}
.datepicker table tr td.today:active,
.datepicker table tr td.today.active {
color: #000;
background-color: #ffc966;
border-color: #f59e00;
}
.datepicker table tr td.today:active:hover,
.datepicker table tr td.today.active:hover,
.datepicker table tr td.today:active:focus,
.datepicker table tr td.today.active:focus,
.datepicker table tr td.today:active.focus,
.datepicker table tr td.today.active.focus {
color: #000;
background-color: #ffbc42;
border-color: #b37400;
}
.datepicker table tr td.today.disabled:hover,
.datepicker table tr td.today[disabled]:hover,
fieldset[disabled] .datepicker table tr td.today:hover,
.datepicker table tr td.today.disabled:focus,
.datepicker table tr td.today[disabled]:focus,
fieldset[disabled] .datepicker table tr td.today:focus,
.datepicker table tr td.today.disabled.focus,
.datepicker table tr td.today[disabled].focus,
fieldset[disabled] .datepicker table tr td.today.focus {
background-color: #ffdb99;
border-color: #ffb733;
}
.datepicker table tr td.today.focused {
background: #ffc966;
}
.datepicker table tr td.today.disabled,
.datepicker table tr td.today.disabled:active {
background: #ffdb99;
color: #777777;
}
.datepicker table tr td.range {
color: #000;
background-color: #eeeeee;
border-color: #bbbbbb;
border-radius: 0;
}
.datepicker table tr td.range:focus,
.datepicker table tr td.range.focus {
color: #000;
background-color: #d5d5d5;
border-color: #7c7c7c;
}
.datepicker table tr td.range:hover {
color: #000;
background-color: #d5d5d5;
border-color: #9d9d9d;
}
.datepicker table tr td.range:active,
.datepicker table tr td.range.active {
color: #000;
background-color: #d5d5d5;
border-color: #9d9d9d;
}
.datepicker table tr td.range:active:hover,
.datepicker table tr td.range.active:hover,
.datepicker table tr td.range:active:focus,
.datepicker table tr td.range.active:focus,
.datepicker table tr td.range:active.focus,
.datepicker table tr td.range.active.focus {
color: #000;
background-color: #c3c3c3;
border-color: #7c7c7c;
}
.datepicker table tr td.range.disabled:hover,
.datepicker table tr td.range[disabled]:hover,
fieldset[disabled] .datepicker table tr td.range:hover,
.datepicker table tr td.range.disabled:focus,
.datepicker table tr td.range[disabled]:focus,
fieldset[disabled] .datepicker table tr td.range:focus,
.datepicker table tr td.range.disabled.focus,
.datepicker table tr td.range[disabled].focus,
fieldset[disabled] .datepicker table tr td.range.focus {
background-color: #eeeeee;
border-color: #bbbbbb;
}
.datepicker table tr td.range.focused {
background: #d5d5d5;
}
.datepicker table tr td.range.disabled,
.datepicker table tr td.range.disabled:active {
background: #eeeeee;
color: #777777;
}
.datepicker table tr td.range.highlighted {
color: #000;
background-color: #e4eef3;
border-color: #9dc1d3;
}
.datepicker table tr td.range.highlighted:focus,
.datepicker table tr td.range.highlighted.focus {
color: #000;
background-color: #c1d7e3;
border-color: #4b88a6;
}
.datepicker table tr td.range.highlighted:hover {
color: #000;
background-color: #c1d7e3;
border-color: #73a6c0;
}
.datepicker table tr td.range.highlighted:active,
.datepicker table tr td.range.highlighted.active {
color: #000;
background-color: #c1d7e3;
border-color: #73a6c0;
}
.datepicker table tr td.range.highlighted:active:hover,
.datepicker table tr td.range.highlighted.active:hover,
.datepicker table tr td.range.highlighted:active:focus,
.datepicker table tr td.range.highlighted.active:focus,
.datepicker table tr td.range.highlighted:active.focus,
.datepicker table tr td.range.highlighted.active.focus {
color: #000;
background-color: #a8c8d8;
border-color: #4b88a6;
}
.datepicker table tr td.range.highlighted.disabled:hover,
.datepicker table tr td.range.highlighted[disabled]:hover,
fieldset[disabled] .datepicker table tr td.range.highlighted:hover,
.datepicker table tr td.range.highlighted.disabled:focus,
.datepicker table tr td.range.highlighted[disabled]:focus,
fieldset[disabled] .datepicker table tr td.range.highlighted:focus,
.datepicker table tr td.range.highlighted.disabled.focus,
.datepicker table tr td.range.highlighted[disabled].focus,
fieldset[disabled] .datepicker table tr td.range.highlighted.focus {
background-color: #e4eef3;
border-color: #9dc1d3;
}
.datepicker table tr td.range.highlighted.focused {
background: #c1d7e3;
}
.datepicker table tr td.range.highlighted.disabled,
.datepicker table tr td.range.highlighted.disabled:active {
background: #e4eef3;
color: #777777;
}
.datepicker table tr td.range.today {
color: #000;
background-color: #f7ca77;
border-color: #f1a417;
}
.datepicker table tr td.range.today:focus,
.datepicker table tr td.range.today.focus {
color: #000;
background-color: #f4b747;
border-color: #815608;
}
.datepicker table tr td.range.today:hover {
color: #000;
background-color: #f4b747;
border-color: #bf800c;
}
.datepicker table tr td.range.today:active,
.datepicker table tr td.range.today.active {
color: #000;
background-color: #f4b747;
border-color: #bf800c;
}
.datepicker table tr td.range.today:active:hover,
.datepicker table tr td.range.today.active:hover,
.datepicker table tr td.range.today:active:focus,
.datepicker table tr td.range.today.active:focus,
.datepicker table tr td.range.today:active.focus,
.datepicker table tr td.range.today.active.focus {
color: #000;
background-color: #f2aa25;
border-color: #815608;
}
.datepicker table tr td.range.today.disabled:hover,
.datepicker table tr td.range.today[disabled]:hover,
fieldset[disabled] .datepicker table tr td.range.today:hover,
.datepicker table tr td.range.today.disabled:focus,
.datepicker table tr td.range.today[disabled]:focus,
fieldset[disabled] .datepicker table tr td.range.today:focus,
.datepicker table tr td.range.today.disabled.focus,
.datepicker table tr td.range.today[disabled].focus,
fieldset[disabled] .datepicker table tr td.range.today.focus {
background-color: #f7ca77;
border-color: #f1a417;
}
.datepicker table tr td.range.today.disabled,
.datepicker table tr td.range.today.disabled:active {
background: #f7ca77;
color: #777777;
}
.datepicker table tr td.selected,
.datepicker table tr td.selected.highlighted {
color: #fff;
background-color: #777777;
border-color: #555555;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datepicker table tr td.selected:focus,
.datepicker table tr td.selected.highlighted:focus,
.datepicker table tr td.selected.focus,
.datepicker table tr td.selected.highlighted.focus {
color: #fff;
background-color: #5e5e5e;
border-color: #161616;
}
.datepicker table tr td.selected:hover,
.datepicker table tr td.selected.highlighted:hover {
color: #fff;
background-color: #5e5e5e;
border-color: #373737;
}
.datepicker table tr td.selected:active,
.datepicker table tr td.selected.highlighted:active,
.datepicker table tr td.selected.active,
.datepicker table tr td.selected.highlighted.active {
color: #fff;
background-color: #5e5e5e;
border-color: #373737;
}
.datepicker table tr td.selected:active:hover,
.datepicker table tr td.selected.highlighted:active:hover,
.datepicker table tr td.selected.active:hover,
.datepicker table tr td.selected.highlighted.active:hover,
.datepicker table tr td.selected:active:focus,
.datepicker table tr td.selected.highlighted:active:focus,
.datepicker table tr td.selected.active:focus,
.datepicker table tr td.selected.highlighted.active:focus,
.datepicker table tr td.selected:active.focus,
.datepicker table tr td.selected.highlighted:active.focus,
.datepicker table tr td.selected.active.focus,
.datepicker table tr td.selected.highlighted.active.focus {
color: #fff;
background-color: #4c4c4c;
border-color: #161616;
}
.datepicker table tr td.selected.disabled:hover,
.datepicker table tr td.selected.highlighted.disabled:hover,
.datepicker table tr td.selected[disabled]:hover,
.datepicker table tr td.selected.highlighted[disabled]:hover,
fieldset[disabled] .datepicker table tr td.selected:hover,
fieldset[disabled] .datepicker table tr td.selected.highlighted:hover,
.datepicker table tr td.selected.disabled:focus,
.datepicker table tr td.selected.highlighted.disabled:focus,
.datepicker table tr td.selected[disabled]:focus,
.datepicker table tr td.selected.highlighted[disabled]:focus,
fieldset[disabled] .datepicker table tr td.selected:focus,
fieldset[disabled] .datepicker table tr td.selected.highlighted:focus,
.datepicker table tr td.selected.disabled.focus,
.datepicker table tr td.selected.highlighted.disabled.focus,
.datepicker table tr td.selected[disabled].focus,
.datepicker table tr td.selected.highlighted[disabled].focus,
fieldset[disabled] .datepicker table tr td.selected.focus,
fieldset[disabled] .datepicker table tr td.selected.highlighted.focus {
background-color: #777777;
border-color: #555555;
}
.datepicker table tr td.active,
.datepicker table tr td.active.highlighted {
color: #fff;
background-color: #337ab7;
border-color: #2e6da4;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datepicker table tr td.active:focus,
.datepicker table tr td.active.highlighted:focus,
.datepicker table tr td.active.focus,
.datepicker table tr td.active.highlighted.focus {
color: #fff;
background-color: #286090;
border-color: #122b40;
}
.datepicker table tr td.active:hover,
.datepicker table tr td.active.highlighted:hover {
color: #fff;
background-color: #286090;
border-color: #204d74;
}
.datepicker table tr td.active:active,
.datepicker table tr td.active.highlighted:active,
.datepicker table tr td.active.active,
.datepicker table tr td.active.highlighted.active {
color: #fff;
background-color: #286090;
border-color: #204d74;
}
.datepicker table tr td.active:active:hover,
.datepicker table tr td.active.highlighted:active:hover,
.datepicker table tr td.active.active:hover,
.datepicker table tr td.active.highlighted.active:hover,
.datepicker table tr td.active:active:focus,
.datepicker table tr td.active.highlighted:active:focus,
.datepicker table tr td.active.active:focus,
.datepicker table tr td.active.highlighted.active:focus,
.datepicker table tr td.active:active.focus,
.datepicker table tr td.active.highlighted:active.focus,
.datepicker table tr td.active.active.focus,
.datepicker table tr td.active.highlighted.active.focus {
color: #fff;
background-color: #204d74;
border-color: #122b40;
}
.datepicker table tr td.active.disabled:hover,
.datepicker table tr td.active.highlighted.disabled:hover,
.datepicker table tr td.active[disabled]:hover,
.datepicker table tr td.active.highlighted[disabled]:hover,
fieldset[disabled] .datepicker table tr td.active:hover,
fieldset[disabled] .datepicker table tr td.active.highlighted:hover,
.datepicker table tr td.active.disabled:focus,
.datepicker table tr td.active.highlighted.disabled:focus,
.datepicker table tr td.active[disabled]:focus,
.datepicker table tr td.active.highlighted[disabled]:focus,
fieldset[disabled] .datepicker table tr td.active:focus,
fieldset[disabled] .datepicker table tr td.active.highlighted:focus,
.datepicker table tr td.active.disabled.focus,
.datepicker table tr td.active.highlighted.disabled.focus,
.datepicker table tr td.active[disabled].focus,
.datepicker table tr td.active.highlighted[disabled].focus,
fieldset[disabled] .datepicker table tr td.active.focus,
fieldset[disabled] .datepicker table tr td.active.highlighted.focus {
background-color: #337ab7;
border-color: #2e6da4;
}
.datepicker table tr td span {
display: block;
width: 23%;
height: 54px;
line-height: 54px;
float: left;
margin: 1%;
cursor: pointer;
border-radius: 4px;
}
.datepicker table tr td span:hover,
.datepicker table tr td span.focused {
background: #eeeeee;
}
.datepicker table tr td span.disabled,
.datepicker table tr td span.disabled:hover {
background: none;
color: #777777;
cursor: default;
}
.datepicker table tr td span.active,
.datepicker table tr td span.active:hover,
.datepicker table tr td span.active.disabled,
.datepicker table tr td span.active.disabled:hover {
color: #fff;
background-color: #337ab7;
border-color: #2e6da4;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datepicker table tr td span.active:focus,
.datepicker table tr td span.active:hover:focus,
.datepicker table tr td span.active.disabled:focus,
.datepicker table tr td span.active.disabled:hover:focus,
.datepicker table tr td span.active.focus,
.datepicker table tr td span.active:hover.focus,
.datepicker table tr td span.active.disabled.focus,
.datepicker table tr td span.active.disabled:hover.focus {
color: #fff;
background-color: #286090;
border-color: #122b40;
}
.datepicker table tr td span.active:hover,
.datepicker table tr td span.active:hover:hover,
.datepicker table tr td span.active.disabled:hover,
.datepicker table tr td span.active.disabled:hover:hover {
color: #fff;
background-color: #286090;
border-color: #204d74;
}
.datepicker table tr td span.active:active,
.datepicker table tr td span.active:hover:active,
.datepicker table tr td span.active.disabled:active,
.datepicker table tr td span.active.disabled:hover:active,
.datepicker table tr td span.active.active,
.datepicker table tr td span.active:hover.active,
.datepicker table tr td span.active.disabled.active,
.datepicker table tr td span.active.disabled:hover.active {
color: #fff;
background-color: #286090;
border-color: #204d74;
}
.datepicker table tr td span.active:active:hover,
.datepicker table tr td span.active:hover:active:hover,
.datepicker table tr td span.active.disabled:active:hover,
.datepicker table tr td span.active.disabled:hover:active:hover,
.datepicker table tr td span.active.active:hover,
.datepicker table tr td span.active:hover.active:hover,
.datepicker table tr td span.active.disabled.active:hover,
.datepicker table tr td span.active.disabled:hover.active:hover,
.datepicker table tr td span.active:active:focus,
.datepicker table tr td span.active:hover:active:focus,
.datepicker table tr td span.active.disabled:active:focus,
.datepicker table tr td span.active.disabled:hover:active:focus,
.datepicker table tr td span.active.active:focus,
.datepicker table tr td span.active:hover.active:focus,
.datepicker table tr td span.active.disabled.active:focus,
.datepicker table tr td span.active.disabled:hover.active:focus,
.datepicker table tr td span.active:active.focus,
.datepicker table tr td span.active:hover:active.focus,
.datepicker table tr td span.active.disabled:active.focus,
.datepicker table tr td span.active.disabled:hover:active.focus,
.datepicker table tr td span.active.active.focus,
.datepicker table tr td span.active:hover.active.focus,
.datepicker table tr td span.active.disabled.active.focus,
.datepicker table tr td span.active.disabled:hover.active.focus {
color: #fff;
background-color: #204d74;
border-color: #122b40;
}
.datepicker table tr td span.active.disabled:hover,
.datepicker table tr td span.active:hover.disabled:hover,
.datepicker table tr td span.active.disabled.disabled:hover,
.datepicker table tr td span.active.disabled:hover.disabled:hover,
.datepicker table tr td span.active[disabled]:hover,
.datepicker table tr td span.active:hover[disabled]:hover,
.datepicker table tr td span.active.disabled[disabled]:hover,
.datepicker table tr td span.active.disabled:hover[disabled]:hover,
fieldset[disabled] .datepicker table tr td span.active:hover,
fieldset[disabled] .datepicker table tr td span.active:hover:hover,
fieldset[disabled] .datepicker table tr td span.active.disabled:hover,
fieldset[disabled] .datepicker table tr td span.active.disabled:hover:hover,
.datepicker table tr td span.active.disabled:focus,
.datepicker table tr td span.active:hover.disabled:focus,
.datepicker table tr td span.active.disabled.disabled:focus,
.datepicker table tr td span.active.disabled:hover.disabled:focus,
.datepicker table tr td span.active[disabled]:focus,
.datepicker table tr td span.active:hover[disabled]:focus,
.datepicker table tr td span.active.disabled[disabled]:focus,
.datepicker table tr td span.active.disabled:hover[disabled]:focus,
fieldset[disabled] .datepicker table tr td span.active:focus,
fieldset[disabled] .datepicker table tr td span.active:hover:focus,
fieldset[disabled] .datepicker table tr td span.active.disabled:focus,
fieldset[disabled] .datepicker table tr td span.active.disabled:hover:focus,
.datepicker table tr td span.active.disabled.focus,
.datepicker table tr td span.active:hover.disabled.focus,
.datepicker table tr td span.active.disabled.disabled.focus,
.datepicker table tr td span.active.disabled:hover.disabled.focus,
.datepicker table tr td span.active[disabled].focus,
.datepicker table tr td span.active:hover[disabled].focus,
.datepicker table tr td span.active.disabled[disabled].focus,
.datepicker table tr td span.active.disabled:hover[disabled].focus,
fieldset[disabled] .datepicker table tr td span.active.focus,
fieldset[disabled] .datepicker table tr td span.active:hover.focus,
fieldset[disabled] .datepicker table tr td span.active.disabled.focus,
fieldset[disabled] .datepicker table tr td span.active.disabled:hover.focus {
background-color: #337ab7;
border-color: #2e6da4;
}
.datepicker table tr td span.old,
.datepicker table tr td span.new {
color: #777777;
}
.datepicker .datepicker-switch {
width: 145px;
}
.datepicker .datepicker-switch,
.datepicker .prev,
.datepicker .next,
.datepicker tfoot tr th {
cursor: pointer;
}
.datepicker .datepicker-switch:hover,
.datepicker .prev:hover,
.datepicker .next:hover,
.datepicker tfoot tr th:hover {
background: #eeeeee;
}
.datepicker .cw {
font-size: 10px;
width: 12px;
padding: 0 2px 0 5px;
vertical-align: middle;
}
.input-group.date .input-group-addon {
cursor: pointer;
}
.input-daterange {
width: 100%;
}
.input-daterange input {
text-align: center;
}
.input-daterange input:first-child {
border-radius: 3px 0 0 3px;
}
.input-daterange input:last-child {
border-radius: 0 3px 3px 0;
}
.input-daterange .input-group-addon {
width: auto;
min-width: 16px;
padding: 4px 5px;
line-height: 1.42857143;
text-shadow: 0 1px 0 #fff;
border-width: 1px 0;
margin-left: -5px;
margin-right: -5px;
}
/*# sourceMappingURL=bootstrap-datepicker3.css.map */

File diff suppressed because one or more lines are too long

View File

@@ -1,442 +0,0 @@
/*!
* Datepicker for Bootstrap
*
* Copyright 2012 Stefan Petre
* Improvements by Andrew Rowls
* Licensed under the Apache License v2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
*/
.datepicker {
padding: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
direction: ltr;
/*.dow {
border-top: 1px solid #ddd !important;
}*/
}
.datepicker-inline {
width: 220px;
}
.datepicker.datepicker-rtl {
direction: rtl;
}
.datepicker.datepicker-rtl table tr td span {
float: right;
}
.datepicker-dropdown {
top: 0;
left: 0;
}
.datepicker-dropdown:before {
content: '';
display: inline-block;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-bottom-color: rgba(0, 0, 0, 0.2);
position: absolute;
top: -7px;
left: 6px;
}
.datepicker-dropdown:after {
content: '';
display: inline-block;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid #ffffff;
position: absolute;
top: -6px;
left: 7px;
}
.datepicker > div {
display: none;
}
.datepicker.days div.datepicker-days {
display: block;
}
.datepicker.months div.datepicker-months {
display: block;
}
.datepicker.years div.datepicker-years {
display: block;
}
.datepicker table {
margin: 0;
}
.datepicker td,
.datepicker th {
text-align: center;
width: 20px;
height: 20px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
border: none;
}
.table-striped .datepicker table tr td,
.table-striped .datepicker table tr th {
background-color: transparent;
}
.datepicker table tr td.day:hover {
background: #eeeeee;
cursor: pointer;
}
.datepicker table tr td.old,
.datepicker table tr td.new {
color: #999999;
}
.datepicker table tr td.disabled,
.datepicker table tr td.disabled:hover {
background: none;
color: #999999;
cursor: default;
}
.datepicker table tr td.today,
.datepicker table tr td.today:hover,
.datepicker table tr td.today.disabled,
.datepicker table tr td.today.disabled:hover {
background-color: #fde19a;
background-image: -moz-linear-gradient(top, #fdd49a, #fdf59a);
background-image: -ms-linear-gradient(top, #fdd49a, #fdf59a);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a));
background-image: -webkit-linear-gradient(top, #fdd49a, #fdf59a);
background-image: -o-linear-gradient(top, #fdd49a, #fdf59a);
background-image: linear-gradient(top, #fdd49a, #fdf59a);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0);
border-color: #fdf59a #fdf59a #fbed50;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
color: #000 !important;
}
.datepicker table tr td.today:hover,
.datepicker table tr td.today:hover:hover,
.datepicker table tr td.today.disabled:hover,
.datepicker table tr td.today.disabled:hover:hover,
.datepicker table tr td.today:active,
.datepicker table tr td.today:hover:active,
.datepicker table tr td.today.disabled:active,
.datepicker table tr td.today.disabled:hover:active,
.datepicker table tr td.today.active,
.datepicker table tr td.today:hover.active,
.datepicker table tr td.today.disabled.active,
.datepicker table tr td.today.disabled:hover.active,
.datepicker table tr td.today.disabled,
.datepicker table tr td.today:hover.disabled,
.datepicker table tr td.today.disabled.disabled,
.datepicker table tr td.today.disabled:hover.disabled,
.datepicker table tr td.today[disabled],
.datepicker table tr td.today:hover[disabled],
.datepicker table tr td.today.disabled[disabled],
.datepicker table tr td.today.disabled:hover[disabled] {
background-color: #fdf59a;
}
.datepicker table tr td.today:active,
.datepicker table tr td.today:hover:active,
.datepicker table tr td.today.disabled:active,
.datepicker table tr td.today.disabled:hover:active,
.datepicker table tr td.today.active,
.datepicker table tr td.today:hover.active,
.datepicker table tr td.today.disabled.active,
.datepicker table tr td.today.disabled:hover.active {
background-color: #fbf069 \9;
}
.datepicker table tr td.range,
.datepicker table tr td.range:hover,
.datepicker table tr td.range.disabled,
.datepicker table tr td.range.disabled:hover {
background: #eeeeee;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
.datepicker table tr td.range.today,
.datepicker table tr td.range.today:hover,
.datepicker table tr td.range.today.disabled,
.datepicker table tr td.range.today.disabled:hover {
background-color: #f3d17a;
background-image: -moz-linear-gradient(top, #f3c17a, #f3e97a);
background-image: -ms-linear-gradient(top, #f3c17a, #f3e97a);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f3c17a), to(#f3e97a));
background-image: -webkit-linear-gradient(top, #f3c17a, #f3e97a);
background-image: -o-linear-gradient(top, #f3c17a, #f3e97a);
background-image: linear-gradient(top, #f3c17a, #f3e97a);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f3c17a', endColorstr='#f3e97a', GradientType=0);
border-color: #f3e97a #f3e97a #edde34;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
.datepicker table tr td.range.today:hover,
.datepicker table tr td.range.today:hover:hover,
.datepicker table tr td.range.today.disabled:hover,
.datepicker table tr td.range.today.disabled:hover:hover,
.datepicker table tr td.range.today:active,
.datepicker table tr td.range.today:hover:active,
.datepicker table tr td.range.today.disabled:active,
.datepicker table tr td.range.today.disabled:hover:active,
.datepicker table tr td.range.today.active,
.datepicker table tr td.range.today:hover.active,
.datepicker table tr td.range.today.disabled.active,
.datepicker table tr td.range.today.disabled:hover.active,
.datepicker table tr td.range.today.disabled,
.datepicker table tr td.range.today:hover.disabled,
.datepicker table tr td.range.today.disabled.disabled,
.datepicker table tr td.range.today.disabled:hover.disabled,
.datepicker table tr td.range.today[disabled],
.datepicker table tr td.range.today:hover[disabled],
.datepicker table tr td.range.today.disabled[disabled],
.datepicker table tr td.range.today.disabled:hover[disabled] {
background-color: #f3e97a;
}
.datepicker table tr td.range.today:active,
.datepicker table tr td.range.today:hover:active,
.datepicker table tr td.range.today.disabled:active,
.datepicker table tr td.range.today.disabled:hover:active,
.datepicker table tr td.range.today.active,
.datepicker table tr td.range.today:hover.active,
.datepicker table tr td.range.today.disabled.active,
.datepicker table tr td.range.today.disabled:hover.active {
background-color: #efe24b \9;
}
.datepicker table tr td.selected,
.datepicker table tr td.selected:hover,
.datepicker table tr td.selected.disabled,
.datepicker table tr td.selected.disabled:hover {
background-color: #9e9e9e;
background-image: -moz-linear-gradient(top, #b3b3b3, #808080);
background-image: -ms-linear-gradient(top, #b3b3b3, #808080);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#b3b3b3), to(#808080));
background-image: -webkit-linear-gradient(top, #b3b3b3, #808080);
background-image: -o-linear-gradient(top, #b3b3b3, #808080);
background-image: linear-gradient(top, #b3b3b3, #808080);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b3b3b3', endColorstr='#808080', GradientType=0);
border-color: #808080 #808080 #595959;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datepicker table tr td.selected:hover,
.datepicker table tr td.selected:hover:hover,
.datepicker table tr td.selected.disabled:hover,
.datepicker table tr td.selected.disabled:hover:hover,
.datepicker table tr td.selected:active,
.datepicker table tr td.selected:hover:active,
.datepicker table tr td.selected.disabled:active,
.datepicker table tr td.selected.disabled:hover:active,
.datepicker table tr td.selected.active,
.datepicker table tr td.selected:hover.active,
.datepicker table tr td.selected.disabled.active,
.datepicker table tr td.selected.disabled:hover.active,
.datepicker table tr td.selected.disabled,
.datepicker table tr td.selected:hover.disabled,
.datepicker table tr td.selected.disabled.disabled,
.datepicker table tr td.selected.disabled:hover.disabled,
.datepicker table tr td.selected[disabled],
.datepicker table tr td.selected:hover[disabled],
.datepicker table tr td.selected.disabled[disabled],
.datepicker table tr td.selected.disabled:hover[disabled] {
background-color: #808080;
}
.datepicker table tr td.selected:active,
.datepicker table tr td.selected:hover:active,
.datepicker table tr td.selected.disabled:active,
.datepicker table tr td.selected.disabled:hover:active,
.datepicker table tr td.selected.active,
.datepicker table tr td.selected:hover.active,
.datepicker table tr td.selected.disabled.active,
.datepicker table tr td.selected.disabled:hover.active {
background-color: #666666 \9;
}
.datepicker table tr td.active,
.datepicker table tr td.active:hover,
.datepicker table tr td.active.disabled,
.datepicker table tr td.active.disabled:hover {
background-color: #006dcc;
background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
background-image: -o-linear-gradient(top, #0088cc, #0044cc);
background-image: linear-gradient(top, #0088cc, #0044cc);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
border-color: #0044cc #0044cc #002a80;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datepicker table tr td.active:hover,
.datepicker table tr td.active:hover:hover,
.datepicker table tr td.active.disabled:hover,
.datepicker table tr td.active.disabled:hover:hover,
.datepicker table tr td.active:active,
.datepicker table tr td.active:hover:active,
.datepicker table tr td.active.disabled:active,
.datepicker table tr td.active.disabled:hover:active,
.datepicker table tr td.active.active,
.datepicker table tr td.active:hover.active,
.datepicker table tr td.active.disabled.active,
.datepicker table tr td.active.disabled:hover.active,
.datepicker table tr td.active.disabled,
.datepicker table tr td.active:hover.disabled,
.datepicker table tr td.active.disabled.disabled,
.datepicker table tr td.active.disabled:hover.disabled,
.datepicker table tr td.active[disabled],
.datepicker table tr td.active:hover[disabled],
.datepicker table tr td.active.disabled[disabled],
.datepicker table tr td.active.disabled:hover[disabled] {
background-color: #0044cc;
}
.datepicker table tr td.active:active,
.datepicker table tr td.active:hover:active,
.datepicker table tr td.active.disabled:active,
.datepicker table tr td.active.disabled:hover:active,
.datepicker table tr td.active.active,
.datepicker table tr td.active:hover.active,
.datepicker table tr td.active.disabled.active,
.datepicker table tr td.active.disabled:hover.active {
background-color: #003399 \9;
}
.datepicker table tr td span {
display: block;
width: 23%;
height: 54px;
line-height: 54px;
float: left;
margin: 1%;
cursor: pointer;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.datepicker table tr td span:hover {
background: #eeeeee;
}
.datepicker table tr td span.disabled,
.datepicker table tr td span.disabled:hover {
background: none;
color: #999999;
cursor: default;
}
.datepicker table tr td span.active,
.datepicker table tr td span.active:hover,
.datepicker table tr td span.active.disabled,
.datepicker table tr td span.active.disabled:hover {
background-color: #006dcc;
background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
background-image: -o-linear-gradient(top, #0088cc, #0044cc);
background-image: linear-gradient(top, #0088cc, #0044cc);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
border-color: #0044cc #0044cc #002a80;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datepicker table tr td span.active:hover,
.datepicker table tr td span.active:hover:hover,
.datepicker table tr td span.active.disabled:hover,
.datepicker table tr td span.active.disabled:hover:hover,
.datepicker table tr td span.active:active,
.datepicker table tr td span.active:hover:active,
.datepicker table tr td span.active.disabled:active,
.datepicker table tr td span.active.disabled:hover:active,
.datepicker table tr td span.active.active,
.datepicker table tr td span.active:hover.active,
.datepicker table tr td span.active.disabled.active,
.datepicker table tr td span.active.disabled:hover.active,
.datepicker table tr td span.active.disabled,
.datepicker table tr td span.active:hover.disabled,
.datepicker table tr td span.active.disabled.disabled,
.datepicker table tr td span.active.disabled:hover.disabled,
.datepicker table tr td span.active[disabled],
.datepicker table tr td span.active:hover[disabled],
.datepicker table tr td span.active.disabled[disabled],
.datepicker table tr td span.active.disabled:hover[disabled] {
background-color: #0044cc;
}
.datepicker table tr td span.active:active,
.datepicker table tr td span.active:hover:active,
.datepicker table tr td span.active.disabled:active,
.datepicker table tr td span.active.disabled:hover:active,
.datepicker table tr td span.active.active,
.datepicker table tr td span.active:hover.active,
.datepicker table tr td span.active.disabled.active,
.datepicker table tr td span.active.disabled:hover.active {
background-color: #003399 \9;
}
.datepicker table tr td span.old {
color: #999999;
}
.datepicker th.datepicker-switch {
width: 145px;
}
.datepicker thead tr:first-child th,
.datepicker tfoot tr:first-child th {
cursor: pointer;
}
.datepicker thead tr:first-child th:hover,
.datepicker tfoot tr:first-child th:hover {
background: #eeeeee;
}
.datepicker .cw {
font-size: 10px;
width: 12px;
padding: 0 2px 0 5px;
vertical-align: middle;
}
.datepicker thead tr:first-child th.cw {
cursor: default;
background-color: transparent;
}
.input-append.date .add-on i,
.input-prepend.date .add-on i {
display: block;
cursor: pointer;
width: 16px;
height: 16px;
}
.input-daterange input {
text-align: center;
}
.input-daterange input:first-child {
-webkit-border-radius: 3px 0 0 3px;
-moz-border-radius: 3px 0 0 3px;
border-radius: 3px 0 0 3px;
}
.input-daterange input:last-child {
-webkit-border-radius: 0 3px 3px 0;
-moz-border-radius: 0 3px 3px 0;
border-radius: 0 3px 3px 0;
}
.input-daterange .add-on {
display: inline-block;
width: auto;
min-width: 16px;
height: 18px;
padding: 4px 5px;
font-weight: normal;
line-height: 18px;
text-align: center;
text-shadow: 0 1px 0 #ffffff;
vertical-align: middle;
background-color: #eeeeee;
border: 1px solid #ccc;
margin-left: -5px;
margin-right: -5px;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.ar={days:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت","الأحد"],daysShort:["أحد","اثنين","ثلاثاء","أربعاء","خميس","جمعة","سبت","أحد"],daysMin:["ح","ن","ث","ع","خ","ج","س","ح"],months:["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],monthsShort:["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],today:"هذا اليوم",rtl:!0}}(jQuery);

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.az={days:["Bazar","Bazar ertəsi","Çərşənbə axşamı","Çərşənbə","Cümə axşamı","Cümə","Şənbə"],daysShort:["B.","B.e","Ç.a","Ç.","C.a","C.","Ş."],daysMin:["B.","B.e","Ç.a","Ç.","C.a","C.","Ş."],months:["Yanvar","Fevral","Mart","Aprel","May","İyun","İyul","Avqust","Sentyabr","Oktyabr","Noyabr","Dekabr"],monthsShort:["Yan","Fev","Mar","Apr","May","İyun","İyul","Avq","Sen","Okt","Noy","Dek"],today:"Bu gün",weekStart:1}}(jQuery);

View File

@@ -1,14 +0,0 @@
/**
* Bulgarian translation for bootstrap-datepicker
* Apostol Apostolov <apostol.s.apostolov@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['bg'] = {
days: ["Неделя", "Понеделник", "Вторник", "Сряда", "Четвъртък", "Петък", "Събота", "Неделя"],
daysShort: ["Нед", "Пон", "Вто", "Сря", "Чет", "Пет", "Съб", "Нед"],
daysMin: ["Н", "П", "В", "С", "Ч", "П", "С", "Н"],
months: ["Януари", "Февруари", "Март", "Април", "Май", "Юни", "Юли", "Август", "Септември", "Октомври", "Ноември", "Декември"],
monthsShort: ["Ян", "Фев", "Мар", "Апр", "Май", "Юни", "Юли", "Авг", "Сеп", "Окт", "Ное", "Дек"],
today: "днес"
};
}(jQuery));

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.bg={days:["Неделя","Понеделник","Вторник","Сряда","Четвъртък","Петък","Събота"],daysShort:["Нед","Пон","Вто","Сря","Чет","Пет","Съб"],daysMin:["Н","П","В","С","Ч","П","С"],months:["Януари","Февруари","Март","Април","Май","Юни","Юли","Август","Септември","Октомври","Ноември","Декември"],monthsShort:["Ян","Фев","Мар","Апр","Май","Юни","Юли","Авг","Сеп","Окт","Ное","Дек"],today:"днес"}}(jQuery);

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.bs={days:["Nedjelja","Ponedjeljak","Utorak","Srijeda","Četvrtak","Petak","Subota"],daysShort:["Ned","Pon","Uto","Sri","Čet","Pet","Sub"],daysMin:["N","Po","U","Sr","Č","Pe","Su"],months:["Januar","Februar","Mart","April","Maj","Juni","Juli","August","Septembar","Oktobar","Novembar","Decembar"],monthsShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],today:"Danas",weekStart:1,format:"dd.mm.yyyy"}}(jQuery);

View File

@@ -1,14 +0,0 @@
/**
* Catalan translation for bootstrap-datepicker
* J. Garcia <jogaco.en@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['ca'] = {
days: ["Diumenge", "Dilluns", "Dimarts", "Dimecres", "Dijous", "Divendres", "Dissabte", "Diumenge"],
daysShort: ["Diu", "Dil", "Dmt", "Dmc", "Dij", "Div", "Dis", "Diu"],
daysMin: ["dg", "dl", "dt", "dc", "dj", "dv", "ds", "dg"],
months: ["Gener", "Febrer", "Març", "Abril", "Maig", "Juny", "Juliol", "Agost", "Setembre", "Octubre", "Novembre", "Desembre"],
monthsShort: ["Gen", "Feb", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Oct", "Nov", "Des"],
today: "Avui"
};
}(jQuery));

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.ca={days:["Diumenge","Dilluns","Dimarts","Dimecres","Dijous","Divendres","Dissabte"],daysShort:["Diu","Dil","Dmt","Dmc","Dij","Div","Dis"],daysMin:["dg","dl","dt","dc","dj","dv","ds"],months:["Gener","Febrer","Març","Abril","Maig","Juny","Juliol","Agost","Setembre","Octubre","Novembre","Desembre"],monthsShort:["Gen","Feb","Mar","Abr","Mai","Jun","Jul","Ago","Set","Oct","Nov","Des"],today:"Avui",monthsTitle:"Mesos",clear:"Esborrar",weekStart:1,format:"dd/mm/yyyy"}}(jQuery);

View File

@@ -1,15 +0,0 @@
/**
* Czech translation for bootstrap-datepicker
* Matěj Koubík <matej@koubik.name>
* Fixes by Michal Remiš <michal.remis@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['cs'] = {
days: ["Neděle", "Pondělí", "Úterý", "Středa", "Čtvrtek", "Pátek", "Sobota", "Neděle"],
daysShort: ["Ned", "Pon", "Úte", "Stř", "Čtv", "Pát", "Sob", "Ned"],
daysMin: ["Ne", "Po", "Út", "St", "Čt", "Pá", "So", "Ne"],
months: ["Leden", "Únor", "Březen", "Duben", "Květen", "Červen", "Červenec", "Srpen", "Září", "Říjen", "Listopad", "Prosinec"],
monthsShort: ["Led", "Úno", "Bře", "Dub", "Kvě", "Čer", "Čnc", "Srp", "Zář", "Říj", "Lis", "Pro"],
today: "Dnes"
};
}(jQuery));

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.cs={days:["Neděle","Pondělí","Úterý","Středa","Čtvrtek","Pátek","Sobota"],daysShort:["Ned","Pon","Úte","Stř","Čtv","Pát","Sob"],daysMin:["Ne","Po","Út","St","Čt","Pá","So"],months:["Leden","Únor","Březen","Duben","Květen","Červen","Červenec","Srpen","Září","Říjen","Listopad","Prosinec"],monthsShort:["Led","Úno","Bře","Dub","Kvě","Čer","Čnc","Srp","Zář","Říj","Lis","Pro"],today:"Dnes",clear:"Vymazat",weekStart:1,format:"dd.m.yyyy"}}(jQuery);

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.cy={days:["Sul","Llun","Mawrth","Mercher","Iau","Gwener","Sadwrn"],daysShort:["Sul","Llu","Maw","Mer","Iau","Gwe","Sad"],daysMin:["Su","Ll","Ma","Me","Ia","Gwe","Sa"],months:["Ionawr","Chewfror","Mawrth","Ebrill","Mai","Mehefin","Gorfennaf","Awst","Medi","Hydref","Tachwedd","Rhagfyr"],monthsShort:["Ion","Chw","Maw","Ebr","Mai","Meh","Gor","Aws","Med","Hyd","Tach","Rha"],today:"Heddiw"}}(jQuery);

View File

@@ -1,14 +0,0 @@
/**
* Danish translation for bootstrap-datepicker
* Christian Pedersen <http://github.com/chripede>
*/
;(function($){
$.fn.datepicker.dates['da'] = {
days: ["Søndag", "Mandag", "Tirsdag", "Onsdag", "Torsdag", "Fredag", "Lørdag", "Søndag"],
daysShort: ["Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør", "Søn"],
daysMin: ["Sø", "Ma", "Ti", "On", "To", "Fr", "Lø", "Sø"],
months: ["Januar", "Februar", "Marts", "April", "Maj", "Juni", "Juli", "August", "September", "Oktober", "November", "December"],
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"],
today: "I Dag"
};
}(jQuery));

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.da={days:["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"],daysShort:["søn","man","tir","ons","tor","fre","lør"],daysMin:["sø","ma","ti","on","to","fr","lø"],months:["januar","februar","marts","april","maj","juni","juli","august","september","oktober","november","december"],monthsShort:["jan","feb","mar","apr","maj","jun","jul","aug","sep","okt","nov","dec"],today:"I Dag",clear:"Nulstil"}}(jQuery);

View File

@@ -1,16 +0,0 @@
/**
* German translation for bootstrap-datepicker
* Sam Zurcher <sam@orelias.ch>
*/
;(function($){
$.fn.datepicker.dates['de'] = {
days: ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"],
daysShort: ["Son", "Mon", "Die", "Mit", "Don", "Fre", "Sam", "Son"],
daysMin: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"],
months: ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"],
monthsShort: ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"],
today: "Heute",
weekStart: 1,
format: "dd.mm.yyyy"
};
}(jQuery));

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.de={days:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],daysShort:["Son","Mon","Die","Mit","Don","Fre","Sam"],daysMin:["So","Mo","Di","Mi","Do","Fr","Sa"],months:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthsShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],today:"Heute",monthsTitle:"Monate",clear:"Löschen",weekStart:1,format:"dd.mm.yyyy"}}(jQuery);

View File

@@ -1,13 +0,0 @@
/**
* Greek translation for bootstrap-datepicker
*/
;(function($){
$.fn.datepicker.dates['el'] = {
days: ["Κυριακή", "Δευτέρα", "Τρίτη", "Τετάρτη", "Πέμπτη", "Παρασκευή", "Σάββατο", "Κυριακή"],
daysShort: ["Κυρ", "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ", "Κυρ"],
daysMin: ["Κυ", "Δε", "Τρ", "Τε", "Πε", "Πα", "Σα", "Κυ"],
months: ["Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος"],
monthsShort: ["Ιαν", "Φεβ", "Μαρ", "Απρ", "Μάι", "Ιουν", "Ιουλ", "Αυγ", "Σεπ", "Οκτ", "Νοε", "Δεκ"],
today: "Σήμερα"
};
}(jQuery));

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.el={days:["Κυριακή","Δευτέρα","Τρίτη","Τετάρτη","Πέμπτη","Παρασκευή","Σάββατο"],daysShort:["Κυρ","Δευ","Τρι","Τετ","Πεμ","Παρ","Σαβ"],daysMin:["Κυ","Δε","Τρ","Τε","Πε","Πα","Σα"],months:["Ιανουάριος","Φεβρουάριος","Μάρτιος","Απρίλιος","Μάιος","Ιούνιος","Ιούλιος","Αύγουστος","Σεπτέμβριος","Οκτώβριος","Νοέμβριος","Δεκέμβριος"],monthsShort:["Ιαν","Φεβ","Μαρ","Απρ","Μάι","Ιουν","Ιουλ","Αυγ","Σεπ","Οκτ","Νοε","Δεκ"],today:"Σήμερα",clear:"Καθαρισμός",weekStart:1,format:"d/m/yyyy"}}(jQuery);

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates["en-AU"]={days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",monthsTitle:"Months",clear:"Clear",weekStart:1,format:"d/mm/yyyy"}}(jQuery);

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates["en-GB"]={days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",monthsTitle:"Months",clear:"Clear",weekStart:1,format:"dd/mm/yyyy"}}(jQuery);

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.eo={days:["dimanĉo","lundo","mardo","merkredo","ĵaŭdo","vendredo","sabato"],daysShort:["dim.","lun.","mar.","mer.","ĵaŭ.","ven.","sam."],daysMin:["d","l","ma","me","ĵ","v","s"],months:["januaro","februaro","marto","aprilo","majo","junio","julio","aŭgusto","septembro","oktobro","novembro","decembro"],monthsShort:["jan.","feb.","mar.","apr.","majo","jun.","jul.","aŭg.","sep.","okt.","nov.","dec."],today:"Hodiaŭ",clear:"Nuligi",weekStart:1,format:"yyyy-mm-dd"}}(jQuery);

View File

@@ -1,14 +0,0 @@
/**
* Spanish translation for bootstrap-datepicker
* Bruno Bonamin <bruno.bonamin@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['es'] = {
days: ["Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo"],
daysShort: ["Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb", "Dom"],
daysMin: ["Do", "Lu", "Ma", "Mi", "Ju", "Vi", "Sa", "Do"],
months: ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"],
monthsShort: ["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"],
today: "Hoy"
};
}(jQuery));

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.es={days:["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"],daysShort:["Dom","Lun","Mar","Mié","Jue","Vie","Sáb"],daysMin:["Do","Lu","Ma","Mi","Ju","Vi","Sa"],months:["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"],monthsShort:["Ene","Feb","Mar","Abr","May","Jun","Jul","Ago","Sep","Oct","Nov","Dic"],today:"Hoy",monthsTitle:"Meses",clear:"Borrar",weekStart:1,format:"dd/mm/yyyy"}}(jQuery);

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.et={days:["Pühapäev","Esmaspäev","Teisipäev","Kolmapäev","Neljapäev","Reede","Laupäev"],daysShort:["Pühap","Esmasp","Teisip","Kolmap","Neljap","Reede","Laup"],daysMin:["P","E","T","K","N","R","L"],months:["Jaanuar","Veebruar","Märts","Aprill","Mai","Juuni","Juuli","August","September","Oktoober","November","Detsember"],monthsShort:["Jaan","Veebr","Märts","Apr","Mai","Juuni","Juuli","Aug","Sept","Okt","Nov","Dets"],today:"Täna",clear:"Tühjenda",weekStart:1,format:"dd.mm.yyyy"}}(jQuery);

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.eu={days:["Igandea","Astelehena","Asteartea","Asteazkena","Osteguna","Ostirala","Larunbata"],daysShort:["Ig","Al","Ar","Az","Og","Ol","Lr"],daysMin:["Ig","Al","Ar","Az","Og","Ol","Lr"],months:["Urtarrila","Otsaila","Martxoa","Apirila","Maiatza","Ekaina","Uztaila","Abuztua","Iraila","Urria","Azaroa","Abendua"],monthsShort:["Urt","Ots","Mar","Api","Mai","Eka","Uzt","Abu","Ira","Urr","Aza","Abe"],today:"Gaur"}}(jQuery);

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.fa={days:["یک‌شنبه","دوشنبه","سه‌شنبه","چهارشنبه","پنج‌شنبه","جمعه","شنبه","یک‌شنبه"],daysShort:["یک","دو","سه","چهار","پنج","جمعه","شنبه","یک"],daysMin:["ی","د","س","چ","پ","ج","ش","ی"],months:["ژانویه","فوریه","مارس","آوریل","مه","ژوئن","ژوئیه","اوت","سپتامبر","اکتبر","نوامبر","دسامبر"],monthsShort:["ژان","فور","مار","آور","مه","ژون","ژوی","اوت","سپت","اکت","نوا","دسا"],today:"امروز",clear:"پاک کن",weekStart:1,format:"yyyy/mm/dd"}}(jQuery);

View File

@@ -1,14 +0,0 @@
/**
* Finnish translation for bootstrap-datepicker
* Jaakko Salonen <https://github.com/jsalonen>
*/
;(function($){
$.fn.datepicker.dates['fi'] = {
days: ["sunnuntai", "maanantai", "tiistai", "keskiviikko", "torstai", "perjantai", "lauantai", "sunnuntai"],
daysShort: ["sun", "maa", "tii", "kes", "tor", "per", "lau", "sun"],
daysMin: ["su", "ma", "ti", "ke", "to", "pe", "la", "su"],
months: ["tammikuu", "helmikuu", "maaliskuu", "huhtikuu", "toukokuu", "kesäkuu", "heinäkuu", "elokuu", "syyskuu", "lokakuu", "marraskuu", "joulukuu"],
monthsShort: ["tam", "hel", "maa", "huh", "tou", "kes", "hei", "elo", "syy", "lok", "mar", "jou"],
today: "tänään"
};
}(jQuery));

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.fi={days:["sunnuntai","maanantai","tiistai","keskiviikko","torstai","perjantai","lauantai"],daysShort:["sun","maa","tii","kes","tor","per","lau"],daysMin:["su","ma","ti","ke","to","pe","la"],months:["tammikuu","helmikuu","maaliskuu","huhtikuu","toukokuu","kesäkuu","heinäkuu","elokuu","syyskuu","lokakuu","marraskuu","joulukuu"],monthsShort:["tam","hel","maa","huh","tou","kes","hei","elo","syy","lok","mar","jou"],today:"tänään",clear:"Tyhjennä",weekStart:1,format:"d.m.yyyy"}}(jQuery);

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.fo={days:["Sunnudagur","Mánadagur","Týsdagur","Mikudagur","Hósdagur","Fríggjadagur","Leygardagur"],daysShort:["Sun","Mán","Týs","Mik","Hós","Frí","Ley"],daysMin:["Su","Má","Tý","Mi","Hó","Fr","Le"],months:["Januar","Februar","Marts","Apríl","Mei","Juni","Juli","August","Septembur","Oktobur","Novembur","Desembur"],monthsShort:["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Aug","Sep","Okt","Nov","Des"],today:"Í Dag",clear:"Reinsa"}}(jQuery);

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.fr={days:["Dimanche","Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi"],daysShort:["Dim","Lun","Mar","Mer","Jeu","Ven","Sam"],daysMin:["D","L","Ma","Me","J","V","S"],months:["Janvier","Février","Mars","Avril","Mai","Juin","Juillet","Août","Septembre","Octobre","Novembre","Décembre"],monthsShort:["Jan","Fév","Mar","Avr","Mai","Jui","Jul","Aou","Sep","Oct","Nov","Déc"],today:"Aujourd'hui",monthsTitle:"Mois",clear:"Effacer",weekStart:1,format:"dd.mm.yyyy"}}(jQuery);

View File

@@ -1,16 +0,0 @@
/**
* French translation for bootstrap-datepicker
* Nico Mollet <nico.mollet@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['fr'] = {
days: ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche"],
daysShort: ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam", "Dim"],
daysMin: ["D", "L", "Ma", "Me", "J", "V", "S", "D"],
months: ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"],
monthsShort: ["Jan", "Fev", "Mar", "Avr", "Mai", "Jui", "Jul", "Aou", "Sep", "Oct", "Nov", "Dec"],
today: "Aujourd'hui",
weekStart: 1,
format: "dd/mm/yyyy"
};
}(jQuery));

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