mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-12 00:19:06 -05:00
Compare commits
392 Commits
joe/bugfix
...
feature/ap
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
417f7f7236 | ||
|
|
c1d92c2767 | ||
|
|
3d2f677e2f | ||
|
|
b15cc6cbc0 | ||
|
|
4fbb8a436c | ||
|
|
308b41b8e8 | ||
|
|
516d0cd2ca | ||
|
|
f9d8217f90 | ||
|
|
58ad213a6f | ||
|
|
6ef5c7728e | ||
|
|
58a5fe9a84 | ||
|
|
c633c8b7dd | ||
|
|
e75c99672d | ||
|
|
7faba72ebe | ||
|
|
cbe8fc1bdf | ||
|
|
f66a7660e2 | ||
|
|
5f3159a203 | ||
|
|
76aeda4436 | ||
|
|
fa791cd28c | ||
|
|
d836c68ee5 | ||
|
|
519d90f0a7 | ||
|
|
26400be6f7 | ||
|
|
92ba7e9d54 | ||
|
|
25eafe1e69 | ||
|
|
118a9ca861 | ||
|
|
174a1fe834 | ||
|
|
1e0f3f40a9 | ||
|
|
19623694f5 | ||
|
|
55a16043e1 | ||
|
|
29943b7edd | ||
|
|
a1e2af9533 | ||
|
|
c350e2a668 | ||
|
|
e0868ba2ab | ||
|
|
bcefd1fbd8 | ||
|
|
f5fbad0abf | ||
|
|
95b1a197be | ||
|
|
39169a36f5 | ||
|
|
3b1a409f07 | ||
|
|
accd70d4b4 | ||
|
|
3c7f4b760f | ||
|
|
f7d7ccfd2c | ||
|
|
de98a03887 | ||
|
|
0e11c240cb | ||
|
|
c0a298e484 | ||
|
|
907b9a9862 | ||
|
|
8d70d91cf4 | ||
|
|
6fb86859ce | ||
|
|
fe733b319f | ||
|
|
08b58f3055 | ||
|
|
9f6659f526 | ||
|
|
d28397df93 | ||
|
|
2e1c37146b | ||
|
|
903adc8f97 | ||
|
|
fc7f454382 | ||
|
|
ef35fc63a1 | ||
|
|
52a193b183 | ||
|
|
dad401a6ec | ||
|
|
ec3f8118db | ||
|
|
cfc0194c00 | ||
|
|
dd28f52301 | ||
|
|
9dcbd532e6 | ||
|
|
16b4a2cad2 | ||
|
|
bd9d8a035a | ||
|
|
d55ffb0212 | ||
|
|
e76ddfd005 | ||
|
|
59145a3b40 | ||
|
|
c993f5343b | ||
|
|
b62acec5ee | ||
|
|
b34ab9cdd5 | ||
|
|
e0a8ab852e | ||
|
|
bd5ebd0e41 | ||
|
|
661e21d25b | ||
|
|
dc69a2bc94 | ||
|
|
e6fec6b27d | ||
|
|
27b92f9838 | ||
|
|
3446def4dd | ||
|
|
2700206715 | ||
|
|
fdfc6f70f3 | ||
|
|
065c288edb | ||
|
|
3121d2c23e | ||
|
|
7cd3bb524c | ||
|
|
6b8cc97779 | ||
|
|
b7112a1edd | ||
|
|
28965b7356 | ||
|
|
bd3aa28416 | ||
|
|
9fed4ce24c | ||
|
|
90383e30dd | ||
|
|
13f184e957 | ||
|
|
a7a2c6d7ff | ||
|
|
d1bf39d0ac | ||
|
|
7dff6b8415 | ||
|
|
656e019829 | ||
|
|
2133b0f498 | ||
|
|
bc4dcee2b1 | ||
|
|
0e8cf95739 | ||
|
|
e133290c57 | ||
|
|
1429b0677e | ||
|
|
d03ee36647 | ||
|
|
6e5880c642 | ||
|
|
fa93cffafb | ||
|
|
ce9af0fb57 | ||
|
|
95700d8d51 | ||
|
|
fb15e98519 | ||
|
|
3054cb7971 | ||
|
|
f84587cf5a | ||
|
|
538f38f314 | ||
|
|
06578349c7 | ||
|
|
a807476171 | ||
|
|
7aacf9ca89 | ||
|
|
50dae5fb83 | ||
|
|
0853c425fe | ||
|
|
edcc676693 | ||
|
|
c8a742a121 | ||
|
|
ee14a7e15f | ||
|
|
e1eaccf409 | ||
|
|
d2aae52868 | ||
|
|
9158fb4745 | ||
|
|
0ff5ef5337 | ||
|
|
1ace145f85 | ||
|
|
565eb4b450 | ||
|
|
f39861c43f | ||
|
|
72838c248f | ||
|
|
9be8765ccf | ||
|
|
48732c4393 | ||
|
|
5bf0b7c920 | ||
|
|
51a4580d0f | ||
|
|
266e611afa | ||
|
|
22598b693c | ||
|
|
008fe38f10 | ||
|
|
24e8123240 | ||
|
|
6054f03c0d | ||
|
|
476f6d83e2 | ||
|
|
ec57109f39 | ||
|
|
d73488f887 | ||
|
|
3201380c29 | ||
|
|
1f04b39ae3 | ||
|
|
9e2b47027c | ||
|
|
662149a98a | ||
|
|
fafa31589d | ||
|
|
43a5940b9e | ||
|
|
33908624fd | ||
|
|
ffef8a341f | ||
|
|
a48c5df844 | ||
|
|
37b6a668ab | ||
|
|
2a9a7cc897 | ||
|
|
c62e6b5734 | ||
|
|
6ec1d0b935 | ||
|
|
6c5769fdd8 | ||
|
|
09acc5920c | ||
|
|
9613c58bf8 | ||
|
|
147f9ac64b | ||
|
|
cc1e8961a1 | ||
|
|
3b1b2f401d | ||
|
|
58a87b9b61 | ||
|
|
f09475a6b5 | ||
|
|
750422d858 | ||
|
|
03d911d335 | ||
|
|
2747c11a46 | ||
|
|
5a9fe2637f | ||
|
|
a5787f9988 | ||
|
|
85e22bb515 | ||
|
|
5e1e90ad80 | ||
|
|
fe85421c7e | ||
|
|
38af6ce279 | ||
|
|
fe92f16da4 | ||
|
|
edc4b562f2 | ||
|
|
7b6a91064c | ||
|
|
a32414c6fc | ||
|
|
259b4e29de | ||
|
|
e56f80b546 | ||
|
|
1ff52c5290 | ||
|
|
70bd249f43 | ||
|
|
f2f7e43579 | ||
|
|
c36d60fcd4 | ||
|
|
0950f307d1 | ||
|
|
a9b7e4a85e | ||
|
|
912a886539 | ||
|
|
f7484f49e5 | ||
|
|
9f68be1925 | ||
|
|
ef298f8d7a | ||
|
|
c038f0e6ee | ||
|
|
3c53a93447 | ||
|
|
7e86e65cce | ||
|
|
ad171d6cbb | ||
|
|
76ffc20836 | ||
|
|
c4cc5b6dfc | ||
|
|
878c9210d3 | ||
|
|
35c982b367 | ||
|
|
9c4ff080af | ||
|
|
d32ca64a03 | ||
|
|
53b89390be | ||
|
|
a8e09d7fe6 | ||
|
|
0c7cf20e7e | ||
|
|
6ebcee33c5 | ||
|
|
c73544fb59 | ||
|
|
37c1f93bcb | ||
|
|
95aa2e10fc | ||
|
|
279e6e3925 | ||
|
|
8a661d5ee4 | ||
|
|
67fcb40455 | ||
|
|
641524c80e | ||
|
|
55802354d4 | ||
|
|
75f4f5c0bd | ||
|
|
382e9dee55 | ||
|
|
6861d4029e | ||
|
|
370ef16854 | ||
|
|
9dbe434792 | ||
|
|
21a0e95623 | ||
|
|
57c6307479 | ||
|
|
01d8b1f468 | ||
|
|
ef6b82a0a3 | ||
|
|
19b7d1a7c5 | ||
|
|
097d901191 | ||
|
|
a1b5846b29 | ||
|
|
dbdb353e69 | ||
|
|
4456eac1fd | ||
|
|
ba3f8f432e | ||
|
|
bab539f52c | ||
|
|
42dbb128be | ||
|
|
5e4a6cb15f | ||
|
|
73e45ce911 | ||
|
|
1e40043456 | ||
|
|
7f3b952ec7 | ||
|
|
82887dc1c1 | ||
|
|
71380ab37a | ||
|
|
5d00804758 | ||
|
|
84364c65b0 | ||
|
|
1b59b705ae | ||
|
|
bc90fe6f99 | ||
|
|
c8d6a0833e | ||
|
|
d8fc7d27ec | ||
|
|
1e44b19ff0 | ||
|
|
cc8b2cd20e | ||
|
|
057b1e294c | ||
|
|
0653e790c7 | ||
|
|
6d72bbcb76 | ||
|
|
59e6f08455 | ||
|
|
8fdccf50a8 | ||
|
|
1c7e11c5d9 | ||
|
|
1756fbbb23 | ||
|
|
7bb939ab7f | ||
|
|
4fa0abbd5a | ||
|
|
1e5f0266ef | ||
|
|
3dee62105e | ||
|
|
680b2323d5 | ||
|
|
562b4dad4d | ||
|
|
079a82dfe4 | ||
|
|
16f7eb43b0 | ||
|
|
4b0ed3f224 | ||
|
|
1d453b694d | ||
|
|
751e8c189e | ||
|
|
183e9a3d0b | ||
|
|
5f0f4dd485 | ||
|
|
20f05662aa | ||
|
|
963471b43f | ||
|
|
fdb52e0243 | ||
|
|
d1e4483f98 | ||
|
|
b194ada316 | ||
|
|
3a25a2dcbf | ||
|
|
85e4497fbe | ||
|
|
0bfa5e7ea6 | ||
|
|
013599890f | ||
|
|
519e552405 | ||
|
|
32a4ec49f7 | ||
|
|
3223332906 | ||
|
|
f78bd08440 | ||
|
|
99b5f92d7a | ||
|
|
1cc5e84104 | ||
|
|
8346f5ab08 | ||
|
|
dc60a39ba9 | ||
|
|
16f7872553 | ||
|
|
75e7c4b2ca | ||
|
|
c5d5ddd7d2 | ||
|
|
98a81e3708 | ||
|
|
ecdbdb944a | ||
|
|
8a2846461b | ||
|
|
6bfb9a2f57 | ||
|
|
85dfb2d4eb | ||
|
|
8f1d0c2b8f | ||
|
|
829494c03e | ||
|
|
f89fade28d | ||
|
|
9081b1dadd | ||
|
|
84dac544af | ||
|
|
49f1ac333d | ||
|
|
a2b761094b | ||
|
|
54bd3d480f | ||
|
|
b281f8fa32 | ||
|
|
a3732f845e | ||
|
|
38f6d0a020 | ||
|
|
a0e5da758a | ||
|
|
dcbe10c1c7 | ||
|
|
1f823d2a1b | ||
|
|
12d0a9e11a | ||
|
|
326b294c83 | ||
|
|
50f213ae71 | ||
|
|
91d4fd8849 | ||
|
|
3f1985a9dc | ||
|
|
573a71f09d | ||
|
|
57900fa287 | ||
|
|
fa721d9614 | ||
|
|
e64bbacf68 | ||
|
|
6ee2edc757 | ||
|
|
9ec4faf0d0 | ||
|
|
5be3ba2ffa | ||
|
|
a5ee96656b | ||
|
|
2db71d0323 | ||
|
|
dcf321047f | ||
|
|
4982110be9 | ||
|
|
1c970c8176 | ||
|
|
460f4769a5 | ||
|
|
f465643b75 | ||
|
|
a8afd71f96 | ||
|
|
2274d60207 | ||
|
|
57159bccfd | ||
|
|
2888124752 | ||
|
|
408e751dcf | ||
|
|
af5dcc38a4 | ||
|
|
81434640d6 | ||
|
|
cb52706f2f | ||
|
|
f44d232e8b | ||
|
|
a0ac79b9dd | ||
|
|
deb9b74f27 | ||
|
|
591de3cbe8 | ||
|
|
f7151e2132 | ||
|
|
44521be6dd | ||
|
|
30416cdbb5 | ||
|
|
d04da2d256 | ||
|
|
231d8a1949 | ||
|
|
3207bec805 | ||
|
|
425a71e382 | ||
|
|
daa12ab2ec | ||
|
|
f69d88a656 | ||
|
|
098cbc1456 | ||
|
|
5f2da953a9 | ||
|
|
638d999fcc | ||
|
|
fa80fd64da | ||
|
|
e4dad82dde | ||
|
|
d65ff924c8 | ||
|
|
96e9661aaa | ||
|
|
8829d2ebd4 | ||
|
|
c019280d8a | ||
|
|
8d3e5fc160 | ||
|
|
d3f1312c0b | ||
|
|
c58f48a1e4 | ||
|
|
979e93509e | ||
|
|
135c3709b4 | ||
|
|
08400d3f18 | ||
|
|
56befda288 | ||
|
|
3d68f1dc62 | ||
|
|
970036ce1a | ||
|
|
62108f28f4 | ||
|
|
66bbb072c3 | ||
|
|
6c52c26a62 | ||
|
|
d52943d1bf | ||
|
|
7cb1bbe3d6 | ||
|
|
2548c46b8b | ||
|
|
dd5118116b | ||
|
|
6696880178 | ||
|
|
8e5952d9ae | ||
|
|
a7aa6ced19 | ||
|
|
97eea669d4 | ||
|
|
c84777928e | ||
|
|
490064a953 | ||
|
|
d5975195b3 | ||
|
|
781e15cb84 | ||
|
|
9742001a71 | ||
|
|
6fd626a3ec | ||
|
|
bb4ce2f978 | ||
|
|
ca2a07b816 | ||
|
|
38c7bb35e0 | ||
|
|
4f6408f3e1 | ||
|
|
7910d9fde4 | ||
|
|
0258d7e24f | ||
|
|
85556ed532 | ||
|
|
cecb04b097 | ||
|
|
8a7c5c18d0 | ||
|
|
14a1a3f574 | ||
|
|
c19f2a7499 | ||
|
|
df95be5455 | ||
|
|
00bef13f1c | ||
|
|
a6a35905a7 | ||
|
|
93f28ef55c | ||
|
|
bbcb9573cd | ||
|
|
43cc6e19d4 | ||
|
|
f4a44664c7 | ||
|
|
d1353e8eae | ||
|
|
935a76d16b | ||
|
|
db4c41f420 | ||
|
|
62f5af8e0b | ||
|
|
ff9aefb649 | ||
|
|
2b10d03e1f | ||
|
|
a27efbd937 |
@@ -16,3 +16,5 @@
|
||||
^CONTRIBUTING.md$
|
||||
^cran-comments.md$
|
||||
^.*\.o$
|
||||
^appveyor\.yml$
|
||||
^revdep$
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
language: r
|
||||
r:
|
||||
- oldrel
|
||||
- release
|
||||
- devel
|
||||
sudo: false
|
||||
cache: packages
|
||||
|
||||
|
||||
@@ -1,10 +1,38 @@
|
||||
|
||||
We welcome contributions to the **shiny** package. To submit a contribution:
|
||||
|
||||
1. [Fork](https://github.com/rstudio/shiny/fork) the repository and make your changes.
|
||||
|
||||
2. Ensure that you have signed the [individual](http://www.rstudio.com/wp-content/uploads/2014/06/RStudioIndividualContributorAgreement.pdf) or [corporate](http://www.rstudio.com/wp-content/uploads/2014/06/RStudioCorporateContributorAgreement.pdf) contributor agreement as appropriate. You can send the signed copy to jj@rstudio.com.
|
||||
2. If the change is non-trivial, ensure that you have signed the [individual](http://www.rstudio.com/wp-content/uploads/2014/06/RStudioIndividualContributorAgreement.pdf) or [corporate](http://www.rstudio.com/wp-content/uploads/2014/06/RStudioCorporateContributorAgreement.pdf) contributor agreement as appropriate. You can send the signed copy to jj@rstudio.com. For trivial changes (like typo fixes), a contributor agreement is not needed.
|
||||
|
||||
3. Submit a [pull request](https://help.github.com/articles/using-pull-requests).
|
||||
|
||||
We'll try to be as responsive as possible in reviewing and accepting pull requests. We appreciate your contributions!
|
||||
We generally do not merge pull requests that update included web libraries (such as Bootstrap or jQuery) because it is difficult for us to verify that the update is done correctly; we prefer to update these libraries ourselves.
|
||||
|
||||
|
||||
## How to make changes
|
||||
|
||||
Before you submit a pull request, please do the following:
|
||||
|
||||
* Add an entry to NEWS.md concisely describing what you changed.
|
||||
|
||||
* If appropriate, add unit tests in the tests/ directory.
|
||||
|
||||
* If you made any changes to the JavaScript files in the srcjs/ directory, make sure you build the output JavaScript files. See tools/README.md file for information on using the build system.
|
||||
|
||||
* Run Build->Check Package in the RStudio IDE, or `devtools::check()`, to make sure your change did not add any messages, warnings, or errors.
|
||||
|
||||
Doing these things will make it easier for the Shiny development team to evaluate your pull request. Even so, we may still decide to modify your code or even not merge it at all. Factors that may prevent us from merging the pull request include:
|
||||
|
||||
* breaking backward compatibility
|
||||
* adding a feature that we do not consider relevant for Shiny
|
||||
* is hard to understand
|
||||
* is hard to maintain in the future
|
||||
* is computationally expensive
|
||||
* is not intuitive for people to use
|
||||
|
||||
We will try to be responsive and provide feedback in case we decide not to merge your pull request.
|
||||
|
||||
|
||||
## Filing issues
|
||||
|
||||
If you find a bug in Shiny, you can also [file an issue](https://github.com/rstudio/shiny/issues/new). Please provide as much relevant information as you can, and include a minimal reproducible example if possible.
|
||||
|
||||
17
DESCRIPTION
17
DESCRIPTION
@@ -1,8 +1,7 @@
|
||||
Package: shiny
|
||||
Type: Package
|
||||
Title: Web Application Framework for R
|
||||
Version: 0.13.2.9004
|
||||
Date: 2016-02-17
|
||||
Version: 1.0.0
|
||||
Authors@R: c(
|
||||
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@rstudio.com"),
|
||||
person("Joe", "Cheng", role = "aut", email = "joe@rstudio.com"),
|
||||
@@ -15,7 +14,7 @@ Authors@R: c(
|
||||
person(family = "jQuery contributors", role = c("ctb", "cph"),
|
||||
comment = "jQuery library; authors listed in inst/www/shared/jquery-AUTHORS.txt"),
|
||||
person(family = "jQuery UI contributors", role = c("ctb", "cph"),
|
||||
comment = "jQuery UI library; authors listed in inst/www/shared/jqueryui/1.10.4/AUTHORS.txt"),
|
||||
comment = "jQuery UI library; authors listed in inst/www/shared/jqueryui/AUTHORS.txt"),
|
||||
person("Mark", "Otto", role = "ctb",
|
||||
comment = "Bootstrap library"),
|
||||
person("Jacob", "Thornton", role = "ctb",
|
||||
@@ -80,12 +79,15 @@ Suggests:
|
||||
knitr (>= 1.6),
|
||||
markdown,
|
||||
rmarkdown,
|
||||
ggplot2
|
||||
ggplot2,
|
||||
magrittr
|
||||
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 +97,6 @@ Collate:
|
||||
'cache.R'
|
||||
'diagnose.R'
|
||||
'fileupload.R'
|
||||
'stack.R'
|
||||
'graph.R'
|
||||
'hooks.R'
|
||||
'html-deps.R'
|
||||
@@ -116,6 +117,7 @@ Collate:
|
||||
'input-slider.R'
|
||||
'input-submit.R'
|
||||
'input-text.R'
|
||||
'input-textarea.R'
|
||||
'input-utils.R'
|
||||
'insert-ui.R'
|
||||
'jqueryui.R'
|
||||
@@ -132,13 +134,16 @@ 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'
|
||||
'showcase.R'
|
||||
'tar.R'
|
||||
'test-export.R'
|
||||
'timer.R'
|
||||
'update-input.R'
|
||||
RoxygenNote: 5.0.1
|
||||
|
||||
37
NAMESPACE
37
NAMESPACE
@@ -2,17 +2,24 @@
|
||||
|
||||
S3method("$",reactivevalues)
|
||||
S3method("$",session_proxy)
|
||||
S3method("$",shinyapi)
|
||||
S3method("$",shinyoutput)
|
||||
S3method("$<-",reactivevalues)
|
||||
S3method("$<-",session_proxy)
|
||||
S3method("$<-",shinyapi)
|
||||
S3method("$<-",shinyoutput)
|
||||
S3method("[",reactivevalues)
|
||||
S3method("[",shinyapi)
|
||||
S3method("[",shinyoutput)
|
||||
S3method("[<-",reactivevalues)
|
||||
S3method("[<-",shinyapi)
|
||||
S3method("[<-",shinyoutput)
|
||||
S3method("[[",reactivevalues)
|
||||
S3method("[[",session_proxy)
|
||||
S3method("[[",shinyapi)
|
||||
S3method("[[",shinyoutput)
|
||||
S3method("[[<-",reactivevalues)
|
||||
S3method("[[<-",shinyapi)
|
||||
S3method("[[<-",shinyoutput)
|
||||
S3method("names<-",reactivevalues)
|
||||
S3method(as.list,reactivevalues)
|
||||
@@ -39,6 +46,7 @@ export(addResourcePath)
|
||||
export(animationOptions)
|
||||
export(as.shiny.appobj)
|
||||
export(basicPage)
|
||||
export(bookmarkButton)
|
||||
export(bootstrapLib)
|
||||
export(bootstrapPage)
|
||||
export(br)
|
||||
@@ -46,7 +54,6 @@ export(browserViewer)
|
||||
export(brushOpts)
|
||||
export(brushedPoints)
|
||||
export(callModule)
|
||||
export(cancelOutput)
|
||||
export(captureStackTraces)
|
||||
export(checkboxGroupInput)
|
||||
export(checkboxInput)
|
||||
@@ -60,13 +67,16 @@ export(dataTableOutput)
|
||||
export(dateInput)
|
||||
export(dateRangeInput)
|
||||
export(dblclickOpts)
|
||||
export(debounce)
|
||||
export(dialogViewer)
|
||||
export(div)
|
||||
export(downloadButton)
|
||||
export(downloadHandler)
|
||||
export(downloadLink)
|
||||
export(em)
|
||||
export(enableBookmarking)
|
||||
export(eventReactive)
|
||||
export(exportTestValues)
|
||||
export(exprToFunction)
|
||||
export(extractStackTrace)
|
||||
export(fileInput)
|
||||
@@ -80,7 +90,9 @@ export(flowLayout)
|
||||
export(fluidPage)
|
||||
export(fluidRow)
|
||||
export(formatStackTrace)
|
||||
export(freezeReactiveValue)
|
||||
export(getDefaultReactiveDomain)
|
||||
export(getShinyOption)
|
||||
export(h1)
|
||||
export(h2)
|
||||
export(h3)
|
||||
@@ -110,6 +122,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 +145,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 +190,7 @@ export(renderText)
|
||||
export(renderUI)
|
||||
export(repeatable)
|
||||
export(req)
|
||||
export(restoreInput)
|
||||
export(runApp)
|
||||
export(runExample)
|
||||
export(runGadget)
|
||||
@@ -179,13 +200,21 @@ export(runUrl)
|
||||
export(safeError)
|
||||
export(selectInput)
|
||||
export(selectizeInput)
|
||||
export(serveCSV)
|
||||
export(serveJSON)
|
||||
export(servePlot)
|
||||
export(serveRaw)
|
||||
export(serveText)
|
||||
export(serverInfo)
|
||||
export(setBookmarkExclude)
|
||||
export(setProgress)
|
||||
export(shinyApp)
|
||||
export(shinyAppDir)
|
||||
export(shinyAppFile)
|
||||
export(shinyOptions)
|
||||
export(shinyServer)
|
||||
export(shinyUI)
|
||||
export(showBookmarkUrlModal)
|
||||
export(showModal)
|
||||
export(showNotification)
|
||||
export(showReactLog)
|
||||
@@ -209,8 +238,10 @@ export(tagAppendChildren)
|
||||
export(tagList)
|
||||
export(tagSetChildren)
|
||||
export(tags)
|
||||
export(textAreaInput)
|
||||
export(textInput)
|
||||
export(textOutput)
|
||||
export(throttle)
|
||||
export(titlePanel)
|
||||
export(uiOutput)
|
||||
export(updateActionButton)
|
||||
@@ -221,12 +252,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)
|
||||
@@ -244,3 +278,4 @@ import(httpuv)
|
||||
import(methods)
|
||||
import(mime)
|
||||
import(xtable)
|
||||
importFrom(utils,write.csv)
|
||||
|
||||
54
R/app.R
54
R/app.R
@@ -20,19 +20,29 @@
|
||||
#' @param onStart A function that will be called before the app is actually run.
|
||||
#' This is only needed for \code{shinyAppObj}, since in the \code{shinyAppDir}
|
||||
#' case, a \code{global.R} file can be used for this purpose.
|
||||
#' @param options Named options that should be passed to the `runApp` call. You
|
||||
#' can also specify \code{width} and \code{height} parameters which provide a
|
||||
#' hint to the embedding environment about the ideal height/width for the app.
|
||||
#' @param options Named options that should be passed to the \code{runApp} call
|
||||
#' (these can be any of the following: "port", "launch.browser", "host", "quiet",
|
||||
#' "display.mode" and "test.mode"). You can also specify \code{width} and
|
||||
#' \code{height} parameters which provide a hint to the embedding environment
|
||||
#' about the ideal height/width for the app.
|
||||
#' @param uiPattern A regular expression that will be applied to each \code{GET}
|
||||
#' request to determine whether the \code{ui} should be used to handle the
|
||||
#' request. Note that the entire request path must match the regular
|
||||
#' expression in order for the match to be considered successful.
|
||||
#' @param enableBookmarking Can be one of \code{"url"}, \code{"server"}, or
|
||||
#' \code{"disable"}. This is equivalent to calling the
|
||||
#' \code{\link{enableBookmarking}()} function just before calling
|
||||
#' \code{shinyApp()}. With the default value (\code{NULL}), the app will
|
||||
#' respect the setting from any previous calls to \code{enableBookmarking()}.
|
||||
#' See \code{\link{enableBookmarking}} for more information.
|
||||
#' @return An object that represents the app. Printing the object or passing it
|
||||
#' to \code{\link{runApp}} will run the app.
|
||||
#'
|
||||
#' @examples
|
||||
#' ## Only run this example in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' options(device.ask.default = FALSE)
|
||||
#'
|
||||
#' shinyApp(
|
||||
#' ui = fluidPage(
|
||||
#' numericInput("n", "n", 1),
|
||||
@@ -59,10 +69,9 @@
|
||||
#'
|
||||
#' runApp(app)
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
shinyApp <- function(ui=NULL, server=NULL, onStart=NULL, options=list(),
|
||||
uiPattern="/") {
|
||||
uiPattern="/", enableBookmarking = NULL) {
|
||||
if (is.null(server)) {
|
||||
stop("`server` missing from shinyApp")
|
||||
}
|
||||
@@ -76,12 +85,24 @@ shinyApp <- function(ui=NULL, server=NULL, onStart=NULL, options=list(),
|
||||
server
|
||||
}
|
||||
|
||||
if (!is.null(enableBookmarking)) {
|
||||
bookmarkStore <- match.arg(enableBookmarking, c("url", "server", "disable"))
|
||||
enableBookmarking(bookmarkStore)
|
||||
}
|
||||
|
||||
# Store the appDir and bookmarking-related options, so that we can read them
|
||||
# from within the app.
|
||||
shinyOptions(appDir = getwd())
|
||||
appOptions <- consumeAppOptions()
|
||||
|
||||
structure(
|
||||
list(
|
||||
httpHandler = httpHandler,
|
||||
serverFuncSource = serverFuncSource,
|
||||
onStart = onStart,
|
||||
options = options),
|
||||
options = options,
|
||||
appOptions = appOptions
|
||||
),
|
||||
class = "shiny.appobj"
|
||||
)
|
||||
}
|
||||
@@ -113,7 +134,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 +201,8 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
|
||||
}
|
||||
}
|
||||
|
||||
shinyOptions(appDir = appDir)
|
||||
|
||||
oldwd <- NULL
|
||||
monitorHandle <- NULL
|
||||
onStart <- function() {
|
||||
@@ -199,7 +224,8 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
|
||||
serverFuncSource = serverFuncSource,
|
||||
onStart = onStart,
|
||||
onEnd = onEnd,
|
||||
options = options),
|
||||
options = options
|
||||
),
|
||||
class = "shiny.appobj"
|
||||
)
|
||||
}
|
||||
@@ -209,13 +235,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 +278,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 +292,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)
|
||||
}
|
||||
)
|
||||
@@ -348,7 +377,8 @@ is.shiny.appobj <- function(x) {
|
||||
print.shiny.appobj <- function(x, ...) {
|
||||
opts <- x$options %OR% list()
|
||||
opts <- opts[names(opts) %in%
|
||||
c("port", "launch.browser", "host", "quiet", "display.mode")]
|
||||
c("port", "launch.browser", "host", "quiet",
|
||||
"display.mode", "test.mode")]
|
||||
|
||||
args <- c(list(x), opts)
|
||||
|
||||
|
||||
28
R/bookmark-state-local.R
Normal file
28
R/bookmark-state-local.R
Normal file
@@ -0,0 +1,28 @@
|
||||
# Function wrappers for saving and restoring state to/from disk when running
|
||||
# Shiny locally.
|
||||
#
|
||||
# These functions provide a directory to the callback function.
|
||||
#
|
||||
# @param id A session ID to save.
|
||||
# @param callback A callback function that saves state to or restores state from
|
||||
# a directory. It must take one argument, \code{stateDir}, which is a
|
||||
# directory to which it writes/reads.
|
||||
|
||||
saveInterfaceLocal <- function(id, callback) {
|
||||
# Try to save in app directory
|
||||
appDir <- getShinyOption("appDir", default = getwd())
|
||||
|
||||
stateDir <- file.path(appDir, "shiny_bookmarks", id)
|
||||
if (!dirExists(stateDir))
|
||||
dir.create(stateDir, recursive = TRUE)
|
||||
|
||||
callback(stateDir)
|
||||
}
|
||||
|
||||
loadInterfaceLocal <- function(id, callback) {
|
||||
# Try to load from app directory
|
||||
appDir <- getShinyOption("appDir", default = getwd())
|
||||
|
||||
stateDir <- file.path(appDir, "shiny_bookmarks", id)
|
||||
callback(stateDir)
|
||||
}
|
||||
1097
R/bookmark-state.R
Normal file
1097
R/bookmark-state.R
Normal file
File diff suppressed because it is too large
Load Diff
@@ -277,6 +277,7 @@ titlePanel <- function(title, windowTitle=title) {
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' options(device.ask.default = FALSE)
|
||||
#'
|
||||
#' # Define UI
|
||||
#' ui <- fluidPage(
|
||||
@@ -418,7 +419,6 @@ flowLayout <- function(..., cellArgs = list()) {
|
||||
#' suitable for wrapping inputs.
|
||||
#'
|
||||
#' @param ... Input controls or other HTML elements.
|
||||
#'
|
||||
#' @export
|
||||
inputPanel <- function(...) {
|
||||
div(class = "shiny-input-panel",
|
||||
@@ -443,6 +443,7 @@ inputPanel <- function(...) {
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' options(device.ask.default = FALSE)
|
||||
#'
|
||||
#' # Server code used for all examples
|
||||
#' server <- function(input, output) {
|
||||
|
||||
@@ -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,14 +335,25 @@ 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)
|
||||
|
||||
# function to return plain or fluid class name
|
||||
className <- function(name) {
|
||||
if (fluid)
|
||||
paste(name, "-fluid", sep="")
|
||||
else
|
||||
name
|
||||
}
|
||||
|
||||
# built the container div dynamically to support optional collapsibility
|
||||
if (collapsible) {
|
||||
navId <- paste("navbar-collapse-", p_randomInt(1000, 10000), sep="")
|
||||
containerDiv <- div(class="container",
|
||||
containerDiv <- div(class=className("container"),
|
||||
div(class="navbar-header",
|
||||
tags$button(type="button", class="navbar-toggle collapsed",
|
||||
`data-toggle`="collapse", `data-target`=paste0("#", navId),
|
||||
@@ -359,7 +367,7 @@ navbarPage <- function(title,
|
||||
div(class="navbar-collapse collapse", id=navId, tabset$navList)
|
||||
)
|
||||
} else {
|
||||
containerDiv <- div(class="container",
|
||||
containerDiv <- div(class=className("container"),
|
||||
div(class="navbar-header",
|
||||
span(class="navbar-brand", pageTitle)
|
||||
),
|
||||
@@ -367,14 +375,6 @@ navbarPage <- function(title,
|
||||
)
|
||||
}
|
||||
|
||||
# function to return plain or fluid class name
|
||||
className <- function(name) {
|
||||
if (fluid)
|
||||
paste(name, "-fluid", sep="")
|
||||
else
|
||||
name
|
||||
}
|
||||
|
||||
# build the main tab content div
|
||||
contentDiv <- div(class=className("container"))
|
||||
if (!is.null(header))
|
||||
@@ -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,
|
||||
@@ -930,21 +935,34 @@ textOutput <- function(outputId, container = if (inline) span else div, inline =
|
||||
#' Render a reactive output variable as verbatim text within an
|
||||
#' application page. The text will be included within an HTML \code{pre} tag.
|
||||
#' @param outputId output variable to read the value from
|
||||
#' @param placeholder if the output is empty or \code{NULL}, should an empty
|
||||
#' rectangle be displayed to serve as a placeholder? (does not affect
|
||||
#' behavior when the the output in nonempty)
|
||||
#' @return A verbatim text output element that can be included in a panel
|
||||
#' @details Text is HTML-escaped prior to rendering. This element is often used
|
||||
#' with the \link{renderPrint} function to preserve fixed-width formatting
|
||||
#' of printed objects.
|
||||
#' with the \link{renderPrint} function to preserve fixed-width formatting
|
||||
#' of printed objects.
|
||||
#' @examples
|
||||
#' mainPanel(
|
||||
#' h4("Summary"),
|
||||
#' verbatimTextOutput("summary"),
|
||||
#'
|
||||
#' h4("Observations"),
|
||||
#' tableOutput("view")
|
||||
#' )
|
||||
#' ## Only run this example in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' shinyApp(
|
||||
#' ui = basicPage(
|
||||
#' textInput("txt", "Enter the text to display below:"),
|
||||
#' verbatimTextOutput("default"),
|
||||
#' verbatimTextOutput("placeholder", placeholder = TRUE)
|
||||
#' ),
|
||||
#' server = function(input, output) {
|
||||
#' output$default <- renderText({ input$txt })
|
||||
#' output$placeholder <- renderText({ input$txt })
|
||||
#' }
|
||||
#' )
|
||||
#' }
|
||||
#' @export
|
||||
verbatimTextOutput <- function(outputId) {
|
||||
textOutput(outputId, container = pre)
|
||||
verbatimTextOutput <- function(outputId, placeholder = FALSE) {
|
||||
pre(id = outputId,
|
||||
class = paste(c("shiny-text-output", if (!placeholder) "noplaceholder"),
|
||||
collapse = " ")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1118,7 +1136,7 @@ imageOutput <- function(outputId, width = "100%", height="400px",
|
||||
#' same \code{id} to disappear.
|
||||
#' @inheritParams textOutput
|
||||
#' @note The arguments \code{clickId} and \code{hoverId} only work for R base
|
||||
#' graphics (see the \pkg{\link{graphics}} package). They do not work for
|
||||
#' graphics (see the \pkg{\link[graphics:graphics-package]{graphics}} package). They do not work for
|
||||
#' \pkg{\link[grid:grid-package]{grid}}-based graphics, such as \pkg{ggplot2},
|
||||
#' \pkg{lattice}, and so on.
|
||||
#'
|
||||
@@ -1416,6 +1434,7 @@ uiOutput <- htmlOutput
|
||||
#' is assigned to.
|
||||
#' @param label The label that should appear on the button.
|
||||
#' @param class Additional CSS classes to apply to the tag, if any.
|
||||
#' @param ... Other arguments to pass to the container tag function.
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
@@ -1438,23 +1457,25 @@ uiOutput <- htmlOutput
|
||||
#' @export
|
||||
downloadButton <- function(outputId,
|
||||
label="Download",
|
||||
class=NULL) {
|
||||
class=NULL, ...) {
|
||||
aTag <- tags$a(id=outputId,
|
||||
class=paste('btn btn-default shiny-download-link', class),
|
||||
href='',
|
||||
target='_blank',
|
||||
download=NA,
|
||||
icon("download"),
|
||||
label)
|
||||
label, ...)
|
||||
}
|
||||
|
||||
#' @rdname downloadButton
|
||||
#' @export
|
||||
downloadLink <- function(outputId, label="Download", class=NULL) {
|
||||
downloadLink <- function(outputId, label="Download", class=NULL, ...) {
|
||||
tags$a(id=outputId,
|
||||
class=paste(c('shiny-download-link', class), collapse=" "),
|
||||
href='',
|
||||
target='_blank',
|
||||
label)
|
||||
download=NA,
|
||||
label, ...)
|
||||
}
|
||||
|
||||
|
||||
@@ -1495,7 +1516,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 +1543,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.7.0", c(href="shared/font-awesome"),
|
||||
stylesheet = "css/font-awesome.min.css"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,11 +21,10 @@
|
||||
#' @param width Width in pixels.
|
||||
#' @param height Height in pixels.
|
||||
#' @param res Resolution in pixels per inch. This value is passed to
|
||||
#' \code{\link{png}}. Note that this affects the resolution of PNG rendering in
|
||||
#' \code{\link[grDevices]{png}}. Note that this affects the resolution of PNG rendering in
|
||||
#' R; it won't change the actual ppi of the browser.
|
||||
#' @param ... Arguments to be passed through to \code{\link[grDevices]{png}}.
|
||||
#' These can be used to set the width, height, background color, etc.
|
||||
#'
|
||||
#' @export
|
||||
plotPNG <- function(func, filename=tempfile(fileext='.png'),
|
||||
width=400, height=400, res=72, ...) {
|
||||
|
||||
@@ -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),
|
||||
...
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#' \item \code{yy} Year without century (12)
|
||||
#' \item \code{yyyy} Year with century (2012)
|
||||
#' \item \code{mm} Month number, with leading zero (01-12)
|
||||
#' \item \code{m} Month number, without leading zero (01-12)
|
||||
#' \item \code{m} Month number, without leading zero (1-12)
|
||||
#' \item \code{M} Abbreviated month name
|
||||
#' \item \code{MM} Full month name
|
||||
#' \item \code{dd} Day of month with leading zero
|
||||
@@ -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>"
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#' \item \code{yy} Year without century (12)
|
||||
#' \item \code{yyyy} Year with century (2012)
|
||||
#' \item \code{mm} Month number, with leading zero (01-12)
|
||||
#' \item \code{m} Month number, without leading zero (01-12)
|
||||
#' \item \code{m} Month number, without leading zero (1-12)
|
||||
#' \item \code{M} Abbreviated month name
|
||||
#' \item \code{MM} Full month name
|
||||
#' \item \code{dd} Day of month with leading zero
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -15,7 +15,12 @@
|
||||
#'
|
||||
#' @inheritParams textInput
|
||||
#' @param choices List of values to select from. If elements of the list are
|
||||
#' named then that name rather than the value is displayed to the user.
|
||||
#' named, then that name rather than the value is displayed to the user.
|
||||
#' This can also be a named list whose elements are (either named or
|
||||
#' unnamed) lists or vectors. If this is the case, the outermost names
|
||||
#' will be used as the "optgroup" label for the elements in the respective
|
||||
#' sublist. This allows you to group and label similar choices. See the
|
||||
#' example section for a small demo of this feature.
|
||||
#' @param selected The initially selected value (or multiple values if
|
||||
#' \code{multiple = TRUE}). If not specified then defaults to the first value
|
||||
#' for single-select lists and no values for multiple select lists.
|
||||
@@ -34,26 +39,46 @@
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' selectInput("variable", "Variable:",
|
||||
#' c("Cylinders" = "cyl",
|
||||
#' "Transmission" = "am",
|
||||
#' "Gears" = "gear")),
|
||||
#' tableOutput("data")
|
||||
#' # basic example
|
||||
#' shinyApp(
|
||||
#' ui = fluidPage(
|
||||
#' selectInput("variable", "Variable:",
|
||||
#' c("Cylinders" = "cyl",
|
||||
#' "Transmission" = "am",
|
||||
#' "Gears" = "gear")),
|
||||
#' tableOutput("data")
|
||||
#' ),
|
||||
#' server = function(input, output) {
|
||||
#' output$data <- renderTable({
|
||||
#' mtcars[, c("mpg", input$variable), drop = FALSE]
|
||||
#' }, rownames = TRUE)
|
||||
#' }
|
||||
#' )
|
||||
#'
|
||||
#' server <- function(input, output) {
|
||||
#' output$data <- renderTable({
|
||||
#' mtcars[, c("mpg", input$variable), drop = FALSE]
|
||||
#' }, rownames = TRUE)
|
||||
#' }
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
#' # demoing optgroup support in the `choices` arg
|
||||
#' shinyApp(
|
||||
#' ui = fluidPage(
|
||||
#' selectInput("state", "Choose a state:",
|
||||
#' list(`East Coast` = c("NY", "NJ", "CT"),
|
||||
#' `West Coast` = c("WA", "OR", "CA"),
|
||||
#' `Midwest` = c("MN", "WI", "IA"))
|
||||
#' ),
|
||||
#' textOutput("result")
|
||||
#' ),
|
||||
#' server = function(input, output) {
|
||||
#' output$result <- renderText({
|
||||
#' paste("You chose", input$state)
|
||||
#' })
|
||||
#' }
|
||||
#' )
|
||||
#' }
|
||||
#' @export
|
||||
selectInput <- function(inputId, label, choices, selected = NULL,
|
||||
multiple = FALSE, selectize = TRUE, width = NULL,
|
||||
size = NULL) {
|
||||
|
||||
selected <- restoreInput(id = inputId, default = selected)
|
||||
|
||||
# resolve names
|
||||
choices <- choicesWithNames(choices)
|
||||
|
||||
@@ -130,7 +155,7 @@ needOptgroup <- function(choices) {
|
||||
#' @rdname selectInput
|
||||
#' @param ... Arguments passed to \code{selectInput()}.
|
||||
#' @param options A list of options. See the documentation of \pkg{selectize.js}
|
||||
#' for possible options (character option values inside \code{\link{I}()} will
|
||||
#' for possible options (character option values inside \code{\link[base]{I}()} will
|
||||
#' be treated as literal JavaScript code; see \code{\link{renderDataTable}()}
|
||||
#' for details).
|
||||
#' @param width The width of the input, e.g. \code{'400px'}, or \code{'100\%'};
|
||||
@@ -169,7 +194,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'
|
||||
))
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
#' format string, to be passed to the Javascript strftime library. See
|
||||
#' \url{https://github.com/samsonjs/strftime} for more details. The allowed
|
||||
#' format specifications are very similar, but not identical, to those for R's
|
||||
#' \code{\link{strftime}} function. For Dates, the default is \code{"\%F"}
|
||||
#' \code{\link[base]{strftime}} function. For Dates, the default is \code{"\%F"}
|
||||
#' (like \code{"2015-07-01"}), and for POSIXt, the default is \code{"\%F \%T"}
|
||||
#' (like \code{"2015-07-01 15:32:10"}).
|
||||
#' @param timezone Only used if the values are POSIXt objects. A string
|
||||
@@ -51,6 +51,7 @@
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' options(device.ask.default = FALSE)
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' sliderInput("obs", "Number of observations:",
|
||||
@@ -85,6 +86,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 +164,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 +175,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 +250,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,
|
||||
|
||||
@@ -1,8 +1,27 @@
|
||||
#' Create a submit button
|
||||
#'
|
||||
#' Create a submit button for an input form. Forms that include a submit
|
||||
#' Create a submit button for an app. Apps that include a submit
|
||||
#' button do not automatically update their outputs when inputs change,
|
||||
#' rather they wait until the user explicitly clicks the submit button.
|
||||
#' The use of \code{submitButton} is generally discouraged in favor of
|
||||
#' the more versatile \code{\link{actionButton}} (see details below).
|
||||
#'
|
||||
#' Submit buttons are unusual Shiny inputs, and we recommend using
|
||||
#' \code{\link{actionButton}} instead of \code{submitButton} when you
|
||||
#' want to delay a reaction.
|
||||
#' See \href{http://shiny.rstudio.com/articles/action-buttons.html}{this
|
||||
#' article} for more information (including a demo of how to "translate"
|
||||
#' code using a \code{submitButton} to code using an \code{actionButton}).
|
||||
#'
|
||||
#' In essence, the presence of a submit button stops all inputs from
|
||||
#' sending their values automatically to the server. This means, for
|
||||
#' instance, that if there are \emph{two} submit buttons in the same app,
|
||||
#' clicking either one will cause all inputs in the app to send their
|
||||
#' values to the server. This is probably not what you'd want, which is
|
||||
#' why submit button are unwieldy for all but the simplest apps. There
|
||||
#' are other problems with submit buttons: for example, dynamically
|
||||
#' created submit buttons (for example, with \code{\link{renderUI}}
|
||||
#' or \code{\link{insertUI}}) will not work.
|
||||
#'
|
||||
#' @param text Button caption
|
||||
#' @param icon Optional \code{\link{icon}} to appear on the button
|
||||
@@ -13,8 +32,26 @@
|
||||
#' @family input elements
|
||||
#'
|
||||
#' @examples
|
||||
#' submitButton("Update View")
|
||||
#' submitButton("Update View", icon("refresh"))
|
||||
#' if (interactive()) {
|
||||
#'
|
||||
#' shinyApp(
|
||||
#' ui = basicPage(
|
||||
#' numericInput("num", label = "Make changes", value = 1),
|
||||
#' submitButton("Update View", icon("refresh")),
|
||||
#' helpText("When you click the button above, you should see",
|
||||
#' "the output below update to reflect the value you",
|
||||
#' "entered at the top:"),
|
||||
#' verbatimTextOutput("value")
|
||||
#' ),
|
||||
#' server = function(input, output) {
|
||||
#'
|
||||
#' # submit buttons do not have a value of their own,
|
||||
#' # they control when the app accesses values of other widgets.
|
||||
#' # input$num is the value of the number widget.
|
||||
#' output$value <- renderPrint({ input$num })
|
||||
#' }
|
||||
#' )
|
||||
#' }
|
||||
#' @export
|
||||
submitButton <- function(text = "Apply Changes", icon = NULL, width = NULL) {
|
||||
div(
|
||||
|
||||
@@ -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
69
R/input-textarea.R
Normal 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
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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))
|
||||
})
|
||||
|
||||
@@ -73,7 +73,6 @@
|
||||
#' # Complete app with UI and server components
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
insertUI <- function(selector,
|
||||
where = c("beforeBegin", "afterBegin", "beforeEnd", "afterEnd"),
|
||||
@@ -155,7 +154,6 @@ insertUI <- function(selector,
|
||||
#' # Complete app with UI and server components
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
removeUI <- function(selector,
|
||||
multiple = FALSE,
|
||||
|
||||
@@ -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();')
|
||||
|
||||
@@ -41,3 +41,229 @@ sessionHandler <- function(req) {
|
||||
shinysession$handleRequest(subreq)
|
||||
})
|
||||
}
|
||||
|
||||
apiHandler <- function(serverFuncSource) {
|
||||
function(req) {
|
||||
path <- req$PATH_INFO
|
||||
if (is.null(path))
|
||||
return(NULL)
|
||||
|
||||
matches <- regmatches(path, regexec('^/api/(.*)$', path))
|
||||
if (length(matches[[1]]) == 0)
|
||||
return(NULL)
|
||||
|
||||
apiName <- matches[[1]][2]
|
||||
|
||||
sharedSecret <- getOption('shiny.sharedSecret')
|
||||
if (!is.null(sharedSecret)
|
||||
&& !identical(sharedSecret, req$HTTP_SHINY_SHARED_SECRET)) {
|
||||
stop("Incorrect shared secret")
|
||||
}
|
||||
|
||||
if (!is.null(getOption("shiny.observer.error", NULL))) {
|
||||
warning(
|
||||
call. = FALSE,
|
||||
"options(shiny.observer.error) is no longer supported; please unset it!"
|
||||
)
|
||||
stopApp()
|
||||
}
|
||||
|
||||
# need to give a fake websocket to the session
|
||||
ws <- list(
|
||||
request = req,
|
||||
sendMessage = function(...) {
|
||||
#print(list(...))
|
||||
}
|
||||
)
|
||||
|
||||
# Accept JSON query string and/or JSON body as input values
|
||||
inputVals <- c(
|
||||
parseQueryStringJSON(req$QUERY_STRING),
|
||||
parseJSONBody(req)
|
||||
)
|
||||
|
||||
shinysession <- ShinySession$new(ws)
|
||||
on.exit({
|
||||
try({
|
||||
# Clean up the session. Very important, so that observers
|
||||
# and such don't hang around, and to let memory get gc'd.
|
||||
shinysession$wsClosed()
|
||||
appsByToken$remove(shinysession$token)
|
||||
})
|
||||
}, add = TRUE)
|
||||
appsByToken$set(shinysession$token, shinysession)
|
||||
shinysession$setShowcase(.globals$showcaseDefault)
|
||||
|
||||
serverFunc <- withReactiveDomain(NULL, serverFuncSource())
|
||||
|
||||
tryCatch({
|
||||
withReactiveDomain(shinysession, {
|
||||
shinysession$manageInputs(inputVals)
|
||||
do.call(serverFunc, argsForServerFunc(serverFunc, shinysession))
|
||||
result <- NULL
|
||||
shinysession$enableApi(apiName, function(value) {
|
||||
result <<- try(withLogErrors(value), silent = TRUE)
|
||||
})
|
||||
flushReact()
|
||||
resultToResponse(result)
|
||||
})
|
||||
}, error = function(e) {
|
||||
return(httpResponse(
|
||||
status=500,
|
||||
content=htmlEscape(conditionMessage(e))
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
apiWsHandler <- function(serverFuncSource) {
|
||||
function(ws) {
|
||||
path <- ws$request$PATH_INFO
|
||||
if (is.null(path))
|
||||
return(NULL)
|
||||
|
||||
matches <- regmatches(path, regexec('^/api/(.*)$', path))
|
||||
if (length(matches[[1]]) == 0)
|
||||
return(NULL)
|
||||
|
||||
apiName <- matches[[1]][2]
|
||||
|
||||
sharedSecret <- getOption('shiny.sharedSecret')
|
||||
if (!is.null(sharedSecret)
|
||||
&& !identical(sharedSecret, ws$request$HTTP_SHINY_SHARED_SECRET)) {
|
||||
ws$close()
|
||||
return(TRUE)
|
||||
}
|
||||
|
||||
if (!is.null(getOption("shiny.observer.error", NULL))) {
|
||||
warning(
|
||||
call. = FALSE,
|
||||
"options(shiny.observer.error) is no longer supported; please unset it!"
|
||||
)
|
||||
stopApp()
|
||||
}
|
||||
|
||||
inputVals <- parseQueryStringJSON(ws$request$QUERY_STRING)
|
||||
|
||||
# Give a fake websocket to suppress messages from session
|
||||
shinysession <- ShinySession$new(list(
|
||||
request = ws$request,
|
||||
sendMessage = function(...) {
|
||||
#print(list(...))
|
||||
}
|
||||
))
|
||||
appsByToken$set(shinysession$token, shinysession)
|
||||
shinysession$setShowcase(.globals$showcaseDefault)
|
||||
|
||||
serverFunc <- withReactiveDomain(NULL, serverFuncSource())
|
||||
|
||||
tryCatch({
|
||||
withReactiveDomain(shinysession, {
|
||||
shinysession$manageInputs(inputVals)
|
||||
do.call(serverFunc, argsForServerFunc(serverFunc, shinysession))
|
||||
shinysession$enableApi(apiName, function(value) {
|
||||
resp <- resultToResponse(value)
|
||||
if (resp$status != 200L) {
|
||||
warning("Error: ", responseToContent(resp))
|
||||
ws$close()
|
||||
} else {
|
||||
content <- responseToContent(resp)
|
||||
if (grepl("^image/", resp$content_type)) {
|
||||
content <- paste0("data:", resp$content_type, ";base64,",
|
||||
httpuv::rawToBase64(content))
|
||||
}
|
||||
try(ws$send(content), silent=TRUE)
|
||||
}
|
||||
})
|
||||
flushReact()
|
||||
})
|
||||
}, error = function(e) {
|
||||
ws$close()
|
||||
})
|
||||
|
||||
ws$onClose(function() {
|
||||
# Clean up the session. Very important, so that observers
|
||||
# and such don't hang around, and to let memory get gc'd.
|
||||
shinysession$wsClosed()
|
||||
appsByToken$remove(shinysession$token)
|
||||
})
|
||||
|
||||
# TODO: What to do on ws$onMessage?
|
||||
}
|
||||
}
|
||||
|
||||
parseJSONBody <- function(req) {
|
||||
if (identical(req[["REQUEST_METHOD"]], "POST")) {
|
||||
if (isTRUE(grepl(perl=TRUE, "^(text|application)/json(;\\s*charset\\s*=\\s*utf-8)?$", req[["HTTP_CONTENT_TYPE"]]))) {
|
||||
tmp <- file("", "w+b")
|
||||
on.exit(close(tmp))
|
||||
|
||||
input_file <- req[["rook.input"]]
|
||||
while (TRUE) {
|
||||
chunk <- input_file$read(8192L)
|
||||
if (length(chunk) == 0)
|
||||
break
|
||||
writeBin(chunk, tmp)
|
||||
}
|
||||
|
||||
return(jsonlite::fromJSON(tmp))
|
||||
}
|
||||
|
||||
if (is.null(req[["HTTP_CONTENT_TYPE"]])) {
|
||||
if (!is.null(req[["rook.input"]]) && length(req[["rook.input"]]$read(1L)) > 0) {
|
||||
stop("Invalid POST request (body provided without content type)")
|
||||
}
|
||||
return()
|
||||
}
|
||||
|
||||
stop("Invalid POST request (content type not supported)")
|
||||
}
|
||||
}
|
||||
|
||||
resultToResponse <- function(result) {
|
||||
if (inherits(result, "httpResponse")) {
|
||||
return(result)
|
||||
} else if (inherits(result, "try-error")) {
|
||||
return(httpResponse(
|
||||
status=500,
|
||||
content_type="text/plain",
|
||||
content=conditionMessage(attr(result, "condition"))
|
||||
))
|
||||
} else if (!is.null(attr(result, "content.type"))) {
|
||||
return(httpResponse(
|
||||
status=200L,
|
||||
content_type=attr(result, "content.type"),
|
||||
content=result
|
||||
))
|
||||
} else {
|
||||
return(httpResponse(
|
||||
status=200L,
|
||||
content_type="application/json",
|
||||
content=toJSON(result, pretty=TRUE)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
responseToContent <- function(result) {
|
||||
ct <- result$content_type
|
||||
textMode <- grepl("^text/", ct) || ct == "application/json" ||
|
||||
grepl("^application/xml($|\\+)", ct)
|
||||
|
||||
# TODO: Make sure text is UTF-8
|
||||
|
||||
if ("file" %in% names(result$content)) {
|
||||
filename <- result$content$file
|
||||
if ("owned" %in% names(result$content) && result$content$owned) {
|
||||
on.exit(unlink(filename), add = TRUE)
|
||||
}
|
||||
if (textMode)
|
||||
return(paste(readLines(filename), collapse = "\n"))
|
||||
else
|
||||
return(readBin(filename, raw(), file.info(filename)$size))
|
||||
} else {
|
||||
if (textMode)
|
||||
return(paste(result$content, collapse = "\n"))
|
||||
else
|
||||
return(result$content)
|
||||
}
|
||||
}
|
||||
|
||||
20
R/modal.R
20
R/modal.R
@@ -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)
|
||||
|
||||
19
R/modules.R
19
R/modules.R
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
57
R/progress.R
57
R/progress.R
@@ -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.
|
||||
@@ -213,6 +245,7 @@ Progress <- R6Class(
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' options(device.ask.default = FALSE)
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' plotOutput("plot")
|
||||
@@ -237,10 +270,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 +283,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({
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
640
R/reactives.R
640
R/reactives.R
@@ -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(...)
|
||||
@@ -299,7 +344,7 @@ as.list.reactivevalues <- function(x, all.names=FALSE, ...) {
|
||||
|
||||
#' Convert a reactivevalues object to a list
|
||||
#'
|
||||
#' This function does something similar to what you might \code{\link{as.list}}
|
||||
#' This function does something similar to what you might \code{\link[base]{as.list}}
|
||||
#' to do. The difference is that the calling context will take dependencies on
|
||||
#' every object in the reactivevalues object. To avoid taking dependencies on
|
||||
#' all the objects, you can wrap the call with \code{\link{isolate}()}.
|
||||
@@ -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(
|
||||
@@ -405,6 +526,7 @@ Observable <- R6Class(
|
||||
.mostRecentCtxId <<- ctx$id
|
||||
ctx$onInvalidate(function() {
|
||||
.invalidated <<- TRUE
|
||||
.value <<- NULL # Value can be GC'd, it won't be read once invalidated
|
||||
.dependents$invalidate()
|
||||
})
|
||||
.execCount <<- .execCount + 1L
|
||||
@@ -499,7 +621,6 @@ Observable <- R6Class(
|
||||
#' isolate(reactiveB())
|
||||
#' isolate(reactiveC())
|
||||
#' isolate(reactiveD())
|
||||
#'
|
||||
#' @export
|
||||
reactive <- function(x, env = parent.frame(), quoted = FALSE, label = NULL,
|
||||
domain = getDefaultReactiveDomain(),
|
||||
@@ -562,7 +683,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 +694,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,6 +712,11 @@ 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',
|
||||
@@ -612,15 +738,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
|
||||
@@ -628,7 +753,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()
|
||||
@@ -706,11 +833,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() {
|
||||
@@ -743,6 +887,11 @@ registerDebugHook("observerFunc", environment(), label)
|
||||
suspend()
|
||||
.destroyed <<- TRUE
|
||||
|
||||
if (!is.null(.autoDestroyHandle)) {
|
||||
.autoDestroyHandle()
|
||||
}
|
||||
.autoDestroyHandle <<- NULL
|
||||
|
||||
if (!is.null(.ctx)) {
|
||||
.ctx$invalidate()
|
||||
}
|
||||
@@ -774,8 +923,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.
|
||||
@@ -786,12 +935,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.
|
||||
@@ -850,7 +1000,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,
|
||||
@@ -890,9 +1039,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
|
||||
@@ -961,7 +1110,7 @@ setAutoflush <- local({
|
||||
#' @return A no-parameter function that can be called from a reactive context,
|
||||
#' in order to cause that context to be invalidated the next time the timer
|
||||
#' interval elapses. Calling the returned function also happens to yield the
|
||||
#' current time (as in \code{\link{Sys.time}}).
|
||||
#' current time (as in \code{\link[base]{Sys.time}}).
|
||||
#' @seealso \code{\link{invalidateLater}}
|
||||
#'
|
||||
#' @examples
|
||||
@@ -1000,7 +1149,6 @@ setAutoflush <- local({
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
reactiveTimer <- function(intervalMs=1000, session = getDefaultReactiveDomain()) {
|
||||
dependents <- Map$new()
|
||||
@@ -1159,7 +1307,6 @@ coerceToFunc <- function(x) {
|
||||
#' data()
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
reactivePoll <- function(intervalMillis, session, checkFunc, valueFunc) {
|
||||
intervalMillis <- coerceToFunc(intervalMillis)
|
||||
@@ -1237,7 +1384,6 @@ reactivePoll <- function(intervalMillis, session, checkFunc, valueFunc) {
|
||||
#' })
|
||||
#' }
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
reactiveFileReader <- function(intervalMillis, session, filePath, readFunc, ...) {
|
||||
filePath <- coerceToFunc(filePath)
|
||||
@@ -1272,7 +1418,7 @@ reactiveFileReader <- function(intervalMillis, session, filePath, readFunc, ...)
|
||||
#' The expression given to \code{isolate()} is evaluated in the calling
|
||||
#' environment. This means that if you assign a variable inside the
|
||||
#' \code{isolate()}, its value will be visible outside of the \code{isolate()}.
|
||||
#' If you want to avoid this, you can use \code{\link{local}()} inside the
|
||||
#' If you want to avoid this, you can use \code{\link[base]{local}()} inside the
|
||||
#' \code{isolate()}.
|
||||
#'
|
||||
#' This function can also be useful for calling reactive expression at the
|
||||
@@ -1325,7 +1471,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')
|
||||
@@ -1347,7 +1492,6 @@ isolate <- function(expr) {
|
||||
#' @return The value of \code{expr}.
|
||||
#'
|
||||
#' @seealso \code{\link{isolate}}
|
||||
#'
|
||||
#' @export
|
||||
maskReactiveContext <- function(expr) {
|
||||
.getReactiveEnvironment()$runWith(NULL, function() {
|
||||
@@ -1387,6 +1531,8 @@ maskReactiveContext <- function(expr) {
|
||||
#' invalidations that come from its reactive dependencies; it only invalidates
|
||||
#' in response to the given event.
|
||||
#'
|
||||
#' @section \code{ignoreNULL} and \code{ignoreInit}:
|
||||
#'
|
||||
#' Both \code{observeEvent} and \code{eventReactive} take an \code{ignoreNULL}
|
||||
#' parameter that affects behavior when the \code{eventExpr} evaluates to
|
||||
#' \code{NULL} (or in the special case of an \code{\link{actionButton}},
|
||||
@@ -1399,6 +1545,44 @@ maskReactiveContext <- function(expr) {
|
||||
#' the action/calculation and just let the user re-initiate it (like a
|
||||
#' "Recalculate" button).
|
||||
#'
|
||||
#' Unlike what happens for \code{ignoreNULL}, only \code{observeEvent} takes in an
|
||||
#' \code{ignoreInit} argument. By default, \code{observeEvent} will run right when
|
||||
#' it is created (except if, at that moment, \code{eventExpr} evaluates to \code{NULL}
|
||||
#' and \code{ignoreNULL} is \code{TRUE}). But when responding to a click of an action
|
||||
#' button, it may often be useful to set \code{ignoreInit} to \code{TRUE}. For
|
||||
#' example, if you're setting up an \code{observeEvent} for a dynamically created
|
||||
#' button, then \code{ignoreInit = TRUE} will guarantee that the action (in
|
||||
#' \code{handlerExpr}) will only be triggered when the button is actually clicked,
|
||||
#' instead of also being triggered when it is created/initialized.
|
||||
#'
|
||||
#' Even though \code{ignoreNULL} and \code{ignoreInit} can be used for similar
|
||||
#' purposes they are independent from one another. Here's the result of combining
|
||||
#' these:
|
||||
#'
|
||||
#' \describe{
|
||||
#' \item{\code{ignoreNULL = TRUE} and \code{ignoreInit = FALSE}}{
|
||||
#' This is the default. This combination means that \code{handlerExpr} will
|
||||
#' run every time that \code{eventExpr} is not \code{NULL}. If, at the time
|
||||
#' of the \code{observeEvent}'s creation, \code{handleExpr} happens to
|
||||
#' \emph{not} be \code{NULL}, then the code runs.
|
||||
#' }
|
||||
#' \item{\code{ignoreNULL = FALSE} and \code{ignoreInit = FALSE}}{
|
||||
#' This combination means that \code{handlerExpr} will run every time no
|
||||
#' matter what.
|
||||
#' }
|
||||
#' \item{\code{ignoreNULL = FALSE} and \code{ignoreInit = TRUE}}{
|
||||
#' This combination means that \code{handlerExpr} will \emph{not} run when
|
||||
#' the \code{observeEvent} is created (because \code{ignoreInit = TRUE}),
|
||||
#' but it will run every other time.
|
||||
#' }
|
||||
#' \item{\code{ignoreNULL = TRUE} and \code{ignoreInit = TRUE}}{
|
||||
#' This combination means that \code{handlerExpr} will \emph{not} run when
|
||||
#' the \code{observeEvent} is created (because \code{ignoreInit = TRUE}).
|
||||
#' After that, \code{handlerExpr} will run every time that \code{eventExpr}
|
||||
#' is not \code{NULL}.
|
||||
#' }
|
||||
#' }
|
||||
#'
|
||||
#' @param eventExpr A (quoted or unquoted) expression that represents the event;
|
||||
#' this can be a simple reactive value like \code{input$click}, a call to a
|
||||
#' reactive expression like \code{dataset()}, or even a complex expression
|
||||
@@ -1440,6 +1624,15 @@ maskReactiveContext <- function(expr) {
|
||||
#' @param ignoreNULL Whether the action should be triggered (or value
|
||||
#' calculated, in the case of \code{eventReactive}) when the input is
|
||||
#' \code{NULL}. See Details.
|
||||
#' @param ignoreInit If \code{TRUE}, then, when this \code{observeEvent} is
|
||||
#' first created/initialized, ignore the \code{handlerExpr} (the second
|
||||
#' argument), whether it is otherwise supposed to run or not. The default is
|
||||
#' \code{FALSE}. See Details.
|
||||
#' @param once Whether this \code{observeEvent} should be immediately destroyed
|
||||
#' after the first time that the code in \code{handlerExpr} is run. This
|
||||
#' pattern is useful when you want to subscribe to a event that should only
|
||||
#' happen once.
|
||||
#'
|
||||
#' @return \code{observeEvent} returns an observer reference class object (see
|
||||
#' \code{\link{observe}}). \code{eventReactive} returns a reactive expression
|
||||
#' object (see \code{\link{reactive}}).
|
||||
@@ -1449,38 +1642,71 @@ maskReactiveContext <- function(expr) {
|
||||
#' @examples
|
||||
#' ## Only run this example in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' ui <- fluidPage(
|
||||
#' column(4,
|
||||
#' numericInput("x", "Value", 5),
|
||||
#' br(),
|
||||
#' actionButton("button", "Show")
|
||||
#' ),
|
||||
#' column(8, tableOutput("table"))
|
||||
#' )
|
||||
#' server <- function(input, output) {
|
||||
#' # Take an action every time button is pressed;
|
||||
#' # here, we just print a message to the console
|
||||
#' observeEvent(input$button, {
|
||||
#' cat("Showing", input$x, "rows\n")
|
||||
#' })
|
||||
#' # Take a reactive dependency on input$button, but
|
||||
#' # not on any of the stuff inside the function
|
||||
#' df <- eventReactive(input$button, {
|
||||
#' head(cars, input$x)
|
||||
#' })
|
||||
#' output$table <- renderTable({
|
||||
#' df()
|
||||
#' })
|
||||
#' }
|
||||
#' shinyApp(ui=ui, server=server)
|
||||
#' }
|
||||
#'
|
||||
#' ## App 1: Sample usage
|
||||
#' shinyApp(
|
||||
#' ui = fluidPage(
|
||||
#' column(4,
|
||||
#' numericInput("x", "Value", 5),
|
||||
#' br(),
|
||||
#' actionButton("button", "Show")
|
||||
#' ),
|
||||
#' column(8, tableOutput("table"))
|
||||
#' ),
|
||||
#' server = function(input, output) {
|
||||
#' # Take an action every time button is pressed;
|
||||
#' # here, we just print a message to the console
|
||||
#' observeEvent(input$button, {
|
||||
#' cat("Showing", input$x, "rows\n")
|
||||
#' })
|
||||
#' # Take a reactive dependency on input$button, but
|
||||
#' # not on any of the stuff inside the function
|
||||
#' df <- eventReactive(input$button, {
|
||||
#' head(cars, input$x)
|
||||
#' })
|
||||
#' output$table <- renderTable({
|
||||
#' df()
|
||||
#' })
|
||||
#' }
|
||||
#' )
|
||||
#'
|
||||
#' ## App 2: Using `once`
|
||||
#' shinyApp(
|
||||
#' ui = basicPage( actionButton("go", "Go")),
|
||||
#' server = function(input, output, session) {
|
||||
#' observeEvent(input$go, {
|
||||
#' print(paste("This will only be printed once; all",
|
||||
#' "subsequent button clicks won't do anything"))
|
||||
#' }, once = TRUE)
|
||||
#' }
|
||||
#' )
|
||||
#'
|
||||
#' ## App 3: Using `ignoreInit` and `once`
|
||||
#' shinyApp(
|
||||
#' ui = basicPage(actionButton("go", "Go")),
|
||||
#' server = function(input, output, session) {
|
||||
#' observeEvent(input$go, {
|
||||
#' insertUI("#go", "afterEnd",
|
||||
#' actionButton("dynamic", "click to remove"))
|
||||
#'
|
||||
#' # set up an observer that depends on the dynamic
|
||||
#' # input, so that it doesn't run when the input is
|
||||
#' # created, and only runs once after that (since
|
||||
#' # the side effect is remove the input from the DOM)
|
||||
#' observeEvent(input$dynamic, {
|
||||
#' removeUI("#dynamic")
|
||||
#' }, ignoreInit = TRUE, once = TRUE)
|
||||
#' })
|
||||
#' }
|
||||
#' )
|
||||
#' }
|
||||
#' @export
|
||||
observeEvent <- function(eventExpr, handlerExpr,
|
||||
event.env = parent.frame(), event.quoted = FALSE,
|
||||
handler.env = parent.frame(), handler.quoted = FALSE,
|
||||
label=NULL, suspended=FALSE, priority=0, domain=getDefaultReactiveDomain(),
|
||||
autoDestroy = TRUE, ignoreNULL = TRUE) {
|
||||
label = NULL, suspended = FALSE, priority = 0,
|
||||
domain = getDefaultReactiveDomain(), autoDestroy = TRUE,
|
||||
ignoreNULL = TRUE, ignoreInit = FALSE, once = FALSE) {
|
||||
|
||||
eventFunc <- exprToFunction(eventExpr, event.env, event.quoted)
|
||||
if (is.null(label))
|
||||
@@ -1490,16 +1716,29 @@ observeEvent <- function(eventExpr, handlerExpr,
|
||||
handlerFunc <- exprToFunction(handlerExpr, handler.env, handler.quoted)
|
||||
handlerFunc <- wrapFunctionLabel(handlerFunc, "observeEventHandler", ..stacktraceon = TRUE)
|
||||
|
||||
invisible(observe({
|
||||
initialized <- FALSE
|
||||
|
||||
o <- observe({
|
||||
e <- eventFunc()
|
||||
|
||||
if (ignoreInit && !initialized) {
|
||||
initialized <<- TRUE
|
||||
return()
|
||||
}
|
||||
|
||||
if (ignoreNULL && isNullEvent(e)) {
|
||||
return()
|
||||
}
|
||||
|
||||
if (once) {
|
||||
on.exit(o$destroy())
|
||||
}
|
||||
|
||||
isolate(handlerFunc())
|
||||
}, label = label, suspended = suspended, priority = priority, domain = domain,
|
||||
autoDestroy = TRUE, ..stacktraceon = FALSE))
|
||||
autoDestroy = TRUE, ..stacktraceon = FALSE)
|
||||
|
||||
invisible(o)
|
||||
}
|
||||
|
||||
#' @rdname observeEvent
|
||||
@@ -1507,8 +1746,8 @@ observeEvent <- function(eventExpr, handlerExpr,
|
||||
eventReactive <- function(eventExpr, valueExpr,
|
||||
event.env = parent.frame(), event.quoted = FALSE,
|
||||
value.env = parent.frame(), value.quoted = FALSE,
|
||||
label=NULL, domain=getDefaultReactiveDomain(),
|
||||
ignoreNULL = TRUE) {
|
||||
label = NULL, domain = getDefaultReactiveDomain(),
|
||||
ignoreNULL = TRUE, ignoreInit = FALSE) {
|
||||
|
||||
eventFunc <- exprToFunction(eventExpr, event.env, event.quoted)
|
||||
if (is.null(label))
|
||||
@@ -1518,13 +1757,17 @@ eventReactive <- function(eventExpr, valueExpr,
|
||||
handlerFunc <- exprToFunction(valueExpr, value.env, value.quoted)
|
||||
handlerFunc <- wrapFunctionLabel(handlerFunc, "eventReactiveHandler", ..stacktraceon = TRUE)
|
||||
|
||||
initialized <- FALSE
|
||||
|
||||
invisible(reactive({
|
||||
e <- eventFunc()
|
||||
|
||||
validate(need(
|
||||
!ignoreNULL || !isNullEvent(e),
|
||||
message = FALSE
|
||||
))
|
||||
if (ignoreInit && !initialized) {
|
||||
initialized <<- TRUE
|
||||
req(FALSE)
|
||||
}
|
||||
|
||||
req(!ignoreNULL || !isNullEvent(e))
|
||||
|
||||
isolate(handlerFunc())
|
||||
}, label = label, domain = domain, ..stacktraceon = FALSE))
|
||||
@@ -1533,3 +1776,246 @@ eventReactive <- function(eventExpr, valueExpr,
|
||||
isNullEvent <- function(value) {
|
||||
is.null(value) || (inherits(value, 'shinyActionButtonValue') && value == 0)
|
||||
}
|
||||
|
||||
#' Slow down a reactive expression with debounce/throttle
|
||||
#'
|
||||
#' Transforms a reactive expression by preventing its invalidation signals from
|
||||
#' being sent unnecessarily often. This lets you ignore a very "chatty" reactive
|
||||
#' expression until it becomes idle, which is useful when the intermediate
|
||||
#' values don't matter as much as the final value, and the downstream
|
||||
#' calculations that depend on the reactive expression take a long time.
|
||||
#' \code{debounce} and \code{throttle} use different algorithms for slowing down
|
||||
#' invalidation signals; see Details.
|
||||
#'
|
||||
#' @section Limitations:
|
||||
#'
|
||||
#' Because R is single threaded, we can't come close to guaranteeing that the
|
||||
#' timing of debounce/throttle (or any other timing-related functions in
|
||||
#' Shiny) will be consistent or accurate; at the time we want to emit an
|
||||
#' invalidation signal, R may be performing a different task and we have no
|
||||
#' way to interrupt it (nor would we necessarily want to if we could).
|
||||
#' Therefore, it's best to think of the time windows you pass to these
|
||||
#' functions as minimums.
|
||||
#'
|
||||
#' You may also see undesirable behavior if the amount of time spent doing
|
||||
#' downstream processing for each change approaches or exceeds the time
|
||||
#' window: in this case, debounce/throttle may not have any effect, as the
|
||||
#' time each subsequent event is considered is already after the time window
|
||||
#' has expired.
|
||||
#'
|
||||
#' @details
|
||||
#'
|
||||
#' This is not a true debounce/throttle in that it will not prevent \code{r}
|
||||
#' from being called many times (in fact it may be called more times than
|
||||
#' usual), but rather, the reactive invalidation signal that is produced by
|
||||
#' \code{r} is debounced/throttled instead. Therefore, these functions should be
|
||||
#' used when \code{r} is cheap but the things it will trigger (downstream
|
||||
#' outputs and reactives) are expensive.
|
||||
#'
|
||||
#' Debouncing means that every invalidation from \code{r} will be held for the
|
||||
#' specified time window. If \code{r} invalidates again within that time window,
|
||||
#' then the timer starts over again. This means that as long as invalidations
|
||||
#' continually arrive from \code{r} within the time window, the debounced
|
||||
#' reactive will not invalidate at all. Only after the invalidations stop (or
|
||||
#' slow down sufficiently) will the downstream invalidation be sent.
|
||||
#'
|
||||
#' \code{ooo-oo-oo---- => -----------o-}
|
||||
#'
|
||||
#' (In this graphical depiction, each character represents a unit of time, and
|
||||
#' the time window is 3 characters.)
|
||||
#'
|
||||
#' Throttling, on the other hand, delays invalidation if the \emph{throttled}
|
||||
#' reactive recently (within the time window) invalidated. New \code{r}
|
||||
#' invalidations do not reset the time window. This means that if invalidations
|
||||
#' continually come from \code{r} within the time window, the throttled reactive
|
||||
#' will invalidate regularly, at a rate equal to or slower than than the time
|
||||
#' window.
|
||||
#'
|
||||
#' \code{ooo-oo-oo---- => o--o--o--o---}
|
||||
#'
|
||||
#' @param r A reactive expression (that invalidates too often).
|
||||
#' @param millis The debounce/throttle time window. You may optionally pass a
|
||||
#' no-arg function or reactive expression instead, e.g. to let the end-user
|
||||
#' control the time window.
|
||||
#' @param priority Debounce/throttle is implemented under the hood using
|
||||
#' \link[=observe]{observers}. Use this parameter to set the priority of
|
||||
#' these observers. Generally, this should be higher than the priorities of
|
||||
#' downstream observers and outputs (which default to zero).
|
||||
#' @param domain See \link{domains}.
|
||||
#'
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' options(device.ask.default = FALSE)
|
||||
#'
|
||||
#' library(shiny)
|
||||
#' library(magrittr)
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' plotOutput("plot", click = clickOpts("hover")),
|
||||
#' helpText("Quickly click on the plot above, while watching the result table below:"),
|
||||
#' tableOutput("result")
|
||||
#' )
|
||||
#'
|
||||
#' server <- function(input, output, session) {
|
||||
#' hover <- reactive({
|
||||
#' if (is.null(input$hover))
|
||||
#' list(x = NA, y = NA)
|
||||
#' else
|
||||
#' input$hover
|
||||
#' })
|
||||
#' hover_d <- hover %>% debounce(1000)
|
||||
#' hover_t <- hover %>% throttle(1000)
|
||||
#'
|
||||
#' output$plot <- renderPlot({
|
||||
#' plot(cars)
|
||||
#' })
|
||||
#'
|
||||
#' output$result <- renderTable({
|
||||
#' data.frame(
|
||||
#' mode = c("raw", "throttle", "debounce"),
|
||||
#' x = c(hover()$x, hover_t()$x, hover_d()$x),
|
||||
#' y = c(hover()$y, hover_t()$y, hover_d()$y)
|
||||
#' )
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
debounce <- function(r, millis, priority = 100, domain = getDefaultReactiveDomain()) {
|
||||
|
||||
# TODO: make a nice label for the observer(s)
|
||||
|
||||
force(r)
|
||||
force(millis)
|
||||
|
||||
if (!is.function(millis)) {
|
||||
origMillis <- millis
|
||||
millis <- function() origMillis
|
||||
}
|
||||
|
||||
v <- reactiveValues(
|
||||
trigger = NULL,
|
||||
when = NULL # the deadline for the timer to fire; NULL if not scheduled
|
||||
)
|
||||
|
||||
# Responsible for tracking when f() changes.
|
||||
firstRun <- TRUE
|
||||
observe({
|
||||
r()
|
||||
|
||||
if (firstRun) {
|
||||
# During the first run we don't want to set v$when, as this will kick off
|
||||
# the timer. We only want to do that when we see r() change.
|
||||
firstRun <<- FALSE
|
||||
return()
|
||||
}
|
||||
|
||||
# The value (or possibly millis) changed. Start or reset the timer.
|
||||
v$when <- Sys.time() + millis()/1000
|
||||
}, label = "debounce tracker", domain = domain, priority = priority)
|
||||
|
||||
# This observer is the timer. It rests until v$when elapses, then touches
|
||||
# v$trigger.
|
||||
observe({
|
||||
if (is.null(v$when))
|
||||
return()
|
||||
|
||||
now <- Sys.time()
|
||||
if (now >= v$when) {
|
||||
# Mod by 999999999 to get predictable overflow behavior
|
||||
v$trigger <- isolate(v$trigger %OR% 0) %% 999999999 + 1
|
||||
v$when <- NULL
|
||||
} else {
|
||||
invalidateLater((v$when - now) * 1000)
|
||||
}
|
||||
}, label = "debounce timer", domain = domain, priority = priority)
|
||||
|
||||
# This is the actual reactive that is returned to the user. It returns the
|
||||
# value of r(), but only invalidates/updates when v$trigger is touched.
|
||||
er <- eventReactive(v$trigger, {
|
||||
r()
|
||||
}, label = "debounce result", ignoreNULL = FALSE, domain = domain)
|
||||
|
||||
# Force the value of er to be immediately cached upon creation. It's very hard
|
||||
# to explain why this observer is needed, but if you want to understand, try
|
||||
# commenting it out and studying the unit test failure that results.
|
||||
primer <- observe({
|
||||
primer$destroy()
|
||||
er()
|
||||
}, label = "debounce primer", domain = domain, priority = priority)
|
||||
|
||||
er
|
||||
}
|
||||
|
||||
#' @rdname debounce
|
||||
#' @export
|
||||
throttle <- function(r, millis, priority = 100, domain = getDefaultReactiveDomain()) {
|
||||
|
||||
# TODO: make a nice label for the observer(s)
|
||||
|
||||
force(r)
|
||||
force(millis)
|
||||
|
||||
if (!is.function(millis)) {
|
||||
origMillis <- millis
|
||||
millis <- function() origMillis
|
||||
}
|
||||
|
||||
v <- reactiveValues(
|
||||
trigger = 0,
|
||||
lastTriggeredAt = NULL, # Last time we fired; NULL if never
|
||||
pending = FALSE # If TRUE, trigger again when timer elapses
|
||||
)
|
||||
|
||||
blackoutMillisLeft <- function() {
|
||||
if (is.null(v$lastTriggeredAt)) {
|
||||
0
|
||||
} else {
|
||||
max(0, (v$lastTriggeredAt + millis()/1000) - Sys.time()) * 1000
|
||||
}
|
||||
}
|
||||
|
||||
trigger <- function() {
|
||||
v$lastTriggeredAt <- Sys.time()
|
||||
# Mod by 999999999 to get predictable overflow behavior
|
||||
v$trigger <- isolate(v$trigger) %% 999999999 + 1
|
||||
v$pending <- FALSE
|
||||
}
|
||||
|
||||
# Responsible for tracking when f() changes.
|
||||
observeEvent(r(), {
|
||||
if (v$pending) {
|
||||
# In a blackout period and someone already scheduled; do nothing
|
||||
} else if (blackoutMillisLeft() > 0) {
|
||||
# In a blackout period but this is the first change in that period; set
|
||||
# v$pending so that a trigger will be scheduled at the end of the period
|
||||
v$pending <- TRUE
|
||||
} else {
|
||||
# Not in a blackout period. Trigger, which will start a new blackout
|
||||
# period.
|
||||
trigger()
|
||||
}
|
||||
}, label = "throttle tracker", ignoreNULL = FALSE, priority = priority, domain = domain)
|
||||
|
||||
observe({
|
||||
if (!v$pending) {
|
||||
return()
|
||||
}
|
||||
|
||||
timeout <- blackoutMillisLeft()
|
||||
if (timeout > 0) {
|
||||
invalidateLater(timeout)
|
||||
} else {
|
||||
trigger()
|
||||
}
|
||||
}, priority = priority, domain = domain)
|
||||
|
||||
# This is the actual reactive that is returned to the user. It returns the
|
||||
# value of r(), but only invalidates/updates when v$trigger is touched.
|
||||
eventReactive(v$trigger, {
|
||||
r()
|
||||
}, label = "throttle result", ignoreNULL = FALSE, domain = domain)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#' inline plot, you must provide numeric values (in pixels) to both
|
||||
#' \code{width} and \code{height}.
|
||||
#' @param res Resolution of resulting plot, in pixels per inch. This value is
|
||||
#' passed to \code{\link{png}}. Note that this affects the resolution of PNG
|
||||
#' passed to \code{\link[grDevices]{png}}. Note that this affects the resolution of PNG
|
||||
#' rendering in R; it won't change the actual ppi of the browser.
|
||||
#' @param ... Arguments to be passed through to \code{\link[grDevices]{png}}.
|
||||
#' These can be used to set the width, height, background color, etc.
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
72
R/serializers.R
Normal 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
|
||||
}
|
||||
@@ -71,6 +71,63 @@ removeInputHandler <- function(type){
|
||||
inputHandlers$remove(type)
|
||||
}
|
||||
|
||||
|
||||
# Apply input handler to a single input value
|
||||
applyInputHandler <- function(name, val, shinysession) {
|
||||
splitName <- strsplit(name, ':')[[1]]
|
||||
if (length(splitName) > 1) {
|
||||
if (!inputHandlers$containsKey(splitName[[2]])) {
|
||||
# No input handler registered for this type
|
||||
stop("No handler registered for type ", name)
|
||||
}
|
||||
|
||||
inputName <- splitName[[1]]
|
||||
|
||||
# Get the function for processing this type of input
|
||||
inputHandler <- inputHandlers$get(splitName[[2]])
|
||||
|
||||
return(inputHandler(val, shinysession, inputName))
|
||||
|
||||
} else if (is.list(val) && is.null(names(val))) {
|
||||
return(unlist(val, recursive = TRUE))
|
||||
} else {
|
||||
return(val)
|
||||
}
|
||||
}
|
||||
|
||||
#' Apply input handlers to raw input values
|
||||
#'
|
||||
#' The purpose of this function is to make it possible for external packages to
|
||||
#' test Shiny inputs. It takes a named list of raw input values, applies input
|
||||
#' handlers to those values, and then returns a named list of the processed
|
||||
#' values.
|
||||
#'
|
||||
#' The raw input values should be in a named list. Some values may have names
|
||||
#' like \code{"x:shiny.date"}. This function would apply the \code{"shiny.date"}
|
||||
#' input handler to the value, and then rename the result to \code{"x"}, in the
|
||||
#' output.
|
||||
#'
|
||||
#' @param inputs A named list of input values.
|
||||
#' @param shinysession A Shiny session object.
|
||||
#'
|
||||
#' @seealso registerInputHandler
|
||||
#' @keywords internal
|
||||
applyInputHandlers <- function(inputs, shinysession = getDefaultReactiveDomain()) {
|
||||
inputs <- mapply(applyInputHandler, names(inputs), inputs,
|
||||
MoreArgs = list(shinysession = shinysession),
|
||||
SIMPLIFY = FALSE)
|
||||
|
||||
# Convert names like "button1:shiny.action" to "button1"
|
||||
names(inputs) <- vapply(
|
||||
names(inputs),
|
||||
function(name) { strsplit(name, ":")[[1]][1] },
|
||||
FUN.VALUE = character(1)
|
||||
)
|
||||
|
||||
inputs
|
||||
}
|
||||
|
||||
|
||||
# Takes a list-of-lists and returns a matrix. The lists
|
||||
# must all be the same length. NULL is replaced by NA.
|
||||
registerInputHandler("shiny.matrix", function(data, ...) {
|
||||
@@ -89,10 +146,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 +180,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
|
||||
})
|
||||
|
||||
306
R/server.R
306
R/server.R
@@ -34,7 +34,7 @@ registerClient <- function(client) {
|
||||
#' JavaScript/CSS files available to their components.
|
||||
#'
|
||||
#' @param prefix The URL prefix (without slashes). Valid characters are a-z,
|
||||
#' A-Z, 0-9, hyphen, period, and underscore; and must begin with a-z or A-Z.
|
||||
#' A-Z, 0-9, hyphen, period, and underscore.
|
||||
#' For example, a value of 'foo' means that any request paths that begin with
|
||||
#' '/foo' will be mapped to the given directory.
|
||||
#' @param directoryPath The directory that contains the static resources to be
|
||||
@@ -49,11 +49,10 @@ registerClient <- function(client) {
|
||||
#'
|
||||
#' @examples
|
||||
#' addResourcePath('datasets', system.file('data', package='datasets'))
|
||||
#'
|
||||
#' @export
|
||||
addResourcePath <- function(prefix, directoryPath) {
|
||||
prefix <- prefix[1]
|
||||
if (!grepl('^[a-z][a-z0-9\\-_.]*$', prefix, ignore.case=TRUE, perl=TRUE)) {
|
||||
if (!grepl('^[a-z0-9\\-_][a-z0-9\\-_.]*$', prefix, ignore.case=TRUE, perl=TRUE)) {
|
||||
stop("addResourcePath called with invalid prefix; please see documentation")
|
||||
}
|
||||
|
||||
@@ -141,7 +140,6 @@ resourcePathHandler <- function(req) {
|
||||
#' })
|
||||
#' }
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
shinyServer <- function(func) {
|
||||
.globals$server <- list(func)
|
||||
@@ -191,6 +189,7 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
|
||||
appHandlers <- list(
|
||||
http = joinHandlers(c(
|
||||
sessionHandler,
|
||||
apiHandler(serverFuncSource),
|
||||
httpHandlers,
|
||||
sys.www.root,
|
||||
resourcePathHandler,
|
||||
@@ -202,6 +201,11 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
|
||||
return(TRUE)
|
||||
}
|
||||
|
||||
if (grepl("^/api/", ws$request$PATH_INFO)) {
|
||||
apiWsHandler(serverFuncSource)(ws)
|
||||
return(TRUE)
|
||||
}
|
||||
|
||||
if (!is.null(getOption("shiny.observer.error", NULL))) {
|
||||
warning(
|
||||
call. = FALSE,
|
||||
@@ -220,7 +224,8 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
|
||||
if (is.character(msg))
|
||||
msg <- charToRaw(msg)
|
||||
|
||||
if (isTRUE(getOption('shiny.trace'))) {
|
||||
traceOption <- getOption('shiny.trace', FALSE)
|
||||
if (isTRUE(traceOption) || traceOption == "recv") {
|
||||
if (binary)
|
||||
message("RECV ", '$$binary data$$')
|
||||
else
|
||||
@@ -232,121 +237,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 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 +436,12 @@ startApp <- function(appObj, port, host, quiet) {
|
||||
message('\n', 'Listening on domain socket ', port)
|
||||
}
|
||||
mask <- attr(port, 'mask')
|
||||
if (is.null(mask)) {
|
||||
stop("`port` is not a valid domain socket (missing `mask` attribute). ",
|
||||
"Note that if you're using the default `host` + `port` ",
|
||||
"configuration (and not domain sockets), then `port` must ",
|
||||
"be numeric, not a string.")
|
||||
}
|
||||
return(startPipeServer(port, mask, handlerManager$createHttpuvApp()))
|
||||
}
|
||||
}
|
||||
@@ -460,10 +455,7 @@ serviceApp <- function() {
|
||||
}
|
||||
|
||||
flushReact()
|
||||
|
||||
for (shinysession in appsByToken$values()) {
|
||||
shinysession$flushOutput()
|
||||
}
|
||||
flushAllSessions()
|
||||
}
|
||||
|
||||
# If this R session is interactive, then call service() with a short timeout
|
||||
@@ -476,6 +468,9 @@ serviceApp <- function() {
|
||||
|
||||
.shinyServerMinVersion <- '0.3.4'
|
||||
|
||||
# Global flag that's TRUE whenever we're inside of the scope of a call to runApp
|
||||
.globals$running <- FALSE
|
||||
|
||||
#' Run Shiny Application
|
||||
#'
|
||||
#' Runs a Shiny application. This function normally does not return; interrupt R
|
||||
@@ -517,6 +512,9 @@ serviceApp <- function() {
|
||||
#' application. If set to \code{"normal"}, displays the application normally.
|
||||
#' Defaults to \code{"auto"}, which displays the application in the mode given
|
||||
#' in its \code{DESCRIPTION} file, if any.
|
||||
#' @param test.mode Should the application be launched in test mode? This is
|
||||
#' only used for recording or running automated tests. Defaults to the
|
||||
#' \code{shiny.testmode} option, or FALSE if the option is not set.
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
@@ -529,6 +527,8 @@ serviceApp <- function() {
|
||||
#'
|
||||
#' ## Only run this example in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' options(device.ask.default = FALSE)
|
||||
#'
|
||||
#' # Apps can be run without a server.r and ui.r file
|
||||
#' runApp(list(
|
||||
#' ui = bootstrapPage(
|
||||
@@ -560,23 +560,76 @@ runApp <- function(appDir=getwd(),
|
||||
interactive()),
|
||||
host=getOption('shiny.host', '127.0.0.1'),
|
||||
workerId="", quiet=FALSE,
|
||||
display.mode=c("auto", "normal", "showcase")) {
|
||||
display.mode=c("auto", "normal", "showcase"),
|
||||
test.mode=getOption('shiny.testmode', FALSE)) {
|
||||
on.exit({
|
||||
handlerManager$clear()
|
||||
}, add = TRUE)
|
||||
|
||||
if (.globals$running) {
|
||||
stop("Can't call `runApp()` from within `runApp()`. If your ,",
|
||||
"application code contains `runApp()`, please remove it.")
|
||||
}
|
||||
.globals$running <- TRUE
|
||||
on.exit({
|
||||
.globals$running <- FALSE
|
||||
}, add = TRUE)
|
||||
|
||||
if (is.null(host) || is.na(host))
|
||||
host <- '0.0.0.0'
|
||||
# Enable per-app Shiny options
|
||||
oldOptionSet <- .globals$options
|
||||
on.exit({
|
||||
.globals$options <- oldOptionSet
|
||||
},add = TRUE)
|
||||
|
||||
# Make warnings print immediately
|
||||
# Set pool.scheduler to support pool package
|
||||
ops <- options(warn = 1, pool.scheduler = scheduleTask)
|
||||
on.exit(options(ops), add = TRUE)
|
||||
|
||||
appParts <- as.shiny.appobj(appDir)
|
||||
|
||||
# The lines below set some of the app's running options, which
|
||||
# can be:
|
||||
# - left unspeficied (in which case the arguments' default
|
||||
# values from `runApp` kick in);
|
||||
# - passed through `shinyApp`
|
||||
# - passed through `runApp` (this function)
|
||||
# - passed through both `shinyApp` and `runApp` (the latter
|
||||
# takes precedence)
|
||||
#
|
||||
# Matrix of possibilities:
|
||||
# | IN shinyApp | IN runApp | result | check |
|
||||
# |-------------|-----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------|
|
||||
# | no | no | use defaults | exhaust all possibilities: if it's missing (runApp does not specify); THEN if it's not in shinyApp appParts$options; THEN use defaults |
|
||||
# | yes | no | use shinyApp | if it's missing (runApp does not specify); THEN if it's in shinyApp appParts$options; THEN use shinyApp |
|
||||
# | no | yes | use runApp | if it's not missing (runApp specifies), use those |
|
||||
# | yes | yes | use runApp | if it's not missing (runApp specifies), use those |
|
||||
#
|
||||
# I tried to make this as compact and intuitive as possible,
|
||||
# given that there are four distinct possibilities to check
|
||||
appOps <- appParts$options
|
||||
findVal <- function(arg, default) {
|
||||
if (arg %in% names(appOps)) appOps[[arg]] else default
|
||||
}
|
||||
|
||||
if (missing(port))
|
||||
port <- findVal("port", port)
|
||||
if (missing(launch.browser))
|
||||
launch.browser <- findVal("launch.browser", launch.browser)
|
||||
if (missing(host))
|
||||
host <- findVal("host", host)
|
||||
if (missing(quiet))
|
||||
quiet <- findVal("quiet", quiet)
|
||||
if (missing(display.mode))
|
||||
display.mode <- findVal("display.mode", display.mode)
|
||||
if (missing(test.mode))
|
||||
test.mode <- findVal("test.mode", test.mode)
|
||||
|
||||
if (is.null(host) || is.na(host)) host <- '0.0.0.0'
|
||||
|
||||
workerId(workerId)
|
||||
|
||||
if (nzchar(Sys.getenv('SHINY_PORT'))) {
|
||||
if (inShinyServer()) {
|
||||
# If SHINY_PORT is set, we're running under Shiny Server. Check the version
|
||||
# to make sure it is compatible. Older versions of Shiny Server don't set
|
||||
# SHINY_SERVER_VERSION, those will return "" which is considered less than
|
||||
@@ -593,6 +646,11 @@ runApp <- function(appDir=getwd(),
|
||||
# the display.mode parameter. The latter takes precedence.
|
||||
setShowcaseDefault(0)
|
||||
|
||||
.globals$testMode <- test.mode
|
||||
if (test.mode) {
|
||||
message("Running application in test mode.")
|
||||
}
|
||||
|
||||
# If appDir specifies a path, and display mode is specified in the
|
||||
# DESCRIPTION file at that path, apply it here.
|
||||
if (is.character(appDir)) {
|
||||
@@ -680,7 +738,11 @@ runApp <- function(appDir=getwd(),
|
||||
}
|
||||
}
|
||||
|
||||
appParts <- as.shiny.appobj(appDir)
|
||||
# Extract appOptions (which is a list) and store them as shinyOptions, for
|
||||
# this app. (This is the only place we have to store settings that are
|
||||
# accessible both the UI and server portion of the app.)
|
||||
unconsumeAppOptions(appParts$appOptions)
|
||||
|
||||
# Set up the onEnd before we call onStart, so that it gets called even if an
|
||||
# error happens in onStart.
|
||||
if (!is.null(appParts$onEnd))
|
||||
@@ -719,12 +781,16 @@ runApp <- function(appDir=getwd(),
|
||||
# Top-level ..stacktraceoff..; matches with ..stacktraceon in observe(),
|
||||
# reactive(), Callbacks$invoke(), and others
|
||||
..stacktraceoff..(
|
||||
captureStackTraces(
|
||||
captureStackTraces({
|
||||
# If any observers were created before runApp was called, this will make
|
||||
# sure they run once the app starts. (Issue #1013)
|
||||
scheduleFlush()
|
||||
|
||||
while (!.globals$stopped) {
|
||||
serviceApp()
|
||||
Sys.sleep(0.001)
|
||||
}
|
||||
)
|
||||
})
|
||||
)
|
||||
|
||||
if (isTRUE(.globals$reterror)) {
|
||||
@@ -743,7 +809,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
|
||||
@@ -862,7 +927,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) {
|
||||
@@ -958,3 +1022,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
83
R/shiny-options.R
Normal 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)
|
||||
}
|
||||
}
|
||||
52
R/shinyui.R
52
R/shinyui.R
@@ -24,7 +24,7 @@ withMathJax <- function(...) {
|
||||
)
|
||||
}
|
||||
|
||||
renderPage <- function(ui, connection, showcase=0) {
|
||||
renderPage <- function(ui, connection, showcase=0, testMode=FALSE) {
|
||||
# If the ui is a NOT complete document (created by htmlTemplate()), then do some
|
||||
# preprocessing and make sure it's a complete document.
|
||||
if (!inherits(ui, "html_document")) {
|
||||
@@ -44,12 +44,20 @@ 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",
|
||||
stylesheet = "shiny.css")
|
||||
)
|
||||
|
||||
if (testMode) {
|
||||
# Add code injection listener if in test mode
|
||||
shiny_deps[[length(shiny_deps) + 1]] <-
|
||||
htmlDependency("shiny-testmode", utils::packageVersion("shiny"),
|
||||
c(href="shared"), script = "shiny-testmode.js")
|
||||
}
|
||||
|
||||
html <- renderDocument(ui, shiny_deps, processDep = createWebDependency)
|
||||
writeUTF8(html, con = connection)
|
||||
}
|
||||
@@ -64,7 +72,6 @@ renderPage <- function(ui, connection, showcase=0) {
|
||||
#'
|
||||
#' @param ui A user interace definition
|
||||
#' @return The user interface definition, without modifications or side effects.
|
||||
#'
|
||||
#' @export
|
||||
shinyUI <- function(ui) {
|
||||
.globals$ui <- list(ui)
|
||||
@@ -91,21 +98,40 @@ 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())
|
||||
}
|
||||
|
||||
testMode <- .globals$testMode %OR% FALSE
|
||||
|
||||
# Create a restore context using query string
|
||||
bookmarkStore <- getShinyOption("bookmarkStore", default = "disable")
|
||||
if (bookmarkStore == "disable") {
|
||||
# If bookmarking is disabled, use empty context
|
||||
restoreContext <- RestoreContext$new()
|
||||
} else {
|
||||
ui
|
||||
restoreContext <- RestoreContext$new(req$QUERY_STRING)
|
||||
}
|
||||
|
||||
withRestoreContext(restoreContext, {
|
||||
uiValue <- NULL
|
||||
|
||||
if (is.function(ui)) {
|
||||
if (length(formals(ui)) > 0) {
|
||||
# No corresponding ..stacktraceoff.., this is pure user code
|
||||
uiValue <- ..stacktraceon..(ui(req))
|
||||
} else {
|
||||
# No corresponding ..stacktraceoff.., this is pure user code
|
||||
uiValue <- ..stacktraceon..(ui())
|
||||
}
|
||||
} else {
|
||||
if (getCurrentRestoreContext()$active) {
|
||||
warning("Trying to restore saved app state, but UI code must be a function for this to work! See ?enableBookmarking")
|
||||
}
|
||||
uiValue <- ui
|
||||
}
|
||||
})
|
||||
if (is.null(uiValue))
|
||||
return(NULL)
|
||||
|
||||
renderPage(uiValue, textConn, showcaseMode)
|
||||
renderPage(uiValue, textConn, showcaseMode, testMode)
|
||||
html <- paste(readLines(textConn, encoding = 'UTF-8'), collapse='\n')
|
||||
return(httpResponse(200, content=enc2utf8(html)))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -128,6 +127,7 @@ as.tags.shiny.render.function <- function(x, ..., inline = FALSE) {
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' options(device.ask.default = FALSE)
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' sliderInput("n", "Number of observations", 2, 1000, 500),
|
||||
@@ -221,7 +221,7 @@ renderImage <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
#'
|
||||
#' Makes a reactive version of the given function that captures any printed
|
||||
#' output, and also captures its printable result (unless
|
||||
#' \code{\link{invisible}}), into a string. The resulting function is suitable
|
||||
#' \code{\link[base]{invisible}}), into a string. The resulting function is suitable
|
||||
#' for assigning to an \code{output} slot.
|
||||
#'
|
||||
#' The corresponding HTML output tag can be anything (though \code{pre} is
|
||||
@@ -233,14 +233,14 @@ renderImage <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
#'
|
||||
#' Note that unlike most other Shiny output functions, if the given function
|
||||
#' returns \code{NULL} then \code{NULL} will actually be visible in the output.
|
||||
#' To display nothing, make your function return \code{\link{invisible}()}.
|
||||
#' To display nothing, make your function return \code{\link[base]{invisible}()}.
|
||||
#'
|
||||
#' @param expr An expression that may print output and/or return a printable R
|
||||
#' object.
|
||||
#' @param env The environment in which to evaluate \code{expr}.
|
||||
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
|
||||
#' is useful if you want to save an expression in a variable.
|
||||
#' @param width The value for \code{\link{options}('width')}.
|
||||
#' @param width The value for \code{\link[base]{options}('width')}.
|
||||
#' @param outputArgs A list of arguments to be passed through to the implicit
|
||||
#' call to \code{\link{verbatimTextOutput}} when \code{renderPrint} is used
|
||||
#' in an interactive R Markdown document.
|
||||
@@ -248,7 +248,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 +288,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 +319,6 @@ renderText <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
#' interactive R Markdown document.
|
||||
#'
|
||||
#' @seealso conditionalPanel
|
||||
#'
|
||||
#' @export
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
@@ -357,6 +354,98 @@ renderUI <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
markRenderFunction(uiOutput, renderFunc, outputArgs = outputArgs)
|
||||
}
|
||||
|
||||
#' @export
|
||||
serveJSON <- function(expr, env=parent.frame(), quoted=FALSE) {
|
||||
installExprFunction(expr, "func", env, quoted)
|
||||
function() {
|
||||
structure(
|
||||
toJSON(func(), pretty = TRUE),
|
||||
content.type = "application/json"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#' @export
|
||||
servePlot <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
defaultWidth = 600, defaultHeight = 400) {
|
||||
|
||||
if (!is.function(defaultWidth))
|
||||
defaultWidth <- valueToFunc(defaultWidth)
|
||||
if (!is.function(defaultHeight))
|
||||
defaultHeight <- valueToFunc(defaultHeight)
|
||||
|
||||
installExprFunction(expr, "func", env, quoted)
|
||||
function() {
|
||||
input <- getDefaultReactiveDomain()$input
|
||||
w <- if (!is.null(input$`plot-width`)) as.numeric(input$`plot-width`) else defaultWidth()
|
||||
h <- if (!is.null(input$`plot-height`)) as.numeric(input$`plot-height`) else defaultHeight()
|
||||
|
||||
pngfile <- plotPNG(function() {
|
||||
result <- withVisible(func())
|
||||
if (result$visible) {
|
||||
# Use capture.output to squelch printing to the actual console; we
|
||||
# are only interested in plot output
|
||||
utils::capture.output({
|
||||
# The value needs to be printed just in case it's an object that
|
||||
# requires printing to generate plot output, similar to ggplot2. But
|
||||
# for base graphics, it would already have been rendered when func was
|
||||
# called above, and the print should have no effect.
|
||||
print(result$value)
|
||||
})
|
||||
}
|
||||
}, width = w, height = h)
|
||||
|
||||
structure(
|
||||
list(file = pngfile, owned = TRUE),
|
||||
content.type = "image/png"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#' @importFrom utils write.csv
|
||||
#' @export
|
||||
serveCSV <- function(expr, env=parent.frame(), quoted=FALSE, row.names=FALSE) {
|
||||
installExprFunction(expr, "func", env, quoted)
|
||||
function() {
|
||||
tmp <- tempfile(".csv")
|
||||
write.csv(func(), tmp, row.names=row.names)
|
||||
structure(
|
||||
list(file = tmp, owned = TRUE),
|
||||
content.type = "text/csv"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#' @export
|
||||
serveText <- function(expr, env=parent.frame(), quoted=FALSE) {
|
||||
installExprFunction(expr, "func", env, quoted)
|
||||
function() {
|
||||
structure(
|
||||
paste(func(), collapse = "\n"),
|
||||
content.type = "text/plain"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#' @export
|
||||
serveRaw <- function(expr, env=parent.frame(), quoted=FALSE, contentType) {
|
||||
|
||||
if (!is.function(contentType))
|
||||
contentType <- valueToFunc(contentType)
|
||||
|
||||
installExprFunction(expr, "func", env, quoted)
|
||||
function() {
|
||||
bytes <- func()
|
||||
if (!is.raw(bytes)) {
|
||||
stop("serveRaw expects raw vector data")
|
||||
}
|
||||
structure(
|
||||
bytes,
|
||||
content.type = contentType()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#' File Downloads
|
||||
#'
|
||||
#' Allows content from the Shiny application to be made available to the user as
|
||||
@@ -424,7 +513,7 @@ downloadHandler <- function(filename, content, contentType=NA, outputArgs=list()
|
||||
#' the server infrastructure.
|
||||
#'
|
||||
#' For the \code{options} argument, the character elements that have the class
|
||||
#' \code{"AsIs"} (usually returned from \code{\link{I}()}) will be evaluated in
|
||||
#' \code{"AsIs"} (usually returned from \code{\link[base]{I}()}) will be evaluated in
|
||||
#' JavaScript. This is useful when the type of the option value is not supported
|
||||
#' in JSON, e.g., a JavaScript function, which can be obtained by evaluating a
|
||||
#' character string. Note this only applies to the root-level elements of the
|
||||
|
||||
@@ -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"),
|
||||
|
||||
61
R/test-export.R
Normal file
61
R/test-export.R
Normal file
@@ -0,0 +1,61 @@
|
||||
#' Register expressions for export in test mode
|
||||
#'
|
||||
#' This function registers expressions that will be evaluated when a test export
|
||||
#' event occurs. These events are triggered by accessing a snapshot URL.
|
||||
#'
|
||||
#' This function only has an effect if the app is launched in test mode. This is
|
||||
#' done by calling \code{runApp()} with \code{test.mode=TRUE}, or by setting the
|
||||
#' global option \code{shiny.testmode} to \code{TRUE}.
|
||||
#'
|
||||
#' @param quoted_ Are the expression quoted? Default is \code{FALSE}.
|
||||
#' @param env_ The environment in which the expression should be evaluated.
|
||||
#' @param session_ A Shiny session object.
|
||||
#' @param ... Named arguments that are quoted or unquoted expressions that will
|
||||
#' be captured and evaluated when snapshot URL is visited.
|
||||
#' @examples
|
||||
#' ## Only run this example in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#'
|
||||
#' options(shiny.testmode = TRUE)
|
||||
#'
|
||||
#' # This application shows the test snapshot URL; clicking on it will
|
||||
#' # fetch the input, output, and exported values in JSON format.
|
||||
#' shinyApp(
|
||||
#' ui = basicPage(
|
||||
#' h4("Snapshot URL: "),
|
||||
#' uiOutput("url"),
|
||||
#' h4("Current values:"),
|
||||
#' verbatimTextOutput("values"),
|
||||
#' actionButton("inc", "Increment x")
|
||||
#' ),
|
||||
#'
|
||||
#' server = function(input, output, session) {
|
||||
#' vals <- reactiveValues(x = 1)
|
||||
#' y <- reactive({ vals$x + 1 })
|
||||
#'
|
||||
#' observeEvent(input$inc, {
|
||||
#' vals$x <<- vals$x + 1
|
||||
#' })
|
||||
#'
|
||||
#' exportTestValues(
|
||||
#' x = vals$x,
|
||||
#' y = y()
|
||||
#' )
|
||||
#'
|
||||
#' output$url <- renderUI({
|
||||
#' url <- session$getTestSnapshotUrl(format="json")
|
||||
#' a(href = url, url)
|
||||
#' })
|
||||
#'
|
||||
#' output$values <- renderText({
|
||||
#' paste0("vals$x: ", vals$x, "\ny: ", y())
|
||||
#' })
|
||||
#' }
|
||||
#' )
|
||||
#' }
|
||||
#' @export
|
||||
exportTestValues <- function(..., quoted_ = FALSE, env_ = parent.frame(),
|
||||
session_ = getDefaultReactiveDomain())
|
||||
{
|
||||
session_$exportTestValues(..., quoted_ = quoted_, env_ = env_)
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
))
|
||||
}
|
||||
|
||||
@@ -580,7 +622,7 @@ updateSelectizeInput <- function(session, inputId, label = NULL, choices = NULL,
|
||||
res <- checkAsIs(options)
|
||||
cfg <- tags$script(
|
||||
type = 'application/json',
|
||||
`data-for` = inputId,
|
||||
`data-for` = session$ns(inputId),
|
||||
`data-eval` = if (length(res$eval)) HTML(toJSON(res$eval)),
|
||||
HTML(toJSON(res$options))
|
||||
)
|
||||
|
||||
276
R/utils.R
276
R/utils.R
@@ -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,55 @@ anyUnnamed <- function(x) {
|
||||
any(!nzchar(nms))
|
||||
}
|
||||
|
||||
|
||||
# Given a vector/list, returns a named vector (the labels will be blank).
|
||||
asNamedVector <- function(x) {
|
||||
if (!is.null(names(x)))
|
||||
return(x)
|
||||
|
||||
names(x) <- rep.int("", length(x))
|
||||
x
|
||||
}
|
||||
|
||||
# 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]
|
||||
}
|
||||
|
||||
# Sort a vector by the names of items. If there are multiple items with the
|
||||
# same name, preserve the original order of those items. For empty
|
||||
# vectors/lists/NULL, return the original value.
|
||||
sortByName <- function(x) {
|
||||
if (anyUnnamed(x))
|
||||
stop("All items must be named")
|
||||
|
||||
# Special case for empty vectors/lists, and NULL
|
||||
if (length(x) == 0)
|
||||
return(x)
|
||||
|
||||
x[order(names(x))]
|
||||
}
|
||||
|
||||
# Wrapper around list2env with a NULL check. In R <3.2.0, if an empty unnamed
|
||||
# list is passed to list2env(), it errors. But an empty named list is OK. For
|
||||
# R >=3.2.0, this wrapper is not necessary.
|
||||
list2env2 <- function(x, ...) {
|
||||
# Ensure that zero-length lists have a name attribute
|
||||
if (length(x) == 0)
|
||||
attr(x, "names") <- character(0)
|
||||
|
||||
list2env(x, ...)
|
||||
}
|
||||
|
||||
# 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 +273,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 +444,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 +477,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 +548,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))
|
||||
@@ -532,6 +576,20 @@ parseQueryString <- function(str, nested = FALSE) {
|
||||
res
|
||||
}
|
||||
|
||||
parseQueryStringJSON <- function(str, nested = FALSE) {
|
||||
vals <- parseQueryString(str, nested)
|
||||
mapply(names(vals), vals, SIMPLIFY = FALSE,
|
||||
FUN = function(name, value) {
|
||||
tryCatch(
|
||||
jsonlite::fromJSON(value),
|
||||
error = function(e) {
|
||||
stop("Failed to parse URL parameter \"", name, "\"")
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
# Assign value to the bottom element of the list x using recursive indices idx
|
||||
assignNestedList <- function(x = list(), idx, value) {
|
||||
for (i in seq_along(idx)) {
|
||||
@@ -553,13 +611,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 +1028,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 +1140,17 @@ 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()) {
|
||||
#' options(device.ask.default = FALSE)
|
||||
#'
|
||||
#' 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 +1159,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 +1183,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
|
||||
@@ -1132,7 +1245,7 @@ need <- function(expr, message = paste(label, "must be provided"), label) {
|
||||
#' \strong{Truthy and falsy values}
|
||||
#'
|
||||
#' The terms "truthy" and "falsy" generally indicate whether a value, when
|
||||
#' coerced to a \code{\link{logical}}, is \code{TRUE} or \code{FALSE}. We use
|
||||
#' coerced to a \code{\link[base]{logical}}, is \code{TRUE} or \code{FALSE}. We use
|
||||
#' the term a little loosely here; our usage tries to match the intuitive
|
||||
#' notions of "Is this value missing or available?", or "Has the user provided
|
||||
#' an answer?", or in the case of action buttons, "Has the button been
|
||||
@@ -1164,20 +1277,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 +1348,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 +1400,8 @@ dotloop <- function(fun_, ...) {
|
||||
invisible()
|
||||
}
|
||||
|
||||
#' @export
|
||||
#' @rdname req
|
||||
isTruthy <- function(x) {
|
||||
if (inherits(x, 'try-error'))
|
||||
return(FALSE)
|
||||
@@ -1413,3 +1599,9 @@ Mutable <- R6Class("Mutable",
|
||||
get = function() { private$value }
|
||||
)
|
||||
)
|
||||
|
||||
# Turn a value into a no-arg function that returns that value
|
||||
valueToFunc <- function(val) {
|
||||
force(val)
|
||||
function() val
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# Shiny
|
||||
|
||||
[](https://travis-ci.org/rstudio/shiny)
|
||||
*Travis:* [](https://travis-ci.org/rstudio/shiny)
|
||||
|
||||
*AppVeyor:* [](https://ci.appveyor.com/project/rstudio/shiny)
|
||||
|
||||
Shiny is a new package from RStudio that makes it incredibly easy to build interactive web applications with R.
|
||||
|
||||
@@ -57,6 +59,10 @@ devtools::install_version("shiny", version = "0.10.2.2")
|
||||
|
||||
The Javascript code in Shiny is minified using tools that run on Node.js. See the tools/ directory for more information.
|
||||
|
||||
## Guidelines for contributing
|
||||
|
||||
We welcome contributions to the **shiny** package. Please see our [CONTRIBUTING.md](CONTRIBUTING.md) file for detailed guidelines of how to contribute.
|
||||
|
||||
## License
|
||||
|
||||
The shiny package is licensed under the GPLv3. See these files in the inst directory for additional details:
|
||||
|
||||
45
appveyor.yml
Normal file
45
appveyor.yml
Normal 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
|
||||
@@ -1,6 +0,0 @@
|
||||
name: 01_hello
|
||||
account: admin
|
||||
server: localhost
|
||||
bundleId: 1
|
||||
url: http://localhost:3939/admin/01_hello/
|
||||
when: 1436550957.65385
|
||||
@@ -1 +1 @@
|
||||
This example demonstrates some additional widgets included in Shiny, such as `helpText` and `submitButton`. The latter is used to delay rendering output until the user explicitly requests it.
|
||||
This example demonstrates some additional widgets included in Shiny, such as `helpText` and `actionButton`. The latter is used to delay rendering output until the user explicitly requests it (a construct which also introduces two important server functions, `eventReactive` and `isolate`).
|
||||
|
||||
@@ -1,26 +1,32 @@
|
||||
library(shiny)
|
||||
library(datasets)
|
||||
|
||||
# Define server logic required to summarize and view the
|
||||
# Define server logic required to summarize and view the
|
||||
# selected dataset
|
||||
function(input, output) {
|
||||
|
||||
# Return the requested dataset
|
||||
datasetInput <- reactive({
|
||||
|
||||
# Return the requested dataset. Note that we use `eventReactive()`
|
||||
# here, which takes a dependency on input$update (the action
|
||||
# button), so that the output is only updated when the user
|
||||
# clicks the button.
|
||||
datasetInput <- eventReactive(input$update, {
|
||||
switch(input$dataset,
|
||||
"rock" = rock,
|
||||
"pressure" = pressure,
|
||||
"cars" = cars)
|
||||
})
|
||||
|
||||
}, ignoreNULL = FALSE)
|
||||
|
||||
# Generate a summary of the dataset
|
||||
output$summary <- renderPrint({
|
||||
dataset <- datasetInput()
|
||||
summary(dataset)
|
||||
})
|
||||
|
||||
# Show the first "n" observations
|
||||
|
||||
# Show the first "n" observations. The use of `isolate()` here
|
||||
# is necessary because we don't want the table to update
|
||||
# whenever input$obs changes (only when the user clicks the
|
||||
# action button).
|
||||
output$view <- renderTable({
|
||||
head(datasetInput(), n = input$obs)
|
||||
head(datasetInput(), n = isolate(input$obs))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,32 +2,32 @@ library(shiny)
|
||||
|
||||
# Define UI for dataset viewer application
|
||||
fluidPage(
|
||||
|
||||
|
||||
# Application title.
|
||||
titlePanel("More Widgets"),
|
||||
|
||||
|
||||
# Sidebar with controls to select a dataset and specify the
|
||||
# number of observations to view. The helpText function is
|
||||
# also used to include clarifying text. Most notably, the
|
||||
# inclusion of a submitButton defers the rendering of output
|
||||
# inclusion of an actionButton defers the rendering of output
|
||||
# until the user explicitly clicks the button (rather than
|
||||
# doing it immediately when inputs change). This is useful if
|
||||
# the computations required to render output are inordinately
|
||||
# time-consuming.
|
||||
sidebarLayout(
|
||||
sidebarPanel(
|
||||
selectInput("dataset", "Choose a dataset:",
|
||||
selectInput("dataset", "Choose a dataset:",
|
||||
choices = c("rock", "pressure", "cars")),
|
||||
|
||||
|
||||
numericInput("obs", "Number of observations to view:", 10),
|
||||
|
||||
|
||||
helpText("Note: while the data view will show only the specified",
|
||||
"number of observations, the summary will still be based",
|
||||
"on the full dataset."),
|
||||
|
||||
submitButton("Update View")
|
||||
|
||||
actionButton("update", "Update View")
|
||||
),
|
||||
|
||||
|
||||
# Show a summary of the dataset and an HTML table with the
|
||||
# requested number of observations. Note the use of the h4
|
||||
# function to provide an additional header above each output
|
||||
@@ -35,7 +35,7 @@ fluidPage(
|
||||
mainPanel(
|
||||
h4("Summary"),
|
||||
verbatimTextOutput("summary"),
|
||||
|
||||
|
||||
h4("Observations"),
|
||||
tableOutput("view")
|
||||
)
|
||||
|
||||
@@ -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",
|
||||
@@ -105,23 +115,25 @@ sd_section("Rendering functions",
|
||||
"reactiveUI"
|
||||
)
|
||||
)
|
||||
sd_section("Reactive constructs",
|
||||
sd_section("Reactive programming",
|
||||
"A sub-library that provides reactive programming facilities for R.",
|
||||
c(
|
||||
"invalidateLater",
|
||||
"is.reactivevalues",
|
||||
"isolate",
|
||||
"makeReactiveBinding",
|
||||
"reactive",
|
||||
"observe",
|
||||
"observeEvent",
|
||||
"reactive",
|
||||
"reactiveValues",
|
||||
"reactiveValuesToList",
|
||||
"is.reactivevalues",
|
||||
"isolate",
|
||||
"invalidateLater",
|
||||
"debounce",
|
||||
"showReactLog",
|
||||
"makeReactiveBinding",
|
||||
"reactiveFileReader",
|
||||
"reactivePoll",
|
||||
"reactiveTimer",
|
||||
"reactiveValues",
|
||||
"reactiveValuesToList",
|
||||
"domains",
|
||||
"showReactLog"
|
||||
"freezeReactiveValue"
|
||||
)
|
||||
)
|
||||
sd_section("Boilerplate",
|
||||
@@ -143,6 +155,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,13 +179,18 @@ 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",
|
||||
"plotPNG",
|
||||
"exportTestValues",
|
||||
"repeatable",
|
||||
"shinyDeprecated",
|
||||
"serverInfo",
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
7
inst/www/shared/bootstrap/css/bootstrap.css
vendored
7
inst/www/shared/bootstrap/css/bootstrap.css
vendored
@@ -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
1
inst/www/shared/bootstrap/css/bootstrap.min.css.map
Normal file
1
inst/www/shared/bootstrap/css/bootstrap.min.css.map
Normal file
File diff suppressed because one or more lines are too long
114
inst/www/shared/bootstrap/js/bootstrap.js
vendored
114
inst/www/shared/bootstrap/js/bootstrap.js
vendored
@@ -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
678
inst/www/shared/datepicker/css/bootstrap-datepicker3.css
vendored
Normal file
678
inst/www/shared/datepicker/css/bootstrap-datepicker3.css
vendored
Normal 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 */
|
||||
9
inst/www/shared/datepicker/css/bootstrap-datepicker3.min.css
vendored
Normal file
9
inst/www/shared/datepicker/css/bootstrap-datepicker3.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -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;
|
||||
}
|
||||
2139
inst/www/shared/datepicker/js/bootstrap-datepicker.js
vendored
2139
inst/www/shared/datepicker/js/bootstrap-datepicker.js
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.ar.min.js
vendored
Normal file
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.ar.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!function(a){a.fn.datepicker.dates.ar={days:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت","الأحد"],daysShort:["أحد","اثنين","ثلاثاء","أربعاء","خميس","جمعة","سبت","أحد"],daysMin:["ح","ن","ث","ع","خ","ج","س","ح"],months:["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],monthsShort:["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],today:"هذا اليوم",rtl:!0}}(jQuery);
|
||||
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.az.min.js
vendored
Normal file
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.az.min.js
vendored
Normal 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);
|
||||
@@ -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));
|
||||
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.bg.min.js
vendored
Normal file
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.bg.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!function(a){a.fn.datepicker.dates.bg={days:["Неделя","Понеделник","Вторник","Сряда","Четвъртък","Петък","Събота"],daysShort:["Нед","Пон","Вто","Сря","Чет","Пет","Съб"],daysMin:["Н","П","В","С","Ч","П","С"],months:["Януари","Февруари","Март","Април","Май","Юни","Юли","Август","Септември","Октомври","Ноември","Декември"],monthsShort:["Ян","Фев","Мар","Апр","Май","Юни","Юли","Авг","Сеп","Окт","Ное","Дек"],today:"днес"}}(jQuery);
|
||||
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.bs.min.js
vendored
Normal file
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.bs.min.js
vendored
Normal 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);
|
||||
@@ -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));
|
||||
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.ca.min.js
vendored
Normal file
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.ca.min.js
vendored
Normal 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);
|
||||
@@ -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));
|
||||
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.cs.min.js
vendored
Normal file
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.cs.min.js
vendored
Normal 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);
|
||||
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.cy.min.js
vendored
Normal file
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.cy.min.js
vendored
Normal 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);
|
||||
@@ -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));
|
||||
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.da.min.js
vendored
Normal file
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.da.min.js
vendored
Normal 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);
|
||||
@@ -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));
|
||||
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.de.min.js
vendored
Normal file
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.de.min.js
vendored
Normal 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);
|
||||
@@ -1,13 +0,0 @@
|
||||
/**
|
||||
* Greek translation for bootstrap-datepicker
|
||||
*/
|
||||
;(function($){
|
||||
$.fn.datepicker.dates['el'] = {
|
||||
days: ["Κυριακή", "Δευτέρα", "Τρίτη", "Τετάρτη", "Πέμπτη", "Παρασκευή", "Σάββατο", "Κυριακή"],
|
||||
daysShort: ["Κυρ", "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ", "Κυρ"],
|
||||
daysMin: ["Κυ", "Δε", "Τρ", "Τε", "Πε", "Πα", "Σα", "Κυ"],
|
||||
months: ["Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος"],
|
||||
monthsShort: ["Ιαν", "Φεβ", "Μαρ", "Απρ", "Μάι", "Ιουν", "Ιουλ", "Αυγ", "Σεπ", "Οκτ", "Νοε", "Δεκ"],
|
||||
today: "Σήμερα"
|
||||
};
|
||||
}(jQuery));
|
||||
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.el.min.js
vendored
Normal file
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.el.min.js
vendored
Normal 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);
|
||||
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.en-AU.min.js
vendored
Normal file
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.en-AU.min.js
vendored
Normal 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);
|
||||
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.en-GB.min.js
vendored
Normal file
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.en-GB.min.js
vendored
Normal 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);
|
||||
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.eo.min.js
vendored
Normal file
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.eo.min.js
vendored
Normal 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);
|
||||
@@ -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));
|
||||
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.es.min.js
vendored
Normal file
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.es.min.js
vendored
Normal 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);
|
||||
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.et.min.js
vendored
Normal file
1
inst/www/shared/datepicker/js/locales/bootstrap-datepicker.et.min.js
vendored
Normal 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);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user