mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-11 07:58:11 -05:00
Compare commits
252 Commits
bookmarkab
...
v0.14
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
28547e90d1 | ||
|
|
f70187597f | ||
|
|
333e454e78 | ||
|
|
61cfd11644 | ||
|
|
1c970c8176 | ||
|
|
1fea54ca5a | ||
|
|
faccc42b22 | ||
|
|
460f4769a5 | ||
|
|
f465643b75 | ||
|
|
a8afd71f96 | ||
|
|
2274d60207 | ||
|
|
57159bccfd | ||
|
|
2888124752 | ||
|
|
408e751dcf | ||
|
|
af5dcc38a4 | ||
|
|
81434640d6 | ||
|
|
a45b58d956 | ||
|
|
2c5e9a5e76 | ||
|
|
9fb847b179 | ||
|
|
b8341b2ba8 | ||
|
|
26d6e4da2c | ||
|
|
b16ed602d5 | ||
|
|
24aab4d5d3 | ||
|
|
2eb69d421a | ||
|
|
cb52706f2f | ||
|
|
f44d232e8b | ||
|
|
a0ac79b9dd | ||
|
|
177a2a8a1e | ||
|
|
8b21a87175 | ||
|
|
6d2dd8e315 | ||
|
|
8b3aff599b | ||
|
|
deb9b74f27 | ||
|
|
591de3cbe8 | ||
|
|
f7151e2132 | ||
|
|
44521be6dd | ||
|
|
30416cdbb5 | ||
|
|
d04da2d256 | ||
|
|
231d8a1949 | ||
|
|
3207bec805 | ||
|
|
425a71e382 | ||
|
|
daa12ab2ec | ||
|
|
4c652389c5 | ||
|
|
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
|
||||
|
||||
@@ -6,3 +10,8 @@ notifications:
|
||||
email:
|
||||
on_success: change
|
||||
on_failure: change
|
||||
|
||||
# Set CXX1X for R-devel, as R-devel does not detect CXX1X support for gcc 4.6.3.
|
||||
# This was causing dependency sourcetools to fail.
|
||||
before_install:
|
||||
- if [[ "$TRAVIS_R_VERSION_STRING" = 'devel' ]]; then mkdir ~/.R && echo 'CXX1X=g++ -std=c++0x -g -O2 -fPIC' > ~/.R/Makevars; fi
|
||||
|
||||
11
DESCRIPTION
11
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: 0.14
|
||||
Authors@R: c(
|
||||
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@rstudio.com"),
|
||||
person("Joe", "Cheng", role = "aut", email = "joe@rstudio.com"),
|
||||
@@ -83,9 +82,11 @@ Suggests:
|
||||
ggplot2
|
||||
URL: http://shiny.rstudio.com
|
||||
BugReports: https://github.com/rstudio/shiny/issues
|
||||
VignetteBuilder: knitr
|
||||
Collate:
|
||||
'app.R'
|
||||
'bookmark-state-local.R'
|
||||
'stack.R'
|
||||
'bookmark-state.R'
|
||||
'bootstrap-layout.R'
|
||||
'conditions.R'
|
||||
'map.R'
|
||||
@@ -95,7 +96,6 @@ Collate:
|
||||
'cache.R'
|
||||
'diagnose.R'
|
||||
'fileupload.R'
|
||||
'stack.R'
|
||||
'graph.R'
|
||||
'hooks.R'
|
||||
'html-deps.R'
|
||||
@@ -116,6 +116,7 @@ Collate:
|
||||
'input-slider.R'
|
||||
'input-submit.R'
|
||||
'input-text.R'
|
||||
'input-textarea.R'
|
||||
'input-utils.R'
|
||||
'insert-ui.R'
|
||||
'jqueryui.R'
|
||||
@@ -132,8 +133,10 @@ Collate:
|
||||
'render-plot.R'
|
||||
'render-table.R'
|
||||
'run-url.R'
|
||||
'serializers.R'
|
||||
'server-input-handlers.R'
|
||||
'server.R'
|
||||
'shiny-options.R'
|
||||
'shiny.R'
|
||||
'shinyui.R'
|
||||
'shinywrappers.R'
|
||||
|
||||
22
NAMESPACE
22
NAMESPACE
@@ -11,6 +11,7 @@ S3method("[",shinyoutput)
|
||||
S3method("[<-",reactivevalues)
|
||||
S3method("[<-",shinyoutput)
|
||||
S3method("[[",reactivevalues)
|
||||
S3method("[[",session_proxy)
|
||||
S3method("[[",shinyoutput)
|
||||
S3method("[[<-",reactivevalues)
|
||||
S3method("[[<-",shinyoutput)
|
||||
@@ -39,6 +40,7 @@ export(addResourcePath)
|
||||
export(animationOptions)
|
||||
export(as.shiny.appobj)
|
||||
export(basicPage)
|
||||
export(bookmarkButton)
|
||||
export(bootstrapLib)
|
||||
export(bootstrapPage)
|
||||
export(br)
|
||||
@@ -46,7 +48,6 @@ export(browserViewer)
|
||||
export(brushOpts)
|
||||
export(brushedPoints)
|
||||
export(callModule)
|
||||
export(cancelOutput)
|
||||
export(captureStackTraces)
|
||||
export(checkboxGroupInput)
|
||||
export(checkboxInput)
|
||||
@@ -66,6 +67,7 @@ export(downloadButton)
|
||||
export(downloadHandler)
|
||||
export(downloadLink)
|
||||
export(em)
|
||||
export(enableBookmarking)
|
||||
export(eventReactive)
|
||||
export(exprToFunction)
|
||||
export(extractStackTrace)
|
||||
@@ -80,7 +82,9 @@ export(flowLayout)
|
||||
export(fluidPage)
|
||||
export(fluidRow)
|
||||
export(formatStackTrace)
|
||||
export(freezeReactiveValue)
|
||||
export(getDefaultReactiveDomain)
|
||||
export(getShinyOption)
|
||||
export(h1)
|
||||
export(h2)
|
||||
export(h3)
|
||||
@@ -110,6 +114,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 +137,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 +182,7 @@ export(renderText)
|
||||
export(renderUI)
|
||||
export(repeatable)
|
||||
export(req)
|
||||
export(restoreInput)
|
||||
export(runApp)
|
||||
export(runExample)
|
||||
export(runGadget)
|
||||
@@ -180,12 +193,15 @@ export(safeError)
|
||||
export(selectInput)
|
||||
export(selectizeInput)
|
||||
export(serverInfo)
|
||||
export(setBookmarkExclude)
|
||||
export(setProgress)
|
||||
export(shinyApp)
|
||||
export(shinyAppDir)
|
||||
export(shinyAppFile)
|
||||
export(shinyOptions)
|
||||
export(shinyServer)
|
||||
export(shinyUI)
|
||||
export(showBookmarkUrlModal)
|
||||
export(showModal)
|
||||
export(showNotification)
|
||||
export(showReactLog)
|
||||
@@ -209,6 +225,7 @@ export(tagAppendChildren)
|
||||
export(tagList)
|
||||
export(tagSetChildren)
|
||||
export(tags)
|
||||
export(textAreaInput)
|
||||
export(textInput)
|
||||
export(textOutput)
|
||||
export(titlePanel)
|
||||
@@ -221,12 +238,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)
|
||||
|
||||
972
NEWS.md
Normal file
972
NEWS.md
Normal file
@@ -0,0 +1,972 @@
|
||||
shiny 0.14
|
||||
=================
|
||||
|
||||
A new Shiny release is upon us! There are many new exciting features, bug fixes, and library updates. We'll just highlight the most important changes here, but you can browse through the full changelog below for details. This will likely be the last release before shiny 1.0, so get out your party hats!
|
||||
|
||||
## Bookmarkable state
|
||||
|
||||
Shiny now supports bookmarkable state: users can save the state of an application and get a URL which will restore the application with that state. There are two types of bookmarking: encoding the state in a URL, and saving the state to the server. With an encoded state, the entire state of the application is contained in the URL’s query string. You can see this in action with this app: https://gallery.shinyapps.io/113-bookmarking-url/. An example of a bookmark URL for this app is https://gallery.shinyapps.io/113-bookmarking-url/?_inputs_&n=200. When the state is saved to the server, the URL might look something like: https://gallery.shinyapps.io/bookmark-saved/?_state_id_=d80625dc681e913a (note that this URL is not for an active app).
|
||||
|
||||
**_Important note_:**
|
||||
> Saved-to-server bookmarking currently works with Shiny Server Open Source. Support on Shiny Server Pro, RStudio Connect, and shinyapps.io is under development and testing. However, URL-encoded bookmarking works on all hosting platforms.
|
||||
|
||||
See [this article](http://shiny.rstudio-staging.com/articles/bookmarking-state.html) to get started with bookmarkable state. There is also an [advanced-level article](http://shiny.rstudio-staging.com/articles/advanced-bookmarking.html) (for apps that have a complex state), and [a modules article](http://shiny.rstudio-staging.com/articles/bookmarking-modules.html) that details how to use bookmarking in conjunction with modules.
|
||||
|
||||
## Notifications
|
||||
|
||||
Shiny can now display notifications on the client browser by using the `showNotification()` function. Use [this demo app](https://gallery.shinyapps.io/116-notifications/) to play around with the notification API. Here's a screenshot of a very simple notification (shown when the button is clicked):
|
||||
|
||||
<p align="center">
|
||||
<img src="http://shiny.rstudio-staging.com/images/notification.png" alt="notification" width="50%"/>
|
||||
</p>
|
||||
|
||||
[Here](http://shiny.rstudio-staging.com/articles/notifications.html)'s our article about it, and the [reference documentation](http://shiny.rstudio-staging.com/reference/shiny/latest/showNotification.html).
|
||||
|
||||
## Progress indicators
|
||||
|
||||
If your Shiny app contains computations that take a long time to complete, a progress bar can improve the user experience by communicating how far along the computation is, and how much is left. Progress bars were added in Shiny 0.10.2. In Shiny 0.14, they were changed to use the notifications system, which gives them a different look.
|
||||
|
||||
**_Important note_:**
|
||||
> If you were already using progress bars and had customized them with your own CSS, you can add the `style = "old"` argument to your `withProgress()` call (or `Progress$new()`). This will result in the same appearance as before. You can also call `shinyOptions(progress.style = "old")` in your app's server function to make all progress indicators use the old styling.
|
||||
|
||||
To see new progress bars in action, see [this app](https://gallery.shinyapps.io/085-progress/) in the gallery. You can also learn more about this in [our article](http://shiny.rstudio-staging.com/articles/progress.html) and in the reference documentation (either for the easier [`withProgress` functional API](http://shiny.rstudio-staging.com/reference/shiny/latest/withProgress.html) or the more complicated, but more powerful, [`Progress` object-oriented API](http://shiny.rstudio-staging.com/reference/shiny/latest/Progress.html).
|
||||
|
||||
## Reconnection
|
||||
|
||||
Shiny can now automatically reconnect to your Shiny session if you temporarily lose network access.
|
||||
|
||||
## Modal windows
|
||||
|
||||
Shiny has now built-in support for displaying modal dialogs like the one below ([live app here](https://gallery.shinyapps.io/114-modal-dialog/)):
|
||||
|
||||
<p align="center">
|
||||
<img src="http://shiny.rstudio-staging.com/images/modal-dialog.png" alt="modal-dialog" width="50%"/>
|
||||
</p>
|
||||
|
||||
To learn more about this, read [our article](http://shiny.rstudio-staging.com/articles/modal-dialogs.html) and the [reference documentation](http://shiny.rstudio-staging.com/reference/shiny/latest/modalDialog.html).
|
||||
|
||||
## `insertUI` and `removeUI`
|
||||
|
||||
Sometimes in a Shiny app, arbitrary HTML UI may need to be created on-the-fly in response to user input. The existing `uiOutput` and `renderUI` functions let you continue using reactive logic to call UI functions and make the results appear in a predetermined place in the UI. The `insertUI` and `removeUI` functions, which are used in the server code, allow you to use imperative logic to add and remove arbitrary chunks of HTML (all independent from one another), as many times as you want, whenever you want, wherever you want. This option may be more convenient when you want to, for example, add a new model to your app each time the user selects a different option (and leave previous models unchanged, rather than substitute the previous one for the latest one).
|
||||
|
||||
See [this simple demo app](https://gallery.shinyapps.io/111-insert-ui/) of how one could use `insertUI` and `removeUI` to insert and remove text elements using a queue. Also see [this other app](https://gallery.shinyapps.io/insertUI/) that demonstrates how to insert and remove a few common Shiny input objects. Finally, [this app](https://gallery.shinyapps.io/insertUI-modules/) shows how to dynamically insert modules using `insertUI`.
|
||||
|
||||
For more, read [our article](http://shiny.rstudio-staging.com/articles/dynamic-ui.html) about dynamic UI generation and the reference documentation about [`insertUI`](http://shiny.rstudio-staging.com/reference/shiny/latest/insertUI.html) and [`removeUI`](http://shiny.rstudio-staging.com/reference/shiny/latest/removeUI.html).
|
||||
|
||||
## Documentation for connecting to an external database
|
||||
|
||||
Many Shiny users have asked about best practices for accessing external databases from their Shiny applications. Although database access has long been possible using various database connector packages in R, it can be challenging to use them robustly in the dynamic environment that Shiny provides. So far, it has been mostly up to application authors to find the appropriate database drivers and to discover how to manage the database connections within an application. In order to demystify this process, we wrote a series of articles ([first one here](http://shiny.rstudio-staging.com/articles/overview.html)) that covers the basics of connecting to an external database, as well as some security precautions to keep in mind (e.g. [how to avoid SQL injection attacks](http://shiny.rstudio-staging.com/articles/sql-injections.html)).
|
||||
|
||||
There are a few packages that you should look at if you're using a relational database in a Shiny app: the `dplyr` and `DBI` packages (both featured in the article linked to above), and the brand new `pool` package, which provides a further layer of abstraction to make it easier and safer to use either `DBI` or `dplyr`. `pool` is not yet on CRAN. In particular, `pool` will take care of managing connections, preventing memory leaks, and ensuring the best performance. See this [`pool` basics article](http://shiny.rstudio-staging.com/articles/pool-basics.html) and the [more advanced-level article](http://shiny.rstudio-staging.com/articles/pool-advanced.html) if you're feeling adventurous! (Both of these articles contain Shiny app examples that use `DBI` to connect to an external MySQL database.) If you are more comfortable with `dplyr` than `DBI`, don't miss the article about the [integration of `pool` and `dplyr`](http://shiny.rstudio-staging.com/articles/pool-dplyr.html).
|
||||
|
||||
If you're new to databases in the Shiny world, we recommend using `dplyr` and `pool` if possible. If you need greater control than `dplyr` offers (for example, if you need to modify data in the database or use transactions), then use `DBI` and `pool`. The `pool` package was introduced to make your life easier, but in no way constrains you, so we don't envision any situation in which you'd be better off *not* using it. The only caveat is that `pool` is not yet on CRAN, so you may prefer to wait for that.
|
||||
|
||||
## Others
|
||||
|
||||
There are many more minor features, small improvements, and bug fixes than we can cover here, so we'll just mention a few of the more noteworthy ones (the full changelog, with links to all the relevant issues and pull requests, is right below this section):
|
||||
|
||||
* **Error Sanitization**: you now have the option to sanitize error messages; in other words, the content of the original error message can be suppressed so that it doesn't leak any sensitive information. To sanitize errors everywhere in your app, just add `options(shiny.sanitize.errors = TRUE)` somewhere in your app. Read [this article](http://shiny.rstudio-staging.com/articles/sanitize-errors.html) for more, or play with the [demo app](https://gallery.shinyapps.io/110-error-sanitization/).
|
||||
|
||||
* **Code Diagnostics**: if there is an error parsing `ui.R`, `server.R`, `app.R`, or `global.R`, Shiny will search the code for missing commas, extra commas, and unmatched braces, parens, and brackets, and will print out messages pointing out those problems. ([#1126](https://github.com/rstudio/shiny/pull/1126))
|
||||
|
||||
* **Reactlog visualization**: by default, the [`showReactLog()` function](http://shiny.rstudio-staging.com/reference/shiny/latest/showReactLog.html) (which brings up the reactive graph) also displays the time that each reactive and observer were active for:
|
||||
|
||||
<p align="center">
|
||||
<img src="http://shiny.rstudio-staging.com/images/reactlog.png" alt="modal-dialog" width="75%"/>
|
||||
</p>
|
||||
|
||||
This new feature can be turned off with `showReactLog(time = FALSE)`. This may be convenient if you have a large graph and don't want to have this new information cluttering it up. The elapsed time info shows up above each relevant node's label, and the time is also coded by color: the slowest reactive will be dark red and the fastest will be light red.
|
||||
|
||||
Additionally, to organize the graph, you can now drag any of the nodes to a specific position and leave it there.
|
||||
|
||||
* **Nicer-looking tables**: we've made tables generated with `renderTable()` look cleaner and more modern. While this won't break any older code, the finished look of your table will be quite a bit different, as the following image shows:
|
||||
|
||||
<p align="center">
|
||||
<img src="http://shiny.rstudio-staging.com/images/render-table.png" alt="render-table" width="75%"/>
|
||||
</p>
|
||||
|
||||
For more, read our [short article](http://shiny.rstudio-staging.com/articles/render-table.html) about this update, experiment with all the new features in this [demo app](https://gallery.shinyapps.io/109-render-table/), or check out the [reference documentation](http://shiny.rstudio-staging.com/reference/shiny/latest/renderTable.html).
|
||||
|
||||
## Full changelog
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Progress indicators can now either use the new notification API, using `style = "notification"` (default), or be displayed with the previous styling, using `style = "old"`. You can also call `shinyOptions(progress.style = "old")` in the server function to make all progress indicators use the old styling. Note that if you had customized your progress indicators with additional CSS, you'll need to use the old style if you want your UI to look the same ([#1160](https://github.com/rstudio/shiny/pull/1160) and [#1329](https://github.com/rstudio/shiny/pull/1329)).
|
||||
|
||||
* Closed [#1161](https://github.com/rstudio/shiny/issues/1161): Deprecated the `position` argument to `tabsetPanel()` since Bootstrap 3 stopped supporting this feature.
|
||||
|
||||
* The long-deprecated ability to pass a `func` argument to many of the `render` functions has been removed.
|
||||
|
||||
### New features
|
||||
|
||||
* Added the ability to bookmark and restore application state. (main PR: [#1209](https://github.com/rstudio/shiny/pull/1209))
|
||||
|
||||
* Added a new notification API. From R, there are new functions `showNotification` and `hideNotification`. From JavaScript, there is a new `Shiny.notification` object that controls notifications. ([#1141](https://github.com/rstudio/shiny/pull/1141))
|
||||
|
||||
* Progress indicators now use the notification API. ([#1160](https://github.com/rstudio/shiny/pull/1160))
|
||||
|
||||
* Added the ability for the client browser to reconnect to a new session on the server, by setting `session$allowReconnect(TRUE)`. This requires a version of Shiny Server that supports reconnections. ([#1074](https://github.com/rstudio/shiny/pull/1074))
|
||||
|
||||
* Added modal dialogs. ([#1157](https://github.com/rstudio/shiny/pull/1157))
|
||||
|
||||
* Added insertUI and removeUI functions to be able to add and remove chunks of UI, standalone, and all independent of one another. ([#1174](https://github.com/rstudio/shiny/pull/1174) and [#1189](https://github.com/rstudio/shiny/pull/1189))
|
||||
|
||||
* Improved `renderTable()` function to make the tables look prettier and also provide the user with a lot more parameters to customize their tables with. ([#1129](https://github.com/rstudio/shiny/pull/1129))
|
||||
|
||||
* Added support for the `pool` package (use Shiny's timer/scheduler). ([#1226](https://github.com/rstudio/shiny/pull/1226))
|
||||
|
||||
### Minor new features and improvements
|
||||
|
||||
* Added `cancelOutput` argument to `req()`. This causes the currently executing reactive to cancel its execution, and leave its previous state alone (as opposed to clearing the output). ([#1272](https://github.com/rstudio/shiny/pull/1272))
|
||||
|
||||
* `Display: Showcase` now displays the .js, .html and .css files in the `www` directory by default. In order to use showcase mode and not display these, include a new line in your Description file: `IncludeWWW: False`. ([#1185](https://github.com/rstudio/shiny/pull/1185))
|
||||
|
||||
* Added an error sanitization option: `options(shiny.sanitize.errors = TRUE)`. By default, this option is `FALSE`. When `TRUE`, normal errors will be sanitized, displaying only a generic error message. This changes the look of an app when errors are printed (but the console log remains the same). ([#1156](https://github.com/rstudio/shiny/pull/1156))
|
||||
|
||||
* Added the option of passing arguments to an `xxxOutput()` function through the corresponding `renderXXX()` function via an `outputArgs` parameter to the latter. This is only valid for snippets of Shiny code in an interactive `runtime: shiny` Rmd document (never for full apps, even if embedded in an Rmd). ([#1443](https://github.com/rstudio/shiny/pull/1143))
|
||||
|
||||
* Added `updateActionButton()` function, so the user can change an `actionButton`'s (or `actionLink`'s) label and/or icon. It also checks that the icon argument (for both creation and updating of a button) is valid and throws a warning otherwise. ([#1134](https://github.com/rstudio/shiny/pull/1134))
|
||||
|
||||
* Added code diagnostics: if there is an error parsing ui.R, server.R, app.R, or global.R, Shiny will search the code for missing commas, extra commas, and unmatched braces, parens, and brackets, and will print out messages pointing out those problems. ([#1126](https://github.com/rstudio/shiny/pull/1126))
|
||||
|
||||
* Added support for horizontal dividers in `navbarMenu`. ([#1147](https://github.com/rstudio/shiny/pull/1147))
|
||||
|
||||
* Added `placeholder` option to `passwordInput`. ([#1152](https://github.com/rstudio/shiny/pull/1152))
|
||||
|
||||
* Added `session$resetBrush(brushId)` (R) and `Shiny.resetBrush(brushId)` (JS) to programatically clear brushes from `imageOutput`/`plotOutput`. ([#1197](https://github.com/rstudio/shiny/pull/1197))
|
||||
|
||||
* Added textAreaInput. (thanks, [@nuno-agostinho](https://github.com/nuno-agostinho)! [#1300](https://github.com/rstudio/shiny/pull/1300))
|
||||
|
||||
* Added `session$sendBinaryMessage(type, message)` method for sending custom binary data to the client. See `?session`. (thanks, [@daef](https://github.com/daef)! [#1316](https://github.com/rstudio/shiny/pull/1316) and [#1320](https://github.com/rstudio/shiny/pull/1320))
|
||||
|
||||
* Almost all code examples now have a runnable example with `shinyApp()`, so that users can run the examples and see them in action. ([#1158](https://github.com/rstudio/shiny/pull/1158))
|
||||
|
||||
* When resized, plots are drawn with `replayPlot()`, instead of re-executing all plotting code. This results in faster plot rendering. ([#1112](https://github.com/rstudio/shiny/pull/1112))
|
||||
|
||||
* Exported the `isTruthy()` function. (part of PR [#1272](https://github.com/rstudio/shiny/pull/1272))
|
||||
|
||||
* Reactive log now shows elapsed time for reactives and observers. ([#1132](https://github.com/rstudio/shiny/pull/1132))
|
||||
|
||||
* Nodes in the reactlog visualization are now sticky if the user drags them. ([#1283](https://github.com/rstudio/shiny/pull/1283))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed [#1350](https://github.com/rstudio/shiny/issues/1350): Highlighting of reactives didn't work in showcase mode.
|
||||
|
||||
* Fixed [#1331](https://github.com/rstudio/shiny/issues/1331): `renderPlot()` now correctly records and replays plots when `execOnResize = FALSE`.
|
||||
|
||||
* `updateDateInput()` and `updateDateRangeInput()` can now clear the date input fields. (thanks, [@gaborcsardi](https://github.com/gaborcsardi)! [#1299](https://github.com/rstudio/shiny/pull/1299), [#1315](https://github.com/rstudio/shiny/pull/1315) and [#1317](https://github.com/rstudio/shiny/pull/1317))
|
||||
|
||||
* Fixed [#561](https://github.com/rstudio/shiny/issues/561): DataTables previously might pop up a warning when the data was updated extremely frequently.
|
||||
|
||||
* Fixed [#776](https://github.com/rstudio/shiny/issues/776): In some browsers, plots sometimes flickered when updated.
|
||||
|
||||
* Fixed [#543](https://github.com/rstudio/shiny/issues/543) and [#855](https://github.com/rstudio/shiny/issues/855): When `navbarPage()` had a `navbarMenu()` as the first item, it did not automatically select an item.
|
||||
|
||||
* Fixed [#970](https://github.com/rstudio/shiny/issues/970): `navbarPage()` previously did not have an option to set the selected tab.
|
||||
|
||||
* Fixed [#1253](https://github.com/rstudio/shiny/issues/1253): Memory could leak when an observer was destroyed without first being invalidated.
|
||||
|
||||
* Fixed [#931](https://github.com/rstudio/shiny/issues/931): Nested observers could leak memory.
|
||||
|
||||
* Fixed [#1144](https://github.com/rstudio/shiny/issues/1144): `updateRadioButton()` and `updateCheckboxGroupInput()` broke controls when used in modules (thanks, [@sipemu](https://github.com/sipemu)!).
|
||||
|
||||
* Fixed [#1093](https://github.com/rstudio/shiny/issues/1093): `updateRadioButtons()` and `updateCheckboxGroupInput()` didn't work if `choices` was numeric vector.
|
||||
|
||||
* Fixed [#1122](https://github.com/rstudio/shiny/issues/1122): `downloadHandler()` popped up empty browser window if the file wasn't present. It now gives a 404 error code.
|
||||
|
||||
* Fixed [#1278](https://github.com/rstudio/shiny/issues/1278): Reactive system was being flushed too often (usually this just means a more-expensive no-op than necessary).
|
||||
|
||||
* Fixed [#803](https://github.com/rstudio/shiny/issues/803) and [#1179](https://github.com/rstudio/shiny/issues/1179): handling malformed dates in `dateInput` and `updateDateInput()`.
|
||||
|
||||
* Fixed [#1257](https://github.com/rstudio/shiny/issues/1257): `updateSelectInput()` didn't work correctly in IE 11 and Edge.
|
||||
|
||||
* Fixed [#971](https://github.com/rstudio/shiny/issues/971): `runApp()` would give confusing error if `port` was not numeric.
|
||||
|
||||
* Shiny now avoids using ports that Chrome deems unsafe. ([#1222](https://github.com/rstudio/shiny/pull/1222))
|
||||
|
||||
* Added workaround for quartz graphics device resolution bug, where resolution is hard-coded to 72 ppi.
|
||||
|
||||
### Library updates
|
||||
|
||||
* Updated to ion.RangeSlider 2.1.2.
|
||||
|
||||
* Updated to Font Awesome 4.6.3.
|
||||
|
||||
* Updated to Bootstrap 3.3.7.
|
||||
|
||||
* Updated to jQuery 1.12.4.
|
||||
|
||||
|
||||
shiny 0.13.2
|
||||
============
|
||||
|
||||
* Updated documentation for `htmlTemplate`.
|
||||
|
||||
|
||||
shiny 0.13.1
|
||||
============
|
||||
|
||||
* `flexCol` did not work on RStudio for Windows or Linux.
|
||||
|
||||
* Fixed RStudio debugger integration.
|
||||
|
||||
* BREAKING CHANGE: The long-deprecated ability to pass functions (rather than expressions) to reactive() and observe() has finally been removed.
|
||||
|
||||
|
||||
shiny 0.13.0
|
||||
============
|
||||
|
||||
* Fixed #962: plot interactions did not work with the development version of ggplot2 (after ggplot2 1.0.1).
|
||||
|
||||
* Fixed #902: the `drag_drop` plugin of the selectize input did not work.
|
||||
|
||||
* Fixed #933: `updateSliderInput()` does not work when only the label is updated.
|
||||
|
||||
* Multiple imageOutput/plotOutput calls can now share the same brush id. Shiny will ensure that performing a brush operation will clear any other brush with the same id.
|
||||
|
||||
* Added `placeholder` option to `textInput`.
|
||||
|
||||
* Improved support for Unicode characters on Windows (#968).
|
||||
|
||||
* Fixed bug in `selectInput` and `selectizeInput` where values with double quotes were not properly escaped.
|
||||
|
||||
* `runApp()` can now take a path to any .R file that yields a `shinyApp` object; previously, the path had to be a directory that contained an app.R file (or server.R if using separately defined server and UI). Similarly, introduced `shinyAppFile()` function which creates a `shinyApp` object for an .R file path, just as `shinyAppDir()` does for a directory path.
|
||||
|
||||
* Introduced Shiny Modules, which are designed to 1) simplify the reuse of Shiny UI/server logic and 2) make authoring and maintaining complex Shiny apps much easier. See the article linked from `?callModule`.
|
||||
|
||||
* `invalidateLater` and `reactiveTimer` no longer require an explicit `session` argument; the default value uses the current session.
|
||||
|
||||
* Added `session$reload()` method, the equivalent of hitting the browser's Reload button.
|
||||
|
||||
* Added `shiny.autoreload` option, which will automatically cause browsers to reload whenever Shiny app files change on disk. This is intended to shorten the feedback cycle when tweaking UI code.
|
||||
|
||||
* Errors are now printed with stack traces! This should make it tremendously easier to track down the causes of errors in Shiny. Try it by calling `stop("message")` from within an output, reactive, or observer. Shiny itself adds a lot of noise to the call stack, so by default, it attempts to hide all but the relevant levels of the call stack. You can turn off this behavior by setting `options(shiny.fullstacktrace=TRUE)` before or during app startup.
|
||||
|
||||
* Fixed #1018: the selected value of a selectize input is guaranteed to be selected in server-side mode.
|
||||
|
||||
* Added `req` function, which provides a simple way to prevent a reactive, observer, or output from executing until all required inputs and values are available. (Similar functionality has been available for a while using validate/need, but req provides a much simpler and more direct interface.)
|
||||
|
||||
* Improve stability with Shiny Server when many subapps are used, by deferring the loading of subapp iframes until a connection has first been established with the server.
|
||||
|
||||
* Upgrade to Font Awesome 4.5.0.
|
||||
|
||||
* Upgraded to Bootstrap 3.3.5.
|
||||
|
||||
* Upgraded to jQuery 1.12.4
|
||||
|
||||
* Switched to an almost-complete build of jQuery UI with the exception of the datepicker extension, which conflicts with Shiny's date picker.
|
||||
|
||||
* Added `fillPage` function, an alternative to `fluidPage`, `fixedPage`, etc. that is designed for apps that fill the entire available page width/height.
|
||||
|
||||
* Added `fillRow` and `fillCol` functions, for laying out proportional grids in `fillPage`. For modern browsers only.
|
||||
|
||||
* Added `runGadget`, `paneViewer`, `dialogViewer`, and `browserViewer` functions to support Shiny Gadgets. More detailed docs about gadgets coming soon.
|
||||
|
||||
* Added support for the new htmltools 0.3 feature `htmlTemplate`. It's now possible to use regular HTML markup to design your UI, but still use R expressions to define inputs, outputs, and HTML widgets.
|
||||
|
||||
|
||||
shiny 0.12.2
|
||||
============
|
||||
|
||||
* GitHub changed URLs for gists from .tar.gz to .zip, so `runGist` was updated to work with the new URLs.
|
||||
|
||||
* Callbacks from the session object are now guaranteed to execute in the order in which registration occurred.
|
||||
|
||||
* Minor bugs in sliderInput's animation behavior have been fixed. (#852)
|
||||
|
||||
* Updated to ion.rangeSlider to 2.0.12.
|
||||
|
||||
* Added `shiny.minified` option, which controls whether the minified version of shiny.js is used. Setting it to FALSe can be useful for debugging. (#826, #850)
|
||||
|
||||
* Fixed an issue for outputting plots from ggplot objects which also have an additional class whose print method takes precedence over `print.ggplot`. (#840, 841)
|
||||
|
||||
* Added `width` option to Shiny's input functions. (#589, #834)
|
||||
|
||||
* Added two alias functions of `updateTabsetPanel()` to update the selected tab: `updateNavbarPage()` and `updateNavlistPanel()`. (#881)
|
||||
|
||||
* All non-base functions are now explicitly namespaced, to pass checks in R-devel.
|
||||
|
||||
* Shiny now correctly handles HTTP HEAD requests. (#876)
|
||||
|
||||
|
||||
shiny 0.12.1
|
||||
============
|
||||
|
||||
* Fixed an issue where unbindAll() causes subsequent bindAll() to be ignored for previously bound outputs. (#856)
|
||||
|
||||
* Undeprecate `dataTableOutput` and `renderDataTable`, which had been deprecated in favor of the new DT package. The DT package is a bit too new and has a slightly different API, we were too hasty in deprecating the existing Shiny functions.
|
||||
|
||||
|
||||
shiny 0.12.0
|
||||
============
|
||||
|
||||
In addition to the changes listed below (in the *Full Changelog* section), there is an infrastructure change that could affect existing Shiny apps.
|
||||
|
||||
### JSON serialization
|
||||
|
||||
In Shiny 0.12.0, we've switched from RJSONIO to jsonlite. For the vast majority of users, this will result in no noticeable changes; however, if you use any packages in your Shiny apps which rely on the [htmlwidgets](http://www.htmlwidgets.org/), you will also need to update htmlwidgets to 0.4.0. Both of these packages will issue a message when loaded, if the other package needs to be upgraded.
|
||||
|
||||
POSIXt objects are now serialized to JSON in UTC8601 format (like
|
||||
"2015-03-20T20:00:00Z"), instead of as seconds from the epoch. If you have a Shiny app which uses `sendCustomMessage()` to send datetime (POSIXt) objects, then you may need to modify your Javascript code to receive time data in this format.
|
||||
|
||||
### A note about Data Tables
|
||||
|
||||
Shiny 0.12.0 deprecated Shiny's dataTableOutput and renderDataTable functions and instructed you to migrate to the nascent [DT](https://rstudio.github.io/DT/) package instead. (We'll talk more about DT in a future blog post.) User feedback has indicated this transition was too sudden and abrupt, so we've undeprecated these functions in 0.12.1. We'll continue to support these functions until DT has had more time to mature.
|
||||
|
||||
## Full Changelog
|
||||
|
||||
* Switched from RJSONIO to jsonlite. This improves consistency and speed when converting between R data structures and JSON. One notable change is that POSIXt objects are now serialized to JSON in UTC8601 format (like "2015-03-20T20:00:00Z"), instead of as seconds from the epoch).
|
||||
|
||||
* In addition to the existing support for clicking and hovering on plots created by base graphics, added support for double-clicking and brushing. (#769)
|
||||
|
||||
* Added support for clicking, hovering, double-clicking, and brushing for plots created by ggplot2, including support for facets. (#802)
|
||||
|
||||
* Added `nearPoints` and `brushedPoints` functions for easily selecting rows of data that are clicked/hovered, or brushed. (#802)
|
||||
|
||||
* Added `shiny.port` option. If this is option is set, `runApp()` will listen on this port by default. (#756)
|
||||
|
||||
* `runUrl`, `runGist`, and `runGitHub` now can save downloaded applications, with the `destdir` argument. (#688)
|
||||
|
||||
* Restored ability to set labels for `selectInput`. (#741)
|
||||
|
||||
* Travis continuous integration now uses Travis's native R support.
|
||||
|
||||
* Fixed encoding issue when the server receives data from the client browser. (#742)
|
||||
|
||||
* The `session` object now has class `ShinySession`, making it easier to test whether an object is indeed a session object. (#720, #746)
|
||||
|
||||
* Fix JavaScript error when an output appears in nested uiOutputs. (Thanks, Gregory Zhang. #749)
|
||||
|
||||
* Eliminate delay on receiving new value when `updateSliderInput(value=...)` is called.
|
||||
|
||||
* Updated to DataTables (Javascript library) 1.10.5.
|
||||
|
||||
* Fixed downloading of files that have no filename extension. (#575, #753)
|
||||
|
||||
* Fixed bug where nested UI outputs broke outputs. (#749, #750)
|
||||
|
||||
* Removed unneeded HTML ID attributes for `checkboxGroupInputs` and `radioButtons`. (#684)
|
||||
|
||||
* Fixed bug where checkboxes were still active even after `Shiny.unbindAll()` was called. (#206)
|
||||
|
||||
* The server side selectize input will load the first 1000 options by default before users start to type and search in the box. (#823)
|
||||
|
||||
* renderDataTable() and dataTableOutput() have been deprecated in shiny and will be removed in future versions of shiny. Please use the DT package instead: http://rstudio.github.io/DT/ (#807)
|
||||
|
||||
|
||||
shiny 0.11.1
|
||||
============
|
||||
|
||||
* Major client-side performance improvements for pages that have many conditionalPanels, tabPanels, and plotOutputs. (#693, #717, #723)
|
||||
|
||||
* `tabPanel`s now use the `title` for `value` by default. This fixes a bug in which an icon in the title caused problems with a conditionalPanel's test condition. (#725, #728)
|
||||
|
||||
* `selectInput` now has a `size` argument to control the height of the input box. (#729)
|
||||
|
||||
* `navbarPage` no longer includes a first row of extra whitespace when `header=NULL`. (#722)
|
||||
|
||||
* `selectInput`s now use Bootstrap styling when `selectize=FALSE`. (#724)
|
||||
|
||||
* Better vertical spacing of label for checkbox groups and radio buttons.
|
||||
|
||||
* `selectInput` correctly uses width for both selectize and non-selectize inputs. (#702)
|
||||
|
||||
* The wrapper tag generated by `htmlOutput` and `uiOutput` can now be any type of HTML tag, instead of just span and div. Also, custom classes are now allowed on the tag. (#704)
|
||||
|
||||
* Slider problems in IE 11 and Chrome on touchscreen-equipped Windows computers have been fixed. (#700)
|
||||
|
||||
* Sliders now work correctly with draggable panels. (#711)
|
||||
|
||||
* Fixed arguments in `fixedPanel`. (#709)
|
||||
|
||||
* downloadHandler content callback functions are now invoked with a temp file name that has the same extension as the final filename that will be used by the download. This is to deal with the fact that some file writing functions in R will auto-append the extension for their file type (pdf, zip).
|
||||
|
||||
|
||||
shiny 0.11
|
||||
==========
|
||||
|
||||
Shiny 0.11 switches away from the Bootstrap 2 web framework to the next version, Bootstrap 3. This is in part because Bootstrap 2 is no longer being developed, and in part because it allows us to tap into the ecosystem of Bootstrap 3 themes.
|
||||
|
||||
|
||||
### Known issues for migration
|
||||
|
||||
* In Bootstrap 3, images in `<img>` tags are no longer automatically scaled to the width of their container. If you use `img()` in your UI code, or `<img>` tags in your raw HTML source, it's possible that they will be too large in the new version of Shiny. To address this you can add the `img-responsive` class:
|
||||
|
||||
```r
|
||||
img(src = "picture.png", class = "img-responsive")
|
||||
```
|
||||
|
||||
The R code above will generate the following HTML:
|
||||
|
||||
```html
|
||||
<img src="picture.png" class="img-responsive">
|
||||
```
|
||||
|
||||
|
||||
* The sliders have been replaced. Previously, Shiny used the [jslider](https://github.com/egorkhmelev/jslider) library, but now it uses [ion.RangeSlider](https://github.com/IonDen/ion.rangeSlider). The new sliders have an updated appearance, and they have allowed us to fix many long-standing interface issues with the sliders.
|
||||
|
||||
* The `sliderInput()` function no longer uses the `format` or `locale` options. Instead, you can use `pre`, `post`, and `sep` options to control the prefix, postfix, and thousands separator.
|
||||
|
||||
|
||||
* `updateSliderInput()` can now control the min, max, value, and step size of a slider. Previously, only the value could be controlled this way, and if you wanted to change other values, you needed to use Shiny's dynamic UI.
|
||||
|
||||
|
||||
* If in your HTML you are using custom CSS classes that are specific to Bootstrap, you may need to update them for Bootstrap 3. See the Bootstrap [migration guide](http://getbootstrap.com/migration/).
|
||||
|
||||
|
||||
If you encounter other migration issues, please let us know on the [shiny-discuss](https://groups.google.com/forum/#!forum/shiny-discuss) mailing list, or on the Shiny [issue tracker](https://github.com/rstudio/shiny/issues).
|
||||
|
||||
### Using shinybootstrap2
|
||||
|
||||
If you would like to use Shiny 0.11 with Bootstrap 2, you can use the **shinybootstrap2** package. Installation and usage instructions are on available on the [project page](https://github.com/rstudio/shinybootstrap2). We recommend that you do this only as a temporary solution because future development on Shiny will use Bootstrap 3.
|
||||
|
||||
### Installing an older version of Shiny
|
||||
|
||||
If you want to install a specific version of Shiny other than the latest CRAN release, you can use the `install_version()` function from devtools:
|
||||
|
||||
```r
|
||||
# Install devtools if you don't already have it:
|
||||
install.package("devtools")
|
||||
|
||||
# Install the last version of Shiny prior to 0.11
|
||||
devtools::install_version("shiny", "0.10.2.2")
|
||||
```
|
||||
|
||||
### Themes
|
||||
|
||||
Along with the release of Shiny 0.11, we've packaged up some Bootstrap 3 themes in the [shinythemes](http://rstudio.github.io/shinythemes/) package. This package makes it easy to use Bootstrap themes with Shiny.
|
||||
|
||||
## Full Changelog
|
||||
|
||||
* Changed sliders from jquery-slider to ion.rangeSlider. These sliders have an improved appearance, support updating more properties from the server, and can be controlled with keyboard input.
|
||||
|
||||
* Switched from Bootstrap 2 to Bootstrap 3. For most users, this will work seamlessly, but some users may need to use the shinybootstrap2 package for backward compatibility.
|
||||
|
||||
* The UI of a Shiny app can now have a body tag. This is useful for CSS templates that use classes on the body tag.
|
||||
|
||||
* `actionButton` and `actionLink` now pass their `...` arguments to the underlying tag function. (#607)
|
||||
|
||||
* Added `observeEvent` and `eventReactive` functions for clearer, more concise handling of `actionButton`, plot clicks, and other naturally-imperative inputs.
|
||||
|
||||
* Errors that happen in reactives no longer prevent any remaining pending observers from executing. It is also now possible for users to control how errors are handled, with the 'shiny.observer.error' global option. (#603, #604)
|
||||
|
||||
* Added an `escape` argument to `renderDataTable()` to escape the HTML entities in the data table for security reasons. This might break tables from previous versions of shiny that use raw HTML in the table content, and the old behavior can be brought back by `escape = FALSE` if you are aware of the security implications. (#627)
|
||||
|
||||
* Changed the URI encoding/decoding functions internally to use `encodeURI()`, `encodeURIComponent()`, and `decodeURIComponent()` from the httpuv package instead of `utils::URLencode()` and `utils::URLdecode()`. (#630)
|
||||
|
||||
* Shiny's web assets are now minified.
|
||||
|
||||
* The default reactive domain is now available in event handler functions. (#669)
|
||||
|
||||
* Password input fields can now be used, with `passwordInput()`. (#672)
|
||||
|
||||
|
||||
shiny 0.10.2.2
|
||||
==============
|
||||
|
||||
* Remove use of `rstudio::viewer` in a code example, for R CMD check.
|
||||
|
||||
|
||||
shiny 0.10.2.1
|
||||
==============
|
||||
|
||||
* Changed some examples to use \donttest instead of \dontrun.
|
||||
|
||||
|
||||
shiny 0.10.2
|
||||
============
|
||||
|
||||
* The minimal version of R required for the shiny package is 3.0.0 now.
|
||||
|
||||
* Shiny apps can now consist of a single file, app.R, instead of ui.R and server.R.
|
||||
|
||||
* Upgraded DataTables from 1.9.4 to 1.10.2. This might be a breaking change if you have customized the DataTables options in your apps. (More info: https://github.com/rstudio/shiny/pull/558)
|
||||
|
||||
* File uploading via `fileInput()` works for Internet Explorer 8 and 9 now. Note: IE8/9 do not support multiple files from a single file input. If you need to upload multiple files, you have to use one file input for each file.
|
||||
|
||||
* Switched away from reference classes to R6.
|
||||
|
||||
* Reactive log performance has been greatly improved.
|
||||
|
||||
* Added `Progress` and `withProgress`, to display the progress of computation on the client browser.
|
||||
|
||||
* Fixed #557: updateSelectizeInput(choices, server = TRUE) did not work when `choices` is a character vector.
|
||||
|
||||
* Searching in DataTables is case-insensitive and the search strings are not treated as regular expressions by default now. If you want case-sensitive searching or regular expressions, you can use the configuration options `search$caseInsensitive` and `search$regex`, e.g. `renderDataTable(..., options = list(search = list(caseInsensitve = FALSE, regex = TRUE)))`.
|
||||
|
||||
* Added support for `htmltools::htmlDependency`'s new `attachment` parameter to `renderUI`/`uiOutput`.
|
||||
|
||||
* Exported `createWebDependency`. It takes an `htmltools::htmlDependency` object and makes it available over Shiny's built-in web server.
|
||||
|
||||
* Custom output bindings can now render `htmltools::htmlDependency` objects at runtime using `Shiny.renderDependencies()`.
|
||||
|
||||
* Fixes to rounding behavior of sliderInput. (#301, #502)
|
||||
|
||||
* Updated selectize.js to version 0.11.2. (#596)
|
||||
|
||||
* Added `position` parameter to `navbarPage`.
|
||||
|
||||
|
||||
shiny 0.10.1
|
||||
============
|
||||
|
||||
* Added Unicode support for Windows. Shiny apps running on Windows must use the UTF-8 encoding for ui.R and server.R (also the optional global.R) if they contain non-ASCII characters. See this article for details and examples: http://shiny.rstudio.com/gallery/unicode-characters.html (#516)
|
||||
|
||||
* `runGitHub()` also allows the 'username/repo' syntax now, which is equivalent to `runGitHub('repo', 'username')`. (#427)
|
||||
|
||||
* `navbarPage()` now accepts a `windowTitle` parameter to set the web browser page title to something other than the title displayed in the navbar.
|
||||
|
||||
* Added an `inline` argument to `textOutput()`, `imageOutput()`, `plotOutput()`, and `htmlOutput()`. When `inline = TRUE`, these outputs will be put in `span()` instead of the default `div()`. This occurs automatically when these outputs are created via the inline expressions (e.g. `r renderText(expr)`) in R Markdown documents. See an R Markdown example at http://shiny.rstudio.com/gallery/inline-output.html (#512)
|
||||
|
||||
* Added support for option groups in the select/selectize inputs. When the `choices` argument for `selectInput()`/`selectizeInput()` is a list of sub-lists and any sub-list is of length greater than 1, the HTML tag `<optgroup>` will be used. See an example at http://shiny.rstudio.com/gallery/option-groups-for-selectize-input.html (#542)
|
||||
|
||||
|
||||
shiny 0.10.0
|
||||
============
|
||||
|
||||
* BREAKING CHANGE: By default, observers now terminate themselves if they were created during a session and that session ends. See ?domains for more details.
|
||||
|
||||
* Shiny can now be used in R Markdown v2 documents, to create "Shiny Docs": reports and presentations that combine narrative, statically computed output, and fully dynamic inputs and outputs. For more info, including examples, see http://rmarkdown.rstudio.com/authoring_shiny.html.
|
||||
|
||||
* The `session` object that can be passed into a server function (e.g. shinyServer(function(input, output, session) {...})) is now documented: see `?session`.
|
||||
|
||||
* Most inputs can now accept `NULL` label values to omit the label altogether.
|
||||
|
||||
* New `actionLink` input control; like `actionButton`, but with the appearance of a normal link.
|
||||
|
||||
* `renderPlot` now calls `print` on its result if it's visible (i.e. no more explicit `print()` required for ggplot2).
|
||||
|
||||
* Introduced Shiny app objects (see `?shinyApp`). These essentially replace the little-advertised ability for `runApp` to take a `list(ui=..., server=...)` as the first argument instead of a directory (though that ability remains for backward compatibility). Unlike those lists, Shiny app objects are tagged with class `shiny.appobj` so they can be run simply by printing them.
|
||||
|
||||
* Added `maskReactiveContext` function. It blocks the current reactive context, to evaluate expressions that shouldn't use reactive sources directly. (This should not be commonly needed.)
|
||||
|
||||
* Added `flowLayout`, `splitLayout`, and `inputPanel` functions for putting UI elements side by side. `flowLayout` lays out its children in a left-to-right, top-to-bottom arrangement. `splitLayout` evenly divides its horizontal space among its children (or unevenly divides if `cellWidths` argument is provided). `inputPanel` is like `flowPanel`, but with a light grey background, and is intended to be used to encapsulate small input controls wherever vertical space is at a premium.
|
||||
|
||||
* Added `serverInfo` to obtain info about the Shiny Server if the app is served through it.
|
||||
|
||||
* Added an `inline` argument (TRUE/FALSE) in `checkboxGroupInput()` and `radioButtons()` to allow the horizontal layout (inline = TRUE) of checkboxes or radio buttons. (Thanks, @saurfang, #481)
|
||||
|
||||
* `sliderInput` and `selectizeInput`/`selectInput` now use a standard horizontal size instead of filling up all available horizontal space. Pass `width="100%"` explicitly for the old behavior.
|
||||
|
||||
* Added the `updateSelectizeInput()` function to make it possible to process searching on the server side (i.e. using R), which can be much faster than the client side processing (i.e. using HTML and JavaScript). See the article at http://shiny.rstudio.com/articles/selectize.html for a detailed introduction.
|
||||
|
||||
* Fixed a bug of renderDataTable() when the data object only has 1 row and 1 column. (Thanks, ZJ Dai, #429)
|
||||
|
||||
* `renderPrint` gained a new argument 'width' to control the width of the text output, e.g. renderPrint({mtcars}, width = 40).
|
||||
|
||||
* Fixed #220: the zip file for a directory created by some programs may not have the directory name as its first entry, in which case runUrl() can fail. (#220)
|
||||
|
||||
* `runGitHub()` can also take a value of the form "username/repo" in its first argument, e.g. both runGitHub("shiny_example", "rstudio") and runGitHub("rstudio/shiny_example") are valid ways to run the GitHub repo.
|
||||
|
||||
|
||||
shiny 0.9.1
|
||||
===========
|
||||
|
||||
* Fixed warning 'Error in Context$new : could not find function "loadMethod"' that was happening to dependent packages on "R CMD check".
|
||||
|
||||
|
||||
shiny 0.9.0
|
||||
===========
|
||||
|
||||
* BREAKING CHANGE: Added a `host` parameter to runApp() and runExample(), which defaults to the shiny.host option if it is non-NULL, or "127.0.0.1" otherwise. This means that by default, Shiny applications can only be accessed on the same machine from which they are served. To allow other clients to connect, as in previous versions of Shiny, use "0.0.0.0" (or the IP address of one of your network interfaces, if you care to be explicit about it).
|
||||
|
||||
* Added a new function `selectizeInput()` to use the JavaScript library selectize.js (https://github.com/brianreavis/selectize.js), which extends the basic select input in many aspects.
|
||||
|
||||
* The `selectInput()` function also gained a new argument `selectize = TRUE` to makes use of selectize.js by default. If you want to revert back to the original select input, you have to call selectInput(..., selectize = FALSE).
|
||||
|
||||
* Added Showcase mode, which displays the R code for an app right in the app itself. You can invoke Showcase mode by passing `display.mode="showcase"` to the `runApp()` function. Or, if an app is designed to run in Showcase mode by default, add a DESCRIPTION file in the app dir with Title, Author, and License fields; with "Type: Shiny"; and with "DisplayMode: Showcase".
|
||||
|
||||
* Upgraded to Bootstrap 2.3.2 and jQuery 1.11.0.
|
||||
|
||||
* Make `tags$head()` and `singleton()` behave correctly when used with `renderUI()` and `uiOutput()`. Previously, "hoisting content to the head" and "only rendering items a single time" were features that worked only when the page was initially loading, not in dynamic rendering.
|
||||
|
||||
* Files are now sourced with the `keep.source` option, to help with debugging and profiling.
|
||||
|
||||
* Support user-defined input parsers for data coming in from JavaScript using the parseShinyInput method.
|
||||
|
||||
* Fixed the bug #299: renderDataTable() can deal with 0-row data frames now. (reported by Harlan Harris)
|
||||
|
||||
* Added `navbarPage()` and `navbarMenu()` functions to create applications with multiple top level panels.
|
||||
|
||||
* Added `navlistPanel()` function to create layouts with a bootstrap navlist on the left and tabPanels on the right
|
||||
|
||||
* Added `type` parameter to `tabsetPanel()` to enable the use of pill style tabs in addition to the standard ones.
|
||||
|
||||
* Added `position` paramter to `tabsetPanel()` to enable positioning of tabs above, below, left, or right of tab content.
|
||||
|
||||
* Added `fluidPage()` and `fixedPage()` functions as well as related row and column layout functions for creating arbitrary bootstrap grid layouts.
|
||||
|
||||
* Added `hr()` builder function for creating horizontal rules.
|
||||
|
||||
* Automatically concatenate duplicate attributes in tag definitions
|
||||
|
||||
* Added `responsive` parameter to page building functions for opting-out of bootstrap responsive css.
|
||||
|
||||
* Added `theme` parameter to page building functions for specifying alternate bootstrap css styles.
|
||||
|
||||
* Added `icon()` function for embedding icons from the [font awesome](http://fontawesome.io/) icon library
|
||||
|
||||
* Added `makeReactiveBinding` function to turn a "regular" variable into a reactive one (i.e. reading the variable makes the current reactive context dependent on it, and setting the variable is a source of reactivity).
|
||||
|
||||
* Added a function `withMathJax()` to include the MathJax library in an app.
|
||||
|
||||
* The argument `selected` in checkboxGroupInput(), selectInput(), and radioButtons() refers to the value(s) instead of the name(s) of the argument `choices` now. For example, the value of the `selected` argument in selectInput(..., choices = c('Label 1' = 'x1', 'Label 2' = 'x2'), selected = 'Label 2') must be updated to 'x2', although names/labels will be automatically converted to values internally for backward compatibility. The same change applies to updateCheckboxGroupInput(), updateSelectInput(), and updateRadioButtons() as well. (#340)
|
||||
|
||||
* Now it is possible to only update the value of a checkbox group, select input, or radio buttons using the `selected` argument without providing the `choices` argument in updateCheckboxGroupInput(), updateSelectInput(), and updateRadioButtons(), respectively. (#340)
|
||||
|
||||
* Added `absolutePanel` and `fixedPanel` functions for creating absolute- and fixed-position panels. They can be easily made user-draggable by specifying `draggable = TRUE`.
|
||||
|
||||
* For the `options` argument of the function `renderDataTable()`, we can pass literal JavaScript code to the DataTables library via `I()`. This makes it possible to use any JavaScript object in the options, e.g. a JavaScript function (which is not supported in JSON). See `?renderDataTable` for details and examples.
|
||||
|
||||
* DataTables also works under IE8 now.
|
||||
|
||||
* Fixed a bug in DataTables pagination when searching is turned on, which caused failures for matrices as well as empty rows when displaying data frames using renderDataTable().
|
||||
|
||||
* The `options` argument in `renderDataTable()` can also take a function that returns a list. This makes it possible to use reactive values in the options. (#392)
|
||||
|
||||
* `renderDataTable()` respects more DataTables options now: (1) either bPaginate = FALSE or iDisplayLength = -1 will disable pagination (i.e. all rows are returned from the data); besides, this means we can also use -1 in the length menu, e.g. aLengthMenu = list(c(10, 30, -1), list(10, 30, 'All')); (2) we can disable searching for individual columns through the bSearchable option, e.g. aoColumns = list(list(bSearchable = FALSE), list(bSearchable = TRUE),...) (the search box for the first column is hidden); (3) we can turn off searching entirely (for both global searching and individual columns) using the option bFilter = FALSE.
|
||||
|
||||
* Added an argument `callback` in `renderDataTable()` so that a custom JavaScript function can be applied to the DataTable object. This makes it much easier to use DataTables plug-ins.
|
||||
|
||||
* For numeric columns in a DataTable, the search boxes support lower and upper bounds now: a search query of the form "lower,upper" (without quotes) indicates the limits [lower, upper]. For a column X, this means the rows corresponding to X >= lower & X <= upper are returned. If we omit either the lower limit or the upper limit, only the other limit will be used, e.g. ",upper" means X <= upper.
|
||||
|
||||
* `updateNumericInput(value)` tries to preserve numeric precision by avoiding scientific notation when possible, e.g. 102145 is no longer rounded to 1.0214e+05 = 102140. (Thanks, Martin Loos. #401)
|
||||
|
||||
* `sliderInput()` no longer treats a label wrapped in HTML() as plain text, e.g. the label in sliderInput(..., label = HTML('<em>A Label</em>')) will not be escaped any more. (#119)
|
||||
|
||||
* Fixed #306: the trailing slash in a path could fail `addResourcePath()` under Windows. (Thanks, ZJ Dai)
|
||||
|
||||
* Dots are now legal characters for inputId/outputId. (Thanks, Kevin Lindquist. #358)
|
||||
|
||||
|
||||
shiny 0.8.0
|
||||
===========
|
||||
|
||||
* Debug hooks are registered on all user-provided functions and (reactive) expressions (e.g., in renderPlot()), which makes it possible to set breakpoints in these functions using the latest version of the RStudio IDE, and the RStudio visual debugging tools can be used to debug Shiny apps. Internally, the registration is done via installExprFunction(), which is a new function introduced in this version to replace exprToFunction() so that the registration can be automatically done.
|
||||
|
||||
* Added a new function renderDataTable() to display tables using the JavaScript library DataTables. It includes basic features like pagination, searching (global search or search by individual columns), sorting (by single or multiple columns). All these features are implemented on the R side; for example, we can use R regular expressions for searching. Besides, it also uses the Bootstrap CSS style. See the full documentation and examples in the tutorial: http://rstudio.github.io/shiny/tutorial/#datatables
|
||||
|
||||
* Added a new option `shiny.error` which can take a function as an error handler. It is called when an error occurs in an app (in user-provided code), e.g., after we set options(shiny.error = recover), we can enter a specified environment in the call stack to debug our code after an error occurs.
|
||||
|
||||
* The argument `launch.browser` in runApp() can also be a function, which takes the URL of the shiny app as its input value.
|
||||
|
||||
* runApp() uses a random port between 3000 and 8000 instead of 8100 now. It will try up to 20 ports in case certain ports are not available.
|
||||
|
||||
* Fixed a bug for conditional panels: the value `input.id` in the condition was not correctly retrieved when the input widget had a type, such as numericInput(). (reported by Jason Bryer)
|
||||
|
||||
* Fixed two bugs in plotOutput(); clickId and hoverId did not give correct coordinates in Firefox, or when the axis limits of the plot were changed. (reported by Chris Warth and Greg D)
|
||||
|
||||
* The minimal required version for the httpuv package was increased to 1.2 (on CRAN now).
|
||||
|
||||
|
||||
shiny 0.7.0
|
||||
===========
|
||||
|
||||
* Stopped sending websocket subprotocol. This fixes a compatibility issue with Google Chrome 30.
|
||||
|
||||
* The `input` and `output` objects are now also accessible via `session$input` and `session$output`.
|
||||
|
||||
* Added click and hover events for static plots; see `?plotOutput` for details.
|
||||
|
||||
* Added optional logging of the execution states of a reactive program, and tools for visualizing the log data. To use, start a new R session and call `options(shiny.reactlog=TRUE)`. Then launch a Shiny app and interact with it. Press Ctrl+F3 (or for Mac, Cmd+F3) in the browser to launch an interactive visualization of the reactivity that has occurred. See `?showReactLog` for more information.
|
||||
|
||||
* Added `includeScript()` and `includeCSS()` functions.
|
||||
|
||||
* Reactive expressions now have class="reactive" attribute. Also added `is.reactive()` and `is.reactivevalues()` functions.
|
||||
|
||||
* New `stopApp()` function, which stops an app and returns a value to the caller of `runApp()`.
|
||||
|
||||
* Added the `shiny.usecairo` option, which can be used to tell Shiny not to use Cairo for PNG output even when it is installed. (Defaults to `TRUE`.)
|
||||
|
||||
* Speed increases for `selectInput()` and `radioButtons()`, and their corresponding updater functions, for when they have many options.
|
||||
|
||||
* Added `tagSetChildren()` and `tagAppendChildren()` functions.
|
||||
|
||||
* The HTTP request object that created the websocket is now accessible from the `session` object, as `session$request`. This is a Rook-like request environment that can be used to access HTTP headers, among other things. (Note: When running in a Shiny Server environment, the request will reflect the proxy HTTP request that was made from the Shiny Server process to the R process, not the request that was made from the web browser to Shiny Server.)
|
||||
|
||||
* Fix `getComputedStyle` issue, for IE8 browser compatibility (#196). Note: Shiny Server is still required for IE8/9 compatibility.
|
||||
|
||||
* Add shiny.sharedSecret option, to require the HTTP header Shiny-Shared-Secret to be set to the given value.
|
||||
|
||||
|
||||
shiny 0.6.0
|
||||
===========
|
||||
|
||||
* `tabsetPanel()` can be directed to start with a specific tab selected.
|
||||
|
||||
* Fix bug where multiple file uploads with 3 or more files result in incorrect data.
|
||||
|
||||
* Add `withTags()` function.
|
||||
|
||||
* Add dateInput and dateRangeInput.
|
||||
|
||||
* `shinyServer()` now takes an optional `session` argument, which is used for communication with the session object.
|
||||
|
||||
* Add functions to update values of existing inputs on a page, instead of replacing them entirely.
|
||||
|
||||
* Allow listening on domain sockets.
|
||||
|
||||
* Added `actionButton()` to Shiny.
|
||||
|
||||
* The server can now send custom JSON messages to the client. On the client side, functions can be registered to handle these messages.
|
||||
|
||||
* Callbacks can be registered to be called at the end of a client session.
|
||||
|
||||
* Add ability to set priority of observers and outputs. Each priority level gets its own queue.
|
||||
|
||||
* Fix bug where the presence of a submit button would prevent sending of metadata until the button was clicked.
|
||||
|
||||
* `reactiveTimer()` and `invalidateLater()` by default no longer invalidate reactive objects after the client session has closed.
|
||||
|
||||
* Shiny apps can be run without a server.r and ui.r file.
|
||||
|
||||
|
||||
shiny 0.5.0
|
||||
===========
|
||||
|
||||
* Switch from websockets package for handling websocket connections to httpuv.
|
||||
|
||||
* New method for detecting hidden output objects. Instead of checking that height and width are 0, it checks that the object or any ancestor in the DOM has style display:none.
|
||||
|
||||
* Add `clientData` reactive values object, which carries information about the client. This includes the hidden status of output objects, height/width plot output objects, and the URL of the browser.
|
||||
|
||||
* Add `parseQueryString()` function.
|
||||
|
||||
* Add `renderImage()` function for sending arbitrary image files to the client, and its counterpart, `imageOutput()`.
|
||||
|
||||
* Add support for high-resolution (Retina) displays.
|
||||
|
||||
* Fix bug #55, where `renderTable()` would throw error with an empty data frame.
|
||||
|
||||
|
||||
shiny 0.4.1
|
||||
===========
|
||||
|
||||
* Fix bug where width and height weren't passed along properly from `reactivePlot` to `renderPlot`.
|
||||
|
||||
* Fix bug where infinite recursion would happen when `reactivePlot` was passed a function for width or height.
|
||||
|
||||
|
||||
shiny 0.4.0
|
||||
===========
|
||||
|
||||
* Added suspend/resume capability to observers.
|
||||
|
||||
* Output objects are automatically suspended when they are hidden on the user's web browser.
|
||||
|
||||
* `runGist()` accepts GitHub's new URL format, which includes the username.
|
||||
|
||||
* `reactive()` and `observe()` now take expressions instead of functions.
|
||||
|
||||
* `reactiveText()`, `reactivePlot()`, and so on, have been renamed to `renderText()`, `renderPlot()`, etc. They also now take expressions instead of functions.
|
||||
|
||||
* Fixed a bug where empty values in a numericInput were sent to the R process as 0. They are now sent as NA.
|
||||
|
||||
|
||||
shiny 0.3.1
|
||||
===========
|
||||
|
||||
* Fix issue #91: bug where downloading files did not work.
|
||||
|
||||
* Add [[<- operator for shinyoutput object, making it possible to assign values with `output[['plot1']] <- ...`.
|
||||
|
||||
* Reactive functions now preserve the visible/invisible state of their returned values.
|
||||
|
||||
|
||||
shiny 0.3.0
|
||||
===========
|
||||
|
||||
* Reactive functions are now evaluated lazily.
|
||||
|
||||
* Add `reactiveValues()`.
|
||||
|
||||
* Using `as.list()` to convert a reactivevalues object (like `input`) to a list is deprecated. The new function `reactiveValuesToList()` should be used instead.
|
||||
|
||||
* Add `isolate()`. This function is used for accessing reactive functions, without them invalidating their parent contexts.
|
||||
|
||||
* Fix issue #58: bug where reactive functions are not re-run when all items in a checkboxGroup are unchecked.
|
||||
|
||||
* Fix issue #71, where `reactiveTable()` would return blank if the first element of a data frame was NA.
|
||||
|
||||
* In `plotOutput`, better validation for CSS units when specifying width and height.
|
||||
|
||||
* `reactivePrint()` no longer displays invisible output.
|
||||
|
||||
* `reactiveText()` no longer displays printed output, only the return value from a function.
|
||||
|
||||
* The `runGitHub()` and `runUrl()` functions have been added, for running Shiny apps from GitHub repositories and zip/tar files at remote URLs.
|
||||
|
||||
* Fix issue #64, where pressing Enter in a textbox would cause a form to submit.
|
||||
|
||||
|
||||
shiny 0.2.4
|
||||
===========
|
||||
|
||||
* `runGist` has been updated to use the new download URLs from https://gist.github.com.
|
||||
|
||||
* Shiny now uses `CairoPNG()` for output, when the Cairo package is available. This provides better-looking output on Linux and Windows.
|
||||
|
||||
|
||||
shiny 0.2.3
|
||||
===========
|
||||
|
||||
* Ignore request variables for routing purposes
|
||||
|
||||
|
||||
shiny 0.2.2
|
||||
===========
|
||||
|
||||
* Fix CRAN warning (assigning to global environment)
|
||||
|
||||
|
||||
shiny 0.2.1
|
||||
===========
|
||||
|
||||
* [BREAKING] Modify API of `downloadHandler`: The `content` function now takes a file path, not writable connection, as an argument. This makes it much easier to work with APIs that only write to file paths, not connections.
|
||||
|
||||
|
||||
shiny 0.2.0
|
||||
===========
|
||||
|
||||
* Fix subtle name resolution bug--the usual symptom being S4 methods not being invoked correctly when called from inside of ui.R or server.R
|
||||
|
||||
|
||||
shiny 0.1.14
|
||||
===========
|
||||
|
||||
* Fix slider animator, which broke in 0.1.10
|
||||
|
||||
|
||||
shiny 0.1.13
|
||||
===========
|
||||
|
||||
* Fix temp file leak in reactivePlot
|
||||
|
||||
|
||||
shiny 0.1.12
|
||||
===========
|
||||
|
||||
* Fix problems with runGist on Windows
|
||||
|
||||
* Add feature for on-the-fly file downloads (e.g. CSV data, PDFs)
|
||||
|
||||
* Add CSS hooks for app-wide busy indicators
|
||||
|
||||
|
||||
shiny 0.1.11
|
||||
===========
|
||||
|
||||
* Fix input binding with IE8 on Shiny Server
|
||||
|
||||
* Fix issue #41: reactiveTable should allow print options too
|
||||
|
||||
* Allow dynamic sizing of reactivePlot (i.e. using a function instead of a fixed value)
|
||||
|
||||
|
||||
shiny 0.1.10
|
||||
===========
|
||||
|
||||
* Support more MIME types when serving out of www
|
||||
|
||||
* Fix issue #35: Allow modification of untar args
|
||||
|
||||
* headerPanel can take an explicit window title parameter
|
||||
|
||||
* checkboxInput uses correct attribute `checked` instead of `selected`
|
||||
|
||||
* Fix plot rendering with IE8 on Shiny Server
|
||||
|
||||
|
||||
shiny 0.1.9
|
||||
===========
|
||||
|
||||
* Much less flicker when updating plots
|
||||
|
||||
* More customizable error display
|
||||
|
||||
* Add `includeText`, `includeHTML`, and `includeMarkdown` functions for putting text, HTML, and Markdown content from external files in the application's UI.
|
||||
|
||||
|
||||
shiny 0.1.8
|
||||
===========
|
||||
|
||||
* Add `runGist` function for conveniently running a Shiny app that is published on gist.github.com.
|
||||
|
||||
* Fix issue #27: Warnings cause reactive functions to stop executing.
|
||||
|
||||
* The server.R and ui.R filenames are now case insensitive.
|
||||
|
||||
* Add `wellPanel` function for creating inset areas on the page.
|
||||
|
||||
* Add `bootstrapPage` function for creating new Bootstrap based layouts from scratch.
|
||||
|
||||
|
||||
shiny 0.1.7
|
||||
===========
|
||||
|
||||
* Fix issue #26: Shiny.OutputBindings not correctly exported.
|
||||
|
||||
* Add `repeatable` function for making easily repeatable versions of random number generating functions.
|
||||
|
||||
* Transcode JSON into UTF-8 (prevents non-ASCII reactivePrint values from causing errors on Windows).
|
||||
|
||||
|
||||
shiny 0.1.6
|
||||
===========
|
||||
|
||||
* Import package dependencies, instead of attaching them (with the exception of websockets, which doesn't currently work unless attached).
|
||||
|
||||
* conditionalPanel was animated, now it is not.
|
||||
|
||||
* bindAll was not correctly sending initial values to the server; fixed.
|
||||
|
||||
|
||||
shiny 0.1.5
|
||||
===========
|
||||
|
||||
* BREAKING CHANGE: JS APIs Shiny.bindInput and Shiny.bindOutput removed and replaced with Shiny.bindAll; Shiny.unbindInput and Shiny.unbindOutput removed and replaced with Shiny.unbindAll.
|
||||
|
||||
* Add file upload support (currently only works with Chrome and Firefox). Use a normal HTML file input, or call the `fileInput` UI function.
|
||||
|
||||
* Shiny.unbindOutputs did not work, now it does.
|
||||
|
||||
* Generally improved robustness of dynamic input/output bindings.
|
||||
|
||||
* Add conditionalPanel UI function to allow showing/hiding UI based on a JS expression; for example, whether an input is a particular value. Also works in raw HTML (add the `data-display-if` attribute to the element that should be shown/hidden).
|
||||
|
||||
* htmlOutput (CSS class `shiny-html-output`) can contain inputs and outputs.
|
||||
|
||||
|
||||
shiny 0.1.4
|
||||
===========
|
||||
|
||||
* Allow Bootstrap tabsets to act as reactive inputs; their value indicates which tab is active
|
||||
|
||||
* Upgrade to Bootstrap 2.1
|
||||
|
||||
* Add `checkboxGroupInput` control, which presents a list of checkboxes and returns a vector of the selected values
|
||||
|
||||
* Add `addResourcePath`, intended for reusable component authors to access CSS, JavaScript, image files, etc. from their package directories
|
||||
|
||||
* Add Shiny.bindInputs(scope), .unbindInputs(scope), .bindOutputs(scope), and .unbindOutputs(scope) JS API calls to allow dynamic binding/unbinding of HTML elements
|
||||
|
||||
|
||||
shiny 0.1.3
|
||||
===========
|
||||
|
||||
* Introduce Shiny.inputBindings.register JS API and InputBinding class, for creating custom input controls
|
||||
|
||||
* Add `step` parameter to numericInput
|
||||
|
||||
* Read names of input using `names(input)`
|
||||
|
||||
* Access snapshot of input as a list using `as.list(input)`
|
||||
|
||||
* Fix issue #10: Plots in tabsets not rendered
|
||||
|
||||
|
||||
shiny 0.1.2
|
||||
===========
|
||||
|
||||
* Initial private beta release!
|
||||
37
R/app.R
37
R/app.R
@@ -27,6 +27,12 @@
|
||||
#' request to determine whether the \code{ui} should be used to handle the
|
||||
#' request. Note that the entire request path must match the regular
|
||||
#' expression in order for the match to be considered successful.
|
||||
#' @param enableBookmarking Can be one of \code{"url"}, \code{"server"}, or
|
||||
#' \code{"disable"}. This is equivalent to calling the
|
||||
#' \code{\link{enableBookmarking}()} function just before calling
|
||||
#' \code{shinyApp()}. With the default value (\code{NULL}), the app will
|
||||
#' respect the setting from any previous calls to \code{enableBookmarking()}.
|
||||
#' See \code{\link{enableBookmarking}} for more information.
|
||||
#' @return An object that represents the app. Printing the object or passing it
|
||||
#' to \code{\link{runApp}} will run the app.
|
||||
#'
|
||||
@@ -59,10 +65,9 @@
|
||||
#'
|
||||
#' runApp(app)
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
shinyApp <- function(ui=NULL, server=NULL, onStart=NULL, options=list(),
|
||||
uiPattern="/") {
|
||||
uiPattern="/", enableBookmarking = NULL) {
|
||||
if (is.null(server)) {
|
||||
stop("`server` missing from shinyApp")
|
||||
}
|
||||
@@ -76,12 +81,24 @@ shinyApp <- function(ui=NULL, server=NULL, onStart=NULL, options=list(),
|
||||
server
|
||||
}
|
||||
|
||||
if (!is.null(enableBookmarking)) {
|
||||
bookmarkStore <- match.arg(enableBookmarking, c("url", "server", "disable"))
|
||||
enableBookmarking(bookmarkStore)
|
||||
}
|
||||
|
||||
# Store the appDir and bookmarking-related options, so that we can read them
|
||||
# from within the app.
|
||||
shinyOptions(appDir = getwd())
|
||||
appOptions <- consumeAppOptions()
|
||||
|
||||
structure(
|
||||
list(
|
||||
httpHandler = httpHandler,
|
||||
serverFuncSource = serverFuncSource,
|
||||
onStart = onStart,
|
||||
options = options),
|
||||
options = options,
|
||||
appOptions = appOptions
|
||||
),
|
||||
class = "shiny.appobj"
|
||||
)
|
||||
}
|
||||
@@ -113,7 +130,9 @@ shinyAppDir <- function(appDir, options=list()) {
|
||||
#' @export
|
||||
shinyAppFile <- function(appFile, options=list()) {
|
||||
appFile <- normalizePath(appFile, mustWork = TRUE)
|
||||
shinyAppDir_appR(basename(appFile), dirname(appFile), options = options)
|
||||
appDir <- dirname(appFile)
|
||||
|
||||
shinyAppDir_appR(basename(appFile), appDir, options = options)
|
||||
}
|
||||
|
||||
# This reads in an app dir in the case that there's a server.R (and ui.R/www)
|
||||
@@ -178,6 +197,8 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
|
||||
}
|
||||
}
|
||||
|
||||
shinyOptions(appDir = appDir)
|
||||
|
||||
oldwd <- NULL
|
||||
monitorHandle <- NULL
|
||||
onStart <- function() {
|
||||
@@ -199,7 +220,8 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
|
||||
serverFuncSource = serverFuncSource,
|
||||
onStart = onStart,
|
||||
onEnd = onEnd,
|
||||
options = options),
|
||||
options = options
|
||||
),
|
||||
class = "shiny.appobj"
|
||||
)
|
||||
}
|
||||
@@ -252,7 +274,8 @@ initAutoReloadMonitor <- function(dir) {
|
||||
|
||||
# This reads in an app dir for a single-file application (e.g. app.R), and
|
||||
# returns a shiny.appobj.
|
||||
shinyAppDir_appR <- function(fileName, appDir, options=list()) {
|
||||
shinyAppDir_appR <- function(fileName, appDir, options=list())
|
||||
{
|
||||
fullpath <- file.path.ci(appDir, fileName)
|
||||
|
||||
# This sources app.R and caches the content. When appObj() is called but
|
||||
@@ -265,6 +288,8 @@ shinyAppDir_appR <- function(fileName, appDir, options=list()) {
|
||||
if (!is.shiny.appobj(result))
|
||||
stop("app.R did not return a shiny.appobj object.")
|
||||
|
||||
unconsumeAppOptions(result$appOptions)
|
||||
|
||||
return(result)
|
||||
}
|
||||
)
|
||||
|
||||
28
R/bookmark-state-local.R
Normal file
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)
|
||||
}
|
||||
1113
R/bookmark-state.R
Normal file
1113
R/bookmark-state.R
Normal file
File diff suppressed because it is too large
Load Diff
@@ -418,7 +418,6 @@ flowLayout <- function(..., cellArgs = list()) {
|
||||
#' suitable for wrapping inputs.
|
||||
#'
|
||||
#' @param ... Input controls or other HTML elements.
|
||||
#'
|
||||
#' @export
|
||||
inputPanel <- function(...) {
|
||||
div(class = "shiny-input-panel",
|
||||
|
||||
@@ -25,7 +25,6 @@ NULL
|
||||
#' \code{\link{fluidPage}} function instead.
|
||||
#'
|
||||
#' @seealso \code{\link{fluidPage}}, \code{\link{fixedPage}}
|
||||
#'
|
||||
#' @export
|
||||
bootstrapPage <- function(..., title = NULL, responsive = NULL, theme = NULL) {
|
||||
|
||||
@@ -61,7 +60,7 @@ bootstrapPage <- function(..., title = NULL, responsive = NULL, theme = NULL) {
|
||||
#' @inheritParams bootstrapPage
|
||||
#' @export
|
||||
bootstrapLib <- function(theme = NULL) {
|
||||
htmlDependency("bootstrap", "3.3.6",
|
||||
htmlDependency("bootstrap", "3.3.7",
|
||||
c(
|
||||
href = "shared/bootstrap",
|
||||
file = system.file("www/shared/bootstrap", package = "shiny")
|
||||
@@ -153,7 +152,6 @@ basicPage <- function(...) {
|
||||
#' div(style = "background-color: blue; width: 100%; height: 100%;")
|
||||
#' )
|
||||
#' )
|
||||
#'
|
||||
#' @export
|
||||
fillPage <- function(..., padding = 0, title = NULL, bootstrap = TRUE,
|
||||
theme = NULL) {
|
||||
@@ -215,7 +213,6 @@ collapseSizes <- function(padding) {
|
||||
#' plotOutput("distPlot")
|
||||
#' )
|
||||
#' )
|
||||
#'
|
||||
#' @export
|
||||
pageWithSidebar <- function(headerPanel,
|
||||
sidebarPanel,
|
||||
@@ -338,6 +335,9 @@ navbarPage <- function(title,
|
||||
if (inverse)
|
||||
navbarClass <- paste(navbarClass, "navbar-inverse")
|
||||
|
||||
if (!is.null(id))
|
||||
selected <- restoreInput(id = id, default = selected)
|
||||
|
||||
# build the tabset
|
||||
tabs <- list(...)
|
||||
tabset <- buildTabset(tabs, "nav navbar-nav", NULL, id, selected)
|
||||
@@ -430,7 +430,6 @@ headerPanel <- function(title, windowTitle=title) {
|
||||
#'
|
||||
#' @param ... UI elements to include inside the panel.
|
||||
#' @return The newly created panel.
|
||||
#'
|
||||
#' @export
|
||||
wellPanel <- function(...) {
|
||||
div(class="well", ...)
|
||||
@@ -534,7 +533,6 @@ mainPanel <- function(..., width = 8) {
|
||||
#' )
|
||||
#' )
|
||||
#' )
|
||||
#'
|
||||
#' @export
|
||||
conditionalPanel <- function(condition, ...) {
|
||||
div('data-display-if'=condition, ...)
|
||||
@@ -635,9 +633,13 @@ tabsetPanel <- function(...,
|
||||
version = "0.10.2.2")
|
||||
}
|
||||
|
||||
if (!is.null(id))
|
||||
selected <- restoreInput(id = id, default = selected)
|
||||
|
||||
# build the tabset
|
||||
tabs <- list(...)
|
||||
type <- match.arg(type)
|
||||
|
||||
tabset <- buildTabset(tabs, paste0("nav nav-", type), NULL, id, selected)
|
||||
|
||||
# create the content
|
||||
@@ -700,6 +702,9 @@ navlistPanel <- function(...,
|
||||
tags$li(class="navbar-brand", text)
|
||||
}
|
||||
|
||||
if (!is.null(id))
|
||||
selected <- restoreInput(id = id, default = selected)
|
||||
|
||||
# build the tabset
|
||||
tabs <- list(...)
|
||||
tabset <- buildTabset(tabs,
|
||||
@@ -1495,7 +1500,6 @@ downloadLink <- function(outputId, label="Download", class=NULL) {
|
||||
#' tabPanel("Summary", icon = icon("list-alt")),
|
||||
#' tabPanel("Table", icon = icon("table"))
|
||||
#' )
|
||||
#'
|
||||
#' @export
|
||||
icon <- function(name, class = NULL, lib = "font-awesome") {
|
||||
prefixes <- list(
|
||||
@@ -1523,7 +1527,7 @@ icon <- function(name, class = NULL, lib = "font-awesome") {
|
||||
# font-awesome needs an additional dependency (glyphicon is in bootstrap)
|
||||
if (lib == "font-awesome") {
|
||||
htmlDependencies(iconTag) <- htmlDependency(
|
||||
"font-awesome", "4.5.0", c(href="shared/font-awesome"),
|
||||
"font-awesome", "4.6.3", c(href="shared/font-awesome"),
|
||||
stylesheet = "css/font-awesome.min.css"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#' R; it won't change the actual ppi of the browser.
|
||||
#' @param ... Arguments to be passed through to \code{\link[grDevices]{png}}.
|
||||
#' These can be used to set the width, height, background color, etc.
|
||||
#'
|
||||
#' @export
|
||||
plotPNG <- function(func, filename=tempfile(fileext='.png'),
|
||||
width=400, height=400, res=72, ...) {
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -81,6 +81,8 @@ 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")
|
||||
|
||||
value <- restoreInput(id = inputId, default = value)
|
||||
|
||||
attachDependencies(
|
||||
tags$div(id = inputId,
|
||||
class = "shiny-date-input form-group shiny-input-container",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -54,6 +54,9 @@
|
||||
selectInput <- function(inputId, label, choices, selected = NULL,
|
||||
multiple = FALSE, selectize = TRUE, width = NULL,
|
||||
size = NULL) {
|
||||
|
||||
selected <- restoreInput(id = inputId, default = selected)
|
||||
|
||||
# resolve names
|
||||
choices <- choicesWithNames(choices)
|
||||
|
||||
|
||||
@@ -85,6 +85,8 @@ sliderInput <- function(inputId, label, min, max, value, step = NULL,
|
||||
version = "0.10.2.2")
|
||||
}
|
||||
|
||||
value <- restoreInput(id = inputId, default = value)
|
||||
|
||||
# If step is NULL, use heuristic to set the step size.
|
||||
findStepSize <- function(min, max, step) {
|
||||
if (!is.null(step)) return(step)
|
||||
@@ -242,7 +244,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,
|
||||
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -17,11 +17,6 @@
|
||||
#' will determine the element(s) relative to which you want to insert your
|
||||
#' UI object.
|
||||
#'
|
||||
#' @param multiple In case your selector matches more than one element,
|
||||
#' \code{multiple} determines whether Shiny should insert the UI object
|
||||
#' relative to all matched elements or just relative to the first
|
||||
#' matched element (default).
|
||||
#'
|
||||
#' @param where Where your UI object should go relative to the selector:
|
||||
#' \describe{
|
||||
#' \item{\code{beforeBegin}}{Before the selector element itself}
|
||||
@@ -42,6 +37,11 @@
|
||||
#' reference or remove it later on). If you want to insert raw html, use
|
||||
#' \code{ui = HTML()}.
|
||||
#'
|
||||
#' @param multiple In case your selector matches more than one element,
|
||||
#' \code{multiple} determines whether Shiny should insert the UI object
|
||||
#' relative to all matched elements or just relative to the first
|
||||
#' matched element (default).
|
||||
#'
|
||||
#' @param immediate Whether the UI object should be immediately inserted into
|
||||
#' the app when you call \code{insertUI}, or whether Shiny should wait until
|
||||
#' all outputs have been updated and all observers have been run (default).
|
||||
@@ -73,12 +73,11 @@
|
||||
#' # Complete app with UI and server components
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
insertUI <- function(selector,
|
||||
multiple = FALSE,
|
||||
where = c("beforeBegin", "afterBegin", "beforeEnd", "afterEnd"),
|
||||
ui,
|
||||
multiple = FALSE,
|
||||
immediate = FALSE,
|
||||
session = getDefaultReactiveDomain()) {
|
||||
|
||||
@@ -155,7 +154,6 @@ insertUI <- function(selector,
|
||||
#' # Complete app with UI and server components
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
removeUI <- function(selector,
|
||||
multiple = FALSE,
|
||||
|
||||
@@ -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,
|
||||
|
||||
15
R/modal.R
15
R/modal.R
@@ -36,6 +36,8 @@ 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
|
||||
@@ -80,7 +82,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 +122,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 +143,17 @@ removeModal <- function(session = getDefaultReactiveDomain()) {
|
||||
#' }
|
||||
#' @export
|
||||
modalDialog <- function(..., title = NULL, footer = modalButton("Dismiss"),
|
||||
easyClose = FALSE) {
|
||||
size = c("m", "s", "l"), easyClose = FALSE) {
|
||||
|
||||
size <- match.arg(size)
|
||||
|
||||
div(id = "shiny-modal", class = "modal fade", 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)
|
||||
|
||||
56
R/progress.R
56
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.
|
||||
@@ -237,10 +269,12 @@ Progress <- R6Class(
|
||||
#' @rdname withProgress
|
||||
#' @export
|
||||
withProgress <- function(expr, min = 0, max = 1,
|
||||
value = min + (max - min) * 0.1,
|
||||
message = NULL, detail = NULL,
|
||||
session = getDefaultReactiveDomain(),
|
||||
env = parent.frame(), quoted = FALSE) {
|
||||
value = min + (max - min) * 0.1,
|
||||
message = NULL, detail = NULL,
|
||||
style = getShinyOption("progress.style", default = "notification"),
|
||||
session = getDefaultReactiveDomain(),
|
||||
env = parent.frame(), quoted = FALSE)
|
||||
{
|
||||
|
||||
if (!quoted)
|
||||
expr <- substitute(expr)
|
||||
@@ -248,7 +282,9 @@ withProgress <- function(expr, min = 0, max = 1,
|
||||
if (is.null(session$progressStack))
|
||||
stop("'session' is not a ShinySession object.")
|
||||
|
||||
p <- Progress$new(session, min = min, max = max)
|
||||
style <- match.arg(style, c("notification", "old"))
|
||||
|
||||
p <- Progress$new(session, min = min, max = max, style = style)
|
||||
|
||||
session$progressStack$push(p)
|
||||
on.exit({
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
247
R/reactives.R
247
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(...)
|
||||
@@ -317,10 +362,25 @@ as.list.reactivevalues <- function(x, all.names=FALSE, ...) {
|
||||
#' # isolate() can also be used when calling from outside a reactive context (e.g.
|
||||
#' # at the console)
|
||||
#' isolate(reactiveValuesToList(values))
|
||||
#'
|
||||
#' @export
|
||||
reactiveValuesToList <- function(x, all.names=FALSE) {
|
||||
.subset2(x, 'impl')$toList(all.names)
|
||||
# Default case
|
||||
res <- .subset2(x, 'impl')$toList(all.names)
|
||||
|
||||
prefix <- .subset2(x, 'ns')("")
|
||||
# Special handling for namespaces
|
||||
if (nzchar(prefix)) {
|
||||
fullNames <- names(res)
|
||||
|
||||
# Filter out items that match namespace
|
||||
fullNames <- fullNames[substring(fullNames, 1, nchar(prefix)) == prefix]
|
||||
res <- res[fullNames]
|
||||
|
||||
# Remove namespace prefix
|
||||
names(res) <- substring(fullNames, nchar(prefix) + 1)
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
# This function is needed because str() on a reactivevalues object will call
|
||||
@@ -334,6 +394,67 @@ str.reactivevalues <- function(object, indent.str = " ", ...) {
|
||||
utils::str(class(object))
|
||||
}
|
||||
|
||||
|
||||
#' Freeze a reactive value
|
||||
#'
|
||||
#' This freezes a reactive value. If the value is accessed while frozen, a
|
||||
#' "silent" exception is raised and the operation is stopped. This is the same
|
||||
#' thing that happens if \code{req(FALSE)} is called. The value is thawed
|
||||
#' (un-frozen; accessing it will no longer raise an exception) when the current
|
||||
#' reactive domain is flushed. In a Shiny application, this occurs after all of
|
||||
#' the observers are executed.
|
||||
#'
|
||||
#' @param x A \code{\link{reactiveValues}} object (like \code{input}).
|
||||
#' @param name The name of a value in the \code{\link{reactiveValues}} object.
|
||||
#'
|
||||
#' @seealso \code{\link{req}}
|
||||
#' @examples
|
||||
#' ## Only run this examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' selectInput("data", "Data Set", c("mtcars", "pressure")),
|
||||
#' checkboxGroupInput("cols", "Columns (select 2)", character(0)),
|
||||
#' plotOutput("plot")
|
||||
#' )
|
||||
#'
|
||||
#' server <- function(input, output, session) {
|
||||
#' observe({
|
||||
#' data <- get(input$data)
|
||||
#' # Sets a flag on input$cols to essentially do req(FALSE) if input$cols
|
||||
#' # is accessed. Without this, an error will momentarily show whenever a
|
||||
#' # new data set is selected.
|
||||
#' freezeReactiveValue(input, "cols")
|
||||
#' updateCheckboxGroupInput(session, "cols", choices = names(data))
|
||||
#' })
|
||||
#'
|
||||
#' output$plot <- renderPlot({
|
||||
#' # When a new data set is selected, input$cols will have been invalidated
|
||||
#' # above, and this will essentially do the same as req(FALSE), causing
|
||||
#' # this observer to stop and raise a silent exception.
|
||||
#' cols <- input$cols
|
||||
#' data <- get(input$data)
|
||||
#'
|
||||
#' if (length(cols) == 2) {
|
||||
#' plot(data[[ cols[1] ]], data[[ cols[2] ]])
|
||||
#' }
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#' @export
|
||||
freezeReactiveValue <- function(x, name) {
|
||||
domain <- getDefaultReactiveDomain()
|
||||
if (is.null(getDefaultReactiveDomain)) {
|
||||
stop("freezeReactiveValue() must be called when a default reactive domain is active.")
|
||||
}
|
||||
|
||||
domain$freezeValue(x, name)
|
||||
invisible()
|
||||
}
|
||||
|
||||
|
||||
# Observable ----------------------------------------------------------------
|
||||
|
||||
Observable <- R6Class(
|
||||
@@ -499,7 +620,6 @@ Observable <- R6Class(
|
||||
#' isolate(reactiveB())
|
||||
#' isolate(reactiveC())
|
||||
#' isolate(reactiveD())
|
||||
#'
|
||||
#' @export
|
||||
reactive <- function(x, env = parent.frame(), quoted = FALSE, label = NULL,
|
||||
domain = getDefaultReactiveDomain(),
|
||||
@@ -562,7 +682,7 @@ srcrefToLabel <- function(srcref, defaultLabel) {
|
||||
|
||||
#' @export
|
||||
print.reactive <- function(x, ...) {
|
||||
label <- attr(x, "observable")$.label
|
||||
label <- attr(x, "observable", exact = TRUE)$.label
|
||||
cat(label, "\n")
|
||||
}
|
||||
|
||||
@@ -573,7 +693,7 @@ is.reactive <- function(x) inherits(x, "reactive")
|
||||
# Return the number of times that a reactive expression or observer has been run
|
||||
execCount <- function(x) {
|
||||
if (is.reactive(x))
|
||||
return(attr(x, "observable")$.execCount)
|
||||
return(attr(x, "observable", exact = TRUE)$.execCount)
|
||||
else if (inherits(x, 'Observer'))
|
||||
return(x$.execCount)
|
||||
else
|
||||
@@ -591,12 +711,18 @@ Observer <- R6Class(
|
||||
.domain = 'ANY',
|
||||
.priority = numeric(0),
|
||||
.autoDestroy = logical(0),
|
||||
# A function that, when invoked, unsubscribes the autoDestroy
|
||||
# listener (or NULL if autodestroy is disabled for this observer).
|
||||
# We must unsubscribe when this observer is destroyed, or else
|
||||
# the observer cannot be garbage collected until the session ends.
|
||||
.autoDestroyHandle = 'ANY',
|
||||
.invalidateCallbacks = list(),
|
||||
.execCount = integer(0),
|
||||
.onResume = 'function',
|
||||
.suspended = logical(0),
|
||||
.destroyed = logical(0),
|
||||
.prevId = character(0),
|
||||
.ctx = NULL,
|
||||
|
||||
initialize = function(observerFunc, label, suspended = FALSE, priority = 0,
|
||||
domain = getDefaultReactiveDomain(),
|
||||
@@ -611,15 +737,14 @@ registerDebugHook("observerFunc", environment(), label)
|
||||
..stacktraceon..(observerFunc())
|
||||
else
|
||||
observerFunc(),
|
||||
validation = function(e) {
|
||||
# It's OK for a validation error to cause an observer to stop
|
||||
# running
|
||||
}
|
||||
# It's OK for shiny.silent.error errors to cause an observer to stop running
|
||||
shiny.silent.error = function(e) NULL
|
||||
# validation = function(e) NULL,
|
||||
# shiny.output.cancel = function(e) NULL
|
||||
)
|
||||
}
|
||||
.label <<- label
|
||||
.domain <<- domain
|
||||
.autoDestroy <<- autoDestroy
|
||||
.priority <<- normalizePriority(priority)
|
||||
.execCount <<- 0L
|
||||
.suspended <<- suspended
|
||||
@@ -627,7 +752,9 @@ registerDebugHook("observerFunc", environment(), label)
|
||||
.destroyed <<- FALSE
|
||||
.prevId <<- ''
|
||||
|
||||
onReactiveDomainEnded(.domain, self$.onDomainEnded)
|
||||
.autoDestroy <<- FALSE
|
||||
.autoDestroyHandle <<- NULL
|
||||
setAutoDestroy(autoDestroy)
|
||||
|
||||
# Defer the first running of this until flushReact is called
|
||||
.createContext()$invalidate()
|
||||
@@ -636,7 +763,23 @@ registerDebugHook("observerFunc", environment(), label)
|
||||
ctx <- Context$new(.domain, .label, type='observer', prevId=.prevId)
|
||||
.prevId <<- ctx$id
|
||||
|
||||
if (!is.null(.ctx)) {
|
||||
# If this happens, something went wrong.
|
||||
warning("Created a new context without invalidating previous context.")
|
||||
}
|
||||
# Store the context explicitly in the Observer object. This is necessary
|
||||
# to make sure that when the observer is destroyed, it also gets
|
||||
# invalidated. Otherwise the upstream reactive (on which the observer
|
||||
# depends) will hold a (indirect) reference to this context until the
|
||||
# reactive is invalidated, which may not happen immediately or at all.
|
||||
# This can lead to a memory leak (#1253).
|
||||
.ctx <<- ctx
|
||||
|
||||
ctx$onInvalidate(function() {
|
||||
# Context is invalidated, so we don't need to store a reference to it
|
||||
# anymore.
|
||||
.ctx <<- NULL
|
||||
|
||||
lapply(.invalidateCallbacks, function(invalidateCallback) {
|
||||
invalidateCallback()
|
||||
NULL
|
||||
@@ -689,11 +832,28 @@ registerDebugHook("observerFunc", environment(), label)
|
||||
"Sets whether this observer should be automatically destroyed when its
|
||||
domain (if any) ends. If autoDestroy is TRUE and the domain already
|
||||
ended, then destroy() is called immediately."
|
||||
|
||||
if (.autoDestroy == autoDestroy) {
|
||||
return(.autoDestroy)
|
||||
}
|
||||
|
||||
oldValue <- .autoDestroy
|
||||
.autoDestroy <<- autoDestroy
|
||||
if (!is.null(.domain) && .domain$isEnded()) {
|
||||
destroy()
|
||||
|
||||
if (autoDestroy) {
|
||||
if (!.destroyed && !is.null(.domain)) { # Make sure to not try to destroy twice.
|
||||
if (.domain$isEnded()) {
|
||||
destroy()
|
||||
} else {
|
||||
.autoDestroyHandle <<- onReactiveDomainEnded(.domain, .onDomainEnded)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!is.null(.autoDestroyHandle))
|
||||
.autoDestroyHandle()
|
||||
.autoDestroyHandle <<- NULL
|
||||
}
|
||||
|
||||
invisible(oldValue)
|
||||
},
|
||||
suspend = function() {
|
||||
@@ -719,8 +879,21 @@ registerDebugHook("observerFunc", environment(), label)
|
||||
"Prevents this observer from ever executing again (even if a flush has
|
||||
already been scheduled)."
|
||||
|
||||
# Make sure to not try to destory twice.
|
||||
if (.destroyed)
|
||||
return()
|
||||
|
||||
suspend()
|
||||
.destroyed <<- TRUE
|
||||
|
||||
if (!is.null(.autoDestroyHandle)) {
|
||||
.autoDestroyHandle()
|
||||
}
|
||||
.autoDestroyHandle <<- NULL
|
||||
|
||||
if (!is.null(.ctx)) {
|
||||
.ctx$invalidate()
|
||||
}
|
||||
},
|
||||
.onDomainEnded = function() {
|
||||
if (isTRUE(.autoDestroy)) {
|
||||
@@ -749,8 +922,8 @@ registerDebugHook("observerFunc", environment(), label)
|
||||
#' soon as their dependencies change, they schedule themselves to re-execute.
|
||||
#'
|
||||
#' Starting with Shiny 0.10.0, observers are automatically destroyed by default
|
||||
#' when the \link[=domains]{domain} that owns them ends (e.g. when a Shiny session
|
||||
#' ends).
|
||||
#' when the \link[=domains]{domain} that owns them ends (e.g. when a Shiny
|
||||
#' session ends).
|
||||
#'
|
||||
#' @param x An expression (quoted or unquoted). Any return value will be
|
||||
#' ignored.
|
||||
@@ -761,12 +934,13 @@ registerDebugHook("observerFunc", environment(), label)
|
||||
#' This is useful when you want to use an expression that is stored in a
|
||||
#' variable; to do so, it must be quoted with \code{quote()}.
|
||||
#' @param label A label for the observer, useful for debugging.
|
||||
#' @param suspended If \code{TRUE}, start the observer in a suspended state.
|
||||
#' If \code{FALSE} (the default), start in a non-suspended state.
|
||||
#' @param suspended If \code{TRUE}, start the observer in a suspended state. If
|
||||
#' \code{FALSE} (the default), start in a non-suspended state.
|
||||
#' @param priority An integer or numeric that controls the priority with which
|
||||
#' this observer should be executed. An observer with a given priority level
|
||||
#' will always execute sooner than all observers with a lower priority level.
|
||||
#' Positive, negative, and zero values are allowed.
|
||||
#' this observer should be executed. A higher value means higher priority: an
|
||||
#' observer with a higher priority value will execute before all observers
|
||||
#' with lower priority values. Positive, negative, and zero values are
|
||||
#' allowed.
|
||||
#' @param domain See \link{domains}.
|
||||
#' @param autoDestroy If \code{TRUE} (the default), the observer will be
|
||||
#' automatically destroyed when its domain (if any) ends.
|
||||
@@ -825,7 +999,6 @@ registerDebugHook("observerFunc", environment(), label)
|
||||
#' # In a normal Shiny app, the web client will trigger flush events. If you
|
||||
#' # are at the console, you can force a flush with flushReact()
|
||||
#' shiny:::flushReact()
|
||||
#'
|
||||
#' @export
|
||||
observe <- function(x, env=parent.frame(), quoted=FALSE, label=NULL,
|
||||
suspended=FALSE, priority=0,
|
||||
@@ -865,9 +1038,9 @@ observe <- function(x, env=parent.frame(), quoted=FALSE, label=NULL,
|
||||
#' }
|
||||
#' @export
|
||||
makeReactiveBinding <- function(symbol, env = parent.frame()) {
|
||||
if (exists(symbol, where = env, inherits = FALSE)) {
|
||||
initialValue <- get(symbol, pos = env, inherits = FALSE)
|
||||
rm(list = symbol, pos = env, inherits = FALSE)
|
||||
if (exists(symbol, envir = env, inherits = FALSE)) {
|
||||
initialValue <- env[[symbol]]
|
||||
rm(list = symbol, envir = env, inherits = FALSE)
|
||||
}
|
||||
else
|
||||
initialValue <- NULL
|
||||
@@ -975,7 +1148,6 @@ setAutoflush <- local({
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
reactiveTimer <- function(intervalMs=1000, session = getDefaultReactiveDomain()) {
|
||||
dependents <- Map$new()
|
||||
@@ -1134,7 +1306,6 @@ coerceToFunc <- function(x) {
|
||||
#' data()
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
reactivePoll <- function(intervalMillis, session, checkFunc, valueFunc) {
|
||||
intervalMillis <- coerceToFunc(intervalMillis)
|
||||
@@ -1212,7 +1383,6 @@ reactivePoll <- function(intervalMillis, session, checkFunc, valueFunc) {
|
||||
#' })
|
||||
#' }
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
reactiveFileReader <- function(intervalMillis, session, filePath, readFunc, ...) {
|
||||
filePath <- coerceToFunc(filePath)
|
||||
@@ -1300,7 +1470,6 @@ reactiveFileReader <- function(intervalMillis, session, filePath, readFunc, ...)
|
||||
#'
|
||||
#' # isolate also works if the reactive expression accesses values from the
|
||||
#' # input object, like input$x
|
||||
#'
|
||||
#' @export
|
||||
isolate <- function(expr) {
|
||||
ctx <- Context$new(getDefaultReactiveDomain(), '[isolate]', type='isolate')
|
||||
@@ -1322,7 +1491,6 @@ isolate <- function(expr) {
|
||||
#' @return The value of \code{expr}.
|
||||
#'
|
||||
#' @seealso \code{\link{isolate}}
|
||||
#'
|
||||
#' @export
|
||||
maskReactiveContext <- function(expr) {
|
||||
.getReactiveEnvironment()$runWith(NULL, function() {
|
||||
@@ -1449,7 +1617,6 @@ maskReactiveContext <- function(expr) {
|
||||
#' }
|
||||
#' shinyApp(ui=ui, server=server)
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
observeEvent <- function(eventExpr, handlerExpr,
|
||||
event.env = parent.frame(), event.quoted = FALSE,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -89,10 +89,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 +123,30 @@ 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)
|
||||
|
||||
# Make sure that the paths don't go up the directory tree, for security
|
||||
# reasons.
|
||||
if (any(grepl("..", val$datapath, fixed = TRUE))) {
|
||||
stop("Invalid '..' found in file input path.")
|
||||
}
|
||||
|
||||
# Prepend the persistent dir
|
||||
val$datapath <- file.path(getCurrentRestoreContext()$dir, val$datapath)
|
||||
|
||||
val
|
||||
})
|
||||
|
||||
259
R/server.R
259
R/server.R
@@ -49,7 +49,6 @@ registerClient <- function(client) {
|
||||
#'
|
||||
#' @examples
|
||||
#' addResourcePath('datasets', system.file('data', package='datasets'))
|
||||
#'
|
||||
#' @export
|
||||
addResourcePath <- function(prefix, directoryPath) {
|
||||
prefix <- prefix[1]
|
||||
@@ -141,7 +140,6 @@ resourcePathHandler <- function(req) {
|
||||
#' })
|
||||
#' }
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
shinyServer <- function(func) {
|
||||
.globals$server <- list(func)
|
||||
@@ -232,121 +230,136 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
|
||||
|
||||
msg <- decodeMessage(msg)
|
||||
|
||||
# Do our own list simplifying here. sapply/simplify2array give names to
|
||||
# character vectors, which is rarely what we want.
|
||||
if (!is.null(msg$data)) {
|
||||
for (name in names(msg$data)) {
|
||||
val <- msg$data[[name]]
|
||||
|
||||
splitName <- strsplit(name, ':')[[1]]
|
||||
if (length(splitName) > 1) {
|
||||
msg$data[[name]] <- NULL
|
||||
|
||||
if (!inputHandlers$containsKey(splitName[[2]])){
|
||||
# No input handler registered for this type
|
||||
stop("No handler registered for for type ", name)
|
||||
}
|
||||
|
||||
msg$data[[ splitName[[1]] ]] <-
|
||||
inputHandlers$get(splitName[[2]])(
|
||||
val,
|
||||
shinysession,
|
||||
splitName[[1]] )
|
||||
}
|
||||
else if (is.list(val) && is.null(names(val))) {
|
||||
val_flat <- unlist(val, recursive = TRUE)
|
||||
|
||||
if (is.null(val_flat)) {
|
||||
# This is to assign NULL instead of deleting the item
|
||||
msg$data[name] <- list(NULL)
|
||||
} else {
|
||||
msg$data[[name]] <- val_flat
|
||||
}
|
||||
}
|
||||
# Set up a restore context from .clientdata_url_search before
|
||||
# handling all the input values, because the restore context may be
|
||||
# used by an input handler (like the one for "shiny.file"). This
|
||||
# should only happen once, when the app starts.
|
||||
if (is.null(shinysession$restoreContext)) {
|
||||
bookmarkStore <- getShinyOption("bookmarkStore", default = "disable")
|
||||
if (bookmarkStore == "disable") {
|
||||
# If bookmarking is disabled, use empty context
|
||||
shinysession$restoreContext <- RestoreContext$new()
|
||||
} else {
|
||||
# If there's bookmarked state, save it on the session object
|
||||
shinysession$restoreContext <- RestoreContext$new(msg$data$.clientdata_url_search)
|
||||
}
|
||||
}
|
||||
|
||||
switch(
|
||||
msg$method,
|
||||
init = {
|
||||
withRestoreContext(shinysession$restoreContext, {
|
||||
|
||||
serverFunc <- withReactiveDomain(NULL, serverFuncSource())
|
||||
if (!identicalFunctionBodies(serverFunc, appvars$server)) {
|
||||
appvars$server <- serverFunc
|
||||
if (!is.null(appvars$server))
|
||||
{
|
||||
# Tag this function as the Shiny server function. A debugger may use this
|
||||
# tag to give this function special treatment.
|
||||
# It's very important that it's appvars$server itself and NOT a copy that
|
||||
# is invoked, otherwise new breakpoints won't be picked up.
|
||||
attr(appvars$server, "shinyServerFunction") <- TRUE
|
||||
registerDebugHook("server", appvars, "Server Function")
|
||||
unpackInput <- function(name, val) {
|
||||
splitName <- strsplit(name, ':')[[1]]
|
||||
if (length(splitName) > 1) {
|
||||
if (!inputHandlers$containsKey(splitName[[2]])) {
|
||||
# No input handler registered for this type
|
||||
stop("No handler registered for type ", name)
|
||||
}
|
||||
|
||||
inputName <- splitName[[1]]
|
||||
|
||||
# Get the function for processing this type of input
|
||||
inputHandler <- inputHandlers$get(splitName[[2]])
|
||||
|
||||
return(inputHandler(val, shinysession, inputName))
|
||||
|
||||
} else if (is.list(val) && is.null(names(val))) {
|
||||
return(unlist(val, recursive = TRUE))
|
||||
} else {
|
||||
return(val)
|
||||
}
|
||||
}
|
||||
|
||||
# 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)
|
||||
}
|
||||
msg$data <- mapply(unpackInput, names(msg$data), msg$data,
|
||||
SIMPLIFY = FALSE)
|
||||
|
||||
shinysession$manageInputs(msg$data)
|
||||
# Convert names like "button1:shiny.action" to "button1"
|
||||
names(msg$data) <- vapply(
|
||||
names(msg$data),
|
||||
function(name) { strsplit(name, ":")[[1]][1] },
|
||||
FUN.VALUE = character(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)
|
||||
switch(
|
||||
msg$method,
|
||||
init = {
|
||||
|
||||
withReactiveDomain(shinysession, {
|
||||
do.call(
|
||||
# No corresponding ..stacktraceoff; the server func is pure
|
||||
# user code
|
||||
wrapFunctionLabel(appvars$server, "server",
|
||||
..stacktraceon = TRUE
|
||||
),
|
||||
args
|
||||
)
|
||||
serverFunc <- withReactiveDomain(NULL, serverFuncSource())
|
||||
if (!identicalFunctionBodies(serverFunc, appvars$server)) {
|
||||
appvars$server <- serverFunc
|
||||
if (!is.null(appvars$server))
|
||||
{
|
||||
# Tag this function as the Shiny server function. A debugger may use this
|
||||
# tag to give this function special treatment.
|
||||
# It's very important that it's appvars$server itself and NOT a copy that
|
||||
# is invoked, otherwise new breakpoints won't be picked up.
|
||||
attr(appvars$server, "shinyServerFunction") <- TRUE
|
||||
registerDebugHook("server", appvars, "Server Function")
|
||||
}
|
||||
}
|
||||
|
||||
# Check for switching into/out of showcase mode
|
||||
if (.globals$showcaseOverride &&
|
||||
exists(".clientdata_url_search", where = msg$data)) {
|
||||
mode <- showcaseModeOfQuerystring(msg$data$.clientdata_url_search)
|
||||
if (!is.null(mode))
|
||||
shinysession$setShowcase(mode)
|
||||
}
|
||||
|
||||
shinysession$manageInputs(msg$data)
|
||||
|
||||
# The client tells us what singletons were rendered into
|
||||
# the initial page
|
||||
if (!is.null(msg$data$.clientdata_singletons)) {
|
||||
shinysession$singletons <- strsplit(
|
||||
msg$data$.clientdata_singletons, ',')[[1]]
|
||||
}
|
||||
|
||||
local({
|
||||
args <- argsForServerFunc(serverFunc, shinysession)
|
||||
|
||||
withReactiveDomain(shinysession, {
|
||||
do.call(
|
||||
# No corresponding ..stacktraceoff; the server func is pure
|
||||
# user code
|
||||
wrapFunctionLabel(appvars$server, "server",
|
||||
..stacktraceon = TRUE
|
||||
),
|
||||
args
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
update = {
|
||||
shinysession$manageInputs(msg$data)
|
||||
},
|
||||
shinysession$dispatch(msg)
|
||||
)
|
||||
shinysession$manageHiddenOutputs()
|
||||
},
|
||||
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 +460,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 +479,7 @@ serviceApp <- function() {
|
||||
}
|
||||
|
||||
flushReact()
|
||||
|
||||
for (shinysession in appsByToken$values()) {
|
||||
shinysession$flushOutput()
|
||||
}
|
||||
flushAllSessions()
|
||||
}
|
||||
|
||||
# If this R session is interactive, then call service() with a short timeout
|
||||
@@ -565,17 +581,23 @@ runApp <- function(appDir=getwd(),
|
||||
handlerManager$clear()
|
||||
}, add = TRUE)
|
||||
|
||||
# Enable per-app Shiny options
|
||||
oldOptionSet <- .globals$options
|
||||
on.exit({
|
||||
.globals$options <- oldOptionSet
|
||||
},add = TRUE)
|
||||
|
||||
if (is.null(host) || is.na(host))
|
||||
host <- '0.0.0.0'
|
||||
|
||||
# Make warnings print immediately
|
||||
ops <- options(warn = 1)
|
||||
# Set pool.scheduler to support pool package
|
||||
ops <- options(warn = 1, pool.scheduler = scheduleTask)
|
||||
on.exit(options(ops), add = TRUE)
|
||||
|
||||
workerId(workerId)
|
||||
|
||||
if (nzchar(Sys.getenv('SHINY_PORT'))) {
|
||||
if (inShinyServer()) {
|
||||
# If SHINY_PORT is set, we're running under Shiny Server. Check the version
|
||||
# to make sure it is compatible. Older versions of Shiny Server don't set
|
||||
# SHINY_SERVER_VERSION, those will return "" which is considered less than
|
||||
@@ -659,7 +681,14 @@ runApp <- function(appDir=getwd(),
|
||||
}
|
||||
else {
|
||||
# Try up to 20 random ports
|
||||
port <- p_randomInt(3000, 8000)
|
||||
while (TRUE) {
|
||||
port <- p_randomInt(3000, 8000)
|
||||
# Reject ports in this range that are considered unsafe by Chrome
|
||||
# http://superuser.com/questions/188058/which-ports-are-considered-unsafe-on-chrome
|
||||
if (!port %in% c(3659, 4045, 6000, 6665:6669)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Test port to see if we can use it
|
||||
@@ -673,6 +702,12 @@ runApp <- function(appDir=getwd(),
|
||||
}
|
||||
|
||||
appParts <- as.shiny.appobj(appDir)
|
||||
|
||||
# Extract appOptions (which is a list) and store them as shinyOptions, for
|
||||
# this app. (This is the only place we have to store settings that are
|
||||
# accessible both the UI and server portion of the app.)
|
||||
unconsumeAppOptions(appParts$appOptions)
|
||||
|
||||
# Set up the onEnd before we call onStart, so that it gets called even if an
|
||||
# error happens in onStart.
|
||||
if (!is.null(appParts$onEnd))
|
||||
@@ -735,7 +770,6 @@ runApp <- function(appDir=getwd(),
|
||||
#'
|
||||
#' @param returnValue The value that should be returned from
|
||||
#' \code{\link{runApp}}.
|
||||
#'
|
||||
#' @export
|
||||
stopApp <- function(returnValue = invisible()) {
|
||||
# reterror will indicate whether retval is an error (i.e. it should be passed
|
||||
@@ -854,7 +888,6 @@ runExample <- function(example=NA,
|
||||
#' # ...or as a single app object
|
||||
#' runGadget(shinyApp(ui, server))
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
runGadget <- function(app, server = NULL, port = getOption("shiny.port"),
|
||||
viewer = paneViewer(), stopOnCancel = TRUE) {
|
||||
@@ -950,3 +983,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)
|
||||
}
|
||||
}
|
||||
514
R/shiny.R
514
R/shiny.R
@@ -123,10 +123,14 @@ createUniqueId <- function(bytes, prefix = "", suffix = "") {
|
||||
toJSON <- function(x, ..., dataframe = "columns", null = "null", na = "null",
|
||||
auto_unbox = TRUE, digits = getOption("shiny.json.digits", 16),
|
||||
use_signif = TRUE, force = TRUE, POSIXt = "ISO8601", UTC = TRUE,
|
||||
rownames = FALSE, keep_vec_names = TRUE) {
|
||||
rownames = FALSE, keep_vec_names = TRUE, strict_atomic = TRUE) {
|
||||
|
||||
if (strict_atomic) {
|
||||
x <- I(x)
|
||||
}
|
||||
|
||||
# I(x) is so that length-1 atomic vectors get put in [].
|
||||
jsonlite::toJSON(I(x), dataframe = dataframe, null = null, na = na,
|
||||
jsonlite::toJSON(x, dataframe = dataframe, null = null, na = na,
|
||||
auto_unbox = auto_unbox, digits = digits, use_signif = use_signif,
|
||||
force = force, POSIXt = POSIXt, UTC = UTC, rownames = rownames,
|
||||
keep_vec_names = keep_vec_names, json_verbatim = TRUE, ...)
|
||||
@@ -282,6 +286,12 @@ workerId <- local({
|
||||
#' \code{addCustomMessageHandler} will be invoked each time
|
||||
#' \code{sendCustomMessage} is called on the server.
|
||||
#' }
|
||||
#' \item{sendBinaryMessage(type, message)}{
|
||||
#' Similar to \code{sendCustomMessage}, but the message must be a raw vector
|
||||
#' and the registration method on the client is
|
||||
#' \code{Shiny.addBinaryMessageHandler(type, function(message){...})}. The
|
||||
#' message argument on the client will be a \href{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView}{DataView}.
|
||||
#' }
|
||||
#' \item{sendInputMessage(inputId, message)}{
|
||||
#' Sends a message to an input on the session's client web page; if the input
|
||||
#' is present and bound on the page at the time the message is received, then
|
||||
@@ -290,6 +300,29 @@ workerId <- local({
|
||||
#' from Shiny apps, but through friendlier wrapper functions like
|
||||
#' \code{\link{updateTextInput}}.
|
||||
#' }
|
||||
#' \item{setBookmarkExclude(names)}{
|
||||
#' Set input names to be excluded from bookmarking.
|
||||
#' }
|
||||
#' \item{getBookmarkExclude()}{
|
||||
#' Returns the set of input names to be excluded from bookmarking.
|
||||
#' }
|
||||
#' \item{onBookmark(fun)}{
|
||||
#' Registers a function that will be called just before bookmarking state.
|
||||
#' }
|
||||
#' \item{onBookmarked(fun)}{
|
||||
#' Registers a function that will be called just after bookmarking state.
|
||||
#' }
|
||||
#' \item{onRestore(fun)}{
|
||||
#' Registers a function that will be called when a session is restored, before
|
||||
#' all other reactives, observers, and render functions are run.
|
||||
#' }
|
||||
#' \item{onRestored(fun)}{
|
||||
#' Registers a function that will be called when a session is restored, after
|
||||
#' all other reactives, observers, and render functions are run.
|
||||
#' }
|
||||
#' \item{doBookmark()}{
|
||||
#' Do bookmarking and invoke the onBookmark and onBookmarked callback functions.
|
||||
#' }
|
||||
#'
|
||||
#' @name session
|
||||
NULL
|
||||
@@ -316,7 +349,6 @@ NULL
|
||||
#' @return If \code{id} is missing, returns a function that expects an id string
|
||||
#' as its only argument and returns that id with the namespace prepended.
|
||||
#' @seealso \url{http://shiny.rstudio.com/articles/modules.html}
|
||||
#'
|
||||
#' @export
|
||||
NS <- function(namespace, id = NULL) {
|
||||
if (missing(id)) {
|
||||
@@ -332,6 +364,7 @@ NS <- function(namespace, id = NULL) {
|
||||
#' @export
|
||||
ns.sep <- "-"
|
||||
|
||||
|
||||
#' @include utils.R
|
||||
ShinySession <- R6Class(
|
||||
'ShinySession',
|
||||
@@ -345,7 +378,7 @@ ShinySession <- R6Class(
|
||||
.outputs = list(), # Keeps track of all the output observer objects
|
||||
.outputOptions = list(), # Options for each of the output observer objects
|
||||
progressKeys = 'character',
|
||||
showcase = 'ANY',
|
||||
showcase = FALSE,
|
||||
fileUploadContext = 'FileUploadContext',
|
||||
.input = 'ANY', # Internal ReactiveValues object for normal input sent from client
|
||||
.clientData = 'ANY', # Internal ReactiveValues object for other data sent from the client
|
||||
@@ -354,6 +387,12 @@ ShinySession <- R6Class(
|
||||
flushCallbacks = 'Callbacks',
|
||||
flushedCallbacks = 'Callbacks',
|
||||
inputReceivedCallbacks = 'Callbacks',
|
||||
bookmarkCallbacks = 'Callbacks',
|
||||
bookmarkedCallbacks = 'Callbacks',
|
||||
restoreCallbacks = 'Callbacks',
|
||||
restoredCallbacks = 'Callbacks',
|
||||
bookmarkExclude = character(0), # Names of inputs to exclude from bookmarking
|
||||
|
||||
sendResponse = function(requestMsg, value) {
|
||||
if (is.null(requestMsg$tag)) {
|
||||
warning("Tried to send response for untagged message; method: ",
|
||||
@@ -420,9 +459,99 @@ ShinySession <- R6Class(
|
||||
|
||||
# Clear file upload directories, if present
|
||||
self$onSessionEnded(private$fileUploadContext$rmUploadDirs)
|
||||
},
|
||||
|
||||
createBookmarkObservers = function() {
|
||||
# This is to be called from the initialization. It registers observers
|
||||
# for bookmarking to work.
|
||||
|
||||
# Get bookmarking config
|
||||
store <- getShinyOption("bookmarkStore", default = "disable")
|
||||
if (store == "disable")
|
||||
return()
|
||||
|
||||
# Warn if trying to enable save-to-server bookmarking on a version of SS,
|
||||
# SSP, or Connect that doesn't support it.
|
||||
if (store == "server" && inShinyServer() &&
|
||||
is.null(getShinyOption("save.interface")))
|
||||
{
|
||||
showNotification(
|
||||
"This app tried to enable saved-to-server bookmarking, but it is not supported by the hosting environment.",
|
||||
duration = NULL, type = "warning", session = self
|
||||
)
|
||||
return()
|
||||
}
|
||||
|
||||
withReactiveDomain(self, {
|
||||
# This observer fires when the bookmark button is clicked.
|
||||
observeEvent(self$input[["._bookmark_"]], {
|
||||
self$doBookmark()
|
||||
})
|
||||
|
||||
# If there was an error initializing the current restore context, show
|
||||
# notification in the client.
|
||||
observe({
|
||||
rc <- getCurrentRestoreContext()
|
||||
if (!is.null(rc$initErrorMessage)) {
|
||||
showNotification(
|
||||
paste("Error in RestoreContext initialization:", rc$initErrorMessage),
|
||||
duration = NULL, type = "error"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
# Run the onRestore function at the beginning of the flush cycle, but after
|
||||
# the server function has been executed.
|
||||
observe({
|
||||
if (private$restoreCallbacks$count() > 0) {
|
||||
tryCatch(
|
||||
withLogErrors(
|
||||
isolate({
|
||||
rc <- getCurrentRestoreContext()
|
||||
if (rc$active) {
|
||||
restoreState <- getCurrentRestoreContext()$asList()
|
||||
private$restoreCallbacks$invoke(restoreState)
|
||||
}
|
||||
})
|
||||
),
|
||||
error = function(e) {
|
||||
showNotification(
|
||||
paste0("Error calling onRestore callback: ", e$message),
|
||||
duration = NULL, type = "error"
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}, priority = 1000000)
|
||||
|
||||
# Run the onRestored function after the flush cycle completes and information
|
||||
# is sent to the client.
|
||||
self$onFlushed(function() {
|
||||
if (private$restoredCallbacks$count() > 0) {
|
||||
|
||||
tryCatch(
|
||||
withLogErrors(
|
||||
isolate({
|
||||
rc <- getCurrentRestoreContext()
|
||||
if (rc$active) {
|
||||
restoreState <- getCurrentRestoreContext()$asList()
|
||||
private$restoredCallbacks$invoke(restoreState)
|
||||
}
|
||||
})
|
||||
),
|
||||
error = function(e) {
|
||||
msg <- paste0("Error calling onRestored callback: ", e$message)
|
||||
showNotification(msg, duration = NULL, type = "error")
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
}) # withReactiveDomain
|
||||
}
|
||||
),
|
||||
public = list(
|
||||
restoreContext = NULL,
|
||||
progressStack = 'Stack', # Stack of progress objects
|
||||
input = 'reactivevalues', # Externally-usable S3 wrapper object for .input
|
||||
output = 'ANY', # Externally-usable S3 wrapper object for .outputs
|
||||
@@ -465,6 +594,12 @@ ShinySession <- R6Class(
|
||||
private$.outputs <- list()
|
||||
private$.outputOptions <- list()
|
||||
|
||||
private$bookmarkCallbacks <- Callbacks$new()
|
||||
private$bookmarkedCallbacks <- Callbacks$new()
|
||||
private$restoreCallbacks <- Callbacks$new()
|
||||
private$restoredCallbacks <- Callbacks$new()
|
||||
private$createBookmarkObservers()
|
||||
|
||||
private$registerSessionEndCallbacks()
|
||||
|
||||
if (!is.null(websocket$request$HTTP_SHINY_SERVER_CREDENTIALS)) {
|
||||
@@ -487,10 +622,20 @@ ShinySession <- R6Class(
|
||||
)
|
||||
)
|
||||
},
|
||||
rootScope = function() {
|
||||
self
|
||||
},
|
||||
makeScope = function(namespace) {
|
||||
ns <- NS(namespace)
|
||||
|
||||
createSessionProxy(self,
|
||||
# Private items for this scope. Can't be part of the scope object because
|
||||
# `$<-.session_proxy` doesn't allow assignment on overidden names.
|
||||
bookmarkCallbacks <- Callbacks$new()
|
||||
restoreCallbacks <- Callbacks$new()
|
||||
restoredCallbacks <- Callbacks$new()
|
||||
bookmarkExclude <- character(0)
|
||||
|
||||
scope <- createSessionProxy(self,
|
||||
input = .createReactiveValues(private$.input, readonly = TRUE, ns = ns),
|
||||
output = .createOutputWriter(self, ns = ns),
|
||||
sendInputMessage = function(inputId, message) {
|
||||
@@ -502,12 +647,157 @@ ShinySession <- R6Class(
|
||||
ns = ns,
|
||||
makeScope = function(namespace) {
|
||||
self$makeScope(ns(namespace))
|
||||
},
|
||||
|
||||
setBookmarkExclude = function(names) {
|
||||
bookmarkExclude <<- names
|
||||
},
|
||||
getBookmarkExclude = function() {
|
||||
bookmarkExclude
|
||||
},
|
||||
onBookmark = function(fun) {
|
||||
if (!is.function(fun) || length(fun) != 1) {
|
||||
stop("`fun` must be a function that takes one argument")
|
||||
}
|
||||
bookmarkCallbacks$register(fun)
|
||||
},
|
||||
onBookmarked = function(fun) {
|
||||
stop("onBookmarked() can't be used in a module.")
|
||||
},
|
||||
onRestore = function(fun) {
|
||||
if (!is.function(fun) || length(fun) != 1) {
|
||||
stop("`fun` must be a function that takes one argument")
|
||||
}
|
||||
restoreCallbacks$register(fun)
|
||||
},
|
||||
onRestored = function(fun) {
|
||||
if (!is.function(fun) || length(fun) != 1) {
|
||||
stop("`fun` must be a function that takes one argument")
|
||||
}
|
||||
restoredCallbacks$register(fun)
|
||||
}
|
||||
)
|
||||
|
||||
# Given a char vector, return a logical vector indicating which of those
|
||||
# strings are names of things in the namespace.
|
||||
filterNamespace <- function(x) {
|
||||
nsString <- paste0(namespace, ns.sep)
|
||||
substr(x, 1, nchar(nsString)) == nsString
|
||||
}
|
||||
|
||||
# Given a char vector of namespaced names, return a char vector of corresponding
|
||||
# names with namespace prefix removed.
|
||||
unNamespace <- function(x) {
|
||||
if (!all(filterNamespace(x))) {
|
||||
stop("x contains strings(s) that do not have namespace prefix ", namespace)
|
||||
}
|
||||
|
||||
nsString <- paste0(namespace, ns.sep)
|
||||
substring(x, nchar(nsString) + 1)
|
||||
}
|
||||
|
||||
# Given a restore state object (a list), return a modified version that's
|
||||
# scoped to this namespace.
|
||||
scopeRestoreState <- function(state) {
|
||||
# State is a list. We need to copy and transform some things for the
|
||||
# scope.
|
||||
scopeState <- state
|
||||
# `values` is an environment and we don't want to modify the original.
|
||||
scopeState$values <- new.env(parent = emptyenv())
|
||||
|
||||
# Keep only inputs that are in the scope, and rename them
|
||||
scopeState$input <- scopeState$input[filterNamespace(names(scopeState$input))]
|
||||
names(scopeState$input) <- unNamespace(names(scopeState$input))
|
||||
|
||||
# Same for values. This is an environment so we have to handle a little
|
||||
# differently.
|
||||
origNames <- names(state$values)
|
||||
origNames <- origNames[filterNamespace(origNames)]
|
||||
lapply(origNames, function(origName) {
|
||||
scopedName <- unNamespace(origName)
|
||||
scopeState$values[[scopedName]] <- state$values[[origName]]
|
||||
})
|
||||
|
||||
if (!is.null(state$dir)) {
|
||||
dir <- file.path(state$dir, namespace)
|
||||
if (dirExists(dir))
|
||||
scopeState$dir <- dir
|
||||
}
|
||||
|
||||
scopeState
|
||||
}
|
||||
|
||||
# When scope is created, register these bookmarking callbacks on the main
|
||||
# session object. They will invoke the scope's own callbacks, if any are
|
||||
# present.
|
||||
self$onBookmark(function(state) {
|
||||
# Exit if no user-defined callbacks.
|
||||
if (bookmarkCallbacks$count() == 0)
|
||||
return()
|
||||
|
||||
scopeState <- ShinySaveState$new(scope$input, scope$getBookmarkExclude())
|
||||
|
||||
# Create subdir for this scope
|
||||
if (!is.null(state$dir)) {
|
||||
scopeState$dir <- file.path(state$dir, namespace)
|
||||
res <- dir.create(scopeState$dir)
|
||||
if (res == FALSE) {
|
||||
stop("Error creating subdirectory for scope ", namespace)
|
||||
}
|
||||
}
|
||||
|
||||
# Invoke the callback on the scopeState object
|
||||
bookmarkCallbacks$invoke(scopeState)
|
||||
|
||||
# Copy `values` from scopeState to state, adding namespace
|
||||
if (length(scopeState$values) != 0) {
|
||||
if (anyUnnamed(scopeState$values)) {
|
||||
stop("All scope values in must be named.")
|
||||
}
|
||||
|
||||
lapply(names(scopeState$values), function(origName) {
|
||||
scopedName <- ns(origName)
|
||||
state$values[[scopedName]] <- scopeState$values[[origName]]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
self$onRestore(function(state) {
|
||||
# Exit if no user-defined callbacks.
|
||||
if (restoreCallbacks$count() == 0)
|
||||
return()
|
||||
|
||||
scopeState <- scopeRestoreState(state)
|
||||
# Invoke user callbacks
|
||||
restoreCallbacks$invoke(scopeState)
|
||||
})
|
||||
|
||||
self$onRestored(function(state) {
|
||||
# Exit if no user-defined callbacks.
|
||||
if (restoredCallbacks$count() == 0)
|
||||
return()
|
||||
|
||||
scopeState <- scopeRestoreState(state)
|
||||
# Invoke user callbacks
|
||||
restoredCallbacks$invoke(scopeState)
|
||||
})
|
||||
|
||||
scope
|
||||
},
|
||||
ns = function(id) {
|
||||
NS(NULL, id)
|
||||
},
|
||||
|
||||
# Freeze a value until the flush cycle completes
|
||||
freezeValue = function(x, name) {
|
||||
if (!is.reactivevalues(x))
|
||||
stop("x must be a reactivevalues object")
|
||||
|
||||
impl <- .subset2(x, 'impl')
|
||||
impl$freeze(name)
|
||||
self$onFlushed(function() impl$thaw(name))
|
||||
},
|
||||
|
||||
onSessionEnded = function(sessionEndedCallback) {
|
||||
"Registers the given callback to be invoked when the session is closed
|
||||
(i.e. the connection to the client has been severed). The return value
|
||||
@@ -540,10 +830,7 @@ ShinySession <- R6Class(
|
||||
# ..stacktraceon matches with the top-level ..stacktraceoff..
|
||||
private$closedCallbacks$invoke(onError = printError, ..stacktraceon = TRUE)
|
||||
flushReact()
|
||||
lapply(appsByToken$values(), function(shinysession) {
|
||||
shinysession$flushOutput()
|
||||
NULL
|
||||
})
|
||||
flushAllSessions()
|
||||
},
|
||||
isClosed = function() {
|
||||
return(self$closed)
|
||||
@@ -668,21 +955,43 @@ ShinySession <- R6Class(
|
||||
}
|
||||
},
|
||||
flushOutput = function() {
|
||||
if (self$isClosed())
|
||||
return()
|
||||
|
||||
# Return TRUE if there's any stuff to send to the client.
|
||||
hasPendingUpdates <- function() {
|
||||
# Even though progressKeys isn't sent to the client, we use it in this
|
||||
# check. This is because if it is non-empty, sending `values` to the
|
||||
# client tells it that the flushReact loop is finished, and the client
|
||||
# then knows to stop showing progress.
|
||||
return(
|
||||
length(private$progressKeys) != 0 ||
|
||||
length(private$invalidatedOutputValues) != 0 ||
|
||||
length(private$invalidatedOutputErrors) != 0 ||
|
||||
length(private$inputMessageQueue) != 0
|
||||
)
|
||||
}
|
||||
|
||||
# ..stacktraceon matches with the top-level ..stacktraceoff..
|
||||
private$flushCallbacks$invoke(..stacktraceon = TRUE)
|
||||
# ..stacktraceon matches with the top-level ..stacktraceoff..
|
||||
on.exit(private$flushedCallbacks$invoke(..stacktraceon = TRUE))
|
||||
|
||||
if (length(private$progressKeys) == 0
|
||||
&& length(private$invalidatedOutputValues) == 0
|
||||
&& length(private$invalidatedOutputErrors) == 0
|
||||
&& length(private$inputMessageQueue) == 0) {
|
||||
# Schedule execution of onFlushed callbacks
|
||||
on.exit({
|
||||
# ..stacktraceon matches with the top-level ..stacktraceoff..
|
||||
private$flushedCallbacks$invoke(..stacktraceon = TRUE)
|
||||
|
||||
# If one of the flushedCallbacks added anything to send to the client,
|
||||
# or invalidated any observers, set up another flush cycle.
|
||||
if (hasPendingUpdates() || .getReactiveEnvironment()$hasPendingFlush()) {
|
||||
scheduleFlush()
|
||||
}
|
||||
})
|
||||
|
||||
if (!hasPendingUpdates()) {
|
||||
return(invisible())
|
||||
}
|
||||
|
||||
private$progressKeys <- character(0)
|
||||
|
||||
values <- private$invalidatedOutputValues
|
||||
private$invalidatedOutputValues <- Map$new()
|
||||
errors <- private$invalidatedOutputErrors
|
||||
@@ -745,6 +1054,13 @@ ShinySession <- R6Class(
|
||||
private$sendResponse(msg, value)
|
||||
}
|
||||
},
|
||||
sendBinaryMessage = function(type, message) {
|
||||
typeBytes <- charToRaw(type)
|
||||
if (length(typeBytes) > 255) {
|
||||
stop("'type' argument is too long")
|
||||
}
|
||||
private$write(c(as.raw(length(typeBytes)), typeBytes, message))
|
||||
},
|
||||
sendCustomMessage = function(type, message) {
|
||||
data <- list()
|
||||
data[[type]] <- message
|
||||
@@ -778,9 +1094,92 @@ ShinySession <- R6Class(
|
||||
return(dereg)
|
||||
}
|
||||
},
|
||||
|
||||
setBookmarkExclude = function(names) {
|
||||
private$bookmarkExclude <- names
|
||||
},
|
||||
getBookmarkExclude = function() {
|
||||
private$bookmarkExclude
|
||||
},
|
||||
onBookmark = function(fun) {
|
||||
if (!is.function(fun) || length(fun) != 1) {
|
||||
stop("`fun` must be a function that takes one argument")
|
||||
}
|
||||
private$bookmarkCallbacks$register(fun)
|
||||
},
|
||||
onBookmarked = function(fun) {
|
||||
if (!is.function(fun) || length(fun) != 1) {
|
||||
stop("`fun` must be a function that takes one argument")
|
||||
}
|
||||
private$bookmarkedCallbacks$register(fun)
|
||||
},
|
||||
onRestore = function(fun) {
|
||||
if (!is.function(fun) || length(fun) != 1) {
|
||||
stop("`fun` must be a function that takes one argument")
|
||||
}
|
||||
private$restoreCallbacks$register(fun)
|
||||
},
|
||||
onRestored = function(fun) {
|
||||
if (!is.function(fun) || length(fun) != 1) {
|
||||
stop("`fun` must be a function that takes one argument")
|
||||
}
|
||||
private$restoredCallbacks$register(fun)
|
||||
},
|
||||
doBookmark = function() {
|
||||
# Get bookmarking store config
|
||||
store <- getShinyOption("bookmarkStore", default = "disable")
|
||||
if (store == "disable")
|
||||
return()
|
||||
|
||||
tryCatch(
|
||||
withLogErrors({
|
||||
saveState <- ShinySaveState$new(
|
||||
input = self$input,
|
||||
exclude = self$getBookmarkExclude(),
|
||||
onSave = function(state) {
|
||||
private$bookmarkCallbacks$invoke(state)
|
||||
}
|
||||
)
|
||||
|
||||
if (store == "server") {
|
||||
url <- saveShinySaveState(saveState)
|
||||
} else if (store == "url") {
|
||||
url <- encodeShinySaveState(saveState)
|
||||
} else {
|
||||
stop("Unknown store type: ", store)
|
||||
}
|
||||
|
||||
clientData <- self$clientData
|
||||
url <- paste0(
|
||||
clientData$url_protocol, "//",
|
||||
clientData$url_hostname,
|
||||
if (nzchar(clientData$url_port)) paste0(":", clientData$url_port),
|
||||
clientData$url_pathname,
|
||||
"?", url
|
||||
)
|
||||
|
||||
|
||||
# If onBookmarked callback was provided, invoke it; if not call
|
||||
# the default.
|
||||
if (private$bookmarkedCallbacks$count() > 0) {
|
||||
private$bookmarkedCallbacks$invoke(url)
|
||||
} else {
|
||||
showBookmarkUrlModal(url)
|
||||
}
|
||||
}),
|
||||
error = function(e) {
|
||||
msg <- paste0("Error bookmarking state: ", e$message)
|
||||
showNotification(msg, duration = NULL, type = "error")
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
|
||||
reactlog = function(logEntry) {
|
||||
# Use sendCustomMessage instead of sendMessage, because the handler in
|
||||
# shiny-showcase.js only has access to public API of the Shiny object.
|
||||
if (private$showcase)
|
||||
private$sendMessage(reactlog = logEntry)
|
||||
self$sendCustomMessage("reactlog", logEntry)
|
||||
},
|
||||
reload = function() {
|
||||
private$sendMessage(reload = TRUE)
|
||||
@@ -803,6 +1202,9 @@ ShinySession <- R6Class(
|
||||
)
|
||||
)
|
||||
},
|
||||
updateQueryString = function(queryString) {
|
||||
private$sendMessage(updateQueryString = list(queryString = queryString))
|
||||
},
|
||||
resetBrush = function(brushId) {
|
||||
private$sendMessage(
|
||||
resetBrush = list(
|
||||
@@ -836,6 +1238,9 @@ ShinySession <- R6Class(
|
||||
`@uploadEnd` = function(jobId, inputId) {
|
||||
fileData <- private$fileUploadContext$getUploadOperation(jobId)$finish()
|
||||
private$.input$set(inputId, fileData)
|
||||
|
||||
private$.input$setMeta(inputId, "shiny.serializer", serializerFileInput)
|
||||
|
||||
invisible()
|
||||
},
|
||||
# Provides a mechanism for handling direct HTTP requests that are posted
|
||||
@@ -947,6 +1352,10 @@ ShinySession <- R6Class(
|
||||
unlink(tmpdata)
|
||||
stop(attr(result, "condition", exact = TRUE))
|
||||
}
|
||||
if (!file.exists(tmpdata)) {
|
||||
# If no file was created, return a 404
|
||||
return(httpResponse(404, content = "404 Not found"))
|
||||
}
|
||||
return(httpResponse(
|
||||
200,
|
||||
download$contentType %OR% getContentType(filename),
|
||||
@@ -1017,10 +1426,11 @@ ShinySession <- R6Class(
|
||||
registerDataObj = function(name, data, filterFunc) {
|
||||
# abusing downloads at the moment
|
||||
self$downloads$set(name, list(data = data, filter = filterFunc))
|
||||
return(sprintf('session/%s/dataobj/%s?w=%s',
|
||||
return(sprintf('session/%s/dataobj/%s?w=%s&nonce=%s',
|
||||
URLencode(self$token, TRUE),
|
||||
URLencode(name, TRUE),
|
||||
workerId()))
|
||||
workerId(),
|
||||
URLencode(createUniqueId(8), TRUE)))
|
||||
},
|
||||
# This function suspends observers for hidden outputs and resumes observers
|
||||
# for un-hidden outputs.
|
||||
@@ -1186,10 +1596,72 @@ ShinySession <- R6Class(
|
||||
#' @param ... Options to set for the output observer.
|
||||
#' @export
|
||||
outputOptions <- function(x, name, ...) {
|
||||
if (!inherits(x, "shinyoutput"))
|
||||
if (!inherits(x, "shinyoutput")) {
|
||||
stop("x must be a shinyoutput object.")
|
||||
}
|
||||
|
||||
name <- .subset2(x, 'ns')(name)
|
||||
if (!missing(name)) {
|
||||
name <- .subset2(x, 'ns')(name)
|
||||
} else {
|
||||
name <- NULL
|
||||
}
|
||||
|
||||
.subset2(x, 'impl')$outputOptions(name, ...)
|
||||
}
|
||||
|
||||
|
||||
#' Add callbacks for Shiny session events
|
||||
#'
|
||||
#' These functions are for registering callbacks on Shiny session events.
|
||||
#' \code{onFlush} registers a function that will be called before Shiny flushes
|
||||
#' the reactive system. \code{onFlushed} registers a function that will be
|
||||
#' called after Shiny flushes the reactive system. \code{onSessionEnded}
|
||||
#' registers a function to be called after the client has disconnected.
|
||||
#'
|
||||
#' These functions should be called within the application's server function.
|
||||
#'
|
||||
#' All of these functions return a function which can be called with no
|
||||
#' arguments to cancel the registration.
|
||||
#'
|
||||
#' @param fun A callback function.
|
||||
#' @param once Should the function be run once, and then cleared, or should it
|
||||
#' re-run each time the event occurs. (Only for \code{onFlush} and
|
||||
#' \code{onFlushed}.)
|
||||
#' @param session A shiny session object.
|
||||
#'
|
||||
#' @export
|
||||
onFlush <- function(fun, once = TRUE, session = getDefaultReactiveDomain()) {
|
||||
session$onFlush(fun, once = once)
|
||||
}
|
||||
|
||||
#' @rdname onFlush
|
||||
#' @export
|
||||
onFlushed <- function(fun, once = TRUE, session = getDefaultReactiveDomain()) {
|
||||
session$onFlushed(fun, once = once)
|
||||
}
|
||||
|
||||
#' @rdname onFlush
|
||||
#' @export
|
||||
onSessionEnded <- function(fun, session = getDefaultReactiveDomain()) {
|
||||
session$onSessionEnded(fun)
|
||||
}
|
||||
|
||||
|
||||
scheduleFlush <- function() {
|
||||
timerCallbacks$schedule(0, function() {})
|
||||
}
|
||||
|
||||
flushAllSessions <- function() {
|
||||
lapply(appsByToken$values(), function(shinysession) {
|
||||
tryCatch(
|
||||
shinysession$flushOutput(),
|
||||
|
||||
stop = function(e) {
|
||||
# If there are any uncaught errors that bubbled up to here, close the
|
||||
# session.
|
||||
shinysession$close()
|
||||
}
|
||||
)
|
||||
NULL
|
||||
})
|
||||
}
|
||||
|
||||
38
R/shinyui.R
38
R/shinyui.R
@@ -44,7 +44,7 @@ renderPage <- function(ui, connection, showcase=0) {
|
||||
|
||||
shiny_deps <- list(
|
||||
htmlDependency("json2", "2014.02.04", c(href="shared"), script = "json2-min.js"),
|
||||
htmlDependency("jquery", "1.11.3", c(href="shared"), script = "jquery.min.js"),
|
||||
htmlDependency("jquery", "1.12.4", c(href="shared"), script = "jquery.min.js"),
|
||||
htmlDependency("babel-polyfill", "6.7.2", c(href="shared"), script = "babel-polyfill.min.js"),
|
||||
htmlDependency("shiny", utils::packageVersion("shiny"), c(href="shared"),
|
||||
script = if (getOption("shiny.minified", TRUE)) "shiny.min.js" else "shiny.js",
|
||||
@@ -64,7 +64,6 @@ renderPage <- function(ui, connection, showcase=0) {
|
||||
#'
|
||||
#' @param ui A user interace definition
|
||||
#' @return The user interface definition, without modifications or side effects.
|
||||
#'
|
||||
#' @export
|
||||
shinyUI <- function(ui) {
|
||||
.globals$ui <- list(ui)
|
||||
@@ -91,17 +90,34 @@ uiHttpHandler <- function(ui, uiPattern = "^/$") {
|
||||
if (!is.null(mode))
|
||||
showcaseMode <- mode
|
||||
}
|
||||
uiValue <- if (is.function(ui)) {
|
||||
if (length(formals(ui)) > 0) {
|
||||
# No corresponding ..stacktraceoff.., this is pure user code
|
||||
..stacktraceon..(ui(req))
|
||||
} else {
|
||||
# No corresponding ..stacktraceoff.., this is pure user code
|
||||
..stacktraceon..(ui())
|
||||
}
|
||||
|
||||
# Create a restore context using query string
|
||||
bookmarkStore <- getShinyOption("bookmarkStore", default = "disable")
|
||||
if (bookmarkStore == "disable") {
|
||||
# If bookmarking is disabled, use empty context
|
||||
restoreContext <- RestoreContext$new()
|
||||
} else {
|
||||
ui
|
||||
restoreContext <- RestoreContext$new(req$QUERY_STRING)
|
||||
}
|
||||
|
||||
withRestoreContext(restoreContext, {
|
||||
uiValue <- NULL
|
||||
|
||||
if (is.function(ui)) {
|
||||
if (length(formals(ui)) > 0) {
|
||||
# No corresponding ..stacktraceoff.., this is pure user code
|
||||
uiValue <- ..stacktraceon..(ui(req))
|
||||
} else {
|
||||
# No corresponding ..stacktraceoff.., this is pure user code
|
||||
uiValue <- ..stacktraceon..(ui())
|
||||
}
|
||||
} else {
|
||||
if (getCurrentRestoreContext()$active) {
|
||||
warning("Trying to restore saved app state, but UI code must be a function for this to work! See ?enableBookmarking")
|
||||
}
|
||||
uiValue <- ui
|
||||
}
|
||||
})
|
||||
if (is.null(uiValue))
|
||||
return(NULL)
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ globalVariables('func')
|
||||
#' dynamically generated UIs, such as those created by Shiny code snippets
|
||||
#' embedded in R Markdown documents).
|
||||
#' @return The \code{renderFunc} function, with annotations.
|
||||
#'
|
||||
#' @export
|
||||
markRenderFunction <- function(uiFunc, renderFunc, outputArgs = list()) {
|
||||
# a mutable object that keeps track of whether `useRenderFunction` has been
|
||||
@@ -248,7 +247,6 @@ renderImage <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
#' function, instead of the printed output.
|
||||
#'
|
||||
#' @example res/text-example.R
|
||||
#'
|
||||
#' @export
|
||||
renderPrint <- function(expr, env = parent.frame(), quoted = FALSE,
|
||||
width = getOption('width'), outputArgs=list()) {
|
||||
@@ -289,7 +287,6 @@ renderPrint <- function(expr, env = parent.frame(), quoted = FALSE,
|
||||
#' function, rather than the returned text value.
|
||||
#'
|
||||
#' @example res/text-example.R
|
||||
#'
|
||||
#' @export
|
||||
renderText <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
outputArgs=list()) {
|
||||
@@ -321,7 +318,6 @@ renderText <- function(expr, env=parent.frame(), quoted=FALSE,
|
||||
#' interactive R Markdown document.
|
||||
#'
|
||||
#' @seealso conditionalPanel
|
||||
#'
|
||||
#' @export
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
|
||||
20
R/timer.R
20
R/timer.R
@@ -22,6 +22,11 @@ TimerCallbacks <- R6Class(
|
||||
.times <<- data.frame()
|
||||
},
|
||||
schedule = function(millis, func) {
|
||||
# If args could fail to evaluate, let's make them do that before
|
||||
# we change any state
|
||||
force(millis)
|
||||
force(func)
|
||||
|
||||
id <- .nextId
|
||||
.nextId <<- .nextId + 1L
|
||||
|
||||
@@ -56,7 +61,7 @@ TimerCallbacks <- R6Class(
|
||||
},
|
||||
executeElapsed = function() {
|
||||
elapsed <- takeElapsed()
|
||||
if (length(elapsed) == 0)
|
||||
if (nrow(elapsed) == 0)
|
||||
return(FALSE)
|
||||
|
||||
for (id in elapsed$id) {
|
||||
@@ -71,3 +76,16 @@ TimerCallbacks <- R6Class(
|
||||
)
|
||||
|
||||
timerCallbacks <- TimerCallbacks$new()
|
||||
|
||||
scheduleTask <- function(millis, callback) {
|
||||
cancelled <- FALSE
|
||||
timerCallbacks$schedule(millis, function() {
|
||||
if (!cancelled)
|
||||
callback()
|
||||
})
|
||||
|
||||
function() {
|
||||
cancelled <<- TRUE
|
||||
callback <<- NULL # to allow for callback to be gc'ed
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -166,11 +204,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 +226,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
|
||||
@@ -229,7 +274,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 +460,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)
|
||||
))
|
||||
}
|
||||
|
||||
@@ -511,6 +556,9 @@ updateCheckboxGroupInput <- function(session, inputId, label = NULL,
|
||||
#' @export
|
||||
updateRadioButtons <- function(session, inputId, label = NULL, choices = NULL,
|
||||
selected = NULL, inline = FALSE) {
|
||||
if (!is.null(choices)) choices <- as.character(choices)
|
||||
if (!is.null(selected)) selected <- as.character(selected)
|
||||
|
||||
# you must select at least one radio button
|
||||
if (is.null(selected) && !is.null(choices)) selected <- choices[[1]]
|
||||
updateInputOptions(session, inputId, label, choices, selected, inline, type = 'radio')
|
||||
|
||||
219
R/utils.R
219
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,21 @@ anyUnnamed <- function(x) {
|
||||
any(!nzchar(nms))
|
||||
}
|
||||
|
||||
# Given two named vectors, join them together, and keep only the last element
|
||||
# with a given name in the resulting vector. If b has any elements with the same
|
||||
# name as elements in a, the element in a is dropped. Also, if there are any
|
||||
# duplicated names in a or b, only the last one with that name is kept.
|
||||
mergeVectors <- function(a, b) {
|
||||
if (anyUnnamed(a) || anyUnnamed(b)) {
|
||||
stop("Vectors must be either NULL or have names for all elements")
|
||||
}
|
||||
|
||||
x <- c(a, b)
|
||||
drop_idx <- duplicated(names(x), fromLast = TRUE)
|
||||
x[!drop_idx]
|
||||
}
|
||||
|
||||
|
||||
# Combine dir and (file)name into a file path. If a file already exists with a
|
||||
# name differing only by case, then use it instead.
|
||||
file.path.ci <- function(...) {
|
||||
@@ -235,6 +239,12 @@ find.file.ci <- function(...) {
|
||||
return(matches[1])
|
||||
}
|
||||
|
||||
# The function base::dir.exists was added in R 3.2.0, but for backward
|
||||
# compatibility we need to add this function
|
||||
dirExists <- function(paths) {
|
||||
file.exists(paths) & file.info(paths)$isdir
|
||||
}
|
||||
|
||||
# Attempt to join a path and relative path, and turn the result into a
|
||||
# (normalized) absolute path. The result will only be returned if it is an
|
||||
# existing file/directory and is a descendant of dir.
|
||||
@@ -400,7 +410,6 @@ makeFunction <- function(args = pairlist(), body, env = parent.frame()) {
|
||||
#'
|
||||
#' isolate(tripleA())
|
||||
#' # "text, text, text"
|
||||
#'
|
||||
#' @export
|
||||
exprToFunction <- function(expr, env=parent.frame(), quoted=FALSE) {
|
||||
if (!quoted) {
|
||||
@@ -434,7 +443,6 @@ exprToFunction <- function(expr, env=parent.frame(), quoted=FALSE) {
|
||||
#' the name of the calling function.
|
||||
#' @param wrappedWithLabel,..stacktraceon Advanced use only. For stack manipulation purposes; see
|
||||
#' \code{\link{stacktrace}}.
|
||||
#'
|
||||
#' @export
|
||||
installExprFunction <- function(expr, name, eval.env = parent.frame(2),
|
||||
quoted = FALSE,
|
||||
@@ -506,6 +514,8 @@ parseQueryString <- function(str, nested = FALSE) {
|
||||
str <- substr(str, 2, nchar(str))
|
||||
|
||||
pairs <- strsplit(str, '&', fixed = TRUE)[[1]]
|
||||
# Drop any empty items (if there's leading/trailing/consecutive '&' chars)
|
||||
pairs <- pairs[pairs != ""]
|
||||
pairs <- strsplit(pairs, '=', fixed = TRUE)
|
||||
|
||||
keys <- vapply(pairs, function(x) x[1], FUN.VALUE = character(1))
|
||||
@@ -553,13 +563,7 @@ shinyCallingHandlers <- function(expr) {
|
||||
return()
|
||||
|
||||
handle <- getOption('shiny.error')
|
||||
if (is.function(handle)) {
|
||||
if ("condition" %in% names(formals(handle))) {
|
||||
handle(condition = e)
|
||||
} else {
|
||||
handle()
|
||||
}
|
||||
}
|
||||
if (is.function(handle)) handle()
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -976,6 +980,62 @@ safeError <- function(error) {
|
||||
error
|
||||
}
|
||||
|
||||
#***********************************************************************#
|
||||
#**** Keep this function internal for now, may chnage in the future ****#
|
||||
#***********************************************************************#
|
||||
# #' Propagate an error through Shiny, but catch it before it throws
|
||||
# #'
|
||||
# #' Throws a type of exception that is caught by observers. When such an
|
||||
# #' exception is triggered, all reactive links are broken. So, essentially,
|
||||
# #' \code{reactiveStop()} behaves just like \code{stop()}, except that
|
||||
# #' instead of ending the session, it is silently swalowed by Shiny.
|
||||
# #'
|
||||
# #' This function should be used when you want to disrupt the reactive
|
||||
# #' links in a reactive chain, but do not want to end the session. For
|
||||
# #' example, this enables you to disallow certain inputs, but get back
|
||||
# #' to business as usual when valid inputs are re-entered.
|
||||
# #' \code{reactiveStop} is also called internally by Shiny to create
|
||||
# #' special errors, such as the ones generated by \code{\link{validate}()},
|
||||
# #' \code{\link{req}()} and \code{\link{cancelOutput}()}.
|
||||
# #'
|
||||
# #' @param message An optional error message.
|
||||
# #' @param class An optional class to add to the error.
|
||||
# #' @export
|
||||
# #' @examples
|
||||
# #' ## Note: the breaking of the reactive chain that happens in the app
|
||||
# #' ## below (when input$txt = 'bad' and input$allowBad = 'FALSE') is
|
||||
# #' ## easily visualized with `showReactLog()`
|
||||
# #'
|
||||
# #' ## Only run examples in interactive R sessions
|
||||
# #' if (interactive()) {
|
||||
# #'
|
||||
# #' ui <- fluidPage(
|
||||
# #' textInput('txt', 'Enter some text...'),
|
||||
# #' selectInput('allowBad', 'Allow the string \'bad\'?',
|
||||
# #' c('TRUE', 'FALSE'), selected = 'FALSE')
|
||||
# #' )
|
||||
# #'
|
||||
# #' server <- function(input, output) {
|
||||
# #' val <- reactive({
|
||||
# #' if (!(as.logical(input$allowBad))) {
|
||||
# #' if (identical(input$txt, "bad")) {
|
||||
# #' reactiveStop()
|
||||
# #' }
|
||||
# #' }
|
||||
## ' })
|
||||
# #'
|
||||
# #' observe({
|
||||
# #' val()
|
||||
# #' })
|
||||
# #' }
|
||||
# #'
|
||||
# #' shinyApp(ui, server)
|
||||
# #' }
|
||||
# #' @export
|
||||
reactiveStop <- function(message = "", class = NULL) {
|
||||
stopWithCondition(c("shiny.silent.error", class), message)
|
||||
}
|
||||
|
||||
#' Validate input values and other conditions
|
||||
#'
|
||||
#' For an output rendering function (e.g. \code{\link{renderPlot}()}), you may
|
||||
@@ -1032,15 +1092,16 @@ safeError <- function(error) {
|
||||
#' \code{shiny-output-error-} prepended to this value.
|
||||
#' @export
|
||||
#' @examples
|
||||
#' # in ui.R
|
||||
#' fluidPage(
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' checkboxGroupInput('in1', 'Check some letters', choices = head(LETTERS)),
|
||||
#' selectizeInput('in2', 'Select a state', choices = state.name),
|
||||
#' plotOutput('plot')
|
||||
#' )
|
||||
#'
|
||||
#' # in server.R
|
||||
#' function(input, output) {
|
||||
#' server <- function(input, output) {
|
||||
#' output$plot <- renderPlot({
|
||||
#' validate(
|
||||
#' need(input$in1, 'Check at least one letter!'),
|
||||
@@ -1049,6 +1110,10 @@ safeError <- function(error) {
|
||||
#' plot(1:10, main = paste(c(input$in1, input$in2), collapse = ', '))
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
#'
|
||||
#' }
|
||||
validate <- function(..., errorClass = character(0)) {
|
||||
results <- sapply(list(...), function(x) {
|
||||
# Detect NULL or NA
|
||||
@@ -1069,8 +1134,7 @@ validate <- function(..., errorClass = character(0)) {
|
||||
# There may be empty strings remaining; these are message-less failures that
|
||||
# started as FALSE
|
||||
results <- results[nzchar(results)]
|
||||
stopWithCondition(c("validation", "shiny.silent.error", errorClass),
|
||||
paste(results, collapse="\n"))
|
||||
reactiveStop(paste(results, collapse="\n"), c(errorClass, "validation"))
|
||||
}
|
||||
|
||||
#' @param expr An expression to test. The condition will pass if the expression
|
||||
@@ -1164,20 +1228,67 @@ need <- function(expr, message = paste(label, "must be provided"), label) {
|
||||
#'
|
||||
#' \code{req(input$a != 0)}
|
||||
#'
|
||||
#' \strong{Using \code{req(FALSE)}}
|
||||
#'
|
||||
#' You can use \code{req(FALSE)} (i.e. no condition) if you've already performed
|
||||
#' all the checks you needed to by that point and just want to stop the reactive
|
||||
#' chain now. There is no advantange to this, except perhaps ease of readibility
|
||||
#' if you have a complicated condition to check for (or perhaps if you'd like to
|
||||
#' divide your condition into nested \code{if} statements).
|
||||
#'
|
||||
#' \strong{Using \code{cancelOutput = TRUE}}
|
||||
#'
|
||||
#' When \code{req(..., cancelOutput = TRUE)} is used, the "silent" exception is
|
||||
#' also raised, but it is treated slightly differently if one or more outputs are
|
||||
#' currently being evaluated. In those cases, the reactive chain does not proceed
|
||||
#' or update, but the output(s) are left is whatever state they happen to be in
|
||||
#' (whatever was their last valid state).
|
||||
#'
|
||||
#' Note that this is always going to be the case if
|
||||
#' this is used inside an output context (e.g. \code{output$txt <- ...}). It may
|
||||
#' or may not be the case if it is used inside a non-output context (e.g.
|
||||
#' \code{\link{reactive}}, \code{\link{observe}} or \code{\link{observeEvent}})
|
||||
#' -- depending on whether or not there is an \code{output$...} that is triggered
|
||||
#' as a result of those calls. See the examples below for concrete scenarios.
|
||||
#'
|
||||
#' @param ... Values to check for truthiness.
|
||||
#' @param cancelOutput If \code{TRUE} and an output is being evaluated, stop
|
||||
#' processing as usual but instead of clearing the output, leave it in
|
||||
#' whatever state it happens to be in.
|
||||
#' @param x An expression whose truthiness value we want to determine
|
||||
#' @return The first value that was passed in.
|
||||
#'
|
||||
#' @export
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' ui <- fluidPage(
|
||||
#' textInput('data', 'Enter a dataset from the "datasets" package', 'cars'),
|
||||
#' p('(E.g. "cars", "mtcars", "pressure", "faithful")'), hr(),
|
||||
#' tableOutput('tbl')
|
||||
#' )
|
||||
#'
|
||||
#' server <- function(input, output) {
|
||||
#' output$tbl <- renderTable({
|
||||
#'
|
||||
#' ## to require that the user types something, use: `req(input$data)`
|
||||
#' ## but better: require that input$data is valid and leave the last
|
||||
#' ## valid table up
|
||||
#' req(exists(input$data, "package:datasets", inherits = FALSE),
|
||||
#' cancelOutput = TRUE)
|
||||
#'
|
||||
#' head(get(input$data, "package:datasets", inherits = FALSE))
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
req <- function(..., cancelOutput = FALSE) {
|
||||
dotloop(function(item) {
|
||||
if (!isTruthy(item)) {
|
||||
if (isTRUE(cancelOutput)) {
|
||||
cancelOutput()
|
||||
} else {
|
||||
stopWithCondition(c("validation", "shiny.silent.error"), "")
|
||||
reactiveStop(class = "validation")
|
||||
}
|
||||
}
|
||||
}, ...)
|
||||
@@ -1188,20 +1299,44 @@ req <- function(..., cancelOutput = FALSE) {
|
||||
invisible()
|
||||
}
|
||||
|
||||
#' Cancel processing of the current output
|
||||
#'
|
||||
#' Signals an error that Shiny treats specially if an output is currently being
|
||||
#' evaluated. Execution will stop, but rather than clearing the output (as
|
||||
#' \code{\link{req}} does) or showing an error message (as \code{\link{stop}}
|
||||
#' does), the output simply remains unchanged.
|
||||
#'
|
||||
#' If \code{cancelOutput} is called in any non-output context (like in an
|
||||
#' \code{\link{observe}} or \code{\link{observeEvent}}), the effect is the same
|
||||
#' as \code{\link{req}(FALSE)}.
|
||||
#'
|
||||
#' @export
|
||||
#***********************************************************************#
|
||||
#**** Keep this function internal for now, may chnage in the future ****#
|
||||
#***********************************************************************#
|
||||
# #' Cancel processing of the current output
|
||||
# #'
|
||||
# #' Signals an error that Shiny treats specially if an output is currently being
|
||||
# #' evaluated. Execution will stop, but rather than clearing the output (as
|
||||
# #' \code{\link{req}} does) or showing an error message (as \code{\link{stop}}
|
||||
# #' does), the output simply remains unchanged.
|
||||
# #'
|
||||
# #' If \code{cancelOutput} is called in any non-output context (like in an
|
||||
# #' \code{\link{observe}} or \code{\link{observeEvent}}), the effect is the same
|
||||
# #' as \code{\link{req}(FALSE)}.
|
||||
# #' @export
|
||||
# #' @examples
|
||||
# #' ## Only run examples in interactive R sessions
|
||||
# #' if (interactive()) {
|
||||
# #'
|
||||
# #' # uncomment the desired line to experiment with cancelOutput() vs. req()
|
||||
# #'
|
||||
# #' ui <- fluidPage(
|
||||
# #' textInput('txt', 'Enter text'),
|
||||
# #' textOutput('check')
|
||||
# #' )
|
||||
# #'
|
||||
# #' server <- function(input, output) {
|
||||
# #' output$check <- renderText({
|
||||
# #' # req(input$txt)
|
||||
# #' if (input$txt == 'hi') return('hi')
|
||||
# #' else if (input$txt == 'bye') return('bye')
|
||||
# #' # else cancelOutput()
|
||||
# #' })
|
||||
# #' }
|
||||
# #'
|
||||
# #' shinyApp(ui, server)
|
||||
# #' }
|
||||
cancelOutput <- function() {
|
||||
stopWithCondition(c("shiny.output.cancel", "shiny.silent.error"), "")
|
||||
reactiveStop(class = "shiny.output.cancel")
|
||||
}
|
||||
|
||||
# Execute a function against each element of ..., but only evaluate each element
|
||||
@@ -1216,6 +1351,8 @@ dotloop <- function(fun_, ...) {
|
||||
invisible()
|
||||
}
|
||||
|
||||
#' @export
|
||||
#' @rdname req
|
||||
isTruthy <- function(x) {
|
||||
if (inherits(x, 'try-error'))
|
||||
return(FALSE)
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
42
appveyor.yml
Normal file
42
appveyor.yml
Normal file
@@ -0,0 +1,42 @@
|
||||
# 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
|
||||
|
||||
# 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
|
||||
@@ -44,7 +44,9 @@ sd_section("UI Inputs",
|
||||
"sliderInput",
|
||||
"submitButton",
|
||||
"textInput",
|
||||
"textAreaInput",
|
||||
"passwordInput",
|
||||
"modalButton",
|
||||
"updateActionButton",
|
||||
"updateCheckboxGroupInput",
|
||||
"updateCheckboxInput",
|
||||
@@ -55,7 +57,9 @@ sd_section("UI Inputs",
|
||||
"updateSelectInput",
|
||||
"updateSliderInput",
|
||||
"updateTabsetPanel",
|
||||
"updateTextInput"
|
||||
"updateTextInput",
|
||||
"updateTextAreaInput",
|
||||
"updateQueryString"
|
||||
)
|
||||
)
|
||||
sd_section("UI Outputs",
|
||||
@@ -69,7 +73,11 @@ sd_section("UI Outputs",
|
||||
"verbatimTextOutput",
|
||||
"downloadButton",
|
||||
"Progress",
|
||||
"withProgress"
|
||||
"withProgress",
|
||||
"modalDialog",
|
||||
"urlModal",
|
||||
"showModal",
|
||||
"showNotification"
|
||||
)
|
||||
)
|
||||
sd_section("Interface builder functions",
|
||||
@@ -84,7 +92,9 @@ sd_section("Interface builder functions",
|
||||
"withTags",
|
||||
"htmlTemplate",
|
||||
"bootstrapLib",
|
||||
"suppressDependencies"
|
||||
"suppressDependencies",
|
||||
"insertUI",
|
||||
"removeUI"
|
||||
)
|
||||
)
|
||||
sd_section("Rendering functions",
|
||||
@@ -120,6 +130,7 @@ sd_section("Reactive constructs",
|
||||
"reactiveTimer",
|
||||
"reactiveValues",
|
||||
"reactiveValuesToList",
|
||||
"freezeReactiveValue",
|
||||
"domains",
|
||||
"showReactLog"
|
||||
)
|
||||
@@ -143,6 +154,16 @@ sd_section("Running",
|
||||
"viewer"
|
||||
)
|
||||
)
|
||||
sd_section("Bookmarking state",
|
||||
"Functions that are used for bookmarking and restoring state.",
|
||||
c(
|
||||
"bookmarkButton",
|
||||
"enableBookmarking",
|
||||
"setBookmarkExclude",
|
||||
"showBookmarkUrlModal",
|
||||
"onBookmark"
|
||||
)
|
||||
)
|
||||
sd_section("Extending Shiny",
|
||||
"Functions that are intended to be called by third-party packages that extend Shiny.",
|
||||
c(
|
||||
@@ -157,9 +178,12 @@ 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",
|
||||
"exprToFunction",
|
||||
"installExprFunction",
|
||||
"parseQueryString",
|
||||
|
||||
@@ -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
131
inst/www/shared/font-awesome/css/font-awesome.css
vendored
131
inst/www/shared/font-awesome/css/font-awesome.css
vendored
@@ -1,13 +1,13 @@
|
||||
/*!
|
||||
* Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome
|
||||
* Font Awesome 4.6.3 by @davegandy - http://fontawesome.io - @fontawesome
|
||||
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
|
||||
*/
|
||||
/* FONT PATH
|
||||
* -------------------------- */
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
src: url('../fonts/fontawesome-webfont.eot?v=4.5.0');
|
||||
src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg');
|
||||
src: url('../fonts/fontawesome-webfont.eot?v=4.6.3');
|
||||
src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.6.3') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.6.3') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.6.3') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.6.3') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.6.3#fontawesomeregular') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@@ -118,31 +118,31 @@
|
||||
}
|
||||
}
|
||||
.fa-rotate-90 {
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
|
||||
-webkit-transform: rotate(90deg);
|
||||
-ms-transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
.fa-rotate-180 {
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
|
||||
-webkit-transform: rotate(180deg);
|
||||
-ms-transform: rotate(180deg);
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
.fa-rotate-270 {
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
|
||||
-webkit-transform: rotate(270deg);
|
||||
-ms-transform: rotate(270deg);
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
.fa-flip-horizontal {
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
|
||||
-webkit-transform: scale(-1, 1);
|
||||
-ms-transform: scale(-1, 1);
|
||||
transform: scale(-1, 1);
|
||||
}
|
||||
.fa-flip-vertical {
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
|
||||
-webkit-transform: scale(1, -1);
|
||||
-ms-transform: scale(1, -1);
|
||||
transform: scale(1, -1);
|
||||
@@ -1383,7 +1383,7 @@
|
||||
.fa-digg:before {
|
||||
content: "\f1a6";
|
||||
}
|
||||
.fa-pied-piper:before {
|
||||
.fa-pied-piper-pp:before {
|
||||
content: "\f1a7";
|
||||
}
|
||||
.fa-pied-piper-alt:before {
|
||||
@@ -1509,6 +1509,7 @@
|
||||
content: "\f1ce";
|
||||
}
|
||||
.fa-ra:before,
|
||||
.fa-resistance:before,
|
||||
.fa-rebel:before {
|
||||
content: "\f1d0";
|
||||
}
|
||||
@@ -2084,3 +2085,115 @@
|
||||
.fa-percent:before {
|
||||
content: "\f295";
|
||||
}
|
||||
.fa-gitlab:before {
|
||||
content: "\f296";
|
||||
}
|
||||
.fa-wpbeginner:before {
|
||||
content: "\f297";
|
||||
}
|
||||
.fa-wpforms:before {
|
||||
content: "\f298";
|
||||
}
|
||||
.fa-envira:before {
|
||||
content: "\f299";
|
||||
}
|
||||
.fa-universal-access:before {
|
||||
content: "\f29a";
|
||||
}
|
||||
.fa-wheelchair-alt:before {
|
||||
content: "\f29b";
|
||||
}
|
||||
.fa-question-circle-o:before {
|
||||
content: "\f29c";
|
||||
}
|
||||
.fa-blind:before {
|
||||
content: "\f29d";
|
||||
}
|
||||
.fa-audio-description:before {
|
||||
content: "\f29e";
|
||||
}
|
||||
.fa-volume-control-phone:before {
|
||||
content: "\f2a0";
|
||||
}
|
||||
.fa-braille:before {
|
||||
content: "\f2a1";
|
||||
}
|
||||
.fa-assistive-listening-systems:before {
|
||||
content: "\f2a2";
|
||||
}
|
||||
.fa-asl-interpreting:before,
|
||||
.fa-american-sign-language-interpreting:before {
|
||||
content: "\f2a3";
|
||||
}
|
||||
.fa-deafness:before,
|
||||
.fa-hard-of-hearing:before,
|
||||
.fa-deaf:before {
|
||||
content: "\f2a4";
|
||||
}
|
||||
.fa-glide:before {
|
||||
content: "\f2a5";
|
||||
}
|
||||
.fa-glide-g:before {
|
||||
content: "\f2a6";
|
||||
}
|
||||
.fa-signing:before,
|
||||
.fa-sign-language:before {
|
||||
content: "\f2a7";
|
||||
}
|
||||
.fa-low-vision:before {
|
||||
content: "\f2a8";
|
||||
}
|
||||
.fa-viadeo:before {
|
||||
content: "\f2a9";
|
||||
}
|
||||
.fa-viadeo-square:before {
|
||||
content: "\f2aa";
|
||||
}
|
||||
.fa-snapchat:before {
|
||||
content: "\f2ab";
|
||||
}
|
||||
.fa-snapchat-ghost:before {
|
||||
content: "\f2ac";
|
||||
}
|
||||
.fa-snapchat-square:before {
|
||||
content: "\f2ad";
|
||||
}
|
||||
.fa-pied-piper:before {
|
||||
content: "\f2ae";
|
||||
}
|
||||
.fa-first-order:before {
|
||||
content: "\f2b0";
|
||||
}
|
||||
.fa-yoast:before {
|
||||
content: "\f2b1";
|
||||
}
|
||||
.fa-themeisle:before {
|
||||
content: "\f2b2";
|
||||
}
|
||||
.fa-google-plus-circle:before,
|
||||
.fa-google-plus-official:before {
|
||||
content: "\f2b3";
|
||||
}
|
||||
.fa-fa:before,
|
||||
.fa-font-awesome:before {
|
||||
content: "\f2b4";
|
||||
}
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
border: 0;
|
||||
}
|
||||
.sr-only-focusable:active,
|
||||
.sr-only-focusable:focus {
|
||||
position: static;
|
||||
width: auto;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
clip: auto;
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
@@ -169,7 +169,7 @@
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1408 608v-320q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h704q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v320 q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1792 1472v-512q0 -26 -19 -45t-45 -19t-45 19l-176 176l-652 -652q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l652 652l-176 176q-19 19 -19 45t19 45t45 19h512q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" d="M1184 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45zM1536 992v-704q0 -119 -84.5 -203.5t-203.5 -84.5h-320q-13 0 -22.5 9.5t-9.5 22.5 q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q66 0 113 47t47 113v704q0 66 -47 113t-113 47h-288h-11h-13t-11.5 1t-11.5 3t-8 5.5t-7 9t-2 13.5q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M458 653q-74 162 -74 371h-256v-96q0 -78 94.5 -162t235.5 -113zM1536 928v96h-256q0 -209 -74 -371q141 29 235.5 113t94.5 162zM1664 1056v-128q0 -71 -41.5 -143t-112 -130t-173 -97.5t-215.5 -44.5q-42 -54 -95 -95q-38 -34 -52.5 -72.5t-14.5 -89.5q0 -54 30.5 -91 t97.5 -37q75 0 133.5 -45.5t58.5 -114.5v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 69 58.5 114.5t133.5 45.5q67 0 97.5 37t30.5 91q0 51 -14.5 89.5t-52.5 72.5q-53 41 -95 95q-113 5 -215.5 44.5t-173 97.5t-112 130t-41.5 143v128q0 40 28 68t68 28h288v96 q0 66 47 113t113 47h576q66 0 113 -47t47 -113v-96h288q40 0 68 -28t28 -68z" />
|
||||
<glyph unicode="" d="M394 184q-8 -9 -20 3q-13 11 -4 19q8 9 20 -3q12 -11 4 -19zM352 245q9 -12 0 -19q-8 -6 -17 7t0 18q9 7 17 -6zM291 305q-5 -7 -13 -2q-10 5 -7 12q3 5 13 2q10 -5 7 -12zM322 271q-6 -7 -16 3q-9 11 -2 16q6 6 16 -3q9 -11 2 -16zM451 159q-4 -12 -19 -6q-17 4 -13 15 t19 7q16 -5 13 -16zM514 154q0 -11 -16 -11q-17 -2 -17 11q0 11 16 11q17 2 17 -11zM572 164q2 -10 -14 -14t-18 8t14 15q16 2 18 -9zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-224q-16 0 -24.5 1t-19.5 5t-16 14.5t-5 27.5v239q0 97 -52 142q57 6 102.5 18t94 39 t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103 q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -103t0.5 -68q0 -22 -11 -33.5t-22 -13t-33 -1.5 h-224q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" d="M519 336q4 6 -3 13q-9 7 -14 2q-4 -6 3 -13q9 -7 14 -2zM491 377q-5 7 -12 4q-6 -4 0 -12q7 -8 12 -5q6 4 0 13zM450 417q2 4 -5 8q-7 2 -8 -2q-3 -5 4 -8q8 -2 9 2zM471 394q2 1 1.5 4.5t-3.5 5.5q-6 7 -10 3t1 -11q6 -6 11 -2zM557 319q2 7 -9 11q-9 3 -13 -4 q-2 -7 9 -11q9 -3 13 4zM599 316q0 8 -12 8q-10 0 -10 -8t11 -8t11 8zM638 323q-2 7 -13 5t-9 -9q2 -8 12 -6t10 10zM1280 640q0 212 -150 362t-362 150t-362 -150t-150 -362q0 -167 98 -300.5t252 -185.5q18 -3 26.5 5t8.5 20q0 52 -1 95q-6 -1 -15.5 -2.5t-35.5 -2t-48 4 t-43.5 20t-29.5 41.5q-23 59 -57 74q-2 1 -4.5 3.5l-8 8t-7 9.5t4 7.5t19.5 3.5q6 0 15 -2t30 -15.5t33 -35.5q16 -28 37.5 -42t43.5 -14t38 3.5t30 9.5q7 47 33 69q-49 6 -86 18.5t-73 39t-55.5 76t-19.5 119.5q0 79 53 137q-24 62 5 136q19 6 54.5 -7.5t60.5 -29.5l26 -16 q58 17 128 17t128 -17q11 7 28.5 18t55.5 26t57 9q29 -74 5 -136q53 -58 53 -137q0 -57 -14 -100.5t-35.5 -70t-53.5 -44.5t-62.5 -26t-68.5 -12q35 -31 35 -95q0 -40 -0.5 -89t-0.5 -51q0 -12 8.5 -20t26.5 -5q154 52 252 185.5t98 300.5zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1280 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 288v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h427q21 -56 70.5 -92 t110.5 -36h256q61 0 110.5 36t70.5 92h427q40 0 68 -28t28 -68zM1339 936q-17 -40 -59 -40h-256v-448q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v448h-256q-42 0 -59 40q-17 39 14 69l448 448q18 19 45 19t45 -19l448 -448q31 -30 14 -69z" />
|
||||
<glyph unicode="" d="M1407 710q0 44 -7 113.5t-18 96.5q-12 30 -17 44t-9 36.5t-4 48.5q0 23 5 68.5t5 67.5q0 37 -10 55q-4 1 -13 1q-19 0 -58 -4.5t-59 -4.5q-60 0 -176 24t-175 24q-43 0 -94.5 -11.5t-85 -23.5t-89.5 -34q-137 -54 -202 -103q-96 -73 -159.5 -189.5t-88 -236t-24.5 -248.5 q0 -40 12.5 -120t12.5 -121q0 -23 -11 -66.5t-11 -65.5t12 -36.5t34 -14.5q24 0 72.5 11t73.5 11q57 0 169.5 -15.5t169.5 -15.5q181 0 284 36q129 45 235.5 152.5t166 245.5t59.5 275zM1535 712q0 -165 -70 -327.5t-196 -288t-281 -180.5q-124 -44 -326 -44 q-57 0 -170 14.5t-169 14.5q-24 0 -72.5 -14.5t-73.5 -14.5q-73 0 -123.5 55.5t-50.5 128.5q0 24 11 68t11 67q0 40 -12.5 120.5t-12.5 121.5q0 111 18 217.5t54.5 209.5t100.5 194t150 156q78 59 232 120q194 78 316 78q60 0 175.5 -24t173.5 -24q19 0 57 5t58 5 q81 0 118 -50.5t37 -134.5q0 -23 -5 -68t-5 -68q0 -10 1 -18.5t3 -17t4 -13.5t6.5 -16t6.5 -17q16 -40 25 -118.5t9 -136.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1408 296q0 -27 -10 -70.5t-21 -68.5q-21 -50 -122 -106q-94 -51 -186 -51q-27 0 -52.5 3.5t-57.5 12.5t-47.5 14.5t-55.5 20.5t-49 18q-98 35 -175 83q-128 79 -264.5 215.5t-215.5 264.5q-48 77 -83 175q-3 9 -18 49t-20.5 55.5t-14.5 47.5t-12.5 57.5t-3.5 52.5 q0 92 51 186q56 101 106 122q25 11 68.5 21t70.5 10q14 0 21 -3q18 -6 53 -76q11 -19 30 -54t35 -63.5t31 -53.5q3 -4 17.5 -25t21.5 -35.5t7 -28.5q0 -20 -28.5 -50t-62 -55t-62 -53t-28.5 -46q0 -9 5 -22.5t8.5 -20.5t14 -24t11.5 -19q76 -137 174 -235t235 -174 q2 -1 19 -11.5t24 -14t20.5 -8.5t22.5 -5q18 0 46 28.5t53 62t55 62t50 28.5q14 0 28.5 -7t35.5 -21.5t25 -17.5q25 -15 53.5 -31t63.5 -35t54 -30q70 -35 76 -53q3 -7 3 -21z" />
|
||||
@@ -178,7 +178,7 @@
|
||||
<glyph unicode="" d="M1280 343q0 11 -2 16q-3 8 -38.5 29.5t-88.5 49.5l-53 29q-5 3 -19 13t-25 15t-21 5q-18 0 -47 -32.5t-57 -65.5t-44 -33q-7 0 -16.5 3.5t-15.5 6.5t-17 9.5t-14 8.5q-99 55 -170.5 126.5t-126.5 170.5q-2 3 -8.5 14t-9.5 17t-6.5 15.5t-3.5 16.5q0 13 20.5 33.5t45 38.5 t45 39.5t20.5 36.5q0 10 -5 21t-15 25t-13 19q-3 6 -15 28.5t-25 45.5t-26.5 47.5t-25 40.5t-16.5 18t-16 2q-48 0 -101 -22q-46 -21 -80 -94.5t-34 -130.5q0 -16 2.5 -34t5 -30.5t9 -33t10 -29.5t12.5 -33t11 -30q60 -164 216.5 -320.5t320.5 -216.5q6 -2 30 -11t33 -12.5 t29.5 -10t33 -9t30.5 -5t34 -2.5q57 0 130.5 34t94.5 80q22 53 22 101zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1620 1128q-67 -98 -162 -167q1 -14 1 -42q0 -130 -38 -259.5t-115.5 -248.5t-184.5 -210.5t-258 -146t-323 -54.5q-271 0 -496 145q35 -4 78 -4q225 0 401 138q-105 2 -188 64.5t-114 159.5q33 -5 61 -5q43 0 85 11q-112 23 -185.5 111.5t-73.5 205.5v4q68 -38 146 -41 q-66 44 -105 115t-39 154q0 88 44 163q121 -149 294.5 -238.5t371.5 -99.5q-8 38 -8 74q0 134 94.5 228.5t228.5 94.5q140 0 236 -102q109 21 205 78q-37 -115 -142 -178q93 10 186 50z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M959 1524v-264h-157q-86 0 -116 -36t-30 -108v-189h293l-39 -296h-254v-759h-306v759h-255v296h255v218q0 186 104 288.5t277 102.5q147 0 228 -12z" />
|
||||
<glyph unicode="" d="M1536 640q0 -251 -146.5 -451.5t-378.5 -277.5q-27 -5 -39.5 7t-12.5 30v211q0 97 -52 142q57 6 102.5 18t94 39t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5 q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23 q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -89t0.5 -54q0 -18 -13 -30t-40 -7q-232 77 -378.5 277.5t-146.5 451.5q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5q0 -251 -146.5 -451.5t-378.5 -277.5q-27 -5 -40 7t-13 30q0 3 0.5 76.5t0.5 134.5q0 97 -52 142q57 6 102.5 18t94 39t81 66.5t53 105t20.5 150.5q0 119 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24 q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-85 13.5q-45 -113 -8 -204q-79 -87 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-39 -36 -49 -103q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5 t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -88.5t0.5 -54.5q0 -18 -13 -30t-40 -7q-232 77 -378.5 277.5t-146.5 451.5q0 209 103 385.5t279.5 279.5t385.5 103zM291 305q3 7 -7 12 q-10 3 -13 -2q-3 -7 7 -12q9 -6 13 2zM322 271q7 5 -2 16q-10 9 -16 3q-7 -5 2 -16q10 -10 16 -3zM352 226q9 7 0 19q-8 13 -17 6q-9 -5 0 -18t17 -7zM394 184q8 8 -4 19q-12 12 -20 3q-9 -8 4 -19q12 -12 20 -3zM451 159q3 11 -13 16q-15 4 -19 -7t13 -15q15 -6 19 6z M514 154q0 13 -17 11q-16 0 -16 -11q0 -13 17 -11q16 0 16 11zM572 164q-2 11 -18 9q-16 -3 -14 -15t18 -8t14 14z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1664 960v-256q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45v256q0 106 -75 181t-181 75t-181 -75t-75 -181v-192h96q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h672v192q0 185 131.5 316.5t316.5 131.5 t316.5 -131.5t131.5 -316.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M1760 1408q66 0 113 -47t47 -113v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600zM160 1280q-13 0 -22.5 -9.5t-9.5 -22.5v-224h1664v224q0 13 -9.5 22.5t-22.5 9.5h-1600zM1760 0q13 0 22.5 9.5t9.5 22.5v608h-1664v-608 q0 -13 9.5 -22.5t22.5 -9.5h1600zM256 128v128h256v-128h-256zM640 128v128h384v-128h-384z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M384 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 69q2 -28 -17 -48q-18 -21 -47 -21h-135q-25 0 -43 16.5t-20 41.5q-22 229 -184.5 391.5t-391.5 184.5q-25 2 -41.5 20t-16.5 43v135q0 29 21 47q17 17 43 17h5q160 -13 306 -80.5 t259 -181.5q114 -113 181.5 -259t80.5 -306zM1408 67q2 -27 -18 -47q-18 -20 -46 -20h-143q-26 0 -44.5 17.5t-19.5 42.5q-12 215 -101 408.5t-231.5 336t-336 231.5t-408.5 102q-25 1 -42.5 19.5t-17.5 43.5v143q0 28 20 46q18 18 44 18h3q262 -13 501.5 -120t425.5 -294 q187 -186 294 -425.5t120 -501.5z" />
|
||||
@@ -363,7 +363,7 @@
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1280 640q0 37 -30 54l-512 320q-31 20 -65 2q-33 -18 -33 -56v-640q0 -38 33 -56q16 -8 31 -8q20 0 34 10l512 320q30 17 30 54zM1792 640q0 -96 -1 -150t-8.5 -136.5t-22.5 -147.5q-16 -73 -69 -123t-124 -58q-222 -25 -671 -25t-671 25q-71 8 -124.5 58t-69.5 123 q-14 65 -21.5 147.5t-8.5 136.5t-1 150t1 150t8.5 136.5t22.5 147.5q16 73 69 123t124 58q222 25 671 25t671 -25q71 -8 124.5 -58t69.5 -123q14 -65 21.5 -147.5t8.5 -136.5t1 -150z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M402 829l494 -305l-342 -285l-490 319zM1388 274v-108l-490 -293v-1l-1 1l-1 -1v1l-489 293v108l147 -96l342 284v2l1 -1l1 1v-2l343 -284zM554 1418l342 -285l-494 -304l-338 270zM1390 829l338 -271l-489 -319l-343 285zM1239 1418l489 -319l-338 -270l-494 304z" />
|
||||
<glyph unicode="" d="M1289 -96h-1118v480h-160v-640h1438v640h-160v-480zM347 428l33 157l783 -165l-33 -156zM450 802l67 146l725 -339l-67 -145zM651 1158l102 123l614 -513l-102 -123zM1048 1536l477 -641l-128 -96l-477 641zM330 65v159h800v-159h-800z" />
|
||||
<glyph unicode="" d="M1362 110v648h-135q20 -63 20 -131q0 -126 -64 -232.5t-174 -168.5t-240 -62q-197 0 -337 135.5t-140 327.5q0 68 20 131h-141v-648q0 -26 17.5 -43.5t43.5 -17.5h1069q25 0 43 17.5t18 43.5zM1078 643q0 124 -90.5 211.5t-218.5 87.5q-127 0 -217.5 -87.5t-90.5 -211.5 t90.5 -211.5t217.5 -87.5q128 0 218.5 87.5t90.5 211.5zM1362 1003v165q0 28 -20 48.5t-49 20.5h-174q-29 0 -49 -20.5t-20 -48.5v-165q0 -29 20 -49t49 -20h174q29 0 49 20t20 49zM1536 1211v-1142q0 -81 -58 -139t-139 -58h-1142q-81 0 -139 58t-58 139v1142q0 81 58 139 t139 58h1142q81 0 139 -58t58 -139z" />
|
||||
<glyph unicode="" d="M1024 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1162 640q0 -164 -115 -279t-279 -115t-279 115t-115 279t115 279t279 115t279 -115t115 -279zM1270 1050q0 -38 -27 -65t-65 -27t-65 27t-27 65t27 65t65 27t65 -27t27 -65zM768 1270 q-7 0 -76.5 0.5t-105.5 0t-96.5 -3t-103 -10t-71.5 -18.5q-50 -20 -88 -58t-58 -88q-11 -29 -18.5 -71.5t-10 -103t-3 -96.5t0 -105.5t0.5 -76.5t-0.5 -76.5t0 -105.5t3 -96.5t10 -103t18.5 -71.5q20 -50 58 -88t88 -58q29 -11 71.5 -18.5t103 -10t96.5 -3t105.5 0t76.5 0.5 t76.5 -0.5t105.5 0t96.5 3t103 10t71.5 18.5q50 20 88 58t58 88q11 29 18.5 71.5t10 103t3 96.5t0 105.5t-0.5 76.5t0.5 76.5t0 105.5t-3 96.5t-10 103t-18.5 71.5q-20 50 -58 88t-88 58q-29 11 -71.5 18.5t-103 10t-96.5 3t-105.5 0t-76.5 -0.5zM1536 640q0 -229 -5 -317 q-10 -208 -124 -322t-322 -124q-88 -5 -317 -5t-317 5q-208 10 -322 124t-124 322q-5 88 -5 317t5 317q10 208 124 322t322 124q88 5 317 5t317 -5q208 -10 322 -124t124 -322q5 -88 5 -317z" />
|
||||
<glyph unicode="" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM698 640q0 88 -62 150t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150zM1262 640q0 88 -62 150 t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150z" />
|
||||
<glyph unicode="" d="M768 914l201 -306h-402zM1133 384h94l-459 691l-459 -691h94l104 160h522zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M815 677q8 -63 -50.5 -101t-111.5 -6q-39 17 -53.5 58t-0.5 82t52 58q36 18 72.5 12t64 -35.5t27.5 -67.5zM926 698q-14 107 -113 164t-197 13q-63 -28 -100.5 -88.5t-34.5 -129.5q4 -91 77.5 -155t165.5 -56q91 8 152 84t50 168zM1165 1240q-20 27 -56 44.5t-58 22 t-71 12.5q-291 47 -566 -2q-43 -7 -66 -12t-55 -22t-50 -43q30 -28 76 -45.5t73.5 -22t87.5 -11.5q228 -29 448 -1q63 8 89.5 12t72.5 21.5t75 46.5zM1222 205q-8 -26 -15.5 -76.5t-14 -84t-28.5 -70t-58 -56.5q-86 -48 -189.5 -71.5t-202 -22t-201.5 18.5q-46 8 -81.5 18 t-76.5 27t-73 43.5t-52 61.5q-25 96 -57 292l6 16l18 9q223 -148 506.5 -148t507.5 148q21 -6 24 -23t-5 -45t-8 -37zM1403 1166q-26 -167 -111 -655q-5 -30 -27 -56t-43.5 -40t-54.5 -31q-252 -126 -610 -88q-248 27 -394 139q-15 12 -25.5 26.5t-17 35t-9 34t-6 39.5 t-5.5 35q-9 50 -26.5 150t-28 161.5t-23.5 147.5t-22 158q3 26 17.5 48.5t31.5 37.5t45 30t46 22.5t48 18.5q125 46 313 64q379 37 676 -50q155 -46 215 -122q16 -20 16.5 -51t-5.5 -54z" />
|
||||
@@ -484,7 +484,7 @@
|
||||
<glyph unicode="" horiz-adv-x="2048" d="M1024 13q-20 0 -93 73.5t-73 93.5q0 32 62.5 54t103.5 22t103.5 -22t62.5 -54q0 -20 -73 -93.5t-93 -73.5zM1294 284q-2 0 -40 25t-101.5 50t-128.5 25t-128.5 -25t-101 -50t-40.5 -25q-18 0 -93.5 75t-75.5 93q0 13 10 23q78 77 196 121t233 44t233 -44t196 -121 q10 -10 10 -23q0 -18 -75.5 -93t-93.5 -75zM1567 556q-11 0 -23 8q-136 105 -252 154.5t-268 49.5q-85 0 -170.5 -22t-149 -53t-113.5 -62t-79 -53t-31 -22q-17 0 -92 75t-75 93q0 12 10 22q132 132 320 205t380 73t380 -73t320 -205q10 -10 10 -22q0 -18 -75 -93t-92 -75z M1838 827q-11 0 -22 9q-179 157 -371.5 236.5t-420.5 79.5t-420.5 -79.5t-371.5 -236.5q-11 -9 -22 -9q-17 0 -92.5 75t-75.5 93q0 13 10 23q187 186 445 288t527 102t527 -102t445 -288q10 -10 10 -23q0 -18 -75.5 -93t-92.5 -75z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M384 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5 t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5 t37.5 90.5zM384 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 768q0 53 -37.5 90.5t-90.5 37.5 t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1536 0v384q0 52 -38 90t-90 38t-90 -38t-38 -90v-384q0 -52 38 -90t90 -38t90 38t38 90zM1152 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5z M1536 1088v256q0 26 -19 45t-45 19h-1280q-26 0 -45 -19t-19 -45v-256q0 -26 19 -45t45 -19h1280q26 0 45 19t19 45zM1536 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1408v-1536q0 -52 -38 -90t-90 -38 h-1408q-52 0 -90 38t-38 90v1536q0 52 38 90t90 38h1408q52 0 90 -38t38 -90z" />
|
||||
<glyph unicode="" d="M1519 890q18 -84 -4 -204q-87 -444 -565 -444h-44q-25 0 -44 -16.5t-24 -42.5l-4 -19l-55 -346l-2 -15q-5 -26 -24.5 -42.5t-44.5 -16.5h-251q-21 0 -33 15t-9 36q9 56 26.5 168t26.5 168t27 167.5t27 167.5q5 37 43 37h131q133 -2 236 21q175 39 287 144q102 95 155 246 q24 70 35 133q1 6 2.5 7.5t3.5 1t6 -3.5q79 -59 98 -162zM1347 1172q0 -107 -46 -236q-80 -233 -302 -315q-113 -40 -252 -42q0 -1 -90 -1l-90 1q-100 0 -118 -96q-2 -8 -85 -530q-1 -10 -12 -10h-295q-22 0 -36.5 16.5t-11.5 38.5l232 1471q5 29 27.5 48t51.5 19h598 q34 0 97.5 -13t111.5 -32q107 -41 163.5 -123t56.5 -196z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M602 949q19 -61 31 -123.5t17 -141.5t-14 -159t-62 -145q-21 81 -67 157t-95.5 127t-99 90.5t-78.5 57.5t-33 19q-62 34 -81.5 100t14.5 128t101 81.5t129 -14.5q138 -83 238 -177zM927 1236q11 -25 20.5 -46t36.5 -100.5t42.5 -150.5t25.5 -179.5t0 -205.5t-47.5 -209.5 t-105.5 -208.5q-51 -72 -138 -72q-54 0 -98 31q-57 40 -69 109t28 127q60 85 81 195t13 199.5t-32 180.5t-39 128t-22 52q-31 63 -8.5 129.5t85.5 97.5q34 17 75 17q47 0 88.5 -25t63.5 -69zM1248 567q-17 -160 -72 -311q-17 131 -63 246q25 174 -5 361q-27 178 -94 342 q114 -90 212 -211q9 -37 15 -80q26 -179 7 -347zM1520 1440q9 -17 23.5 -49.5t43.5 -117.5t50.5 -178t34 -227.5t5 -269t-47 -300t-112.5 -323.5q-22 -48 -66 -75.5t-95 -27.5q-39 0 -74 16q-67 31 -92.5 100t4.5 136q58 126 90 257.5t37.5 239.5t-3.5 213.5t-26.5 180.5 t-38.5 138.5t-32.5 90t-15.5 32.5q-34 65 -11.5 135.5t87.5 104.5q37 20 81 20q49 0 91.5 -25.5t66.5 -70.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M441 864q32 0 52 -26q266 -364 362 -774h-446q-127 441 -367 749q-12 16 -3 33.5t29 17.5h373zM1000 507q-49 -199 -125 -393q-79 310 -256 594q40 221 44 449q211 -340 337 -650zM1099 1216q235 -324 384.5 -698.5t184.5 -773.5h-451q-41 665 -553 1472h435zM1792 640 q0 -424 -101 -812q-67 560 -359 1083q-25 301 -106 584q-4 16 5.5 28.5t25.5 12.5h359q21 0 38.5 -13t22.5 -33q115 -409 115 -850z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M1975 546h-138q14 37 66 179l3 9q4 10 10 26t9 26l12 -55zM531 611l-58 295q-11 54 -75 54h-268l-2 -13q311 -79 403 -336zM710 960l-162 -438l-17 89q-26 70 -85 129.5t-131 88.5l135 -510h175l261 641h-176zM849 318h166l104 642h-166zM1617 944q-69 27 -149 27 q-123 0 -201 -59t-79 -153q-1 -102 145 -174q48 -23 67 -41t19 -39q0 -30 -30 -46t-69 -16q-86 0 -156 33l-22 11l-23 -144q74 -34 185 -34q130 -1 208.5 59t80.5 160q0 106 -140 174q-49 25 -71 42t-22 38q0 22 24.5 38.5t70.5 16.5q70 1 124 -24l15 -8zM2042 960h-128 q-65 0 -87 -54l-246 -588h174l35 96h212q5 -22 20 -96h154zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M671 603h-13q-47 0 -47 -32q0 -22 20 -22q17 0 28 15t12 39zM1066 639h62v3q1 4 0.5 6.5t-1 7t-2 8t-4.5 6.5t-7.5 5t-11.5 2q-28 0 -36 -38zM1606 603h-12q-48 0 -48 -32q0 -22 20 -22q17 0 28 15t12 39zM1925 629q0 41 -30 41q-19 0 -31 -20t-12 -51q0 -42 28 -42 q20 0 32.5 20t12.5 52zM480 770h87l-44 -262h-56l32 201l-71 -201h-39l-4 200l-34 -200h-53l44 262h81l2 -163zM733 663q0 -6 -4 -42q-16 -101 -17 -113h-47l1 22q-20 -26 -58 -26q-23 0 -37.5 16t-14.5 42q0 39 26 60.5t73 21.5q14 0 23 -1q0 3 0.5 5.5t1 4.5t0.5 3 q0 20 -36 20q-29 0 -59 -10q0 4 7 48q38 11 67 11q74 0 74 -62zM889 721l-8 -49q-22 3 -41 3q-27 0 -27 -17q0 -8 4.5 -12t21.5 -11q40 -19 40 -60q0 -72 -87 -71q-34 0 -58 6q0 2 7 49q29 -8 51 -8q32 0 32 19q0 7 -4.5 11.5t-21.5 12.5q-43 20 -43 59q0 72 84 72 q30 0 50 -4zM977 721h28l-7 -52h-29q-2 -17 -6.5 -40.5t-7 -38.5t-2.5 -18q0 -16 19 -16q8 0 16 2l-8 -47q-21 -7 -40 -7q-43 0 -45 47q0 12 8 56q3 20 25 146h55zM1180 648q0 -23 -7 -52h-111q-3 -22 10 -33t38 -11q30 0 58 14l-9 -54q-30 -8 -57 -8q-95 0 -95 95 q0 55 27.5 90.5t69.5 35.5q35 0 55.5 -21t20.5 -56zM1319 722q-13 -23 -22 -62q-22 2 -31 -24t-25 -128h-56l3 14q22 130 29 199h51l-3 -33q14 21 25.5 29.5t28.5 4.5zM1506 763l-9 -57q-28 14 -50 14q-31 0 -51 -27.5t-20 -70.5q0 -30 13.5 -47t38.5 -17q21 0 48 13 l-10 -59q-28 -8 -50 -8q-45 0 -71.5 30.5t-26.5 82.5q0 70 35.5 114.5t91.5 44.5q26 0 61 -13zM1668 663q0 -18 -4 -42q-13 -79 -17 -113h-46l1 22q-20 -26 -59 -26q-23 0 -37 16t-14 42q0 39 25.5 60.5t72.5 21.5q15 0 23 -1q2 7 2 13q0 20 -36 20q-29 0 -59 -10q0 4 8 48 q38 11 67 11q73 0 73 -62zM1809 722q-14 -24 -21 -62q-23 2 -31.5 -23t-25.5 -129h-56l3 14q19 104 29 199h52q0 -11 -4 -33q15 21 26.5 29.5t27.5 4.5zM1950 770h56l-43 -262h-53l3 19q-23 -23 -52 -23q-31 0 -49.5 24t-18.5 64q0 53 27.5 92t64.5 39q31 0 53 -29z M2061 640q0 148 -72.5 273t-198 198t-273.5 73q-181 0 -328 -110q127 -116 171 -284h-50q-44 150 -158 253q-114 -103 -158 -253h-50q44 168 171 284q-147 110 -328 110q-148 0 -273.5 -73t-198 -198t-72.5 -273t72.5 -273t198 -198t273.5 -73q181 0 328 110 q-120 111 -165 264h50q46 -138 152 -233q106 95 152 233h50q-45 -153 -165 -264q147 -110 328 -110q148 0 273.5 73t198 198t72.5 273zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M313 759q0 -51 -36 -84q-29 -26 -89 -26h-17v220h17q61 0 89 -27q36 -31 36 -83zM2089 824q0 -52 -64 -52h-19v101h20q63 0 63 -49zM380 759q0 74 -50 120.5t-129 46.5h-95v-333h95q74 0 119 38q60 51 60 128zM410 593h65v333h-65v-333zM730 694q0 40 -20.5 62t-75.5 42 q-29 10 -39.5 19t-10.5 23q0 16 13.5 26.5t34.5 10.5q29 0 53 -27l34 44q-41 37 -98 37q-44 0 -74 -27.5t-30 -67.5q0 -35 18 -55.5t64 -36.5q37 -13 45 -19q19 -12 19 -34q0 -20 -14 -33.5t-36 -13.5q-48 0 -71 44l-42 -40q44 -64 115 -64q51 0 83 30.5t32 79.5zM1008 604 v77q-37 -37 -78 -37q-49 0 -80.5 32.5t-31.5 82.5q0 48 31.5 81.5t77.5 33.5q43 0 81 -38v77q-40 20 -80 20q-74 0 -125.5 -50.5t-51.5 -123.5t51 -123.5t125 -50.5q42 0 81 19zM2240 0v527q-65 -40 -144.5 -84t-237.5 -117t-329.5 -137.5t-417.5 -134.5t-504 -118h1569 q26 0 45 19t19 45zM1389 757q0 75 -53 128t-128 53t-128 -53t-53 -128t53 -128t128 -53t128 53t53 128zM1541 584l144 342h-71l-90 -224l-89 224h-71l142 -342h35zM1714 593h184v56h-119v90h115v56h-115v74h119v57h-184v-333zM2105 593h80l-105 140q76 16 76 94q0 47 -31 73 t-87 26h-97v-333h65v133h9zM2304 1274v-1268q0 -56 -38.5 -95t-93.5 -39h-2040q-55 0 -93.5 39t-38.5 95v1268q0 56 38.5 95t93.5 39h2040q55 0 93.5 -39t38.5 -95z" />
|
||||
@@ -626,7 +626,7 @@
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M0 32v608h2304v-608q0 -66 -47 -113t-113 -47h-1984q-66 0 -113 47t-47 113zM640 256v-128h384v128h-384zM256 256v-128h256v128h-256zM2144 1408q66 0 113 -47t47 -113v-224h-2304v224q0 66 47 113t113 47h1984z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1549 857q55 0 85.5 -28.5t30.5 -83.5t-34 -82t-91 -27h-136v-177h-25v398h170zM1710 267l-4 -11l-5 -10q-113 -230 -330.5 -366t-474.5 -136q-182 0 -348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71q244 0 454.5 -124t329.5 -338l2 -4l8 -16 q-30 -15 -136.5 -68.5t-163.5 -84.5q-6 -3 -479 -268q384 -183 799 -366zM896 -234q250 0 462.5 132.5t322.5 357.5l-287 129q-72 -140 -206 -222t-292 -82q-151 0 -280 75t-204 204t-75 280t75 280t204 204t280 75t280 -73.5t204 -204.5l280 143q-116 208 -321 329 t-443 121q-119 0 -232.5 -31.5t-209 -87.5t-176.5 -137t-137 -176.5t-87.5 -209t-31.5 -232.5t31.5 -232.5t87.5 -209t137 -176.5t176.5 -137t209 -87.5t232.5 -31.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1427 827l-614 386l92 151h855zM405 562l-184 116v858l1183 -743zM1424 697l147 -95v-858l-532 335zM1387 718l-500 -802h-855l356 571z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M640 528v224q0 16 -16 16h-96q-16 0 -16 -16v-224q0 -16 16 -16h96q16 0 16 16zM1152 528v224q0 16 -16 16h-96q-16 0 -16 -16v-224q0 -16 16 -16h96q16 0 16 16zM1664 496v-752h-640v320q0 80 -56 136t-136 56t-136 -56t-56 -136v-320h-640v752q0 16 16 16h96 q16 0 16 -16v-112h128v624q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h16v393q-32 19 -32 55q0 26 19 45t45 19t45 -19t19 -45q0 -36 -32 -55v-9h272q16 0 16 -16v-224q0 -16 -16 -16h-272v-128h16q16 0 16 -16v-112h128 v112q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h96q16 0 16 -16v-624h128v112q0 16 16 16h96q16 0 16 -16z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M640 528v224q0 16 -16 16h-96q-16 0 -16 -16v-224q0 -16 16 -16h96q16 0 16 16zM1152 528v224q0 16 -16 16h-96q-16 0 -16 -16v-224q0 -16 16 -16h96q16 0 16 16zM1664 496v-752h-640v320q0 80 -56 136t-136 56t-136 -56t-56 -136v-320h-640v752q0 16 16 16h96 q16 0 16 -16v-112h128v624q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h96q16 0 16 -16v-112h128v112q0 6 2.5 9.5t8.5 5t9.5 2t11.5 0t9 -0.5v391q-32 15 -32 50q0 23 16.5 39t38.5 16t38.5 -16t16.5 -39q0 -35 -32 -50v-17q45 10 83 10q21 0 59.5 -7.5t54.5 -7.5 q17 0 47 7.5t37 7.5q16 0 16 -16v-210q0 -15 -35 -21.5t-62 -6.5q-18 0 -54.5 7.5t-55.5 7.5q-40 0 -90 -12v-133q1 0 9 0.5t11.5 0t9.5 -2t8.5 -5t2.5 -9.5v-112h128v112q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h96q16 0 16 -16v-624h128v112q0 16 16 16h96 q16 0 16 -16z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M2288 731q16 -8 16 -27t-16 -27l-320 -192q-8 -5 -16 -5q-9 0 -16 4q-16 10 -16 28v128h-858q37 -58 83 -165q16 -37 24.5 -55t24 -49t27 -47t27 -34t31.5 -26t33 -8h96v96q0 14 9 23t23 9h320q14 0 23 -9t9 -23v-320q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v96h-96 q-32 0 -61 10t-51 23.5t-45 40.5t-37 46t-33.5 57t-28.5 57.5t-28 60.5q-23 53 -37 81.5t-36 65t-44.5 53.5t-46.5 17h-360q-22 -84 -91 -138t-157 -54q-106 0 -181 75t-75 181t75 181t181 75q88 0 157 -54t91 -138h104q24 0 46.5 17t44.5 53.5t36 65t37 81.5q19 41 28 60.5 t28.5 57.5t33.5 57t37 46t45 40.5t51 23.5t61 10h107q21 57 70 92.5t111 35.5q80 0 136 -56t56 -136t-56 -136t-136 -56q-62 0 -111 35.5t-70 92.5h-107q-17 0 -33 -8t-31.5 -26t-27 -34t-27 -47t-24 -49t-24.5 -55q-46 -107 -83 -165h1114v128q0 18 16 28t32 -1z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1150 774q0 -56 -39.5 -95t-95.5 -39h-253v269h253q56 0 95.5 -39.5t39.5 -95.5zM1329 774q0 130 -91.5 222t-222.5 92h-433v-896h180v269h253q130 0 222 91.5t92 221.5zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348 t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M1645 438q0 59 -34 106.5t-87 68.5q-7 -45 -23 -92q-7 -24 -27.5 -38t-44.5 -14q-12 0 -24 3q-31 10 -45 38.5t-4 58.5q23 71 23 143q0 123 -61 227.5t-166 165.5t-228 61q-134 0 -247 -73t-167 -194q108 -28 188 -106q22 -23 22 -55t-22 -54t-54 -22t-55 22 q-75 75 -180 75q-106 0 -181 -74.5t-75 -180.5t75 -180.5t181 -74.5h1046q79 0 134.5 55.5t55.5 133.5zM1798 438q0 -142 -100.5 -242t-242.5 -100h-1046q-169 0 -289 119.5t-120 288.5q0 153 100 267t249 136q62 184 221 298t354 114q235 0 408.5 -158.5t196.5 -389.5 q116 -25 192.5 -118.5t76.5 -214.5zM2048 438q0 -175 -97 -319q-23 -33 -64 -33q-24 0 -43 13q-26 17 -32 48.5t12 57.5q71 104 71 233t-71 233q-18 26 -12 57t32 49t57.5 11.5t49.5 -32.5q97 -142 97 -318zM2304 438q0 -244 -134 -443q-23 -34 -64 -34q-23 0 -42 13 q-26 18 -32.5 49t11.5 57q108 164 108 358q0 195 -108 357q-18 26 -11.5 57.5t32.5 48.5q26 18 57 12t49 -33q134 -198 134 -442z" />
|
||||
@@ -641,15 +641,45 @@
|
||||
<glyph unicode="" d="M841 483l148 -148l-149 -149zM840 1094l149 -149l-148 -148zM710 -130l464 464l-306 306l306 306l-464 464v-611l-255 255l-93 -93l320 -321l-320 -321l93 -93l255 255v-611zM1429 640q0 -209 -32 -365.5t-87.5 -257t-140.5 -162.5t-181.5 -86.5t-219.5 -24.5 t-219.5 24.5t-181.5 86.5t-140.5 162.5t-87.5 257t-32 365.5t32 365.5t87.5 257t140.5 162.5t181.5 86.5t219.5 24.5t219.5 -24.5t181.5 -86.5t140.5 -162.5t87.5 -257t32 -365.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M596 113l173 172l-173 172v-344zM596 823l173 172l-173 172v-344zM628 640l356 -356l-539 -540v711l-297 -296l-108 108l372 373l-372 373l108 108l297 -296v711l539 -540z" />
|
||||
<glyph unicode="" d="M1280 256q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM512 1024q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1536 256q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5 t112.5 -271.5zM1440 1344q0 -20 -13 -38l-1056 -1408q-19 -26 -51 -26h-160q-26 0 -45 19t-19 45q0 20 13 38l1056 1408q19 26 51 26h160q26 0 45 -19t19 -45zM768 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5 t271.5 -112.5t112.5 -271.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M104 830l792 -1015l-868 630q-18 13 -25 34.5t0 42.5l101 308v0zM566 830h660l-330 -1015v0zM368 1442l198 -612h-462l198 612q8 23 33 23t33 -23zM1688 830l101 -308q7 -21 0 -42.5t-25 -34.5l-868 -630l792 1015v0zM1688 830h-462l198 612q8 23 33 23t33 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M384 704h160v224h-160v-224zM1221 372v92q-104 -36 -243 -38q-135 -1 -259.5 46.5t-220.5 122.5l1 -96q88 -80 212 -128.5t272 -47.5q129 0 238 49zM640 704h640v224h-640v-224zM1792 736q0 -187 -99 -352q89 -102 89 -229q0 -157 -129.5 -268t-313.5 -111 q-122 0 -225 52.5t-161 140.5q-19 -1 -57 -1t-57 1q-58 -88 -161 -140.5t-225 -52.5q-184 0 -313.5 111t-129.5 268q0 127 89 229q-99 165 -99 352q0 209 120 385.5t326.5 279.5t449.5 103t449.5 -103t326.5 -279.5t120 -385.5z" />
|
||||
<glyph unicode="" d="M515 625v-128h-252v128h252zM515 880v-127h-252v127h252zM1273 369v-128h-341v128h341zM1273 625v-128h-672v128h672zM1273 880v-127h-672v127h672zM1408 20v1240q0 8 -6 14t-14 6h-32l-378 -256l-210 171l-210 -171l-378 256h-32q-8 0 -14 -6t-6 -14v-1240q0 -8 6 -14 t14 -6h1240q8 0 14 6t6 14zM553 1130l185 150h-406zM983 1130l221 150h-406zM1536 1260v-1240q0 -62 -43 -105t-105 -43h-1240q-62 0 -105 43t-43 105v1240q0 62 43 105t105 43h1240q62 0 105 -43t43 -105z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M896 720q-104 196 -160 278q-139 202 -347 318q-34 19 -70 36q-89 40 -94 32t34 -38l39 -31q62 -43 112.5 -93.5t94.5 -116.5t70.5 -113t70.5 -131q9 -17 13 -25q44 -84 84 -153t98 -154t115.5 -150t131 -123.5t148.5 -90.5q153 -66 154 -60q1 3 -49 37q-53 36 -81 57 q-77 58 -179 211t-185 310zM549 177q-76 60 -132.5 125t-98 143.5t-71 154.5t-58.5 186t-52 209t-60.5 252t-76.5 289q273 0 497.5 -36t379 -92t271 -144.5t185.5 -172.5t110 -198.5t56 -199.5t12.5 -198.5t-9.5 -173t-20 -143.5t-13 -107l323 -327h-104l-281 285 q-22 -2 -91.5 -14t-121.5 -19t-138 -6t-160.5 17t-167.5 59t-179 111z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1374 879q-6 26 -28.5 39.5t-48.5 7.5q-261 -62 -401 -62t-401 62q-26 6 -48.5 -7.5t-28.5 -39.5t7.5 -48.5t39.5 -28.5q194 -46 303 -58q-2 -158 -15.5 -269t-26.5 -155.5t-41 -115.5l-9 -21q-10 -25 1 -49t36 -34q9 -4 23 -4q44 0 60 41l8 20q54 139 71 259h42 q17 -120 71 -259l8 -20q16 -41 60 -41q14 0 23 4q25 10 36 34t1 49l-9 21q-28 71 -41 115.5t-26.5 155.5t-15.5 269q109 12 303 58q26 6 39.5 28.5t7.5 48.5zM1024 1024q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5z M1600 640q0 -143 -55.5 -273.5t-150 -225t-225 -150t-273.5 -55.5t-273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5zM896 1408q-156 0 -298 -61t-245 -164t-164 -245t-61 -298t61 -298 t164 -245t245 -164t298 -61t298 61t245 164t164 245t61 298t-61 298t-164 245t-245 164t-298 61zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
|
||||
<glyph unicode="" d="M1438 723q34 -35 29 -82l-44 -551q-4 -42 -34.5 -70t-71.5 -28q-6 0 -9 1q-44 3 -72.5 36.5t-25.5 77.5l35 429l-143 -8q55 -113 55 -240q0 -216 -148 -372l-137 137q91 101 91 235q0 145 -102.5 248t-247.5 103q-134 0 -236 -92l-137 138q120 114 284 141l264 300 l-149 87l-181 -161q-33 -30 -77 -27.5t-73 35.5t-26.5 77t34.5 73l239 213q26 23 60 26.5t64 -14.5l488 -283q36 -21 48 -68q17 -67 -26 -117l-205 -232l371 20q49 3 83 -32zM1240 1180q-74 0 -126 52t-52 126t52 126t126 52t126.5 -52t52.5 -126t-52.5 -126t-126.5 -52z M613 -62q106 0 196 61l139 -139q-146 -116 -335 -116q-148 0 -273.5 73t-198.5 198t-73 273q0 188 116 336l139 -139q-60 -88 -60 -197q0 -145 102.5 -247.5t247.5 -102.5z" />
|
||||
<glyph unicode="" d="M880 336v-160q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v160q0 14 9 23t23 9h160q14 0 23 -9t9 -23zM1136 832q0 -50 -15 -90t-45.5 -69t-52 -44t-59.5 -36q-32 -18 -46.5 -28t-26 -24t-11.5 -29v-32q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v68q0 35 10.5 64.5 t24 47.5t39 35.5t41 25.5t44.5 21q53 25 75 43t22 49q0 42 -43.5 71.5t-95.5 29.5q-56 0 -95 -27q-29 -20 -80 -83q-9 -12 -25 -12q-11 0 -19 6l-108 82q-10 7 -12 20t5 23q122 192 349 192q129 0 238.5 -89.5t109.5 -214.5zM768 1280q-130 0 -248.5 -51t-204 -136.5 t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5t-51 248.5t-136.5 204t-204 136.5t-248.5 51zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5 t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M366 1225q-64 0 -110 45.5t-46 110.5q0 64 46 109.5t110 45.5t109.5 -45.5t45.5 -109.5q0 -65 -45.5 -110.5t-109.5 -45.5zM917 583q0 -50 -30 -67.5t-63.5 -6.5t-47.5 34l-367 438q-7 12 -14 15.5t-11 1.5l-3 -3q-7 -8 4 -21l122 -139l1 -354l-161 -457 q-67 -192 -92 -234q-16 -26 -28 -32q-50 -26 -103 -1q-29 13 -41.5 43t-9.5 57q2 17 197 618l5 416l-85 -164l35 -222q4 -24 -1 -42t-14 -27.5t-19 -16t-17 -7.5l-7 -2q-19 -3 -34.5 3t-24 16t-14 22t-7.5 19.5t-2 9.5l-46 299l211 381q23 34 113 34q75 0 107 -40l424 -521 q7 -5 14 -17l3 -3l-1 -1q7 -13 7 -29zM514 433q43 -113 88.5 -225t69.5 -168l24 -55q36 -93 42 -125q11 -70 -36 -97q-35 -22 -66 -16t-51 22t-29 35h-1q-6 16 -8 25l-124 351zM1338 -159q31 -49 31 -57q0 -5 -3 -7q-9 -5 -14.5 0.5t-15.5 26t-16 30.5q-114 172 -423 661 q3 -1 7 1t7 4l3 2q11 9 11 17z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M504 542h171l-1 265zM1530 641q0 87 -50.5 140t-146.5 53h-54v-388h52q91 0 145 57t54 138zM956 1018l1 -756q0 -14 -9.5 -24t-23.5 -10h-216q-14 0 -23.5 10t-9.5 24v62h-291l-55 -81q-10 -15 -28 -15h-267q-21 0 -30.5 18t3.5 35l556 757q9 14 27 14h332q14 0 24 -10 t10 -24zM1783 641q0 -193 -125.5 -303t-324.5 -110h-270q-14 0 -24 10t-10 24v756q0 14 10 24t24 10h268q200 0 326 -109t126 -302zM1939 640q0 -11 -0.5 -29t-8 -71.5t-21.5 -102t-44.5 -108t-73.5 -102.5h-51q38 45 66.5 104.5t41.5 112t21 98t9 72.5l1 27q0 8 -0.5 22.5 t-7.5 60t-20 91.5t-41 111.5t-66 124.5h43q41 -47 72 -107t45.5 -111.5t23 -96t10.5 -70.5zM2123 640q0 -11 -0.5 -29t-8 -71.5t-21.5 -102t-45 -108t-74 -102.5h-51q38 45 66.5 104.5t41.5 112t21 98t9 72.5l1 27q0 8 -0.5 22.5t-7.5 60t-19.5 91.5t-40.5 111.5t-66 124.5 h43q41 -47 72 -107t45.5 -111.5t23 -96t10.5 -70.5zM2304 640q0 -11 -0.5 -29t-8 -71.5t-21.5 -102t-44.5 -108t-73.5 -102.5h-51q38 45 66 104.5t41 112t21 98t9 72.5l1 27q0 8 -0.5 22.5t-7.5 60t-19.5 91.5t-40.5 111.5t-66 124.5h43q41 -47 72 -107t45.5 -111.5t23 -96 t9.5 -70.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M617 -153q0 11 -13 58t-31 107t-20 69q-1 4 -5 26.5t-8.5 36t-13.5 21.5q-15 14 -51 14q-23 0 -70 -5.5t-71 -5.5q-34 0 -47 11q-6 5 -11 15.5t-7.5 20t-6.5 24t-5 18.5q-37 128 -37 255t37 255q1 4 5 18.5t6.5 24t7.5 20t11 15.5q13 11 47 11q24 0 71 -5.5t70 -5.5 q36 0 51 14q9 8 13.5 21.5t8.5 36t5 26.5q2 9 20 69t31 107t13 58q0 22 -43.5 52.5t-75.5 42.5q-20 8 -45 8q-34 0 -98 -18q-57 -17 -96.5 -40.5t-71 -66t-46 -70t-45.5 -94.5q-6 -12 -9 -19q-49 -107 -68 -216t-19 -244t19 -244t68 -216q56 -122 83 -161q63 -91 179 -127 l6 -2q64 -18 98 -18q25 0 45 8q32 12 75.5 42.5t43.5 52.5zM776 760q-26 0 -45 19t-19 45.5t19 45.5q37 37 37 90q0 52 -37 91q-19 19 -19 45t19 45t45 19t45 -19q75 -75 75 -181t-75 -181q-21 -19 -45 -19zM957 579q-27 0 -45 19q-19 19 -19 45t19 45q112 114 112 272 t-112 272q-19 19 -19 45t19 45t45 19t45 -19q150 -150 150 -362t-150 -362q-18 -19 -45 -19zM1138 398q-27 0 -45 19q-19 19 -19 45t19 45q90 91 138.5 208t48.5 245t-48.5 245t-138.5 208q-19 19 -19 45t19 45t45 19t45 -19q109 -109 167 -249t58 -294t-58 -294t-167 -249 q-18 -19 -45 -19z" />
|
||||
<glyph unicode="" horiz-adv-x="2176" d="M192 352q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM704 352q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM704 864q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1472 352 q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1984 352q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1472 864q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1984 864 q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1984 1376q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM384 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 192q0 -80 -56 -136 t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM384 704q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 704q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM384 1216q0 -80 -56 -136t-136 -56 t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1664 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 1216q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM2176 192q0 -80 -56 -136t-136 -56t-136 56 t-56 136t56 136t136 56t136 -56t56 -136zM1664 704q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM2176 704q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1664 1216q0 -80 -56 -136t-136 -56t-136 56t-56 136 t56 136t136 56t136 -56t56 -136zM2176 1216q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M128 -192q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45zM320 0q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45zM365 365l256 -256l-90 -90l-256 256zM704 384q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45z M1411 704q0 -59 -11.5 -108.5t-37.5 -93.5t-44 -67.5t-53 -64.5q-31 -35 -45.5 -54t-33.5 -50t-26.5 -64t-7.5 -74q0 -159 -112.5 -271.5t-271.5 -112.5q-26 0 -45 19t-19 45t19 45t45 19q106 0 181 75t75 181q0 57 11.5 105.5t37 91t43.5 66.5t52 63q40 46 59.5 72 t37.5 74.5t18 103.5q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5q0 -26 -19 -45t-45 -19t-45 19t-19 45q0 117 45.5 223.5t123 184t184 123t223.5 45.5t223.5 -45.5t184 -123t123 -184t45.5 -223.5zM896 576q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45 t45 19t45 -19t19 -45zM1184 704q0 -26 -19 -45t-45 -19t-45 19t-19 45q0 93 -65.5 158.5t-158.5 65.5q-92 0 -158 -65.5t-66 -158.5q0 -26 -19 -45t-45 -19t-45 19t-19 45q0 146 103 249t249 103t249 -103t103 -249zM1578 993q10 -25 -1 -49t-36 -34q-9 -4 -23 -4 q-19 0 -35.5 11t-23.5 30q-68 178 -224 295q-21 16 -25 42t12 47q17 21 43 25t47 -12q183 -137 266 -351zM1788 1074q9 -25 -1.5 -49t-35.5 -34q-11 -4 -23 -4q-44 0 -60 41q-92 238 -297 393q-22 16 -25.5 42t12.5 47q16 22 42 25.5t47 -12.5q235 -175 341 -449z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M1032 576q-59 2 -84 55q-17 34 -48 53.5t-68 19.5q-53 0 -90.5 -37.5t-37.5 -90.5q0 -56 36 -89l10 -8q34 -31 82 -31q37 0 68 19.5t48 53.5q25 53 84 55zM1600 704q0 56 -36 89l-10 8q-34 31 -82 31q-37 0 -68 -19.5t-48 -53.5q-25 -53 -84 -55q59 -2 84 -55 q17 -34 48 -53.5t68 -19.5q53 0 90.5 37.5t37.5 90.5zM1174 925q-17 -35 -55 -48t-73 4q-62 31 -134 31q-51 0 -99 -17q3 0 9.5 0.5t9.5 0.5q92 0 170.5 -50t118.5 -133q17 -36 3.5 -73.5t-49.5 -54.5q-18 -9 -39 -9q21 0 39 -9q36 -17 49.5 -54.5t-3.5 -73.5 q-40 -83 -118.5 -133t-170.5 -50h-6q-16 2 -44 4l-290 27l-239 -120q-14 -7 -29 -7q-40 0 -57 35l-160 320q-11 23 -4 47.5t29 37.5l209 119l148 267q17 155 91.5 291.5t195.5 236.5q31 25 70.5 21.5t64.5 -34.5t21.5 -70t-34.5 -65q-70 -59 -117 -128q123 84 267 101 q40 5 71.5 -19t35.5 -64q5 -40 -19 -71.5t-64 -35.5q-84 -10 -159 -55q46 10 99 10q115 0 218 -50q36 -18 49 -55.5t-5 -73.5zM2137 1085l160 -320q11 -23 4 -47.5t-29 -37.5l-209 -119l-148 -267q-17 -155 -91.5 -291.5t-195.5 -236.5q-26 -22 -61 -22q-45 0 -74 35 q-25 31 -21.5 70t34.5 65q70 59 117 128q-123 -84 -267 -101q-4 -1 -12 -1q-36 0 -63.5 24t-31.5 60q-5 40 19 71.5t64 35.5q84 10 159 55q-46 -10 -99 -10q-115 0 -218 50q-36 18 -49 55.5t5 73.5q17 35 55 48t73 -4q62 -31 134 -31q51 0 99 17q-3 0 -9.5 -0.5t-9.5 -0.5 q-92 0 -170.5 50t-118.5 133q-17 36 -3.5 73.5t49.5 54.5q18 9 39 9q-21 0 -39 9q-36 17 -49.5 54.5t3.5 73.5q40 83 118.5 133t170.5 50h6h1q14 -2 42 -4l291 -27l239 120q14 7 29 7q40 0 57 -35z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1056 704q0 -26 19 -45t45 -19t45 19t19 45q0 146 -103 249t-249 103t-249 -103t-103 -249q0 -26 19 -45t45 -19t45 19t19 45q0 93 66 158.5t158 65.5t158 -65.5t66 -158.5zM835 1280q-117 0 -223.5 -45.5t-184 -123t-123 -184t-45.5 -223.5q0 -26 19 -45t45 -19t45 19 t19 45q0 185 131.5 316.5t316.5 131.5t316.5 -131.5t131.5 -316.5q0 -55 -18 -103.5t-37.5 -74.5t-59.5 -72q-34 -39 -52 -63t-43.5 -66.5t-37 -91t-11.5 -105.5q0 -106 -75 -181t-181 -75q-26 0 -45 -19t-19 -45t19 -45t45 -19q159 0 271.5 112.5t112.5 271.5q0 41 7.5 74 t26.5 64t33.5 50t45.5 54q35 41 53 64.5t44 67.5t37.5 93.5t11.5 108.5q0 117 -45.5 223.5t-123 184t-184 123t-223.5 45.5zM591 561l226 -226l-579 -579q-12 -12 -29 -12t-29 12l-168 168q-12 12 -12 29t12 29zM1612 1524l168 -168q12 -12 12 -29t-12 -30l-233 -233 l-26 -25l-71 -71q-66 153 -195 258l91 91l207 207q13 12 30 12t29 -12z" />
|
||||
<glyph unicode="" d="M866 1021q0 -27 -13 -94q-11 -50 -31.5 -150t-30.5 -150q-2 -11 -4.5 -12.5t-13.5 -2.5q-20 -2 -31 -2q-58 0 -84 49.5t-26 113.5q0 88 35 174t103 124q28 14 51 14q28 0 36.5 -16.5t8.5 -47.5zM1352 597q0 14 -39 75.5t-52 66.5q-21 8 -34 8q-91 0 -226 -77l-2 2 q3 22 27.5 135t24.5 178q0 233 -242 233q-24 0 -68 -6q-94 -17 -168.5 -89.5t-111.5 -166.5t-37 -189q0 -146 80.5 -225t227.5 -79q25 0 25 -3t-1 -5q-4 -34 -26 -117q-14 -52 -51.5 -101t-82.5 -49q-42 0 -42 47q0 24 10.5 47.5t25 39.5t29.5 28.5t26 20t11 8.5q0 3 -7 10 q-24 22 -58.5 36.5t-65.5 14.5q-35 0 -63.5 -34t-41 -75t-12.5 -75q0 -88 51.5 -142t138.5 -54q82 0 155 53t117.5 126t65.5 153q6 22 15.5 66.5t14.5 66.5q3 12 14 18q118 60 227 60q48 0 127 -18q1 -1 4 -1q5 0 9.5 4.5t4.5 8.5zM1536 1120v-960q0 -119 -84.5 -203.5 t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1535" d="M744 1231q0 24 -2 38.5t-8.5 30t-21 23t-37.5 7.5q-39 0 -78 -23q-105 -58 -159 -190.5t-54 -269.5q0 -44 8.5 -85.5t26.5 -80.5t52.5 -62.5t81.5 -23.5q4 0 18 -0.5t20 0t16 3t15 8.5t7 16q16 77 48 231.5t48 231.5q19 91 19 146zM1498 575q0 -7 -7.5 -13.5t-15.5 -6.5 l-6 1q-22 3 -62 11t-72 12.5t-63 4.5q-167 0 -351 -93q-15 -8 -21 -27q-10 -36 -24.5 -105.5t-22.5 -100.5q-23 -91 -70 -179.5t-112.5 -164.5t-154.5 -123t-185 -47q-135 0 -214.5 83.5t-79.5 219.5q0 53 19.5 117t63 116.5t97.5 52.5q38 0 120 -33.5t83 -61.5 q0 -1 -16.5 -12.5t-39.5 -31t-46 -44.5t-39 -61t-16 -74q0 -33 16.5 -53t48.5 -20q45 0 85 31.5t66.5 78t48 105.5t32.5 107t16 90v9q0 2 -3.5 3.5t-8.5 1.5h-10t-10 -0.5t-6 -0.5q-227 0 -352 122.5t-125 348.5q0 108 34.5 221t96 210t156 167.5t204.5 89.5q52 9 106 9 q374 0 374 -360q0 -98 -38 -273t-43 -211l3 -3q101 57 182.5 88t167.5 31q22 0 53 -13q19 -7 80 -102.5t61 -116.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M831 863q32 0 59 -18l222 -148q61 -40 110 -97l146 -170q40 -46 29 -106l-72 -413q-6 -32 -29.5 -53.5t-55.5 -25.5l-527 -56l-352 -32h-9q-39 0 -67.5 28t-28.5 68q0 37 27 64t65 32l260 32h-448q-41 0 -69.5 30t-26.5 71q2 39 32 65t69 26l442 1l-521 64q-41 5 -66 37 t-19 73q6 35 34.5 57.5t65.5 22.5h10l481 -60l-351 94q-38 10 -62 41.5t-18 68.5q6 36 33 58.5t62 22.5q6 0 20 -2l448 -96l217 -37q1 0 3 -0.5t3 -0.5q23 0 30.5 23t-12.5 36l-186 125q-35 23 -42 63.5t18 73.5q27 38 76 38zM761 661l186 -125l-218 37l-5 2l-36 38 l-238 262q-1 1 -2.5 3.5t-2.5 3.5q-24 31 -18.5 70t37.5 64q31 23 68 17.5t64 -33.5l142 -147l-4 -4t-5 -4q-32 -45 -23 -99t55 -85zM1648 1115l15 -266q4 -73 -11 -147l-48 -219q-12 -59 -67 -87l-106 -54q2 62 -39 109l-146 170q-53 61 -117 103l-222 148q-34 23 -76 23 q-51 0 -88 -37l-235 312q-25 33 -18 73.5t41 63.5q33 22 71.5 14t62.5 -40l266 -352l-262 455q-21 35 -10.5 75t47.5 59q35 18 72.5 6t57.5 -46l241 -420l-136 337q-15 35 -4.5 74t44.5 56q37 19 76 6t56 -51l193 -415l101 -196q8 -15 23 -17.5t27 7.5t11 26l-12 224 q-2 41 26 71t69 31q39 0 67 -28.5t30 -67.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M335 180q-2 0 -6 2q-86 57 -168.5 145t-139.5 180q-21 30 -21 69q0 9 2 19t4 18t7 18t8.5 16t10.5 17t10 15t12 15.5t11 14.5q184 251 452 365q-110 198 -110 211q0 19 17 29q116 64 128 64q18 0 28 -16l124 -229q92 19 192 19q266 0 497.5 -137.5t378.5 -369.5 q20 -31 20 -69t-20 -69q-91 -142 -218.5 -253.5t-278.5 -175.5q110 -198 110 -211q0 -20 -17 -29q-116 -64 -127 -64q-19 0 -29 16l-124 229l-64 119l-444 820l7 7q-58 -24 -99 -47q3 -5 127 -234t243 -449t119 -223q0 -7 -9 -9q-13 -3 -72 -3q-57 0 -60 7l-456 841 q-39 -28 -82 -68q24 -43 214 -393.5t190 -354.5q0 -10 -11 -10q-14 0 -82.5 22t-72.5 28l-106 197l-224 413q-44 -53 -78 -106q2 -3 18 -25t23 -34l176 -327q0 -10 -10 -10zM1165 282l49 -91q273 111 450 385q-180 277 -459 389q67 -64 103 -148.5t36 -176.5 q0 -106 -47 -200.5t-132 -157.5zM848 896q0 -20 14 -34t34 -14q86 0 147 -61t61 -147q0 -20 14 -34t34 -14t34 14t14 34q0 126 -89 215t-215 89q-20 0 -34 -14t-14 -34zM1214 961l-9 4l7 -7z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M1050 430q0 -215 -147 -374q-148 -161 -378 -161q-232 0 -378 161q-147 159 -147 374q0 147 68 270.5t189 196.5t268 73q96 0 182 -31q-32 -62 -39 -126q-66 28 -143 28q-167 0 -280.5 -123t-113.5 -291q0 -170 112.5 -288.5t281.5 -118.5t281 118.5t112 288.5 q0 89 -32 166q66 13 123 49q41 -98 41 -212zM846 619q0 -192 -79.5 -345t-238.5 -253l-14 -1q-29 0 -62 5q83 32 146.5 102.5t99.5 154.5t58.5 189t30 192.5t7.5 178.5q0 69 -3 103q55 -160 55 -326zM791 947v-2q-73 214 -206 440q88 -59 142.5 -186.5t63.5 -251.5z M1035 744q-83 0 -160 75q218 120 290 247q19 37 21 56q-42 -94 -139.5 -166.5t-204.5 -97.5q-35 54 -35 113q0 37 17 79t43 68q46 44 157 74q59 16 106 58.5t74 100.5q74 -105 74 -253q0 -109 -24 -170q-32 -77 -88.5 -130.5t-130.5 -53.5z" />
|
||||
<glyph unicode="" d="M1050 495q0 78 -28 147q-41 -25 -85 -34q22 -50 22 -114q0 -117 -77 -198.5t-193 -81.5t-193.5 81.5t-77.5 198.5q0 115 78 199.5t193 84.5q53 0 98 -19q4 43 27 87q-60 21 -125 21q-154 0 -257.5 -108.5t-103.5 -263.5t103.5 -261t257.5 -106t257.5 106.5t103.5 260.5z M872 850q2 -24 2 -71q0 -63 -5 -123t-20.5 -132.5t-40.5 -130t-68.5 -106t-100.5 -70.5q21 -3 42 -3h10q219 139 219 411q0 116 -38 225zM872 850q-4 80 -44 171.5t-98 130.5q92 -156 142 -302zM1207 955q0 102 -51 174q-41 -86 -124 -109q-69 -19 -109 -53.5t-40 -99.5 q0 -40 24 -77q74 17 140.5 67t95.5 115q-4 -52 -74.5 -111.5t-138.5 -97.5q52 -52 110 -52q51 0 90 37t60 90q17 43 17 117zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z" />
|
||||
<glyph unicode="" d="M1279 388q0 22 -22 27q-67 15 -118 59t-80 108q-7 19 -7 25q0 15 19.5 26t43 17t43 20.5t19.5 36.5q0 19 -18.5 31.5t-38.5 12.5q-12 0 -32 -8t-31 -8q-4 0 -12 2q5 95 5 114q0 79 -17 114q-36 78 -103 121.5t-152 43.5q-199 0 -275 -165q-17 -35 -17 -114q0 -19 5 -114 q-4 -2 -14 -2q-12 0 -32 7.5t-30 7.5q-21 0 -38.5 -12t-17.5 -32q0 -21 19.5 -35.5t43 -20.5t43 -17t19.5 -26q0 -6 -7 -25q-64 -138 -198 -167q-22 -5 -22 -27q0 -46 137 -68q2 -5 6 -26t11.5 -30.5t23.5 -9.5q12 0 37.5 4.5t39.5 4.5q35 0 67 -15t54 -32.5t57.5 -32.5 t76.5 -15q43 0 79 15t57.5 32.5t53.5 32.5t67 15q14 0 39.5 -4t38.5 -4q16 0 23 10t11 30t6 25q137 22 137 68zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5 t103 -385.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M848 1408q134 1 240.5 -68.5t163.5 -192.5q27 -58 27 -179q0 -47 -9 -191q14 -7 28 -7q18 0 51 13.5t51 13.5q29 0 56 -18t27 -46q0 -32 -31.5 -54t-69 -31.5t-69 -29t-31.5 -47.5q0 -15 12 -43q37 -82 102.5 -150t144.5 -101q28 -12 80 -23q28 -6 28 -35 q0 -70 -219 -103q-7 -11 -11 -39t-14 -46.5t-33 -18.5q-20 0 -62 6.5t-64 6.5q-37 0 -62 -5q-32 -5 -63 -22.5t-58 -38t-58 -40.5t-76 -33.5t-99 -13.5q-52 0 -96.5 13.5t-75 33.5t-57.5 40.5t-58 38t-62 22.5q-26 5 -63 5q-24 0 -65.5 -7.5t-58.5 -7.5q-25 0 -35 18.5 t-14 47.5t-11 40q-219 33 -219 103q0 29 28 35q52 11 80 23q78 32 144.5 101t102.5 150q12 28 12 43q0 28 -31.5 47.5t-69.5 29.5t-69.5 31.5t-31.5 52.5q0 27 26 45.5t55 18.5q15 0 48 -13t53 -13q18 0 32 7q-9 142 -9 190q0 122 27 180q64 137 172 198t264 63z" />
|
||||
<glyph unicode="" d="M1280 388q0 22 -22 27q-67 14 -118 58t-80 109q-7 14 -7 25q0 15 19.5 26t42.5 17t42.5 20.5t19.5 36.5q0 19 -18.5 31.5t-38.5 12.5q-11 0 -31 -8t-32 -8q-4 0 -12 2q5 63 5 115q0 78 -17 114q-36 78 -102.5 121.5t-152.5 43.5q-198 0 -275 -165q-18 -38 -18 -115 q0 -38 6 -114q-10 -2 -15 -2q-11 0 -31.5 8t-30.5 8q-20 0 -37.5 -12.5t-17.5 -32.5q0 -21 19.5 -35.5t42.5 -20.5t42.5 -17t19.5 -26q0 -11 -7 -25q-64 -138 -198 -167q-22 -5 -22 -27q0 -47 138 -69q2 -5 6 -26t11 -30.5t23 -9.5q13 0 38.5 5t38.5 5q35 0 67.5 -15 t54.5 -32.5t57.5 -32.5t76.5 -15q43 0 79 15t57.5 32.5t54 32.5t67.5 15q13 0 39 -4.5t39 -4.5q15 0 22.5 9.5t11.5 31t5 24.5q138 22 138 69zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960 q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M2304 1536q-69 -46 -125 -92t-89 -81t-59.5 -71.5t-37.5 -57.5t-22 -44.5t-14 -29.5q-10 -18 -35.5 -136.5t-48.5 -164.5q-15 -29 -50 -60.5t-67.5 -50.5t-72.5 -41t-48 -28q-47 -31 -151 -231q-341 14 -630 -158q-92 -53 -303 -179q47 16 86 31t55 22l15 7 q71 27 163 64.5t133.5 53.5t108 34.5t142.5 31.5q186 31 465 -7q1 0 10 -3q11 -6 14 -17t-3 -22l-194 -345q-15 -29 -47 -22q-128 24 -354 24q-146 0 -402 -44.5t-392 -46.5q-82 -1 -149 13t-107 37t-61 40t-33 34l-1 1v2q0 6 6 6q138 0 371 55q192 366 374.5 524t383.5 158 q5 0 14.5 -0.5t38 -5t55 -12t61.5 -24.5t63 -39.5t54 -59t40 -82.5l102 177q2 4 21 42.5t44.5 86.5t61 109.5t84 133.5t100.5 137q66 82 128 141.5t121.5 96.5t92.5 53.5t88 39.5z" />
|
||||
<glyph unicode="" d="M1322 640q0 -45 -5 -76l-236 14l224 -78q-19 -73 -58 -141l-214 103l177 -158q-44 -61 -107 -108l-157 178l103 -215q-61 -37 -140 -59l-79 228l14 -240q-38 -6 -76 -6t-76 6l14 238l-78 -226q-74 19 -140 59l103 215l-157 -178q-59 43 -108 108l178 158l-214 -104 q-39 69 -58 141l224 79l-237 -14q-5 42 -5 76q0 35 5 77l238 -14l-225 79q19 73 58 140l214 -104l-177 159q46 61 107 108l158 -178l-103 215q67 39 140 58l77 -224l-13 236q36 6 75 6q38 0 76 -6l-14 -237l78 225q74 -19 140 -59l-103 -214l158 178q61 -47 107 -108 l-177 -159l213 104q37 -62 58 -141l-224 -78l237 14q5 -31 5 -77zM1352 640q0 160 -78.5 295.5t-213 214t-292.5 78.5q-119 0 -227 -46.5t-186.5 -125t-124.5 -187.5t-46 -229q0 -119 46 -228t124.5 -187.5t186.5 -125t227 -46.5q158 0 292.5 78.5t213 214t78.5 294.5z M1425 1023v-766l-657 -383l-657 383v766l657 383zM768 -183l708 412v823l-708 411l-708 -411v-823zM1536 1088v-896l-768 -448l-768 448v896l768 448z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M339 1318h691l-26 -72h-665q-110 0 -188.5 -79t-78.5 -189v-771q0 -95 60.5 -169.5t153.5 -93.5q23 -5 98 -5v-72h-45q-140 0 -239.5 100t-99.5 240v771q0 140 99.5 240t239.5 100zM1190 1536h247l-482 -1294q-23 -61 -40.5 -103.5t-45 -98t-54 -93.5t-64.5 -78.5 t-79.5 -65t-95.5 -41t-116 -18.5v195q163 26 220 182q20 52 20 105q0 54 -20 106l-285 733h228l187 -585zM1664 978v-1111h-795q37 55 45 73h678v1038q0 85 -49.5 155t-129.5 99l25 67q101 -34 163.5 -123.5t62.5 -197.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M852 1227q0 -29 -17 -52.5t-45 -23.5t-45 23.5t-17 52.5t17 52.5t45 23.5t45 -23.5t17 -52.5zM688 -149v114q0 30 -20.5 51.5t-50.5 21.5t-50 -21.5t-20 -51.5v-114q0 -30 20.5 -52t49.5 -22q30 0 50.5 22t20.5 52zM860 -149v114q0 30 -20 51.5t-50 21.5t-50.5 -21.5 t-20.5 -51.5v-114q0 -30 20.5 -52t50.5 -22q29 0 49.5 22t20.5 52zM1034 -149v114q0 30 -20.5 51.5t-50.5 21.5t-50.5 -21.5t-20.5 -51.5v-114q0 -30 20.5 -52t50.5 -22t50.5 22t20.5 52zM1208 -149v114q0 30 -20.5 51.5t-50.5 21.5t-50.5 -21.5t-20.5 -51.5v-114 q0 -30 20.5 -52t50.5 -22t50.5 22t20.5 52zM1476 535q-84 -160 -232 -259.5t-323 -99.5q-123 0 -229.5 51.5t-178.5 137t-113 197.5t-41 232q0 88 21 174q-104 -175 -104 -390q0 -162 65 -312t185 -251q30 57 91 57q56 0 86 -50q32 50 87 50q56 0 86 -50q32 50 87 50t87 -50 q30 50 86 50q28 0 52.5 -15.5t37.5 -40.5q112 94 177 231.5t73 287.5zM1326 564q0 75 -72 75q-17 0 -47 -6q-95 -19 -149 -19q-226 0 -226 243q0 86 30 204q-83 -127 -83 -275q0 -150 89 -260.5t235 -110.5q111 0 210 70q13 48 13 79zM884 1223q0 50 -32 89.5t-81 39.5 t-81 -39.5t-32 -89.5q0 -51 31.5 -90.5t81.5 -39.5t81.5 39.5t31.5 90.5zM1513 884q0 96 -37.5 179t-113 137t-173.5 54q-77 0 -149 -35t-127 -94q-48 -159 -48 -268q0 -104 45.5 -157t147.5 -53q53 0 142 19q36 6 53 6q51 0 77.5 -28t26.5 -80q0 -26 -4 -46 q75 68 117.5 165.5t42.5 200.5zM1792 667q0 -111 -33.5 -249.5t-93.5 -204.5q-58 -64 -195 -142.5t-228 -104.5l-4 -1v-114q0 -43 -29.5 -75t-72.5 -32q-56 0 -86 50q-32 -50 -87 -50t-87 50q-30 -50 -86 -50q-55 0 -87 50q-30 -50 -86 -50q-47 0 -75 33.5t-28 81.5 q-90 -68 -198 -68q-118 0 -211 80q54 1 106 20q-113 31 -182 127q32 -7 71 -7q89 0 164 46q-192 192 -240 306q-24 56 -24 160q0 57 9 125.5t31.5 146.5t55 141t86.5 105t120 42q59 0 81 -52q19 29 42 54q2 3 12 13t13 16q10 15 23 38t25 42t28 39q87 111 211.5 177 t260.5 66q35 0 62 -4q59 64 146 64q83 0 140 -57q5 -5 5 -12q0 -5 -6 -13.5t-12.5 -16t-16 -17l-10.5 -10.5q17 -6 36 -18t19 -24q0 -6 -16 -25q157 -138 197 -378q25 30 60 30q45 0 100 -49q90 -80 90 -279z" />
|
||||
<glyph unicode="" d="M917 631q0 33 -6 64h-362v-132h217q-12 -76 -74.5 -120.5t-142.5 -44.5q-99 0 -169 71.5t-70 170.5t70 170.5t169 71.5q93 0 153 -59l104 101q-108 100 -257 100q-160 0 -272 -112.5t-112 -271.5t112 -271.5t272 -112.5q165 0 266.5 105t101.5 270zM1262 585h109v110 h-109v110h-110v-110h-110v-110h110v-110h110v110zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1536 1024v-839q0 -48 -49 -62q-174 -52 -338 -52q-73 0 -215.5 29.5t-227.5 29.5q-164 0 -370 -48v-338h-160v1368q-63 25 -101 81t-38 124q0 91 64 155t155 64t155 -64t64 -155q0 -68 -38 -124t-101 -81v-68q190 44 343 44q99 0 198 -15q14 -2 111.5 -22.5t149.5 -20.5 q77 0 165 18q11 2 80 21t89 19q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
</font>
|
||||
</defs></svg>
|
||||
|
Before Width: | Height: | Size: 357 KiB After Width: | Height: | Size: 382 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -36,7 +36,7 @@ Justin Meyer <justinbmeyer@gmail.com>
|
||||
Ben Alman <cowboy@rj3.net>
|
||||
James Padolsey <cla@padolsey.net>
|
||||
David Petersen <public@petersendidit.com>
|
||||
Batiste Bieler <batiste@gmail.com>
|
||||
Batiste Bieler <batiste.bieler@gmail.com>
|
||||
Alexander Farkas <info@corrupt-system.de>
|
||||
Rick Waldron <waldron.rick@gmail.com>
|
||||
Filipe Fortes <filipe@fortes.com>
|
||||
@@ -215,3 +215,52 @@ John Hoven <hovenj@gmail.com>
|
||||
Christian Kosmowski <ksmwsk@gmail.com>
|
||||
Liang Peng <poppinlp@gmail.com>
|
||||
TJ VanToll <tj.vantoll@gmail.com>
|
||||
Senya Pugach <upisfree@outlook.com>
|
||||
Aurelio De Rosa <aurelioderosa@gmail.com>
|
||||
Nazar Mokrynskyi <nazar@mokrynskyi.com>
|
||||
Arthur Verschaeve <contact@arthurverschaeve.be>
|
||||
Dan Hart <danhart@notonthehighstreet.com>
|
||||
Scott González <scott.gonzalez@gmail.com>
|
||||
Zheming Sun <mescodasun@gmail.com>
|
||||
Bin Xin <rhyzix@gmail.com>
|
||||
David Corbacho <davidcorbacho@gmail.com>
|
||||
Veaceslav Grimalschi <grimalschi@yandex.ru>
|
||||
Daniel Husar <dano.husar@gmail.com>
|
||||
Jason Bedard <jason+github@jbedard.ca>
|
||||
Ben Toews <mastahyeti@gmail.com>
|
||||
Aditya Raghavan <araghavan3@gmail.com>
|
||||
Nicolas HENRY <icewil@gmail.com>
|
||||
Norman Xu <homyu.shinn@gmail.com>
|
||||
Anne-Gaelle Colom <coloma@westminster.ac.uk>
|
||||
Victor Homyakov <vkhomyackov@gmail.com>
|
||||
George Mauer <gmauer@gmail.com>
|
||||
Leonardo Braga <leonardo.braga@gmail.com>
|
||||
Stephen Edgar <stephen@netweb.com.au>
|
||||
Thomas Tortorini <thomastortorini@gmail.com>
|
||||
Winston Howes <winstonhowes@gmail.com>
|
||||
Jon Hester <jon.d.hester@gmail.com>
|
||||
Alexander O'Mara <me@alexomara.com>
|
||||
Bastian Buchholz <buchholz.bastian@googlemail.com>
|
||||
Arthur Stolyar <nekr.fabula@gmail.com>
|
||||
Calvin Metcalf <calvin.metcalf@gmail.com>
|
||||
Mu Haibao <mhbseal@163.com>
|
||||
Richard McDaniel <rm0026@uah.edu>
|
||||
Chris Rebert <github@rebertia.com>
|
||||
Gilad Peleg <giladp007@gmail.com>
|
||||
Martin Naumann <martin@geekonaut.de>
|
||||
Bruno Pérel <brunoperel@gmail.com>
|
||||
Reed Loden <reed@reedloden.com>
|
||||
Daniel Nill <daniellnill@gmail.com>
|
||||
Yongwoo Jeon <yongwoo.jeon@navercorp.com>
|
||||
Sean Henderson <seanh.za@gmail.com>
|
||||
Adrian Olek <adrianolek@gmail.com>
|
||||
Richard Kraaijenhagen <stdin+git@riichard.com>
|
||||
Gary Ye <garysye@gmail.com>
|
||||
Christian Grete <webmaster@christiangrete.com>
|
||||
Liza Ramo <liza.h.ramo@gmail.com>
|
||||
Joelle Fleurantin <joasqueeniebee@gmail.com>
|
||||
Julian Alexander Murillo <julian.alexander.murillo@gmail.com>
|
||||
Jun Sun <klsforever@gmail.com>
|
||||
Devin Wilson <dwilson6.github@gmail.com>
|
||||
Todor Prikumov <tono_pr@abv.bg>
|
||||
Zack Hall <zackhall@outlook.com>
|
||||
|
||||
4543
inst/www/shared/jquery.js
vendored
4543
inst/www/shared/jquery.js
vendored
File diff suppressed because it is too large
Load Diff
10
inst/www/shared/jquery.min.js
vendored
10
inst/www/shared/jquery.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -124,6 +124,7 @@
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Old-style progress */
|
||||
.shiny-progress-container {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
@@ -167,6 +168,21 @@
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/* New-style progress (uses notifications API) */
|
||||
.shiny-progress-notification .progress {
|
||||
margin-bottom: 5px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.shiny-progress-notification .progress-text .progress-message {
|
||||
font-weight: bold;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.shiny-progress-notification .progress-text .progress-detail {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
.crosshair {
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
@@ -965,7 +965,6 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
if (handler.length !== 1) {
|
||||
throw 'handler must be a function that takes one argument.';
|
||||
}
|
||||
|
||||
messageHandlerOrder.push(type);
|
||||
messageHandlers[type] = handler;
|
||||
}
|
||||
@@ -988,8 +987,23 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
|
||||
exports.addCustomMessageHandler = addCustomMessageHandler;
|
||||
|
||||
this.dispatchMessage = function (msg) {
|
||||
var msgObj = JSON.parse(msg);
|
||||
this.dispatchMessage = function (data) {
|
||||
var msgObj = {};
|
||||
if (typeof data === "string") {
|
||||
msgObj = JSON.parse(data);
|
||||
} else {
|
||||
// data is arraybuffer
|
||||
var len = new DataView(data, 0, 1).getUint8(0);
|
||||
var typedv = new DataView(data, 1, len);
|
||||
var typebuf = [];
|
||||
for (var i = 0; i < len; i++) {
|
||||
typebuf.push(String.fromCharCode(typedv.getUint8(i)));
|
||||
}
|
||||
var type = typebuf.join("");
|
||||
data = data.slice(len + 1);
|
||||
msgObj.custom = {};
|
||||
msgObj.custom[type] = data;
|
||||
}
|
||||
|
||||
var evt = jQuery.Event('shiny:message');
|
||||
evt.message = msgObj;
|
||||
@@ -1008,7 +1022,6 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
// Dispatch messages to handlers, if handler is present
|
||||
for (var i = 0; i < handlerOrder.length; i++) {
|
||||
var msgType = handlerOrder[i];
|
||||
|
||||
if (msgObj.hasOwnProperty(msgType)) {
|
||||
// Execute each handler with 'this' referring to the present value of
|
||||
// 'this'
|
||||
@@ -1162,6 +1175,10 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
});
|
||||
});
|
||||
|
||||
addMessageHandler('updateQueryString', function (message) {
|
||||
window.history.replaceState(null, null, message.queryString);
|
||||
});
|
||||
|
||||
addMessageHandler("resetBrush", function (message) {
|
||||
exports.resetBrush(message.brushId);
|
||||
});
|
||||
@@ -1177,70 +1194,114 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
binding.showProgress(true);
|
||||
}
|
||||
},
|
||||
|
||||
// Open a page-level progress bar
|
||||
open: function open(message) {
|
||||
// Add progress container (for all progress items) if not already present
|
||||
var $container = $('.shiny-progress-container');
|
||||
if ($container.length === 0) {
|
||||
$container = $('<div class="shiny-progress-container"></div>');
|
||||
$('body').append($container);
|
||||
if (message.style === "notification") {
|
||||
// For new-style (starting in Shiny 0.14) progress indicators that use
|
||||
// the notification API.
|
||||
|
||||
// Progress bar starts hidden; will be made visible if a value is provided
|
||||
// during updates.
|
||||
exports.notifications.show({
|
||||
html: '<div id="shiny-progress-' + message.id + '" class="shiny-progress-notification">' + '<div class="progress progress-striped active" style="display: none;"><div class="progress-bar"></div></div>' + '<div class="progress-text">' + '<span class="progress-message">message</span> ' + '<span class="progress-detail"></span>' + '</div>' + '</div>',
|
||||
id: message.id,
|
||||
duration: null
|
||||
});
|
||||
} else if (message.style === "old") {
|
||||
// For old-style (Shiny <=0.13.2) progress indicators.
|
||||
|
||||
// Add progress container (for all progress items) if not already present
|
||||
var $container = $('.shiny-progress-container');
|
||||
if ($container.length === 0) {
|
||||
$container = $('<div class="shiny-progress-container"></div>');
|
||||
$('body').append($container);
|
||||
}
|
||||
|
||||
// Add div for just this progress ID
|
||||
var depth = $('.shiny-progress.open').length;
|
||||
// The 'bar' class is needed for backward compatibility with Bootstrap 2.
|
||||
var $progress = $('<div class="shiny-progress open">' + '<div class="progress progress-striped active"><div class="progress-bar bar"></div></div>' + '<div class="progress-text">' + '<span class="progress-message">message</span>' + '<span class="progress-detail"></span>' + '</div>' + '</div>');
|
||||
|
||||
$progress.attr('id', message.id);
|
||||
$container.append($progress);
|
||||
|
||||
// Stack bars
|
||||
var $progressBar = $progress.find('.progress');
|
||||
$progressBar.css('top', depth * $progressBar.height() + 'px');
|
||||
|
||||
// Stack text objects
|
||||
var $progressText = $progress.find('.progress-text');
|
||||
$progressText.css('top', 3 * $progressBar.height() + depth * $progressText.outerHeight() + 'px');
|
||||
|
||||
$progress.hide();
|
||||
}
|
||||
|
||||
// Add div for just this progress ID
|
||||
var depth = $('.shiny-progress.open').length;
|
||||
var $progress = $(progressHandlers.progressHTML);
|
||||
$progress.attr('id', message.id);
|
||||
$container.append($progress);
|
||||
|
||||
// Stack bars
|
||||
var $progressBar = $progress.find('.progress');
|
||||
$progressBar.css('top', depth * $progressBar.height() + 'px');
|
||||
|
||||
// Stack text objects
|
||||
var $progressText = $progress.find('.progress-text');
|
||||
$progressText.css('top', 3 * $progressBar.height() + depth * $progressText.outerHeight() + 'px');
|
||||
|
||||
$progress.hide();
|
||||
},
|
||||
|
||||
// Update page-level progress bar
|
||||
update: function update(message) {
|
||||
var $progress = $('#' + message.id + '.shiny-progress');
|
||||
if (typeof message.message !== 'undefined') {
|
||||
$progress.find('.progress-message').text(message.message);
|
||||
}
|
||||
if (typeof message.detail !== 'undefined') {
|
||||
$progress.find('.progress-detail').text(message.detail);
|
||||
}
|
||||
if (typeof message.value !== 'undefined') {
|
||||
if (message.value !== null) {
|
||||
$progress.find('.progress').show();
|
||||
$progress.find('.bar').width(message.value * 100 + '%');
|
||||
} else {
|
||||
$progress.find('.progress').hide();
|
||||
}
|
||||
}
|
||||
if (message.style === "notification") {
|
||||
// For new-style (starting in Shiny 0.14) progress indicators that use
|
||||
// the notification API.
|
||||
var $progress = $('#shiny-progress-' + message.id);
|
||||
|
||||
$progress.fadeIn();
|
||||
if ($progress.length === 0) return;
|
||||
|
||||
if (typeof message.message !== 'undefined') {
|
||||
$progress.find('.progress-message').text(message.message);
|
||||
}
|
||||
if (typeof message.detail !== 'undefined') {
|
||||
$progress.find('.progress-detail').text(message.detail);
|
||||
}
|
||||
if (typeof message.value !== 'undefined') {
|
||||
if (message.value !== null) {
|
||||
$progress.find('.progress').show();
|
||||
$progress.find('.progress-bar').width(message.value * 100 + '%');
|
||||
} else {
|
||||
$progress.find('.progress').hide();
|
||||
}
|
||||
}
|
||||
} else if (message.style === "old") {
|
||||
// For old-style (Shiny <=0.13.2) progress indicators.
|
||||
|
||||
var $progress = $('#' + message.id + '.shiny-progress');
|
||||
if (typeof message.message !== 'undefined') {
|
||||
$progress.find('.progress-message').text(message.message);
|
||||
}
|
||||
if (typeof message.detail !== 'undefined') {
|
||||
$progress.find('.progress-detail').text(message.detail);
|
||||
}
|
||||
if (typeof message.value !== 'undefined') {
|
||||
if (message.value !== null) {
|
||||
$progress.find('.progress').show();
|
||||
$progress.find('.bar').width(message.value * 100 + '%');
|
||||
} else {
|
||||
$progress.find('.progress').hide();
|
||||
}
|
||||
}
|
||||
|
||||
$progress.fadeIn();
|
||||
}
|
||||
},
|
||||
|
||||
// Close page-level progress bar
|
||||
close: function close(message) {
|
||||
var $progress = $('#' + message.id + '.shiny-progress');
|
||||
$progress.removeClass('open');
|
||||
if (message.style === "notification") {
|
||||
exports.notifications.remove(message.id);
|
||||
} else if (message.style === "old") {
|
||||
var $progress = $('#' + message.id + '.shiny-progress');
|
||||
$progress.removeClass('open');
|
||||
|
||||
$progress.fadeOut({
|
||||
complete: function complete() {
|
||||
$progress.remove();
|
||||
$progress.fadeOut({
|
||||
complete: function complete() {
|
||||
$progress.remove();
|
||||
|
||||
// If this was the last shiny-progress, remove container
|
||||
if ($('.shiny-progress').length === 0) $('.shiny-progress-container').remove();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// The 'bar' class is needed for backward compatibility with Bootstrap 2.
|
||||
progressHTML: '<div class="shiny-progress open">' + '<div class="progress progress-striped active"><div class="progress-bar bar"></div></div>' + '<div class="progress-text">' + '<span class="progress-message">message</span>' + '<span class="progress-detail"></span>' + '</div>' + '</div>'
|
||||
// If this was the last shiny-progress, remove container
|
||||
if ($('.shiny-progress').length === 0) $('.shiny-progress-container').remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.progressHandlers = progressHandlers;
|
||||
@@ -1362,7 +1423,7 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
_get(id).fadeOut(fadeDuration, function () {
|
||||
|
||||
exports.unbindAll(this);
|
||||
this.remove();
|
||||
$(this).remove();
|
||||
|
||||
// If no more notifications, remove the panel from the DOM.
|
||||
if (_ids().length === 0) {
|
||||
@@ -1468,7 +1529,7 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
|
||||
|
||||
// If there was an existing Bootstrap modal, then there will be a modal-
|
||||
// backdrop div that was added outside of the modal wrapper, and it must be
|
||||
// backdrop div that was added outside of the modal wrapper, and it must be
|
||||
// removed; otherwise there can be multiple of these divs.
|
||||
$('.modal-backdrop').remove();
|
||||
|
||||
@@ -2713,13 +2774,13 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
// No change
|
||||
|
||||
} else if (opts.brushDirection === 'x') {
|
||||
// Extend top and bottom of plotting area
|
||||
min.y = panelBounds.top;
|
||||
max.y = panelBounds.bottom;
|
||||
} else if (opts.brushDirection === 'y') {
|
||||
min.x = panelBounds.left;
|
||||
max.x = panelBounds.right;
|
||||
}
|
||||
// Extend top and bottom of plotting area
|
||||
min.y = panelBounds.top;
|
||||
max.y = panelBounds.bottom;
|
||||
} else if (opts.brushDirection === 'y') {
|
||||
min.x = panelBounds.left;
|
||||
max.x = panelBounds.right;
|
||||
}
|
||||
|
||||
state.boundsPx = {
|
||||
xmin: min.x,
|
||||
@@ -3030,10 +3091,10 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
}
|
||||
|
||||
exports.renderHtml(html, el, dependencies, where);
|
||||
exports.initializeInputs(el);
|
||||
|
||||
var scope = el;
|
||||
if (where === "replace") {
|
||||
exports.initializeInputs(el);
|
||||
exports.bindAll(el);
|
||||
} else {
|
||||
var $parent = $(el).parent();
|
||||
@@ -3044,12 +3105,15 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
if ($grandparent.length > 0) scope = $grandparent;
|
||||
}
|
||||
}
|
||||
exports.initializeInputs(scope);
|
||||
exports.bindAll(scope);
|
||||
}
|
||||
};
|
||||
|
||||
// Render HTML in a DOM element, inserting singletons into head as needed
|
||||
exports.renderHtml = function (html, el, dependencies, where) {
|
||||
exports.renderHtml = function (html, el, dependencies) {
|
||||
var where = arguments.length <= 3 || arguments[3] === undefined ? 'replace' : arguments[3];
|
||||
|
||||
renderDependencies(dependencies);
|
||||
return singletons.renderHtml(html, el, where);
|
||||
};
|
||||
@@ -3386,7 +3450,7 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
var textInputBinding = new InputBinding();
|
||||
$.extend(textInputBinding, {
|
||||
find: function find(scope) {
|
||||
return $(scope).find('input[type="text"], input[type="password"], input[type="search"], input[type="url"], input[type="email"]');
|
||||
return $(scope).find('input[type="text"], input[type="search"], input[type="url"], input[type="email"]');
|
||||
},
|
||||
getId: function getId(el) {
|
||||
return InputBinding.prototype.getId.call(this, el) || el.name;
|
||||
@@ -3441,6 +3505,20 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
});
|
||||
inputBindings.register(textareaInputBinding, 'shiny.textareaInput');
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Source file: ../srcjs/input_binding_password.js
|
||||
|
||||
var passwordInputBinding = {};
|
||||
$.extend(passwordInputBinding, textInputBinding, {
|
||||
find: function find(scope) {
|
||||
return $(scope).find('input[type="password"]');
|
||||
},
|
||||
getType: function getType(el) {
|
||||
return "shiny.password";
|
||||
}
|
||||
});
|
||||
inputBindings.register(passwordInputBinding, 'shiny.passwordInput');
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Source file: ../srcjs/input_binding_number.js
|
||||
|
||||
@@ -3758,6 +3836,12 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
},
|
||||
// value must be an unambiguous string like '2001-01-01', or a Date object.
|
||||
setValue: function setValue(el, value) {
|
||||
// R's NA, which is null here will remove current value
|
||||
if (value === null) {
|
||||
$(el).find('input').val('').datepicker('update');
|
||||
return;
|
||||
}
|
||||
|
||||
var date = this._newDate(value);
|
||||
// If date is invalid, do nothing
|
||||
if (isNaN(date)) return;
|
||||
@@ -3920,7 +4004,7 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
// value must be an array of unambiguous strings like '2001-01-01', or
|
||||
// Date objects.
|
||||
setValue: function setValue(el, value) {
|
||||
if (!(value instanceof Array)) {
|
||||
if (!(value instanceof Object)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3928,13 +4012,22 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
var $inputs = $(el).find('input');
|
||||
|
||||
// If value is undefined, don't try to set
|
||||
if (value[0] !== undefined) {
|
||||
var start = this._newDate(value[0]);
|
||||
$inputs.eq(0).datepicker('update', start);
|
||||
// null will remove the current value
|
||||
if (value.start !== undefined) {
|
||||
if (value.start === null) {
|
||||
$inputs.eq(0).val('').datepicker('update');
|
||||
} else {
|
||||
var start = this._newDate(value.start);
|
||||
$inputs.eq(0).datepicker('update', start);
|
||||
}
|
||||
}
|
||||
if (value[1] !== undefined) {
|
||||
var end = this._newDate(value[1]);
|
||||
$inputs.eq(1).datepicker('update', end);
|
||||
if (value.end !== undefined) {
|
||||
if (value.end === null) {
|
||||
$inputs.eq(1).val('').datepicker('update');
|
||||
} else {
|
||||
var end = this._newDate(value.end);
|
||||
$inputs.eq(1).datepicker('update', end);
|
||||
}
|
||||
}
|
||||
},
|
||||
getState: function getState(el) {
|
||||
@@ -4004,7 +4097,7 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
|
||||
if (end === undefined || end === null) end = this._dateAsUTC(new Date());
|
||||
|
||||
this.setValue(el, [start, end]);
|
||||
this.setValue(el, { "start": start, "end": end });
|
||||
|
||||
// // Set the start and end dates, from min-date and max-date. These always
|
||||
// // use yyyy-mm-dd format, instead of bootstrap-datepicker's built-in
|
||||
@@ -4287,8 +4380,8 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
}
|
||||
// Else assume it's a single value
|
||||
} else {
|
||||
$('input:checkbox[name="' + $escape(el.id) + '"][value="' + $escape(value) + '"]').prop('checked', true);
|
||||
}
|
||||
$('input:checkbox[name="' + $escape(el.id) + '"][value="' + $escape(value) + '"]').prop('checked', true);
|
||||
}
|
||||
},
|
||||
getState: function getState(el) {
|
||||
var $objs = $('input:checkbox[name="' + $escape(el.id) + '"]');
|
||||
@@ -4626,8 +4719,8 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
|
||||
function uploadFiles(evt) {
|
||||
// If previously selected files are uploading, abort that.
|
||||
var el = $(evt.target);
|
||||
var uploader = el.data('currentUploader');
|
||||
var $el = $(evt.target);
|
||||
var uploader = $el.data('currentUploader');
|
||||
if (uploader) uploader.abort();
|
||||
|
||||
var files = evt.target.files;
|
||||
@@ -4638,12 +4731,26 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
|
||||
if (!IE8 && files.length === 0) return;
|
||||
|
||||
// Clear data-restore attribute if present.
|
||||
$el.removeAttr('data-restore');
|
||||
|
||||
// Set the label in the text box
|
||||
var $fileText = $el.closest('div.input-group').find('input[type=text]');
|
||||
if (IE8) {
|
||||
// If we're using IE8/9, just use this placeholder
|
||||
$fileText.val("[Uploaded file]");
|
||||
} else if (files.length === 1) {
|
||||
$fileText.val(files[0].name);
|
||||
} else {
|
||||
$fileText.val(files.length + " files");
|
||||
}
|
||||
|
||||
// Start the new upload and put the uploader in 'currentUploader'.
|
||||
if (IE8) {
|
||||
/*jshint nonew:false */
|
||||
new IE8FileUploader(exports.shinyapp, id, evt.target);
|
||||
} else {
|
||||
el.data('currentUploader', new FileUploader(exports.shinyapp, id, files));
|
||||
$el.data('currentUploader', new FileUploader(exports.shinyapp, id, files));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4656,11 +4763,41 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
return InputBinding.prototype.getId.call(this, el) || el.name;
|
||||
},
|
||||
getValue: function getValue(el) {
|
||||
return null;
|
||||
// This returns a non-undefined value only when there's a 'data-restore'
|
||||
// attribute, which is set only when restoring Shiny state. If a file is
|
||||
// uploaded through the browser, 'data-restore' gets cleared.
|
||||
var data = $(el).attr('data-restore');
|
||||
if (data) {
|
||||
data = JSON.parse(data);
|
||||
|
||||
// Set the label in the text box
|
||||
var $fileText = $(el).closest('div.input-group').find('input[type=text]');
|
||||
if (data.name.length === 1) {
|
||||
$fileText.val(data.name[0]);
|
||||
} else {
|
||||
$fileText.val(data.name.length + " files");
|
||||
}
|
||||
|
||||
// Manually set up progress bar. A bit inelegant because it duplicates
|
||||
// code from FileUploader, but duplication is less bad than alternatives.
|
||||
var $progress = $(el).closest('div.form-group').find('.progress');
|
||||
var $bar = $progress.find('.progress-bar');
|
||||
$progress.removeClass('active');
|
||||
$bar.width('100%');
|
||||
$bar.css('visibility', 'visible');
|
||||
|
||||
return data;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
setValue: function setValue(el, value) {
|
||||
// Not implemented
|
||||
},
|
||||
getType: function getType(el) {
|
||||
// This will be used only when restoring a file from a saved state.
|
||||
return 'shiny.file';
|
||||
},
|
||||
subscribe: function subscribe(el, callback) {
|
||||
$(el).on('change.fileInputBinding', uploadFiles);
|
||||
},
|
||||
@@ -4906,7 +5043,10 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
|
||||
// Iterate over all input objects for this binding
|
||||
for (var j = 0; j < inputObjects.length; j++) {
|
||||
binding.initialize(inputObjects[j]);
|
||||
if (!inputObjects[j]._shiny_initialized) {
|
||||
inputObjects[j]._shiny_initialized = true;
|
||||
binding.initialize(inputObjects[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5103,6 +5243,7 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
|
||||
});
|
||||
} // function initShiny()
|
||||
|
||||
|
||||
// Give any deferred iframes a chance to load.
|
||||
function initDeferredIframes() {
|
||||
if (!window.Shiny || !window.Shiny.shinyapp || !window.Shiny.shinyapp.isConnected()) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
8
inst/www/shared/shiny.min.js
vendored
8
inst/www/shared/shiny.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -13,7 +13,8 @@
|
||||
#'
|
||||
#' For \code{\link{radioButtons}()}, \code{\link{checkboxGroupInput}()} and
|
||||
#' \code{\link{selectInput}()}, the set of choices can be cleared by using
|
||||
#' \code{choices=character(0)}.
|
||||
#' \code{choices=character(0)}. Similarly, for these inputs, the selected item
|
||||
#' can be cleared by using \code{selected=character(0)}.
|
||||
#'
|
||||
#' @param session The \code{session} object passed to function given to
|
||||
#' \code{shinyServer}.
|
||||
|
||||
@@ -27,6 +27,11 @@ de-emphasized appearance relative to \code{message}.}
|
||||
the progress bar, relative to \code{min} and \code{max}.
|
||||
\code{NULL} hides the progress bar, if it is currently visible.}
|
||||
|
||||
\item{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).}
|
||||
|
||||
\item{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.}
|
||||
@@ -48,6 +53,14 @@ created, and it will be displayed the first time the \code{set}
|
||||
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)}}{
|
||||
|
||||
@@ -62,6 +62,6 @@ Other input.elements: \code{\link{checkboxGroupInput}},
|
||||
\code{\link{numericInput}}, \code{\link{passwordInput}},
|
||||
\code{\link{radioButtons}}, \code{\link{selectInput}},
|
||||
\code{\link{sliderInput}}, \code{\link{submitButton}},
|
||||
\code{\link{textInput}}
|
||||
\code{\link{textAreaInput}}, \code{\link{textInput}}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ You can call \code{addResourcePath} multiple times for a given
|
||||
}
|
||||
\examples{
|
||||
addResourcePath('datasets', system.file('data', package='datasets'))
|
||||
|
||||
}
|
||||
\seealso{
|
||||
\code{\link{singleton}}
|
||||
|
||||
73
man/bookmarkButton.Rd
Normal file
73
man/bookmarkButton.Rd
Normal file
@@ -0,0 +1,73 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/bookmark-state.R
|
||||
\name{bookmarkButton}
|
||||
\alias{bookmarkButton}
|
||||
\title{Create a button for bookmarking/sharing}
|
||||
\usage{
|
||||
bookmarkButton(label = "Bookmark...", icon = shiny::icon("link", lib =
|
||||
"glyphicon"),
|
||||
title = "Bookmark this application's state and get a URL for sharing.", ...,
|
||||
id = "._bookmark_")
|
||||
}
|
||||
\arguments{
|
||||
\item{label}{The contents of the button or link--usually a text label, but
|
||||
you could also use any other HTML, like an image.}
|
||||
|
||||
\item{icon}{An optional \code{\link{icon}} to appear on the button.}
|
||||
|
||||
\item{title}{A tooltip that is shown when the mouse cursor hovers over the
|
||||
button.}
|
||||
|
||||
\item{...}{Named attributes to be applied to the button or link.}
|
||||
|
||||
\item{id}{An ID for the bookmark button. The only time it is necessary to set
|
||||
the ID unless you have more than one bookmark button in your application.
|
||||
If you specify an input ID, it should be excluded from bookmarking with
|
||||
\code{\link{setBookmarkExclude}}, and you must create an observer that
|
||||
does the bookmarking when the button is pressed. See the examples below.}
|
||||
}
|
||||
\description{
|
||||
A \code{bookmarkButton} is a \code{\link{actionButton}} with a default label
|
||||
that consists of a link icon and the text "Bookmark...". It is meant to be
|
||||
used for bookmarking state.
|
||||
}
|
||||
\examples{
|
||||
## Only run these examples in interactive sessions
|
||||
if (interactive()) {
|
||||
|
||||
# This example shows how to use multiple bookmark buttons. If you only need
|
||||
# a single bookmark button, see examples in ?enableBookmarking.
|
||||
ui <- function(request) {
|
||||
fluidPage(
|
||||
tabsetPanel(id = "tabs",
|
||||
tabPanel("One",
|
||||
checkboxInput("chk1", "Checkbox 1"),
|
||||
bookmarkButton(id = "bookmark1")
|
||||
),
|
||||
tabPanel("Two",
|
||||
checkboxInput("chk2", "Checkbox 2"),
|
||||
bookmarkButton(id = "bookmark2")
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
server <- function(input, output, session) {
|
||||
# Need to exclude the buttons from themselves being bookmarked
|
||||
setBookmarkExclude(c("bookmark1", "bookmark2"))
|
||||
|
||||
# Trigger bookmarking with either button
|
||||
observeEvent(input$bookmark1, {
|
||||
session$doBookmark()
|
||||
})
|
||||
observeEvent(input$bookmark2, {
|
||||
session$doBookmark()
|
||||
})
|
||||
}
|
||||
enableBookmarking(store = "url")
|
||||
shinyApp(ui, server)
|
||||
}
|
||||
}
|
||||
\seealso{
|
||||
\code{\link{enableBookmarking}} for more examples.
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/utils.R
|
||||
\name{cancelOutput}
|
||||
\alias{cancelOutput}
|
||||
\title{Cancel processing of the current output}
|
||||
\usage{
|
||||
cancelOutput()
|
||||
}
|
||||
\description{
|
||||
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.
|
||||
}
|
||||
\details{
|
||||
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)}.
|
||||
}
|
||||
|
||||
@@ -60,6 +60,6 @@ Other input.elements: \code{\link{actionButton}},
|
||||
\code{\link{numericInput}}, \code{\link{passwordInput}},
|
||||
\code{\link{radioButtons}}, \code{\link{selectInput}},
|
||||
\code{\link{sliderInput}}, \code{\link{submitButton}},
|
||||
\code{\link{textInput}}
|
||||
\code{\link{textAreaInput}}, \code{\link{textInput}}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ Other input.elements: \code{\link{actionButton}},
|
||||
\code{\link{fileInput}}, \code{\link{numericInput}},
|
||||
\code{\link{passwordInput}}, \code{\link{radioButtons}},
|
||||
\code{\link{selectInput}}, \code{\link{sliderInput}},
|
||||
\code{\link{submitButton}}, \code{\link{textInput}}
|
||||
\code{\link{submitButton}}, \code{\link{textAreaInput}},
|
||||
\code{\link{textInput}}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,5 @@ sidebarPanel(
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ createWebDependency(dependency)
|
||||
}
|
||||
\arguments{
|
||||
\item{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.}
|
||||
}
|
||||
\value{
|
||||
|
||||
@@ -101,6 +101,6 @@ Other input.elements: \code{\link{actionButton}},
|
||||
\code{\link{numericInput}}, \code{\link{passwordInput}},
|
||||
\code{\link{radioButtons}}, \code{\link{selectInput}},
|
||||
\code{\link{sliderInput}}, \code{\link{submitButton}},
|
||||
\code{\link{textInput}}
|
||||
\code{\link{textAreaInput}}, \code{\link{textInput}}
|
||||
}
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ Other input.elements: \code{\link{actionButton}},
|
||||
\code{\link{fileInput}}, \code{\link{numericInput}},
|
||||
\code{\link{passwordInput}}, \code{\link{radioButtons}},
|
||||
\code{\link{selectInput}}, \code{\link{sliderInput}},
|
||||
\code{\link{submitButton}}, \code{\link{textInput}}
|
||||
\code{\link{submitButton}}, \code{\link{textAreaInput}},
|
||||
\code{\link{textInput}}
|
||||
}
|
||||
|
||||
|
||||
231
man/enableBookmarking.Rd
Normal file
231
man/enableBookmarking.Rd
Normal file
@@ -0,0 +1,231 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/bookmark-state.R
|
||||
\name{enableBookmarking}
|
||||
\alias{enableBookmarking}
|
||||
\title{Enable bookmarking for a Shiny application}
|
||||
\usage{
|
||||
enableBookmarking(store = c("url", "server", "disable"))
|
||||
}
|
||||
\arguments{
|
||||
\item{store}{Either \code{"url"}, which encodes all of the relevant values in
|
||||
a URL, \code{"server"}, which saves to disk on the server, or
|
||||
\code{"disable"}, which disables any previously-enabled bookmarking.}
|
||||
}
|
||||
\description{
|
||||
There are two types of bookmarking: saving an application's state to disk on
|
||||
the server, and encoding the application's state in a URL. For state that has
|
||||
been saved to disk, the state can be restored with the corresponding state
|
||||
ID. For URL-encoded state, the state of the application is encoded in the
|
||||
URL, and no server-side storage is needed.
|
||||
|
||||
URL-encoded bookmarking is appropriate for applications where there not many
|
||||
input values that need to be recorded. Some browsers have a length limit for
|
||||
URLs of about 2000 characters, and if there are many inputs, the length of
|
||||
the URL can exceed that limit.
|
||||
|
||||
Saved-on-server bookmarking is appropriate when there are many inputs, or
|
||||
when the bookmarked state requires storing files.
|
||||
}
|
||||
\details{
|
||||
For restoring state to work properly, the UI must be a function that takes
|
||||
one argument, \code{request}. In most Shiny applications, the UI is not a
|
||||
function; it might have the form \code{fluidPage(....)}. Converting it to a
|
||||
function is as simple as wrapping it in a function, as in
|
||||
\code{function(request) \{ fluidPage(....) \}}.
|
||||
|
||||
By default, all input values will be bookmarked, except for the values of
|
||||
passwordInputs. fileInputs will be saved if the state is saved on a server,
|
||||
but not if the state is encoded in a URL.
|
||||
|
||||
When bookmarking state, arbitrary values can be stored, by passing a function
|
||||
as the \code{onBookmark} argument. That function will be passed a
|
||||
\code{ShinySaveState} object. The \code{values} field of the object is a list
|
||||
which can be manipulated to save extra information. Additionally, if the
|
||||
state is being saved on the server, and the \code{dir} field of that object
|
||||
can be used to save extra information to files in that directory.
|
||||
|
||||
For saved-to-server state, this is how the state directory is chosen:
|
||||
\itemize{
|
||||
\item If running in a hosting environment such as Shiny Server or
|
||||
Connect, the hosting environment will choose the directory.
|
||||
\item If running an app in a directory with \code{\link{runApp}()}, the
|
||||
saved states will be saved in a subdirectory of the app called
|
||||
shiny_bookmarks.
|
||||
\item If running a Shiny app object that is generated from code (not run
|
||||
from a directory), the saved states will be saved in a subdirectory of
|
||||
the current working directory called shiny_bookmarks.
|
||||
}
|
||||
|
||||
When used with \code{\link{shinyApp}()}, this function must be called before
|
||||
\code{shinyApp()}, or in the \code{shinyApp()}'s \code{onStart} function. An
|
||||
alternative to calling the \code{enableBookmarking()} function is to use the
|
||||
\code{enableBookmarking} \emph{argument} for \code{shinyApp()}. See examples
|
||||
below.
|
||||
}
|
||||
\examples{
|
||||
## Only run these examples in interactive R sessions
|
||||
if (interactive()) {
|
||||
|
||||
# Basic example with state encoded in URL
|
||||
ui <- function(request) {
|
||||
fluidPage(
|
||||
textInput("txt", "Text"),
|
||||
checkboxInput("chk", "Checkbox"),
|
||||
bookmarkButton()
|
||||
)
|
||||
}
|
||||
server <- function(input, output, session) { }
|
||||
enableBookmarking("url")
|
||||
shinyApp(ui, server)
|
||||
|
||||
|
||||
# An alternative to calling enableBookmarking(): use shinyApp's
|
||||
# enableBookmarking argument
|
||||
shinyApp(ui, server, enableBookmarking = "url")
|
||||
|
||||
|
||||
# Same basic example with state saved to disk
|
||||
enableBookmarking("server")
|
||||
shinyApp(ui, server)
|
||||
|
||||
|
||||
# Save/restore arbitrary values
|
||||
ui <- function(req) {
|
||||
fluidPage(
|
||||
textInput("txt", "Text"),
|
||||
checkboxInput("chk", "Checkbox"),
|
||||
bookmarkButton(),
|
||||
br(),
|
||||
textOutput("lastSaved")
|
||||
)
|
||||
}
|
||||
server <- function(input, output, session) {
|
||||
vals <- reactiveValues(savedTime = NULL)
|
||||
output$lastSaved <- renderText({
|
||||
if (!is.null(vals$savedTime))
|
||||
paste("Last saved at", vals$savedTime)
|
||||
else
|
||||
""
|
||||
})
|
||||
|
||||
onBookmark(function(state) {
|
||||
vals$savedTime <- Sys.time()
|
||||
# state is a mutable reference object, and we can add arbitrary values
|
||||
# to it.
|
||||
state$values$time <- vals$savedTime
|
||||
})
|
||||
onRestore(function(state) {
|
||||
vals$savedTime <- state$values$time
|
||||
})
|
||||
}
|
||||
enableBookmarking(store = "url")
|
||||
shinyApp(ui, server)
|
||||
|
||||
|
||||
# Usable with dynamic UI (set the slider, then change the text input,
|
||||
# click the bookmark button)
|
||||
ui <- function(request) {
|
||||
fluidPage(
|
||||
sliderInput("slider", "Slider", 1, 100, 50),
|
||||
uiOutput("ui"),
|
||||
bookmarkButton()
|
||||
)
|
||||
}
|
||||
server <- function(input, output, session) {
|
||||
output$ui <- renderUI({
|
||||
textInput("txt", "Text", input$slider)
|
||||
})
|
||||
}
|
||||
enableBookmarking("url")
|
||||
shinyApp(ui, server)
|
||||
|
||||
|
||||
# Exclude specific inputs (The only input that will be saved in this
|
||||
# example is chk)
|
||||
ui <- function(request) {
|
||||
fluidPage(
|
||||
passwordInput("pw", "Password"), # Passwords are never saved
|
||||
sliderInput("slider", "Slider", 1, 100, 50), # Manually excluded below
|
||||
checkboxInput("chk", "Checkbox"),
|
||||
bookmarkButton()
|
||||
)
|
||||
}
|
||||
server <- function(input, output, session) {
|
||||
setBookmarkExclude("slider")
|
||||
}
|
||||
enableBookmarking("url")
|
||||
shinyApp(ui, server)
|
||||
|
||||
|
||||
# Update the browser's location bar every time an input changes. This should
|
||||
# not be used with enableBookmarking("server"), because that would create a
|
||||
# new saved state on disk every time the user changes an input.
|
||||
ui <- function(req) {
|
||||
fluidPage(
|
||||
textInput("txt", "Text"),
|
||||
checkboxInput("chk", "Checkbox")
|
||||
)
|
||||
}
|
||||
server <- function(input, output, session) {
|
||||
observe({
|
||||
# Trigger this observer every time an input changes
|
||||
reactiveValuesToList(input)
|
||||
session$doBookmark()
|
||||
})
|
||||
onBookmarked(function(url) {
|
||||
updateQueryString(url)
|
||||
})
|
||||
}
|
||||
enableBookmarking("url")
|
||||
shinyApp(ui, server)
|
||||
|
||||
|
||||
# Save/restore uploaded files
|
||||
ui <- function(request) {
|
||||
fluidPage(
|
||||
sidebarLayout(
|
||||
sidebarPanel(
|
||||
fileInput("file1", "Choose CSV File", multiple = TRUE,
|
||||
accept = c(
|
||||
"text/csv",
|
||||
"text/comma-separated-values,text/plain",
|
||||
".csv"
|
||||
)
|
||||
),
|
||||
tags$hr(),
|
||||
checkboxInput("header", "Header", TRUE),
|
||||
bookmarkButton()
|
||||
),
|
||||
mainPanel(
|
||||
tableOutput("contents")
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
server <- function(input, output) {
|
||||
output$contents <- renderTable({
|
||||
inFile <- input$file1
|
||||
if (is.null(inFile))
|
||||
return(NULL)
|
||||
|
||||
if (nrow(inFile) == 1) {
|
||||
read.csv(inFile$datapath, header = input$header)
|
||||
} else {
|
||||
data.frame(x = "multiple files")
|
||||
}
|
||||
})
|
||||
}
|
||||
enableBookmarking("server")
|
||||
shinyApp(ui, server)
|
||||
|
||||
}
|
||||
}
|
||||
\seealso{
|
||||
\code{\link{onBookmark}}, \code{\link{onBookmarked}},
|
||||
\code{\link{onRestore}}, and \code{\link{onRestored}} for registering
|
||||
callback functions that are invoked when the state is bookmarked or
|
||||
restored.
|
||||
|
||||
Also see \code{\link{updateQueryString}}.
|
||||
}
|
||||
|
||||
@@ -57,6 +57,5 @@ tripleA <- renderTriple({
|
||||
|
||||
isolate(tripleA())
|
||||
# "text, text, text"
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +90,7 @@ Other input.elements: \code{\link{actionButton}},
|
||||
\code{\link{dateRangeInput}}, \code{\link{numericInput}},
|
||||
\code{\link{passwordInput}}, \code{\link{radioButtons}},
|
||||
\code{\link{selectInput}}, \code{\link{sliderInput}},
|
||||
\code{\link{submitButton}}, \code{\link{textInput}}
|
||||
\code{\link{submitButton}}, \code{\link{textAreaInput}},
|
||||
\code{\link{textInput}}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,5 @@ fillPage(
|
||||
div(style = "background-color: blue; width: 100\%; height: 100\%;")
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
61
man/freezeReactiveValue.Rd
Normal file
61
man/freezeReactiveValue.Rd
Normal file
@@ -0,0 +1,61 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/reactives.R
|
||||
\name{freezeReactiveValue}
|
||||
\alias{freezeReactiveValue}
|
||||
\title{Freeze a reactive value}
|
||||
\usage{
|
||||
freezeReactiveValue(x, name)
|
||||
}
|
||||
\arguments{
|
||||
\item{x}{A \code{\link{reactiveValues}} object (like \code{input}).}
|
||||
|
||||
\item{name}{The name of a value in the \code{\link{reactiveValues}} object.}
|
||||
}
|
||||
\description{
|
||||
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.
|
||||
}
|
||||
\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)
|
||||
}
|
||||
}
|
||||
\seealso{
|
||||
\code{\link{req}}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ navbarPage("App Title",
|
||||
tabPanel("Summary", icon = icon("list-alt")),
|
||||
tabPanel("Table", icon = icon("table"))
|
||||
)
|
||||
|
||||
}
|
||||
\seealso{
|
||||
For lists of available icons, see
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
\alias{insertUI}
|
||||
\title{Insert UI objects}
|
||||
\usage{
|
||||
insertUI(selector, multiple = FALSE, where = c("beforeBegin", "afterBegin",
|
||||
"beforeEnd", "afterEnd"), ui, immediate = FALSE,
|
||||
insertUI(selector, where = c("beforeBegin", "afterBegin", "beforeEnd",
|
||||
"afterEnd"), ui, multiple = FALSE, immediate = FALSE,
|
||||
session = getDefaultReactiveDomain())
|
||||
}
|
||||
\arguments{
|
||||
@@ -14,11 +14,6 @@ string \code{s} to be placed in a \code{$(s)} jQuery call). This selector
|
||||
will determine the element(s) relative to which you want to insert your
|
||||
UI object.}
|
||||
|
||||
\item{multiple}{In case your selector matches more than one element,
|
||||
\code{multiple} determines whether Shiny should insert the UI object
|
||||
relative to all matched elements or just relative to the first
|
||||
matched element (default).}
|
||||
|
||||
\item{where}{Where your UI object should go relative to the selector:
|
||||
\describe{
|
||||
\item{\code{beforeBegin}}{Before the selector element itself}
|
||||
@@ -39,6 +34,11 @@ advantage that you can give it an \code{id} to make it easier to
|
||||
reference or remove it later on). If you want to insert raw html, use
|
||||
\code{ui = HTML()}.}
|
||||
|
||||
\item{multiple}{In case your selector matches more than one element,
|
||||
\code{multiple} determines whether Shiny should insert the UI object
|
||||
relative to all matched elements or just relative to the first
|
||||
matched element (default).}
|
||||
|
||||
\item{immediate}{Whether the UI object should be immediately inserted into
|
||||
the app when you call \code{insertUI}, or whether Shiny should wait until
|
||||
all outputs have been updated and all observers have been run (default).}
|
||||
@@ -82,7 +82,6 @@ server <- function(input, output, session) {
|
||||
# Complete app with UI and server components
|
||||
shinyApp(ui, server)
|
||||
}
|
||||
|
||||
}
|
||||
\seealso{
|
||||
\code{\link{removeUI}}
|
||||
|
||||
@@ -76,6 +76,5 @@ isolate(fun())
|
||||
|
||||
# isolate also works if the reactive expression accesses values from the
|
||||
# input object, like input$x
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
\title{Create a modal dialog UI}
|
||||
\usage{
|
||||
modalDialog(..., title = NULL, footer = modalButton("Dismiss"),
|
||||
easyClose = FALSE)
|
||||
size = c("m", "s", "l"), easyClose = FALSE)
|
||||
}
|
||||
\arguments{
|
||||
\item{...}{UI elements for the body of the modal dialog box.}
|
||||
@@ -14,6 +14,9 @@ modalDialog(..., title = NULL, footer = modalButton("Dismiss"),
|
||||
|
||||
\item{footer}{UI for footer. Use \code{NULL} for no footer.}
|
||||
|
||||
\item{size}{One of \code{"s"} for small, \code{"m"} (the default) for medium,
|
||||
or \code{"l"} for large.}
|
||||
|
||||
\item{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
|
||||
@@ -63,6 +66,7 @@ shinyApp(
|
||||
)
|
||||
|
||||
|
||||
# Display a modal that requires valid input before continuing.
|
||||
shinyApp(
|
||||
ui = basicPage(
|
||||
actionButton("show", "Show modal dialog"),
|
||||
@@ -102,7 +106,8 @@ shinyApp(
|
||||
# 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 {
|
||||
|
||||
@@ -52,6 +52,7 @@ Other input.elements: \code{\link{actionButton}},
|
||||
\code{\link{dateRangeInput}}, \code{\link{fileInput}},
|
||||
\code{\link{passwordInput}}, \code{\link{radioButtons}},
|
||||
\code{\link{selectInput}}, \code{\link{sliderInput}},
|
||||
\code{\link{submitButton}}, \code{\link{textInput}}
|
||||
\code{\link{submitButton}}, \code{\link{textAreaInput}},
|
||||
\code{\link{textInput}}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user