Compare commits
394 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c98de4c8b | ||
|
|
9613dde4d2 | ||
|
|
d47df2e538 | ||
|
|
6fcacd5159 | ||
|
|
11b39cb020 | ||
|
|
d81f132db6 | ||
|
|
095697e789 | ||
|
|
62d98c3137 | ||
|
|
e80d5dc172 | ||
|
|
421e29db2d | ||
|
|
9e6e53583c | ||
|
|
3f59a7d84e | ||
|
|
21ffd788ab | ||
|
|
8dadfea724 | ||
|
|
00ce52ecf7 | ||
|
|
50ac13d3fd | ||
|
|
58318fec46 | ||
|
|
a49941113e | ||
|
|
595801cb99 | ||
|
|
0b469f09df | ||
|
|
1e1f4e4a47 | ||
|
|
c63e2ae7c8 | ||
|
|
d3d3fa990e | ||
|
|
21980b7e71 | ||
|
|
844ca0d387 | ||
|
|
972ae35300 | ||
|
|
57bfb8eb96 | ||
|
|
ed6e6a9fb2 | ||
|
|
ed402267b6 | ||
|
|
6eec570828 | ||
|
|
22fc1e3f0b | ||
|
|
ae9bd868f1 | ||
|
|
a887012aca | ||
|
|
bc73048ab9 | ||
|
|
c89dd6c379 | ||
|
|
9662debe5e | ||
|
|
057262d917 | ||
|
|
b6723a6219 | ||
|
|
068f3e0a43 | ||
|
|
95635a8c47 | ||
|
|
3ec2071820 | ||
|
|
1696db3044 | ||
|
|
e1a1eab2b3 | ||
|
|
f7865f3358 | ||
|
|
6d5f8ed5f3 | ||
|
|
96a737379f | ||
|
|
d73feec013 | ||
|
|
2ccead1da5 | ||
|
|
8885f2717e | ||
|
|
4448ffc777 | ||
|
|
022d10c598 | ||
|
|
8e6b7043bd | ||
|
|
66eaaff598 | ||
|
|
478c6c134f | ||
|
|
b5d333ba6c | ||
|
|
81723d55ac | ||
|
|
fb784ce962 | ||
|
|
5a37380900 | ||
|
|
b6300f3a5c | ||
|
|
a3e8a2d623 | ||
|
|
7b3a4bdc39 | ||
|
|
cc0b5e5e0f | ||
|
|
5c3f7d8f94 | ||
|
|
8c3f8cd450 | ||
|
|
046582711a | ||
|
|
15756ec92d | ||
|
|
fc49abc9fb | ||
|
|
4a9ff27f3e | ||
|
|
790e6f370f | ||
|
|
16ccc1321d | ||
|
|
8648c94dd4 | ||
|
|
dc4eb720ae | ||
|
|
0b891ad557 | ||
|
|
e96193ae28 | ||
|
|
3ff9075959 | ||
|
|
c03842056c | ||
|
|
6df226b21c | ||
|
|
7dfa7d7426 | ||
|
|
b8b1a891cf | ||
|
|
7df0e8b0f9 | ||
|
|
ff072ae9d9 | ||
|
|
f81ca39741 | ||
|
|
3db1f2a98c | ||
|
|
4865df9be1 | ||
|
|
0c16f2c334 | ||
|
|
d01149620f | ||
|
|
ab9401f390 | ||
|
|
3223c17b74 | ||
|
|
404035bcf0 | ||
|
|
a0185bb0b4 | ||
|
|
1a591cd9f1 | ||
|
|
e9b81b2033 | ||
|
|
cbfc1e8ed1 | ||
|
|
cb63338805 | ||
|
|
bcdc82ccee | ||
|
|
76a4cf6c34 | ||
|
|
872f23b0f0 | ||
|
|
e61f7405fd | ||
|
|
0714871b56 | ||
|
|
8a89fb2a1a | ||
|
|
036544e3ed | ||
|
|
7a6784d809 | ||
|
|
ed9301705b | ||
|
|
21f9694574 | ||
|
|
3a0b11b89d | ||
|
|
d5272e3e74 | ||
|
|
b5197869db | ||
|
|
5f775db40a | ||
|
|
9b84b83627 | ||
|
|
b0d9b5762a | ||
|
|
8d9fd402be | ||
|
|
73a44a4f8e | ||
|
|
a7dd62249e | ||
|
|
42fac871fb | ||
|
|
2782bf6735 | ||
|
|
f2a1ce4977 | ||
|
|
c8969c4cc0 | ||
|
|
cfefb4a07c | ||
|
|
653509368b | ||
|
|
51b269f329 | ||
|
|
c92d2cc32e | ||
|
|
cb4091895a | ||
|
|
b96bc5a710 | ||
|
|
4ace825c58 | ||
|
|
589e0f7bb5 | ||
|
|
347b9e1d7a | ||
|
|
363633b01f | ||
|
|
575350842a | ||
|
|
d49e8d172f | ||
|
|
642d153202 | ||
|
|
8cf7d8b4cb | ||
|
|
b0b7cfa3a5 | ||
|
|
3692d5f008 | ||
|
|
2344dc04a5 | ||
|
|
cf37072bed | ||
|
|
cc5c933e7d | ||
|
|
ad1737f16b | ||
|
|
2ac1895a6e | ||
|
|
4dc7630adc | ||
|
|
66a3d68755 | ||
|
|
ce9213cdc1 | ||
|
|
99b1ed51a6 | ||
|
|
c7dcff56c7 | ||
|
|
fcdb8f08b8 | ||
|
|
daa03999b6 | ||
|
|
cd7c5dc048 | ||
|
|
09f0f85b9c | ||
|
|
8aee7563f0 | ||
|
|
6d6c8cecd6 | ||
|
|
334f6c8757 | ||
|
|
8455343fee | ||
|
|
d234ab016f | ||
|
|
a312b46e97 | ||
|
|
ff06c7997b | ||
|
|
3dc6d84d1f | ||
|
|
ef74483ebb | ||
|
|
d8cf7bcbf8 | ||
|
|
33336a7ad2 | ||
|
|
79865b39d1 | ||
|
|
375125e992 | ||
|
|
ebc5a992dc | ||
|
|
da01184fc9 | ||
|
|
e0a6a6c558 | ||
|
|
93ec81bc57 | ||
|
|
29295fa8a7 | ||
|
|
74e7130bee | ||
|
|
30cd83662a | ||
|
|
5f8f3ca328 | ||
|
|
5744f1c7ee | ||
|
|
ba05f03359 | ||
|
|
43c9ed0655 | ||
|
|
43fa8f53d3 | ||
|
|
258dad0389 | ||
|
|
5d5eaa2065 | ||
|
|
1329136792 | ||
|
|
c6cbcff9ce | ||
|
|
ed2e637596 | ||
|
|
c97aecf9ff | ||
|
|
9672088158 | ||
|
|
995908d3c6 | ||
|
|
74314457ba | ||
|
|
d64c99ed28 | ||
|
|
38bf13e9bf | ||
|
|
4101c1efd0 | ||
|
|
f095700485 | ||
|
|
4ff1c95083 | ||
|
|
c3e14933e2 | ||
|
|
1b3cf52a17 | ||
|
|
e2f8163b21 | ||
|
|
54d3e1a5e1 | ||
|
|
57e088f6e1 | ||
|
|
c759dcd7df | ||
|
|
033eb41a1d | ||
|
|
77f6e138ac | ||
|
|
c5c70b0f49 | ||
|
|
6b37e026fd | ||
|
|
731018082b | ||
|
|
a7eab9f00e | ||
|
|
0d3aebc077 | ||
|
|
fb37e3254d | ||
|
|
6d9da1260a | ||
|
|
0d749f333a | ||
|
|
338463057c | ||
|
|
35c131f661 | ||
|
|
da6771eaae | ||
|
|
fbf3623343 | ||
|
|
2d43817b2f | ||
|
|
01905c51dd | ||
|
|
84494b8a0a | ||
|
|
aded289558 | ||
|
|
f1462fa0d2 | ||
|
|
5cfd546b2a | ||
|
|
3b38792481 | ||
|
|
31b347e8dd | ||
|
|
d87149ab5d | ||
|
|
fd2f4789d3 | ||
|
|
0d8d35743d | ||
|
|
b5a65040b3 | ||
|
|
d44289f036 | ||
|
|
cb4b45aff1 | ||
|
|
0f4851e77d | ||
|
|
42fe86e024 | ||
|
|
3bb0ebb98f | ||
|
|
391310faa5 | ||
|
|
ab0552f409 | ||
|
|
8a6f59e350 | ||
|
|
8e859e53c2 | ||
|
|
a44e475451 | ||
|
|
f958839af1 | ||
|
|
f741851250 | ||
|
|
acd68b5de8 | ||
|
|
466ea7277f | ||
|
|
c80072a62e | ||
|
|
bc0a37e8da | ||
|
|
a323f40da2 | ||
|
|
ee05e6ba03 | ||
|
|
ae9ef5c13f | ||
|
|
fcc90df31c | ||
|
|
d6b6719b54 | ||
|
|
21e8af827f | ||
|
|
5e5d233d83 | ||
|
|
214fd92b12 | ||
|
|
3687790730 | ||
|
|
d0f86078aa | ||
|
|
649cb69466 | ||
|
|
2f342e7664 | ||
|
|
e4fccc2f84 | ||
|
|
61bd2d356b | ||
|
|
66ddb6ce0a | ||
|
|
573b3b1dfd | ||
|
|
560bd3ca85 | ||
|
|
1f5fe5b570 | ||
|
|
d18d2df417 | ||
|
|
91731a86bf | ||
|
|
7108761e8f | ||
|
|
0fe8bacf73 | ||
|
|
ef1afb482f | ||
|
|
134a3de256 | ||
|
|
71975546cb | ||
|
|
b4c02f42f7 | ||
|
|
da7210f43f | ||
|
|
8b4d62e374 | ||
|
|
b68da2c3d3 | ||
|
|
b2db41c7f4 | ||
|
|
c4922d1655 | ||
|
|
94ca77e697 | ||
|
|
c1d076ef79 | ||
|
|
39c69a4aff | ||
|
|
5a0921ed74 | ||
|
|
68c668615f | ||
|
|
e1d5876ae6 | ||
|
|
741910407f | ||
|
|
39d4befc54 | ||
|
|
d13505ce91 | ||
|
|
8c6d586fb0 | ||
|
|
f66c2967dd | ||
|
|
ef44a2295f | ||
|
|
6186231041 | ||
|
|
25ec5550b5 | ||
|
|
1f93610a95 | ||
|
|
01cde51a71 | ||
|
|
7d054c11de | ||
|
|
98f717d5b4 | ||
|
|
6f315144cc | ||
|
|
9ba8f569db | ||
|
|
51f169571f | ||
|
|
b6a9ffb4c7 | ||
|
|
346612aac1 | ||
|
|
205144d92d | ||
|
|
af2e321f45 | ||
|
|
e22a20701b | ||
|
|
f3edde8f81 | ||
|
|
f405a0c905 | ||
|
|
4907df497f | ||
|
|
e551c42f32 | ||
|
|
0c1a235cc1 | ||
|
|
5384b3a8c0 | ||
|
|
0acb5f5857 | ||
|
|
cee124a4d6 | ||
|
|
084b983b44 | ||
|
|
bf15948275 | ||
|
|
8796875128 | ||
|
|
af9c2b1449 | ||
|
|
f0d6b6f558 | ||
|
|
3778e01d7c | ||
|
|
70ebad0410 | ||
|
|
7cf58bd864 | ||
|
|
5858483fca | ||
|
|
fb94d2a99c | ||
|
|
55b5441f00 | ||
|
|
bf397e496c | ||
|
|
a78ae8ca4a | ||
|
|
c635b92991 | ||
|
|
53d406f640 | ||
|
|
701f4b743b | ||
|
|
7466baf1b2 | ||
|
|
13ecf8ef21 | ||
|
|
c946a3973a | ||
|
|
615f265c00 | ||
|
|
4177ba7840 | ||
|
|
393593b2d2 | ||
|
|
e736c3949a | ||
|
|
e1509e7db3 | ||
|
|
49150b07fd | ||
|
|
1d8f1b4c6a | ||
|
|
833f0c67cf | ||
|
|
4b559b5a94 | ||
|
|
55c8d60cfb | ||
|
|
0e129379e9 | ||
|
|
7e3f704285 | ||
|
|
d8a595ac70 | ||
|
|
c13cb9b723 | ||
|
|
8cc83855b9 | ||
|
|
faebbf5753 | ||
|
|
3e297bad1f | ||
|
|
f56949dd0b | ||
|
|
04081ec2d3 | ||
|
|
442f3d93c6 | ||
|
|
b41d9bff51 | ||
|
|
7e1cd68cb4 | ||
|
|
47675633d2 | ||
|
|
8e59834989 | ||
|
|
a87c3cdb88 | ||
|
|
b2f9903e18 | ||
|
|
a48c8056f2 | ||
|
|
dfd6b85296 | ||
|
|
f3aed1bd53 | ||
|
|
41716d160b | ||
|
|
bd87be2f7e | ||
|
|
9bd4ad6e47 | ||
|
|
9bd0c01bdd | ||
|
|
7dc6b4035a | ||
|
|
3a65b9e0e5 | ||
|
|
569b98c724 | ||
|
|
3de022ba05 | ||
|
|
b697718826 | ||
|
|
a16f7b34ab | ||
|
|
0660ddbfbf | ||
|
|
f1a4bf4dd7 | ||
|
|
06c319d1aa | ||
|
|
2d89749c9b | ||
|
|
696bee13af | ||
|
|
c5b835186c | ||
|
|
ea3c1dacea | ||
|
|
7de29090db | ||
|
|
d982d15fbc | ||
|
|
4455810b5b | ||
|
|
00a8372a74 | ||
|
|
108dd4ff24 | ||
|
|
8a687851f2 | ||
|
|
52394d61bf | ||
|
|
270d97f3db | ||
|
|
c5b7e549ec | ||
|
|
891a93a7a3 | ||
|
|
13c7800c8c | ||
|
|
e89f5de680 | ||
|
|
c4fdd04fb4 | ||
|
|
500501497f | ||
|
|
4106161753 | ||
|
|
8ce5a23c4b | ||
|
|
5c524af472 | ||
|
|
4b1123c4e4 | ||
|
|
c3268d0362 | ||
|
|
f3fa9883aa | ||
|
|
8cf7ec9738 | ||
|
|
7c3a92662f | ||
|
|
e05358db1d | ||
|
|
74d450703c | ||
|
|
aee4f3780c | ||
|
|
3aa0702ff8 | ||
|
|
cc51dbd4e6 | ||
|
|
228a83e0a7 | ||
|
|
ee1ed1e9e5 | ||
|
|
6a394cc30e |
9
.Rbuildignore
Normal file
@@ -0,0 +1,9 @@
|
||||
^\.Rproj\.user$
|
||||
^\.git$
|
||||
^examples$
|
||||
^README\.md$
|
||||
^shiny\.Rproj$
|
||||
^shiny\.sh$
|
||||
^shiny\.cmd$
|
||||
^run\.R$
|
||||
^\.gitignore$
|
||||
11
.gitignore
vendored
@@ -1,4 +1,9 @@
|
||||
vendor/ruby
|
||||
\.bundle/
|
||||
\.DS_Store
|
||||
.DS_Store
|
||||
.Rproj.user
|
||||
.Rhistory
|
||||
.Rprofile
|
||||
*.o
|
||||
*.so
|
||||
/src-i386/
|
||||
/src-x86_64/
|
||||
README.html
|
||||
|
||||
45
DESCRIPTION
Normal file
@@ -0,0 +1,45 @@
|
||||
Package: shiny
|
||||
Type: Package
|
||||
Title: Web Application Framework for R
|
||||
Version: 0.2.4
|
||||
Date: 2012-11-30
|
||||
Author: RStudio, Inc.
|
||||
Maintainer: Winston Chang <winston@rstudio.com>
|
||||
Description: Shiny makes it incredibly easy to build interactive web
|
||||
applications with R. Automatic "reactive" binding between inputs and
|
||||
outputs and extensive pre-built widgets make it possible to build
|
||||
beautiful, responsive, and powerful applications with minimal effort.
|
||||
License: GPL-3
|
||||
Depends:
|
||||
R (>= 2.14.1)
|
||||
Imports:
|
||||
stats,
|
||||
tools,
|
||||
utils,
|
||||
datasets,
|
||||
methods,
|
||||
websockets (>= 1.1.6),
|
||||
caTools,
|
||||
RJSONIO,
|
||||
xtable,
|
||||
digest
|
||||
Suggests:
|
||||
markdown,
|
||||
Cairo
|
||||
URL: https://github.com/rstudio/shiny, http://rstudio.github.com/shiny/tutorial
|
||||
BugReports: https://github.com/rstudio/shiny/issues
|
||||
Collate:
|
||||
'map.R'
|
||||
'utils.R'
|
||||
'tar.R'
|
||||
'timer.R'
|
||||
'tags.R'
|
||||
'cache.R'
|
||||
'react.R'
|
||||
'reactives.R'
|
||||
'fileupload.R'
|
||||
'shiny.R'
|
||||
'shinywrappers.R'
|
||||
'shinyui.R'
|
||||
'slider.R'
|
||||
'bootstrap.R'
|
||||
5
Gemfile
@@ -1,5 +0,0 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'em-websocket'
|
||||
gem 'eventmachine_httpserver'
|
||||
gem 'json'
|
||||
18
Gemfile.lock
@@ -1,18 +0,0 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
addressable (2.2.8)
|
||||
em-websocket (0.3.6)
|
||||
addressable (>= 2.1.1)
|
||||
eventmachine (>= 0.12.9)
|
||||
eventmachine (0.12.10)
|
||||
eventmachine_httpserver (0.2.1)
|
||||
json (1.7.3)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
em-websocket
|
||||
eventmachine_httpserver
|
||||
json
|
||||
87
NAMESPACE
Normal file
@@ -0,0 +1,87 @@
|
||||
export(a)
|
||||
export(addResourcePath)
|
||||
export(animationOptions)
|
||||
export(bootstrapPage)
|
||||
export(br)
|
||||
export(checkboxGroupInput)
|
||||
export(checkboxInput)
|
||||
export(code)
|
||||
export(conditionalPanel)
|
||||
export(div)
|
||||
export(downloadButton)
|
||||
export(downloadHandler)
|
||||
export(downloadLink)
|
||||
export(em)
|
||||
export(fileInput)
|
||||
export(h1)
|
||||
export(h2)
|
||||
export(h3)
|
||||
export(h4)
|
||||
export(h5)
|
||||
export(h6)
|
||||
export(headerPanel)
|
||||
export(helpText)
|
||||
export(HTML)
|
||||
export(htmlOutput)
|
||||
export(img)
|
||||
export(includeHTML)
|
||||
export(includeMarkdown)
|
||||
export(includeText)
|
||||
export(invalidateLater)
|
||||
export(mainPanel)
|
||||
export(numericInput)
|
||||
export(observe)
|
||||
export(p)
|
||||
export(pageWithSidebar)
|
||||
export(plotOutput)
|
||||
export(pre)
|
||||
export(radioButtons)
|
||||
export(reactive)
|
||||
export(reactivePlot)
|
||||
export(reactivePrint)
|
||||
export(reactiveTable)
|
||||
export(reactiveText)
|
||||
export(reactiveTimer)
|
||||
export(reactiveUI)
|
||||
export(repeatable)
|
||||
export(runApp)
|
||||
export(runExample)
|
||||
export(runGist)
|
||||
export(selectInput)
|
||||
export(shinyServer)
|
||||
export(shinyUI)
|
||||
export(sidebarPanel)
|
||||
export(singleton)
|
||||
export(sliderInput)
|
||||
export(span)
|
||||
export(strong)
|
||||
export(submitButton)
|
||||
export(tableOutput)
|
||||
export(tabPanel)
|
||||
export(tabsetPanel)
|
||||
export(tag)
|
||||
export(tagAppendChild)
|
||||
export(tagList)
|
||||
export(tags)
|
||||
export(textInput)
|
||||
export(textOutput)
|
||||
export(uiOutput)
|
||||
export(verbatimTextOutput)
|
||||
export(wellPanel)
|
||||
import(caTools)
|
||||
import(digest)
|
||||
import(RJSONIO)
|
||||
import(websockets)
|
||||
import(xtable)
|
||||
S3method(as.character,shiny.tag)
|
||||
S3method(as.character,shiny.tag.list)
|
||||
S3method(as.list,reactvaluesreader)
|
||||
S3method(format,shiny.tag)
|
||||
S3method(format,shiny.tag.list)
|
||||
S3method(names,reactvaluesreader)
|
||||
S3method(print,shiny.tag)
|
||||
S3method(print,shiny.tag.list)
|
||||
S3method(reactive,default)
|
||||
S3method(reactive,"function")
|
||||
S3method("$",reactvaluesreader)
|
||||
S3method("$<-",shinyoutput)
|
||||
158
NEWS
Normal file
@@ -0,0 +1,158 @@
|
||||
shiny 0.2.3
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
* Ignore request variables for routing purposes
|
||||
|
||||
* `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.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 Twitter 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!
|
||||
844
R/bootstrap.R
Normal file
@@ -0,0 +1,844 @@
|
||||
#' Create a Twitter Bootstrap page
|
||||
#'
|
||||
#' Create a Shiny UI page that loads the CSS and JavaScript for
|
||||
#' \href{http://getbootstrap.com}{Twitter Bootstrap}, and has no content in the
|
||||
#' page body (other than what you provide).
|
||||
#'
|
||||
#' This function is primarily intended for users who are proficient in HTML/CSS,
|
||||
#' and know how to lay out pages in Bootstrap. Most users should use template
|
||||
#' functions like \code{\link{pageWithSidebar}}.
|
||||
#'
|
||||
#' @param ... The contents of the document body.
|
||||
#' @return A UI defintion that can be passed to the \link{shinyUI} function.
|
||||
#'
|
||||
#' @export
|
||||
bootstrapPage <- function(...) {
|
||||
# required head tags for boostrap
|
||||
importBootstrap <- function(min = TRUE, responsive = TRUE) {
|
||||
|
||||
ext <- function(ext) {
|
||||
ifelse(min, paste(".min", ext, sep=""), ext)
|
||||
}
|
||||
cssExt <- ext(".css")
|
||||
jsExt = ext(".js")
|
||||
bs <- "shared/bootstrap/"
|
||||
|
||||
result <- tags$head(
|
||||
tags$link(rel="stylesheet",
|
||||
type="text/css",
|
||||
href="shared/slider/css/jquery.slider.min.css"),
|
||||
|
||||
tags$script(src="shared/slider/js/jquery.slider.min.js"),
|
||||
|
||||
tags$link(rel="stylesheet",
|
||||
type="text/css",
|
||||
href=paste(bs, "css/bootstrap", cssExt, sep="")),
|
||||
|
||||
tags$script(src=paste(bs, "js/bootstrap", jsExt, sep=""))
|
||||
)
|
||||
|
||||
if (responsive) {
|
||||
result <- tagAppendChild(
|
||||
result,
|
||||
tags$meta(name="viewport",
|
||||
content="width=device-width, initial-scale=1.0"))
|
||||
result <- tagAppendChild(
|
||||
result,
|
||||
tags$link(rel="stylesheet",
|
||||
type="text/css",
|
||||
href=paste(bs, "css/bootstrap-responsive", cssExt, sep="")))
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
tagList(
|
||||
# inject bootstrap requirements into head
|
||||
importBootstrap(),
|
||||
list(...)
|
||||
)
|
||||
}
|
||||
|
||||
#' Create a page with a sidebar
|
||||
#'
|
||||
#' Create a Shiny UI that contains a header with the application title, a
|
||||
#' sidebar for input controls, and a main area for output.
|
||||
#'
|
||||
#' @param headerPanel The \link{headerPanel} with the application title
|
||||
#' @param sidebarPanel The \link{sidebarPanel} containing input controls
|
||||
#' @param mainPanel The \link{mainPanel} containing outputs
|
||||
#' @return A UI defintion that can be passed to the \link{shinyUI} function
|
||||
#'
|
||||
#' @examples
|
||||
#' # Define UI
|
||||
#' shinyUI(pageWithSidebar(
|
||||
#'
|
||||
#' # Application title
|
||||
#' headerPanel("Hello Shiny!"),
|
||||
#'
|
||||
#' # Sidebar with a slider input
|
||||
#' sidebarPanel(
|
||||
#' sliderInput("obs",
|
||||
#' "Number of observations:",
|
||||
#' min = 0,
|
||||
#' max = 1000,
|
||||
#' value = 500)
|
||||
#' ),
|
||||
#'
|
||||
#' # Show a plot of the generated distribution
|
||||
#' mainPanel(
|
||||
#' plotOutput("distPlot")
|
||||
#' )
|
||||
#' ))
|
||||
#'
|
||||
#' @export
|
||||
pageWithSidebar <- function(headerPanel, sidebarPanel, mainPanel) {
|
||||
|
||||
bootstrapPage(
|
||||
# basic application container divs
|
||||
div(
|
||||
class="container-fluid",
|
||||
div(class="row-fluid",
|
||||
headerPanel
|
||||
),
|
||||
div(class="row-fluid",
|
||||
sidebarPanel,
|
||||
mainPanel
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
#' Create a header panel
|
||||
#'
|
||||
#' Create a header panel containing an application title.
|
||||
#'
|
||||
#' @param title An application title to display
|
||||
#' @param windowTitle The title that should be displayed by the browser window.
|
||||
#' Useful if \code{title} is not a string.
|
||||
#' @return A headerPanel that can be passed to \link{pageWithSidebar}
|
||||
#'
|
||||
#' @examples
|
||||
#' headerPanel("Hello Shiny!")
|
||||
#' @export
|
||||
headerPanel <- function(title, windowTitle=title) {
|
||||
tagList(
|
||||
tags$head(tags$title(windowTitle)),
|
||||
div(class="span12", style="padding: 10px 0px;",
|
||||
h1(title)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
#' Create a well panel
|
||||
#'
|
||||
#' Creates a panel with a slightly inset border and grey background. Equivalent
|
||||
#' to Twitter Bootstrap's \code{well} CSS class.
|
||||
#'
|
||||
#' @param ... UI elements to include inside the panel.
|
||||
#' @return The newly created panel.
|
||||
#'
|
||||
#' @export
|
||||
wellPanel <- function(...) {
|
||||
div(class="well", ...)
|
||||
}
|
||||
|
||||
#' Create a sidebar panel
|
||||
#'
|
||||
#' Create a sidebar panel containing input controls that can in turn be
|
||||
#' passed to \link{pageWithSidebar}.
|
||||
#'
|
||||
#' @param ... UI elements to include on the sidebar
|
||||
#' @return A sidebar that can be passed to \link{pageWithSidebar}
|
||||
#'
|
||||
#' @examples
|
||||
#' # Sidebar with controls to select a dataset and specify
|
||||
#' # the number of observations to view
|
||||
#' sidebarPanel(
|
||||
#' selectInput("dataset", "Choose a dataset:",
|
||||
#' choices = c("rock", "pressure", "cars")),
|
||||
#'
|
||||
#' numericInput("obs", "Observations:", 10)
|
||||
#' )
|
||||
#' @export
|
||||
sidebarPanel <- function(...) {
|
||||
div(class="span4",
|
||||
tags$form(class="well",
|
||||
...
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
#' Create a main panel
|
||||
#'
|
||||
#' Create a main panel containing output elements that can in turn be
|
||||
#' passed to \link{pageWithSidebar}.
|
||||
#'
|
||||
#' @param ... Ouput elements to include in the main panel
|
||||
#' @return A main panel that can be passed to \link{pageWithSidebar}
|
||||
#'
|
||||
#' @examples
|
||||
#' # Show the caption and plot of the requested variable against mpg
|
||||
#' mainPanel(
|
||||
#' h3(textOutput("caption")),
|
||||
#' plotOutput("mpgPlot")
|
||||
#' )
|
||||
#' @export
|
||||
mainPanel <- function(...) {
|
||||
div(class="span8",
|
||||
...
|
||||
)
|
||||
}
|
||||
|
||||
#' Conditional Panel
|
||||
#'
|
||||
#' Creates a panel that is visible or not, depending on the value of a
|
||||
#' JavaScript expression. The JS expression is evaluated once at startup and
|
||||
#' whenever Shiny detects a relevant change in input/output.
|
||||
#'
|
||||
#' In the JS expression, you can refer to \code{input} and \code{output}
|
||||
#' JavaScript objects that contain the current values of input and output. For
|
||||
#' example, if you have an input with an id of \code{foo}, then you can use
|
||||
#' \code{input.foo} to read its value. (Be sure not to modify the input/output
|
||||
#' objects, as this may cause unpredictable behavior.)
|
||||
#'
|
||||
#' @param condition A JavaScript expression that will be evaluated repeatedly to
|
||||
#' determine whether the panel should be displayed.
|
||||
#' @param ... Elements to include in the panel.
|
||||
#'
|
||||
#' @examples
|
||||
#' sidebarPanel(
|
||||
#' selectInput(
|
||||
#' "plotType", "Plot Type",
|
||||
#' c(Scatter = "scatter",
|
||||
#' Histogram = "hist")),
|
||||
#'
|
||||
#' # Only show this panel if the plot type is a histogram
|
||||
#' conditionalPanel(
|
||||
#' condition = "input.plotType == 'hist'",
|
||||
#' selectInput(
|
||||
#' "breaks", "Breaks",
|
||||
#' c("Sturges",
|
||||
#' "Scott",
|
||||
#' "Freedman-Diaconis",
|
||||
#' "[Custom]" = "custom")),
|
||||
#'
|
||||
#' # Only show this panel if Custom is selected
|
||||
#' conditionalPanel(
|
||||
#' condition = "input.breaks == 'custom'",
|
||||
#' sliderInput("breakCount", "Break Count", min=1, max=1000, value=10)
|
||||
#' )
|
||||
#' )
|
||||
#' )
|
||||
#'
|
||||
#' @export
|
||||
conditionalPanel <- function(condition, ...) {
|
||||
div('data-display-if'=condition, ...)
|
||||
}
|
||||
|
||||
#' Create a text input control
|
||||
#'
|
||||
#' Create an input control for entry of unstructured text values
|
||||
#'
|
||||
#' @param inputId Input variable to assign the control's value to
|
||||
#' @param label Display label for the control
|
||||
#' @param value Initial value
|
||||
#' @return A text input control that can be added to a UI definition.
|
||||
#'
|
||||
#' @examples
|
||||
#' textInput("caption", "Caption:", "Data Summary")
|
||||
#' @export
|
||||
textInput <- function(inputId, label, value = "") {
|
||||
tagList(
|
||||
tags$label(label),
|
||||
tags$input(id = inputId, type="text", value=value)
|
||||
)
|
||||
}
|
||||
|
||||
#' Create a numeric input control
|
||||
#'
|
||||
#' Create an input control for entry of numeric values
|
||||
#'
|
||||
#' @param inputId Input variable to assign the control's value to
|
||||
#' @param label Display label for the control
|
||||
#' @param value Initial value
|
||||
#' @param min Minimum allowed value
|
||||
#' @param max Maximum allowed value
|
||||
#' @param step Interval to use when stepping between min and max
|
||||
#' @return A numeric input control that can be added to a UI definition.
|
||||
#'
|
||||
#' @examples
|
||||
#' numericInput("obs", "Observations:", 10,
|
||||
#' min = 1, max = 100)
|
||||
#' @export
|
||||
numericInput <- function(inputId, label, value, min = NA, max = NA, step = NA) {
|
||||
|
||||
# build input tag
|
||||
inputTag <- tags$input(id = inputId, type = "number", value = value)
|
||||
if (!is.na(min))
|
||||
inputTag$attribs$min = min
|
||||
if (!is.na(max))
|
||||
inputTag$attribs$max = max
|
||||
if (!is.na(step))
|
||||
inputTag$attribs$step = step
|
||||
|
||||
tagList(
|
||||
tags$label(label),
|
||||
inputTag
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
#' File Upload Control
|
||||
#'
|
||||
#' Create a file upload control that can be used to upload one or more files.
|
||||
#' \bold{Experimental feature. Only works in some browsers (primarily tested on
|
||||
#' Chrome and Firefox).}
|
||||
#'
|
||||
#' @param inputId Input variable to assign the control's value to.
|
||||
#' @param label Display label for the control.
|
||||
#' @param multiple Whether the user should be allowed to select and upload
|
||||
#' multiple files at once.
|
||||
#' @param accept A character vector of MIME types; gives the browser a hint of
|
||||
#' what kind of files the server is expecting.
|
||||
#'
|
||||
#' @export
|
||||
fileInput <- function(inputId, label, multiple = FALSE, accept = NULL) {
|
||||
inputTag <- tags$input(id = inputId, type = "file")
|
||||
if (multiple)
|
||||
inputTag$attribs$multiple <- "multiple"
|
||||
if (length(accept) > 0)
|
||||
inputTag$attribs$accept <- paste(accept, collapse=',')
|
||||
|
||||
tagList(
|
||||
tags$label(label),
|
||||
inputTag
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
#' Checkbox Input Control
|
||||
#'
|
||||
#' Create a checkbox that can be used to specify logical values.
|
||||
#'
|
||||
#' @param inputId Input variable to assign the control's value to.
|
||||
#' @param label Display label for the control.
|
||||
#' @param value Initial value (\code{TRUE} or \code{FALSE}).
|
||||
#' @return A checkbox control that can be added to a UI definition.
|
||||
#'
|
||||
#' @seealso \code{\link{checkboxGroupInput}}
|
||||
#'
|
||||
#' @examples
|
||||
#' checkboxInput("outliers", "Show outliers", FALSE)
|
||||
#' @export
|
||||
checkboxInput <- function(inputId, label, value = FALSE) {
|
||||
inputTag <- tags$input(id = inputId, type="checkbox")
|
||||
if (!is.null(value) && value)
|
||||
inputTag$attribs$checked <- "checked"
|
||||
tags$label(class = "checkbox", inputTag, label)
|
||||
}
|
||||
|
||||
|
||||
#' Checkbox Group Input Control
|
||||
#'
|
||||
#' Create a group of checkboxes that can be used to toggle multiple choices
|
||||
#' independently. The server will receive the input as a character vector of the
|
||||
#' selected values.
|
||||
#'
|
||||
#' @param inputId Input variable to assign the control's value to.
|
||||
#' @param label Display label for the control.
|
||||
#' @param choices List of values to show checkboxes for. If elements of the list
|
||||
#' are named then that name rather than the value is displayed to the user.
|
||||
#' @param selected Names of items that should be initially selected, if any.
|
||||
#' @return A list of HTML elements that can be added to a UI definition.
|
||||
#'
|
||||
#' @seealso \code{\link{checkboxInput}}
|
||||
#'
|
||||
#' @examples
|
||||
#' checkboxGroupInput("variable", "Variable:",
|
||||
#' c("Cylinders" = "cyl",
|
||||
#' "Transmission" = "am",
|
||||
#' "Gears" = "gear"))
|
||||
#'
|
||||
#' @export
|
||||
checkboxGroupInput <- function(inputId, label, choices, selected = NULL) {
|
||||
# resolve names
|
||||
choices <- choicesWithNames(choices)
|
||||
|
||||
checkboxes <- list()
|
||||
for (choiceName in names(choices)) {
|
||||
|
||||
checkbox <- tags$input(name = inputId, type="checkbox",
|
||||
value = choices[[choiceName]])
|
||||
|
||||
if (choiceName %in% selected)
|
||||
checkbox$attribs$checked <- 'checked'
|
||||
|
||||
checkboxes[[length(checkboxes)+1]] <- checkbox
|
||||
checkboxes[[length(checkboxes)+1]] <- choiceName
|
||||
checkboxes[[length(checkboxes)+1]] <- tags$br()
|
||||
}
|
||||
|
||||
# return label and select tag
|
||||
tags$div(class='control-group',
|
||||
controlLabel(inputId, label),
|
||||
checkboxes)
|
||||
}
|
||||
|
||||
|
||||
#' Create a help text element
|
||||
#'
|
||||
#' Create help text which can be added to an input form to provide additional
|
||||
#' explanation or context.
|
||||
#'
|
||||
#' @param ... One or more help text strings (or other inline HTML elements)
|
||||
#' @return A help text element that can be added to a UI definition.
|
||||
#'
|
||||
#' @examples
|
||||
#' helpText("Note: while the data view will show only",
|
||||
#' "the specified number of observations, the",
|
||||
#' "summary will be based on the full dataset.")
|
||||
#' @export
|
||||
helpText <- function(...) {
|
||||
span(class="help-block", ...)
|
||||
}
|
||||
|
||||
controlLabel <- function(controlName, label) {
|
||||
tags$label(class = "control-label", `for` = controlName, label)
|
||||
}
|
||||
|
||||
choicesWithNames <- function(choices) {
|
||||
# get choice names
|
||||
choiceNames <- names(choices)
|
||||
if (is.null(choiceNames))
|
||||
choiceNames <- character(length(choices))
|
||||
|
||||
# default missing names to choice values
|
||||
missingNames <- choiceNames == ""
|
||||
choiceNames[missingNames] <- paste(choices)[missingNames]
|
||||
names(choices) <- choiceNames
|
||||
|
||||
# return choices
|
||||
return (choices)
|
||||
}
|
||||
|
||||
#' Create a select list input control
|
||||
#'
|
||||
#' Create a select list that can be used to choose a single or
|
||||
#' multiple items from a list of values.
|
||||
#'
|
||||
#' @param inputId Input variable to assign the control's value to
|
||||
#' @param label Display label for the control
|
||||
#' @param choices List of values to select from. If elements of the list are
|
||||
#' named then that name rather than the value is displayed to the user.
|
||||
#' @param selected Name of initially selected item (or multiple names if
|
||||
#' \code{multiple = TRUE}). If not specified then defaults to the first item
|
||||
#' for single-select lists and no items for multiple select lists.
|
||||
#' @param multiple Is selection of multiple items allowed?
|
||||
#' @return A select list control that can be added to a UI definition.
|
||||
#'
|
||||
#' @examples
|
||||
#' selectInput("variable", "Variable:",
|
||||
#' c("Cylinders" = "cyl",
|
||||
#' "Transmission" = "am",
|
||||
#' "Gears" = "gear"))
|
||||
#' @export
|
||||
selectInput <- function(inputId,
|
||||
label,
|
||||
choices,
|
||||
selected = NULL,
|
||||
multiple = FALSE) {
|
||||
# resolve names
|
||||
choices <- choicesWithNames(choices)
|
||||
|
||||
# default value if it's not specified
|
||||
if (is.null(selected) && !multiple)
|
||||
selected <- names(choices)[[1]]
|
||||
|
||||
# create select tag and add options
|
||||
selectTag <- tags$select(id = inputId)
|
||||
if (multiple)
|
||||
selectTag$attribs$multiple <- "multiple"
|
||||
for (choiceName in names(choices)) {
|
||||
optionTag <- tags$option(value = choices[[choiceName]], choiceName)
|
||||
if (choiceName %in% selected)
|
||||
optionTag$attribs$selected = "selected"
|
||||
selectTag <- tagAppendChild(selectTag, optionTag)
|
||||
}
|
||||
|
||||
# return label and select tag
|
||||
tagList(controlLabel(inputId, label), selectTag)
|
||||
}
|
||||
|
||||
#' Create radio buttons
|
||||
#'
|
||||
#' Create a set of radio buttons used to select an item from a list.
|
||||
#'
|
||||
#' @param inputId Input variable to assign the control's value to
|
||||
#' @param label Display label for the control
|
||||
#' @param choices List of values to select from (if elements of the list are
|
||||
#' named then that name rather than the value is displayed to the user)
|
||||
#' @param selected Name of initially selected item (if not specified then
|
||||
#' defaults to the first item)
|
||||
#' @return A set of radio buttons that can be added to a UI definition.
|
||||
#'
|
||||
#' @examples
|
||||
#' radioButtons("dist", "Distribution type:",
|
||||
#' c("Normal" = "norm",
|
||||
#' "Uniform" = "unif",
|
||||
#' "Log-normal" = "lnorm",
|
||||
#' "Exponential" = "exp"))
|
||||
#' @export
|
||||
radioButtons <- function(inputId, label, choices, selected = NULL) {
|
||||
# resolve names
|
||||
choices <- choicesWithNames(choices)
|
||||
|
||||
# default value if it's not specified
|
||||
if (is.null(selected))
|
||||
selected <- names(choices)[[1]]
|
||||
|
||||
# build list of radio button tags
|
||||
inputTags <- list()
|
||||
for (i in 1:length(choices)) {
|
||||
id <- paste(inputId, i, sep="")
|
||||
name <- names(choices)[[i]]
|
||||
value <- choices[[i]]
|
||||
inputTag <- tags$input(type = "radio",
|
||||
name = inputId,
|
||||
id = id,
|
||||
value = value)
|
||||
if (identical(name, selected))
|
||||
inputTag$attribs$checked = "checked"
|
||||
|
||||
labelTag <- tags$label(class = "radio")
|
||||
labelTag <- tagAppendChild(labelTag, inputTag)
|
||||
labelTag <- tagAppendChild(labelTag, name)
|
||||
inputTags[[length(inputTags) + 1]] <- labelTag
|
||||
}
|
||||
|
||||
tagList(tags$label(class = "control-label", label),
|
||||
inputTags)
|
||||
}
|
||||
|
||||
#' Create a submit button
|
||||
#'
|
||||
#' Create a submit button for an input form. Forms that include a submit
|
||||
#' button do not automatically update their outputs when inputs change,
|
||||
#' rather they wait until the user explicitly clicks the submit button.
|
||||
#'
|
||||
#' @param text Button caption
|
||||
#' @return A submit button that can be added to a UI definition.
|
||||
#'
|
||||
#' @examples
|
||||
#' submitButton("Update View")
|
||||
#' @export
|
||||
submitButton <- function(text = "Apply Changes") {
|
||||
div(
|
||||
tags$button(type="submit", class="btn btn-primary", text)
|
||||
)
|
||||
}
|
||||
|
||||
#' Slider Input Widget
|
||||
#'
|
||||
#' Constructs a slider widget to select a numeric value from a range.
|
||||
#'
|
||||
#' @param inputId Specifies the \code{input} slot that will be used to access
|
||||
#' the value.
|
||||
#' @param label A descriptive label to be displayed with the widget.
|
||||
#' @param min The minimum value (inclusive) that can be selected.
|
||||
#' @param max The maximum value (inclusive) that can be selected.
|
||||
#' @param value The initial value of the slider. A warning will be issued if the
|
||||
#' value doesn't fit between \code{min} and \code{max}.
|
||||
#' @param step Specifies the interval between each selectable value on the
|
||||
#' slider (\code{NULL} means no restriction).
|
||||
#' @param round \code{TRUE} to round all values to the nearest integer;
|
||||
#' \code{FALSE} if no rounding is desired; or an integer to round to that
|
||||
#' number of digits (for example, 1 will round to the nearest 10, and -2 will
|
||||
#' round to the nearest .01). Any rounding will be applied after snapping to
|
||||
#' the nearest step.
|
||||
#' @param format Customize format values in slider labels. See
|
||||
#' \url{http://archive.plugins.jquery.com/project/numberformatter} for syntax
|
||||
#' details.
|
||||
#' @param locale The locale to be used when applying \code{format}. See details.
|
||||
#' @param ticks \code{FALSE} to hide tick marks, \code{TRUE} to show them
|
||||
#' according to some simple heuristics.
|
||||
#' @param animate \code{TRUE} to show simple animation controls with default
|
||||
#' settings; \code{FALSE} not to; or a custom settings list, such as those
|
||||
#' created using \code{\link{animationOptions}}.
|
||||
#'
|
||||
#' @details
|
||||
#'
|
||||
#' Valid values for \code{locale} are: \tabular{ll}{ Arab Emirates \tab "ae" \cr
|
||||
#' Australia \tab "au" \cr Austria \tab "at" \cr Brazil \tab "br" \cr Canada
|
||||
#' \tab "ca" \cr China \tab "cn" \cr Czech \tab "cz" \cr Denmark \tab "dk" \cr
|
||||
#' Egypt \tab "eg" \cr Finland \tab "fi" \cr France \tab "fr" \cr Germany \tab
|
||||
#' "de" \cr Greece \tab "gr" \cr Great Britain \tab "gb" \cr Hong Kong \tab "hk"
|
||||
#' \cr India \tab "in" \cr Israel \tab "il" \cr Japan \tab "jp" \cr Russia \tab
|
||||
#' "ru" \cr South Korea \tab "kr" \cr Spain \tab "es" \cr Sweden \tab "se" \cr
|
||||
#' Switzerland \tab "ch" \cr Taiwan \tab "tw" \cr Thailand \tab "th" \cr United
|
||||
#' States \tab "us" \cr Vietnam \tab "vn" \cr }
|
||||
#'
|
||||
#' @export
|
||||
sliderInput <- function(inputId, label, min, max, value, step = NULL,
|
||||
round=FALSE, format='#,##0.#####', locale='us',
|
||||
ticks=TRUE, animate=FALSE) {
|
||||
|
||||
# validate label
|
||||
labelText <- as.character(label)
|
||||
if (!is.character(labelText))
|
||||
stop("label not specified")
|
||||
|
||||
if (identical(animate, TRUE))
|
||||
animate <- animationOptions()
|
||||
|
||||
if (!is.null(animate) && !identical(animate, FALSE)) {
|
||||
if (is.null(animate$playButton))
|
||||
animate$playButton <- tags$i(class='icon-play')
|
||||
if (is.null(animate$pauseButton))
|
||||
animate$pauseButton <- tags$i(class='icon-pause')
|
||||
}
|
||||
|
||||
# build slider
|
||||
tagList(
|
||||
controlLabel(inputId, labelText),
|
||||
slider(inputId, min=min, max=max, value=value, step=step, round=round,
|
||||
locale=locale, format=format, ticks=ticks,
|
||||
animate=animate)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
#' Create a tab panel
|
||||
#'
|
||||
#' Create a tab panel that can be included within a \code{\link{tabsetPanel}}.
|
||||
#'
|
||||
#' @param title Display title for tab
|
||||
#' @param ... UI elements to include within the tab
|
||||
#' @param value The value that should be sent when \code{tabsetPanel} reports
|
||||
#' that this tab is selected. If omitted and \code{tabsetPanel} has an
|
||||
#' \code{id}, then the title will be used.
|
||||
#' @return A tab that can be passed to \code{\link{tabsetPanel}}
|
||||
#'
|
||||
#' @examples
|
||||
#' # Show a tabset that includes a plot, summary, and
|
||||
#' # table view of the generated distribution
|
||||
#' mainPanel(
|
||||
#' tabsetPanel(
|
||||
#' tabPanel("Plot", plotOutput("plot")),
|
||||
#' tabPanel("Summary", verbatimTextOutput("summary")),
|
||||
#' tabPanel("Table", tableOutput("table"))
|
||||
#' )
|
||||
#' )
|
||||
#' @export
|
||||
tabPanel <- function(title, ..., value = NULL) {
|
||||
div(class="tab-pane", title=title, `data-value`=value, ...)
|
||||
}
|
||||
|
||||
#' Create a tabset panel
|
||||
#'
|
||||
#' Create a tabset that contains \code{\link{tabPanel}} elements. Tabsets are
|
||||
#' useful for dividing output into multiple independently viewable sections.
|
||||
#'
|
||||
#' @param ... \code{\link{tabPanel}} elements to include in the tabset
|
||||
#' @param id If provided, you can use \code{input$}\emph{\code{id}} in your server
|
||||
#' logic to determine which of the current tabs is active. The value will
|
||||
#' correspond to the \code{value} argument that is passed to
|
||||
#' \code{\link{tabPanel}}.
|
||||
#' @return A tabset that can be passed to \code{\link{mainPanel}}
|
||||
#'
|
||||
#' @examples
|
||||
#' # Show a tabset that includes a plot, summary, and
|
||||
#' # table view of the generated distribution
|
||||
#' mainPanel(
|
||||
#' tabsetPanel(
|
||||
#' tabPanel("Plot", plotOutput("plot")),
|
||||
#' tabPanel("Summary", verbatimTextOutput("summary")),
|
||||
#' tabPanel("Table", tableOutput("table"))
|
||||
#' )
|
||||
#' )
|
||||
#' @export
|
||||
tabsetPanel <- function(..., id = NULL) {
|
||||
|
||||
# build tab-nav and tab-content divs
|
||||
tabs <- list(...)
|
||||
tabNavList <- tags$ul(class = "nav nav-tabs", id = id)
|
||||
tabContent <- tags$div(class = "tab-content")
|
||||
firstTab <- TRUE
|
||||
tabsetId <- as.integer(stats::runif(1, 1, 10000))
|
||||
tabId <- 1
|
||||
for (divTag in tabs) {
|
||||
# compute id and assign it to the div
|
||||
thisId <- paste("tab", tabsetId, tabId, sep="-")
|
||||
divTag$attribs$id <- thisId
|
||||
tabId <- tabId + 1
|
||||
|
||||
tabValue <- divTag$attribs$`data-value`
|
||||
if (!is.null(tabValue) && is.null(id)) {
|
||||
stop("tabsetPanel doesn't have an id assigned, but one of its tabPanels ",
|
||||
"has a value. The value won't be sent without an id.")
|
||||
}
|
||||
|
||||
# create the li tag
|
||||
liTag <- tags$li(tags$a(href=paste("#", thisId, sep=""),
|
||||
`data-toggle` = "tab",
|
||||
`data-value` = tabValue,
|
||||
divTag$attribs$title))
|
||||
|
||||
# set the first tab as active
|
||||
if (firstTab) {
|
||||
liTag$attribs$class <- "active"
|
||||
divTag$attribs$class <- "tab-pane active"
|
||||
firstTab = FALSE
|
||||
}
|
||||
|
||||
# append the elements to our lists
|
||||
tabNavList <- tagAppendChild(tabNavList, liTag)
|
||||
tabContent <- tagAppendChild(tabContent, divTag)
|
||||
}
|
||||
|
||||
tabDiv <- tags$div(class = "tabbable", tabNavList, tabContent)
|
||||
}
|
||||
|
||||
|
||||
#' Create a text output element
|
||||
#'
|
||||
#' Render a reactive output variable as text within an application page. The
|
||||
#' text will be included within an HTML \code{div} tag.
|
||||
#' @param outputId output variable to read the value from
|
||||
#' @return A text output element that can be included in a panel
|
||||
#' @details Text is HTML-escaped prior to rendering. This element is often used
|
||||
#' to dispaly \link{reactiveText} output variables.
|
||||
#' @examples
|
||||
#' h3(textOutput("caption"))
|
||||
#' @export
|
||||
textOutput <- function(outputId) {
|
||||
div(id = outputId, class = "shiny-text-output")
|
||||
}
|
||||
|
||||
#' Create a verbatim text output element
|
||||
#'
|
||||
#' Render a reactive output variable as verbatim text within an
|
||||
#' application page. The text will be included within an HTML \code{pre} tag.
|
||||
#' @param outputId output variable to read the value from
|
||||
#' @return A verbatim text output element that can be included in a panel
|
||||
#' @details Text is HTML-escaped prior to rendering. This element is often used
|
||||
#' with the \link{reactivePrint} function to preserve fixed-width formatting
|
||||
#' of printed objects.
|
||||
#' @examples
|
||||
#' mainPanel(
|
||||
#' h4("Summary"),
|
||||
#' verbatimTextOutput("summary"),
|
||||
#'
|
||||
#' h4("Observations"),
|
||||
#' tableOutput("view")
|
||||
#' )
|
||||
#' @export
|
||||
verbatimTextOutput <- function(outputId) {
|
||||
pre(id = outputId, class = "shiny-text-output")
|
||||
}
|
||||
|
||||
#' Create a plot output element
|
||||
#'
|
||||
#' Render a \link{reactivePlot} within an application page.
|
||||
#' @param outputId output variable to read the plot from
|
||||
#' @param width Plot width
|
||||
#' @param height Plot height
|
||||
#' @return A plot output element that can be included in a panel
|
||||
#' @examples
|
||||
#' # Show a plot of the generated distribution
|
||||
#' mainPanel(
|
||||
#' plotOutput("distPlot")
|
||||
#' )
|
||||
#' @export
|
||||
plotOutput <- function(outputId, width = "100%", height="400px") {
|
||||
style <- paste("width:", width, ";", "height:", height)
|
||||
div(id = outputId, class="shiny-plot-output", style = style)
|
||||
}
|
||||
|
||||
#' Create a table output element
|
||||
#'
|
||||
#' Render a \link{reactiveTable} within an application page.
|
||||
#' @param outputId output variable to read the table from
|
||||
#' @return A table output element that can be included in a panel
|
||||
#' @examples
|
||||
#' mainPanel(
|
||||
#' tableOutput("view")
|
||||
#' )
|
||||
#' @export
|
||||
tableOutput <- function(outputId) {
|
||||
div(id = outputId, class="shiny-html-output")
|
||||
}
|
||||
|
||||
#' Create an HTML output element
|
||||
#'
|
||||
#' Render a reactive output variable as HTML within an application page. The
|
||||
#' text will be included within an HTML \code{div} tag, and is presumed to
|
||||
#' contain HTML content which should not be escaped.
|
||||
#'
|
||||
#' \code{uiOutput} is intended to be used with \code{reactiveUI} on the
|
||||
#' server side. It is currently just an alias for \code{htmlOutput}.
|
||||
#'
|
||||
#' @param outputId output variable to read the value from
|
||||
#' @return An HTML output element that can be included in a panel
|
||||
#' @examples
|
||||
#' htmlOutput("summary")
|
||||
#' @export
|
||||
htmlOutput <- function(outputId) {
|
||||
div(id = outputId, class="shiny-html-output")
|
||||
}
|
||||
|
||||
#' @rdname htmlOutput
|
||||
#' @export
|
||||
uiOutput <- function(outputId) {
|
||||
htmlOutput(outputId)
|
||||
}
|
||||
|
||||
#' Create a download button or link
|
||||
#'
|
||||
#' Use these functions to create a download button or link; when clicked, it
|
||||
#' will initiate a browser download. The filename and contents are specified by
|
||||
#' the corresponding \code{\link{downloadHandler}} defined in the server
|
||||
#' function.
|
||||
#'
|
||||
#' @param outputId The name of the output slot that the \code{downloadHandler}
|
||||
#' is assigned to.
|
||||
#' @param label The label that should appear on the button.
|
||||
#' @param class Additional CSS classes to apply to the tag, if any.
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' # In server.R:
|
||||
#' output$downloadData <- downloadHandler(
|
||||
#' filename = function() {
|
||||
#' paste('data-', Sys.Date(), '.csv', sep='')
|
||||
#' },
|
||||
#' content = function(con) {
|
||||
#' write.csv(data, con)
|
||||
#' }
|
||||
#' )
|
||||
#'
|
||||
#' # In ui.R:
|
||||
#' downloadLink('downloadData', 'Download')
|
||||
#' }
|
||||
#'
|
||||
#' @aliases downloadLink
|
||||
#' @seealso downloadHandler
|
||||
#' @export
|
||||
downloadButton <- function(outputId, label="Download", class=NULL) {
|
||||
tags$a(id=outputId,
|
||||
class=paste(c('btn shiny-download-link', class), collapse=" "),
|
||||
href='',
|
||||
target='_blank',
|
||||
label)
|
||||
}
|
||||
|
||||
#' @rdname downloadButton
|
||||
#' @export
|
||||
downloadLink <- function(outputId, label="Download", class=NULL) {
|
||||
tags$a(id=outputId,
|
||||
class=paste(c('shiny-download-link', class), collapse=" "),
|
||||
href='',
|
||||
target='_blank',
|
||||
label)
|
||||
}
|
||||
80
R/cache.R
Normal file
@@ -0,0 +1,80 @@
|
||||
# A context object for tracking a cache that needs to be dirtied when a set of
|
||||
# files changes on disk. Each time the cache is dirtied, the set of files is
|
||||
# cleared. Therefore, the set of files needs to be re-built each time the cached
|
||||
# code executes. This approach allows for dynamic dependency graphs.
|
||||
CacheContext <- setRefClass(
|
||||
'CacheContext',
|
||||
fields = list(
|
||||
.dirty = 'logical',
|
||||
.tests = 'list'
|
||||
),
|
||||
methods = list(
|
||||
initialize = function() {
|
||||
.dirty <<- TRUE
|
||||
# List of functions that return TRUE if dirty
|
||||
.tests <<- list()
|
||||
},
|
||||
addDependencyFile = function(file) {
|
||||
if (.dirty)
|
||||
return()
|
||||
|
||||
file <- normalizePath(file)
|
||||
|
||||
mtime <- file.info(file)$mtime
|
||||
.tests <<- c(.tests, function() {
|
||||
newMtime <- try(file.info(file)$mtime, silent=TRUE)
|
||||
if (is(newMtime, 'try-error'))
|
||||
return(TRUE)
|
||||
return(!identical(mtime, newMtime))
|
||||
})
|
||||
invisible()
|
||||
},
|
||||
forceDirty = function() {
|
||||
.dirty <<- TRUE
|
||||
.tests <<- list()
|
||||
invisible()
|
||||
},
|
||||
isDirty = function() {
|
||||
if (.dirty)
|
||||
return(TRUE)
|
||||
|
||||
for (test in .tests) {
|
||||
if (test()) {
|
||||
forceDirty()
|
||||
return(TRUE)
|
||||
}
|
||||
}
|
||||
|
||||
return(FALSE)
|
||||
},
|
||||
reset = function() {
|
||||
.dirty <<- FALSE
|
||||
.tests <<- list()
|
||||
},
|
||||
with = function(func) {
|
||||
oldCC <- .currentCacheContext$cc
|
||||
.currentCacheContext$cc <- .self
|
||||
on.exit(.currentCacheContext$cc <- oldCC)
|
||||
|
||||
return(func())
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
.currentCacheContext <- new.env()
|
||||
|
||||
# Indicates to Shiny that the given file path is part of the dependency graph
|
||||
# for whatever is currently executing (so far, only ui.R). By default, ui.R only
|
||||
# gets re-executed when it is detected to have changed; this function allows the
|
||||
# caller to indicate that it should also re-execute if the given file changes.
|
||||
#
|
||||
# If NULL or NA is given as the argument, then ui.R will re-execute next time.
|
||||
dependsOnFile <- function(filepath) {
|
||||
if (is.null(.currentCacheContext$cc))
|
||||
stop("addFileDependency was called at an unexpected time (no cache context found)")
|
||||
|
||||
if (is.null(filepath) || is.na(filepath))
|
||||
.currentCacheContext$cc$forceDirty()
|
||||
else
|
||||
.currentCacheContext$cc$addDependencyFile(filepath)
|
||||
}
|
||||
95
R/fileupload.R
Normal file
@@ -0,0 +1,95 @@
|
||||
# For HTML5-capable browsers, file uploads happen through a series of requests.
|
||||
#
|
||||
# 1. Client tells server that one or more files are about to be uploaded; the
|
||||
# server responds with a "job ID" that the client should use for the rest of
|
||||
# the upload.
|
||||
#
|
||||
# 2. For each file (sequentially):
|
||||
# a. Client tells server the name, size, and type of the file.
|
||||
# b. Client sends server a small-ish blob of data.
|
||||
# c. Repeat 2b until the entire file has been uploaded.
|
||||
# d. Client tells server that the current file is done.
|
||||
#
|
||||
# 3. Repeat 2 until all files have been uploaded.
|
||||
#
|
||||
# 4. Client tells server that all files have been uploaded, along with the
|
||||
# input ID that this data should be associated with.
|
||||
#
|
||||
# Unfortunately this approach will not work for browsers that don't support
|
||||
# HTML5 File API, but the fallback approach we would like to use (multipart
|
||||
# form upload, i.e. traditional HTTP POST-based file upload) doesn't work with
|
||||
# the websockets package's HTTP server at the moment.
|
||||
|
||||
FileUploadOperation <- setRefClass(
|
||||
'FileUploadOperation',
|
||||
fields = list(
|
||||
.parent = 'ANY',
|
||||
.id = 'character',
|
||||
.files = 'data.frame',
|
||||
.dir = 'character',
|
||||
.currentFileInfo = 'list',
|
||||
.currentFileData = 'ANY'
|
||||
),
|
||||
methods = list(
|
||||
initialize = function(parent, id, dir) {
|
||||
.parent <<- parent
|
||||
.id <<- id
|
||||
.dir <<- dir
|
||||
},
|
||||
fileBegin = function(file) {
|
||||
.currentFileInfo <<- file
|
||||
|
||||
filename <- file.path(.dir, as.character(length(.files)))
|
||||
row <- data.frame(name=file$name, size=file$size, type=file$type,
|
||||
datapath=filename, stringsAsFactors=FALSE)
|
||||
|
||||
if (length(.files) == 0)
|
||||
.files <<- row
|
||||
else
|
||||
.files <<- rbind(.files, row)
|
||||
|
||||
.currentFileData <<- file(filename, open='wb')
|
||||
},
|
||||
fileChunk = function(rawdata) {
|
||||
writeBin(rawdata, .currentFileData)
|
||||
},
|
||||
fileEnd = function() {
|
||||
close(.currentFileData)
|
||||
},
|
||||
finish = function() {
|
||||
.parent$onJobFinished(.id)
|
||||
return(.files)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
FileUploadContext <- setRefClass(
|
||||
'FileUploadContext',
|
||||
fields = list(
|
||||
.basedir = 'character',
|
||||
.operations = 'Map'
|
||||
),
|
||||
methods = list(
|
||||
initialize = function(dir=tempdir()) {
|
||||
.basedir <<- dir
|
||||
},
|
||||
createUploadOperation = function() {
|
||||
while (TRUE) {
|
||||
id <- paste(as.raw(runif(12, min=0, max=0xFF)), collapse='')
|
||||
dir <- file.path(.basedir, id)
|
||||
if (!dir.create(dir))
|
||||
next
|
||||
|
||||
op <- FileUploadOperation$new(.self, id, dir)
|
||||
.operations$set(id, op)
|
||||
return(id)
|
||||
}
|
||||
},
|
||||
getUploadOperation = function(jobId) {
|
||||
.operations$get(jobId)
|
||||
},
|
||||
onJobFinished = function(jobId) {
|
||||
.operations$remove(jobId)
|
||||
}
|
||||
)
|
||||
)
|
||||
80
R/map.R
Normal file
@@ -0,0 +1,80 @@
|
||||
# TESTS
|
||||
# Simple set/get
|
||||
# Simple remove
|
||||
# Simple containsKey
|
||||
# Simple keys
|
||||
# Simple values
|
||||
# Simple clear
|
||||
# Get of unknown key returns NULL
|
||||
# Remove of unknown key does nothing
|
||||
# Setting a key twice always results in last-one-wins
|
||||
# /TESTS
|
||||
Map <- setRefClass(
|
||||
'Map',
|
||||
fields = list(
|
||||
.env = 'environment'
|
||||
),
|
||||
methods = list(
|
||||
initialize = function() {
|
||||
.env <<- new.env(parent=emptyenv())
|
||||
},
|
||||
get = function(key) {
|
||||
if (.self$containsKey(key))
|
||||
return(base::get(key, pos=.env, inherits=FALSE))
|
||||
else
|
||||
return(NULL)
|
||||
},
|
||||
set = function(key, value) {
|
||||
assign(key, value, pos=.env, inherits=FALSE)
|
||||
return(value)
|
||||
},
|
||||
mset = function(...) {
|
||||
args <- list(...)
|
||||
for (key in names(args))
|
||||
set(key, args[[key]])
|
||||
return()
|
||||
},
|
||||
remove = function(key) {
|
||||
if (.self$containsKey(key)) {
|
||||
result <- .self$get(key)
|
||||
rm(list = key, pos=.env, inherits=FALSE)
|
||||
return(result)
|
||||
}
|
||||
return(NULL)
|
||||
},
|
||||
containsKey = function(key) {
|
||||
exists(key, where=.env, inherits=FALSE)
|
||||
},
|
||||
keys = function() {
|
||||
ls(envir=.env, all.names=TRUE)
|
||||
},
|
||||
values = function() {
|
||||
mget(.self$keys(), envir=.env, inherits=FALSE)
|
||||
},
|
||||
clear = function() {
|
||||
.env <<- new.env(parent=emptyenv())
|
||||
invisible(NULL)
|
||||
},
|
||||
size = function() {
|
||||
length(.env)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
`[.Map` <- function(map, name) {
|
||||
map$get(name)
|
||||
}
|
||||
|
||||
`[<-.Map` <- function(map, name, value) {
|
||||
map$set(name, value)
|
||||
return(map)
|
||||
}
|
||||
|
||||
as.list.Map <- function(map) {
|
||||
sapply(map$keys(),
|
||||
map$get,
|
||||
simplify=FALSE)
|
||||
}
|
||||
length.Map <- function(map) {
|
||||
map$size()
|
||||
}
|
||||
322
R/react.R
@@ -1,106 +1,64 @@
|
||||
# TESTS
|
||||
# Simple set/get
|
||||
# Simple remove
|
||||
# Simple contains.key
|
||||
# Simple keys
|
||||
# Simple values
|
||||
# Simple clear
|
||||
# Get of unknown key returns NULL
|
||||
# Remove of unknown key does nothing
|
||||
# Setting a key twice always results in last-one-wins
|
||||
# /TESTS
|
||||
Map <- setRefClass(
|
||||
'Map',
|
||||
fields = list(
|
||||
.env = 'environment'
|
||||
),
|
||||
methods = list(
|
||||
initialize = function() {
|
||||
.env <<- new.env(parent=emptyenv())
|
||||
},
|
||||
get = function(key) {
|
||||
if (.self$contains.key(key))
|
||||
return(base::get(key, pos=.env, inherits=F))
|
||||
else
|
||||
return(NULL)
|
||||
},
|
||||
set = function(key, value) {
|
||||
assign(key, value, pos=.env, inherits=F)
|
||||
return(value)
|
||||
},
|
||||
remove = function(key) {
|
||||
if (.self$contains.key(key)) {
|
||||
result <- .self$get(key)
|
||||
rm(list = key, pos=.env, inherits=F)
|
||||
return(result)
|
||||
}
|
||||
return(NULL)
|
||||
},
|
||||
contains.key = function(key) {
|
||||
exists(key, where=.env, inherits=F)
|
||||
},
|
||||
keys = function() {
|
||||
ls(envir=.env, all.names=T)
|
||||
},
|
||||
values = function() {
|
||||
mget(.self$keys(), envir=.env, inherits=F)
|
||||
},
|
||||
clear = function() {
|
||||
.env <<- new.env(parent=emptyenv())
|
||||
invisible(NULL)
|
||||
},
|
||||
size = function() {
|
||||
length(.env)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
as.list.Map <- function(map) {
|
||||
sapply(map$keys(),
|
||||
map$get,
|
||||
simplify=F)
|
||||
}
|
||||
length.Map <- function(map) {
|
||||
map$size()
|
||||
}
|
||||
|
||||
Context <- setRefClass(
|
||||
'Context',
|
||||
fields = list(
|
||||
id = 'character',
|
||||
.invalidated = 'logical',
|
||||
.callbacks = 'list'
|
||||
.callbacks = 'list',
|
||||
.hintCallbacks = 'list'
|
||||
),
|
||||
methods = list(
|
||||
initialize = function() {
|
||||
id <<- .get.reactive.environment()$next.id()
|
||||
.invalidated <<- F
|
||||
id <<- .getReactiveEnvironment()$nextId()
|
||||
.invalidated <<- FALSE
|
||||
.callbacks <<- list()
|
||||
.hintCallbacks <<- list()
|
||||
},
|
||||
run = function(func) {
|
||||
env <- .get.reactive.environment()
|
||||
old.ctx <- env$current.context(warn=F)
|
||||
env$set.current.context(.self)
|
||||
on.exit(env$set.current.context(old.ctx))
|
||||
func()
|
||||
"Run the provided function under this context."
|
||||
env <- .getReactiveEnvironment()
|
||||
env$runWith(.self, func)
|
||||
},
|
||||
invalidateHint = function() {
|
||||
"Let this context know it may or may not be invalidated very soon; that
|
||||
is, something in its dependency graph has been invalidated but there's no
|
||||
guarantee that the cascade of invalidations will reach all the way here.
|
||||
This is used to show progress in the UI."
|
||||
lapply(.hintCallbacks, function(func) {
|
||||
func()
|
||||
})
|
||||
},
|
||||
invalidate = function() {
|
||||
"Schedule this context for invalidation. It will not actually be
|
||||
invalidated until the next call to \\code{\\link{flushReact}}."
|
||||
if (.invalidated)
|
||||
return()
|
||||
.invalidated <<- T
|
||||
.get.reactive.environment()$add.pending.invalidate(.self)
|
||||
.invalidated <<- TRUE
|
||||
.getReactiveEnvironment()$addPendingInvalidate(.self)
|
||||
NULL
|
||||
},
|
||||
on.invalidate = function(func) {
|
||||
onInvalidate = function(func) {
|
||||
"Register a function to be called when this context is invalidated.
|
||||
If this context is already invalidated, the function is called
|
||||
immediately."
|
||||
if (.invalidated)
|
||||
func()
|
||||
else
|
||||
.callbacks <<- c(.callbacks, func)
|
||||
NULL
|
||||
},
|
||||
execute.callbacks = function() {
|
||||
onInvalidateHint = function(func) {
|
||||
.hintCallbacks <<- c(.hintCallbacks, func)
|
||||
},
|
||||
executeCallbacks = function() {
|
||||
"For internal use only."
|
||||
lapply(.callbacks, function(func) {
|
||||
func()
|
||||
withCallingHandlers({
|
||||
func()
|
||||
}, warning = function(e) {
|
||||
# TODO: Callbacks in app
|
||||
}, error = function(e) {
|
||||
# TODO: Callbacks in app
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -108,34 +66,39 @@ Context <- setRefClass(
|
||||
|
||||
ReactiveEnvironment <- setRefClass(
|
||||
'ReactiveEnvironment',
|
||||
fields = c('.current.context', '.next.id', '.pending.invalidate'),
|
||||
fields = c('.currentContext', '.nextId', '.pendingInvalidate'),
|
||||
methods = list(
|
||||
initialize = function() {
|
||||
.current.context <<- NULL
|
||||
.next.id <<- 0L
|
||||
.pending.invalidate <<- list()
|
||||
.currentContext <<- NULL
|
||||
.nextId <<- 0L
|
||||
.pendingInvalidate <<- list()
|
||||
},
|
||||
next.id = function() {
|
||||
.next.id <<- .next.id + 1L
|
||||
return(as.character(.next.id))
|
||||
nextId = function() {
|
||||
.nextId <<- .nextId + 1L
|
||||
return(as.character(.nextId))
|
||||
},
|
||||
current.context = function(warn=T) {
|
||||
if (warn && is.null(.current.context))
|
||||
warning('No reactive context is active')
|
||||
return(.current.context)
|
||||
currentContext = function() {
|
||||
if (is.null(.currentContext))
|
||||
stop('Operation not allowed without an active reactive context. ',
|
||||
'(You tried to do something that can only be done from inside a ',
|
||||
'reactive function.)')
|
||||
return(.currentContext)
|
||||
},
|
||||
set.current.context = function(ctx) {
|
||||
.current.context <<- ctx
|
||||
runWith = function(ctx, func) {
|
||||
old.ctx <- .currentContext
|
||||
.currentContext <<- ctx
|
||||
on.exit(.currentContext <<- old.ctx)
|
||||
func()
|
||||
},
|
||||
add.pending.invalidate = function(ctx) {
|
||||
.pending.invalidate <<- c(.pending.invalidate, ctx)
|
||||
addPendingInvalidate = function(ctx) {
|
||||
.pendingInvalidate <<- c(.pendingInvalidate, ctx)
|
||||
},
|
||||
flush = function() {
|
||||
while (length(.pending.invalidate) > 0) {
|
||||
contexts <- .pending.invalidate
|
||||
.pending.invalidate <<- list()
|
||||
while (length(.pendingInvalidate) > 0) {
|
||||
contexts <- .pendingInvalidate
|
||||
.pendingInvalidate <<- list()
|
||||
lapply(contexts, function(ctx) {
|
||||
ctx$execute.callbacks()
|
||||
ctx$executeCallbacks()
|
||||
NULL
|
||||
})
|
||||
}
|
||||
@@ -143,161 +106,18 @@ ReactiveEnvironment <- setRefClass(
|
||||
)
|
||||
)
|
||||
|
||||
Values <- setRefClass(
|
||||
'Values',
|
||||
fields = list(
|
||||
.values = 'environment',
|
||||
.dependencies = 'environment'
|
||||
),
|
||||
methods = list(
|
||||
initialize = function() {
|
||||
.values <<- new.env(parent=emptyenv())
|
||||
.dependencies <<- new.env(parent=emptyenv())
|
||||
},
|
||||
get = function(key) {
|
||||
ctx <- .get.reactive.environment()$current.context()
|
||||
dep.key <- paste(key, ':', ctx$id, sep='')
|
||||
if (!exists(dep.key, where=.dependencies, inherits=F)) {
|
||||
assign(dep.key, ctx, pos=.dependencies, inherits=F)
|
||||
ctx$on.invalidate(function() {
|
||||
rm(list=dep.key, pos=.dependencies, inherits=F)
|
||||
})
|
||||
}
|
||||
|
||||
if (!exists(key, where=.values, inherits=F))
|
||||
NULL
|
||||
else
|
||||
base::get(key, pos=.values, inherits=F)
|
||||
},
|
||||
set = function(key, value) {
|
||||
if (exists(key, where=.values, inherits=F)) {
|
||||
if (identical(base::get(key, pos=.values, inherits=F), value)) {
|
||||
return(invisible())
|
||||
}
|
||||
}
|
||||
|
||||
assign(key, value, pos=.values, inherits=F)
|
||||
dep.keys <- objects(
|
||||
pos=.dependencies,
|
||||
pattern=paste('^\\Q', key, ':', '\\E', '\\d+$', sep='')
|
||||
)
|
||||
lapply(
|
||||
mget(dep.keys, envir=.dependencies),
|
||||
function(ctx) {
|
||||
ctx$invalidate()
|
||||
NULL
|
||||
}
|
||||
)
|
||||
invisible()
|
||||
},
|
||||
mset = function(lst) {
|
||||
lapply(names(lst),
|
||||
function(name) {
|
||||
.self$set(name, lst[[name]])
|
||||
})
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
Observable <- setRefClass(
|
||||
'Observable',
|
||||
fields = c(
|
||||
'.func', # function
|
||||
'.dependencies', # Map
|
||||
'.initialized', # logical
|
||||
'.value' # any
|
||||
),
|
||||
methods = list(
|
||||
initialize = function(func) {
|
||||
.func <<- func
|
||||
.dependencies <<- Map$new()
|
||||
.initialized <<- F
|
||||
},
|
||||
get.value = function() {
|
||||
if (!.initialized) {
|
||||
.initialized <<- T
|
||||
.self$.update.value()
|
||||
}
|
||||
|
||||
ctx <- .get.reactive.environment()$current.context()
|
||||
if (!.dependencies$contains.key(ctx$id)) {
|
||||
.dependencies$set(ctx$id, ctx)
|
||||
ctx$on.invalidate(function() {
|
||||
.dependencies$remove(ctx$id)
|
||||
})
|
||||
}
|
||||
return(.value)
|
||||
},
|
||||
.update.value = function() {
|
||||
old.value <- .value
|
||||
|
||||
ctx <- Context$new()
|
||||
ctx$on.invalidate(function() {
|
||||
.self$.update.value()
|
||||
})
|
||||
ctx$run(function() {
|
||||
.value <<- .func()
|
||||
})
|
||||
if (!identical(old.value, .value)) {
|
||||
lapply(
|
||||
.dependencies$values(),
|
||||
function(dep.ctx) {
|
||||
dep.ctx$invalidate()
|
||||
NULL
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
Observer <- setRefClass(
|
||||
'Observer',
|
||||
fields = list(
|
||||
.func = 'function'
|
||||
),
|
||||
methods = list(
|
||||
initialize = function(func) {
|
||||
.func <<- func
|
||||
.self$run()
|
||||
},
|
||||
run = function() {
|
||||
ctx <- Context$new()
|
||||
ctx$on.invalidate(function() {
|
||||
run()
|
||||
})
|
||||
ctx$run(.func)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
.get.reactive.environment <- function() {
|
||||
if (!exists('.ReactiveEnvironment', envir=.GlobalEnv, inherits=F)) {
|
||||
assign('.ReactiveEnvironment', ReactiveEnvironment$new(), envir=.GlobalEnv)
|
||||
}
|
||||
get('.ReactiveEnvironment', envir=.GlobalEnv, inherits=F)
|
||||
.reactiveEnvironment <- ReactiveEnvironment$new()
|
||||
.getReactiveEnvironment <- function() {
|
||||
.reactiveEnvironment
|
||||
}
|
||||
|
||||
flush.react <- function() {
|
||||
.get.reactive.environment()$flush()
|
||||
# Causes any pending invalidations to run.
|
||||
flushReact <- function() {
|
||||
.getReactiveEnvironment()$flush()
|
||||
}
|
||||
|
||||
test <- function () {
|
||||
values <- Values$new()
|
||||
obs <- Observer$new(function() {print(values$get('foo'))})
|
||||
flush.react()
|
||||
values$set('foo', 'bar')
|
||||
flush.react()
|
||||
|
||||
values$set('a', 100)
|
||||
values$set('b', 250)
|
||||
observable <- Observable$new(function() {
|
||||
values$get('a') + values$get('b')
|
||||
})
|
||||
obs2 <- Observer$new(function() {print(paste0('a+b: ', observable$get.value()))})
|
||||
flush.react()
|
||||
values$set('b', 300)
|
||||
flush.react()
|
||||
values$mset(list(a = 10, b = 20))
|
||||
flush.react()
|
||||
# Retrieves the current reactive context, or errors if there is no reactive
|
||||
# context active at the moment.
|
||||
getCurrentContext <- function() {
|
||||
.getReactiveEnvironment()$currentContext()
|
||||
}
|
||||
|
||||
345
R/reactives.R
Normal file
@@ -0,0 +1,345 @@
|
||||
Dependencies <- setRefClass(
|
||||
'Dependencies',
|
||||
fields = list(
|
||||
.dependencies = 'Map'
|
||||
),
|
||||
methods = list(
|
||||
register = function() {
|
||||
ctx <- .getReactiveEnvironment()$currentContext()
|
||||
if (!.dependencies$containsKey(ctx$id)) {
|
||||
.dependencies$set(ctx$id, ctx)
|
||||
ctx$onInvalidate(function() {
|
||||
.dependencies$remove(ctx$id)
|
||||
})
|
||||
}
|
||||
},
|
||||
invalidate = function() {
|
||||
lapply(
|
||||
.dependencies$values(),
|
||||
function(ctx) {
|
||||
ctx$invalidateHint()
|
||||
ctx$invalidate()
|
||||
NULL
|
||||
}
|
||||
)
|
||||
},
|
||||
invalidateHint = function() {
|
||||
lapply(
|
||||
.dependencies$values(),
|
||||
function(dep.ctx) {
|
||||
dep.ctx$invalidateHint()
|
||||
NULL
|
||||
})
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
Values <- setRefClass(
|
||||
'Values',
|
||||
fields = list(
|
||||
.values = 'environment',
|
||||
.dependencies = 'environment',
|
||||
# Dependencies for the list of names
|
||||
.namesDeps = 'Dependencies',
|
||||
# Dependencies for all values
|
||||
.allDeps = 'Dependencies'
|
||||
),
|
||||
methods = list(
|
||||
initialize = function() {
|
||||
.values <<- new.env(parent=emptyenv())
|
||||
.dependencies <<- new.env(parent=emptyenv())
|
||||
},
|
||||
get = function(key) {
|
||||
ctx <- .getReactiveEnvironment()$currentContext()
|
||||
dep.key <- paste(key, ':', ctx$id, sep='')
|
||||
if (!exists(dep.key, where=.dependencies, inherits=FALSE)) {
|
||||
assign(dep.key, ctx, pos=.dependencies, inherits=FALSE)
|
||||
ctx$onInvalidate(function() {
|
||||
rm(list=dep.key, pos=.dependencies, inherits=FALSE)
|
||||
})
|
||||
}
|
||||
|
||||
if (!exists(key, where=.values, inherits=FALSE))
|
||||
NULL
|
||||
else
|
||||
base::get(key, pos=.values, inherits=FALSE)
|
||||
},
|
||||
set = function(key, value) {
|
||||
if (exists(key, where=.values, inherits=FALSE)) {
|
||||
if (identical(base::get(key, pos=.values, inherits=FALSE), value)) {
|
||||
return(invisible())
|
||||
}
|
||||
}
|
||||
else {
|
||||
.namesDeps$invalidate()
|
||||
}
|
||||
.allDeps$invalidate()
|
||||
|
||||
assign(key, value, pos=.values, inherits=FALSE)
|
||||
dep.keys <- objects(
|
||||
pos=.dependencies,
|
||||
pattern=paste('^\\Q', key, ':', '\\E', '\\d+$', sep=''),
|
||||
all.names=TRUE
|
||||
)
|
||||
lapply(
|
||||
mget(dep.keys, envir=.dependencies),
|
||||
function(ctx) {
|
||||
ctx$invalidateHint()
|
||||
ctx$invalidate()
|
||||
NULL
|
||||
}
|
||||
)
|
||||
invisible()
|
||||
},
|
||||
mset = function(lst) {
|
||||
lapply(base::names(lst),
|
||||
function(name) {
|
||||
.self$set(name, lst[[name]])
|
||||
})
|
||||
},
|
||||
names = function() {
|
||||
.namesDeps$register()
|
||||
return(ls(.values, all.names=TRUE))
|
||||
},
|
||||
toList = function() {
|
||||
.allDeps$register()
|
||||
return(as.list(.values))
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
`[.Values` <- function(values, name) {
|
||||
values$get(name)
|
||||
}
|
||||
|
||||
`[<-.Values` <- function(values, name, value) {
|
||||
values$set(name, value)
|
||||
return(values)
|
||||
}
|
||||
|
||||
.createValuesReader <- function(values) {
|
||||
acc <- list(impl=values)
|
||||
class(acc) <- 'reactvaluesreader'
|
||||
return(acc)
|
||||
}
|
||||
|
||||
#' @S3method $ reactvaluesreader
|
||||
`$.reactvaluesreader` <- function(x, name) {
|
||||
x[['impl']]$get(name)
|
||||
}
|
||||
|
||||
#' @S3method names reactvaluesreader
|
||||
names.reactvaluesreader <- function(x) {
|
||||
x[['impl']]$names()
|
||||
}
|
||||
|
||||
#' @S3method as.list reactvaluesreader
|
||||
as.list.reactvaluesreader <- function(x, ...) {
|
||||
x[['impl']]$toList()
|
||||
}
|
||||
|
||||
Observable <- setRefClass(
|
||||
'Observable',
|
||||
fields = list(
|
||||
.func = 'function',
|
||||
.dependencies = 'Dependencies',
|
||||
.initialized = 'logical',
|
||||
.value = 'ANY'
|
||||
),
|
||||
methods = list(
|
||||
initialize = function(func) {
|
||||
if (length(formals(func)) > 0)
|
||||
stop("Can't make a reactive function from a function that takes one ",
|
||||
"or more parameters; only functions without parameters can be ",
|
||||
"reactive.")
|
||||
.func <<- func
|
||||
.initialized <<- FALSE
|
||||
},
|
||||
getValue = function() {
|
||||
if (!.initialized) {
|
||||
.initialized <<- TRUE
|
||||
.self$.updateValue()
|
||||
}
|
||||
|
||||
.dependencies$register()
|
||||
|
||||
if (identical(class(.value), 'try-error'))
|
||||
stop(attr(.value, 'condition'))
|
||||
return(.value)
|
||||
},
|
||||
.updateValue = function() {
|
||||
old.value <- .value
|
||||
|
||||
ctx <- Context$new()
|
||||
ctx$onInvalidate(function() {
|
||||
.self$.updateValue()
|
||||
})
|
||||
ctx$onInvalidateHint(function() {
|
||||
.dependencies$invalidateHint()
|
||||
})
|
||||
ctx$run(function() {
|
||||
.value <<- try(.func(), silent=FALSE)
|
||||
})
|
||||
if (!identical(old.value, .value)) {
|
||||
.dependencies$invalidate()
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
#' Create a Reactive Function
|
||||
#'
|
||||
#' Wraps a normal function to create a reactive function. Conceptually, a
|
||||
#' reactive function is a function whose result will change over time.
|
||||
#'
|
||||
#' Reactive functions are functions that can read reactive values and call other
|
||||
#' reactive functions. Whenever a reactive value changes, any reactive functions
|
||||
#' that depended on it are marked as "invalidated" and will automatically
|
||||
#' re-execute if necessary. If a reactive function is marked as invalidated, any
|
||||
#' other reactive functions that recently called it are also marked as
|
||||
#' invalidated. In this way, invalidations ripple through the functions that
|
||||
#' depend on each other.
|
||||
#'
|
||||
#' See the \href{http://rstudio.github.com/shiny/tutorial/}{Shiny tutorial} for
|
||||
#' more information about reactive functions.
|
||||
#'
|
||||
#' @param x The value or function to make reactive. The function must not have
|
||||
#' any parameters.
|
||||
#' @return A reactive function. (Note that reactive functions can only be called
|
||||
#' from within other reactive functions.)
|
||||
#'
|
||||
#' @export
|
||||
reactive <- function(x) {
|
||||
UseMethod("reactive")
|
||||
}
|
||||
#' @S3method reactive function
|
||||
reactive.function <- function(x) {
|
||||
return(Observable$new(x)$getValue)
|
||||
}
|
||||
#' @S3method reactive default
|
||||
reactive.default <- function(x) {
|
||||
stop("Don't know how to make this object reactive!")
|
||||
}
|
||||
|
||||
Observer <- setRefClass(
|
||||
'Observer',
|
||||
fields = list(
|
||||
.func = 'function',
|
||||
.hintCallbacks = 'list'
|
||||
),
|
||||
methods = list(
|
||||
initialize = function(func) {
|
||||
if (length(formals(func)) > 0)
|
||||
stop("Can't make an observer from a function that takes parameters; ",
|
||||
"only functions without parameters can be reactive.")
|
||||
|
||||
.func <<- func
|
||||
|
||||
# Defer the first running of this until flushReact is called
|
||||
ctx <- Context$new()
|
||||
ctx$onInvalidate(function() {
|
||||
run()
|
||||
})
|
||||
ctx$invalidate()
|
||||
},
|
||||
run = function() {
|
||||
ctx <- Context$new()
|
||||
ctx$onInvalidate(function() {
|
||||
run()
|
||||
})
|
||||
ctx$onInvalidateHint(function() {
|
||||
lapply(.hintCallbacks, function(func) {
|
||||
func()
|
||||
NULL
|
||||
})
|
||||
})
|
||||
ctx$run(.func)
|
||||
},
|
||||
onInvalidateHint = function(func) {
|
||||
.hintCallbacks <<- c(.hintCallbacks, func)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
#' Create a reactive observer
|
||||
#'
|
||||
#' Creates an observer from the given function. An observer is like a reactive
|
||||
#' function in that it can read reactive values and call reactive functions, and
|
||||
#' will automatically re-execute when those dependencies change. But unlike
|
||||
#' reactive functions, it doesn't yield a result and can't be used as an input
|
||||
#' to other reactive functions. Thus, observers are only useful for their side
|
||||
#' effects (for example, performing I/O).
|
||||
#'
|
||||
#' Another contrast between reactive functions and observers is their execution
|
||||
#' strategy. Reactive functions use lazy evaluation; that is, when their
|
||||
#' dependencies change, they don't re-execute right away but rather wait until
|
||||
#' they are called by someone else. Indeed, if they are not called then they
|
||||
#' will never re-execute. In contrast, observers use eager evaluation; as soon
|
||||
#' as their dependencies change, they schedule themselves to re-execute.
|
||||
#'
|
||||
#' @param func The function to observe. It must not have any parameters. Any
|
||||
#' return value from this function will be ignored.
|
||||
#'
|
||||
#' @export
|
||||
observe <- function(func) {
|
||||
Observer$new(func)
|
||||
invisible()
|
||||
}
|
||||
|
||||
#' Timer
|
||||
#'
|
||||
#' Creates a reactive timer with the given interval. A reactive timer is like a
|
||||
#' reactive value, except reactive values are triggered when they are set, while
|
||||
#' reactive timers are triggered simply by the passage of time.
|
||||
#'
|
||||
#' \link[=reactive]{Reactive functions} and observers that want to be
|
||||
#' invalidated by the timer need to call the timer function that
|
||||
#' \code{reactiveTimer} returns, even if the current time value is not actually
|
||||
#' needed.
|
||||
#'
|
||||
#' See \code{\link{invalidateLater}} as a safer and simpler alternative.
|
||||
#'
|
||||
#' @param intervalMs How often to fire, in milliseconds
|
||||
#' @return A no-parameter function that can be called from a reactive context,
|
||||
#' in order to cause that context to be invalidated the next time the timer
|
||||
#' interval elapses. Calling the returned function also happens to yield the
|
||||
#' current time (as in \code{\link{Sys.time}}).
|
||||
#' @seealso invalidateLater
|
||||
#' @export
|
||||
reactiveTimer <- function(intervalMs=1000) {
|
||||
dependencies <- Map$new()
|
||||
timerCallbacks$schedule(intervalMs, function() {
|
||||
timerCallbacks$schedule(intervalMs, sys.function())
|
||||
lapply(
|
||||
dependencies$values(),
|
||||
function(dep.ctx) {
|
||||
dep.ctx$invalidate()
|
||||
NULL
|
||||
})
|
||||
})
|
||||
return(function() {
|
||||
ctx <- .getReactiveEnvironment()$currentContext()
|
||||
if (!dependencies$containsKey(ctx$id)) {
|
||||
dependencies$set(ctx$id, ctx)
|
||||
ctx$onInvalidate(function() {
|
||||
dependencies$remove(ctx$id)
|
||||
})
|
||||
}
|
||||
return(Sys.time())
|
||||
})
|
||||
}
|
||||
|
||||
#' Scheduled Invalidation
|
||||
#'
|
||||
#' Schedules the current reactive context to be invalidated in the given number
|
||||
#' of milliseconds.
|
||||
#' @param millis Approximate milliseconds to wait before invalidating the
|
||||
#' current reactive context.
|
||||
#' @export
|
||||
invalidateLater <- function(millis) {
|
||||
ctx <- .getReactiveEnvironment()$currentContext()
|
||||
timerCallbacks$schedule(millis, function() {
|
||||
ctx$invalidate()
|
||||
})
|
||||
invisible()
|
||||
}
|
||||
205
R/shinyui.R
Normal file
@@ -0,0 +1,205 @@
|
||||
|
||||
#' @export
|
||||
p <- function(...) tags$p(...)
|
||||
|
||||
#' @export
|
||||
h1 <- function(...) tags$h1(...)
|
||||
|
||||
#' @export
|
||||
h2 <- function(...) tags$h2(...)
|
||||
|
||||
#' @export
|
||||
h3 <- function(...) tags$h3(...)
|
||||
|
||||
#' @export
|
||||
h4 <- function(...) tags$h4(...)
|
||||
|
||||
#' @export
|
||||
h5 <- function(...) tags$h5(...)
|
||||
|
||||
#' @export
|
||||
h6 <- function(...) tags$h6(...)
|
||||
|
||||
#' @export
|
||||
a <- function(...) tags$a(...)
|
||||
|
||||
#' @export
|
||||
br <- function(...) tags$br(...)
|
||||
|
||||
#' @export
|
||||
div <- function(...) tags$div(...)
|
||||
|
||||
#' @export
|
||||
span <- function(...) tags$span(...)
|
||||
|
||||
#' @export
|
||||
pre <- function(...) tags$pre(...)
|
||||
|
||||
#' @export
|
||||
code <- function(...) tags$code(...)
|
||||
|
||||
#' @export
|
||||
img <- function(...) tags$img(...)
|
||||
|
||||
#' @export
|
||||
strong <- function(...) tags$strong(...)
|
||||
|
||||
#' @export
|
||||
em <- function(...) tags$em(...)
|
||||
|
||||
#' @export
|
||||
includeHTML <- function(path) {
|
||||
dependsOnFile(path)
|
||||
lines <- readLines(path, warn=FALSE, encoding='UTF-8')
|
||||
return(HTML(paste(lines, collapse='\r\n')))
|
||||
}
|
||||
|
||||
#' @export
|
||||
includeText <- function(path) {
|
||||
dependsOnFile(path)
|
||||
lines <- readLines(path, warn=FALSE, encoding='UTF-8')
|
||||
return(HTML(paste(lines, collapse='\r\n')))
|
||||
}
|
||||
|
||||
#' @export
|
||||
includeMarkdown <- function(path) {
|
||||
if (!require(markdown))
|
||||
stop("Markdown package is not installed")
|
||||
|
||||
dependsOnFile(path)
|
||||
html <- markdown::markdownToHTML(path, fragment.only=TRUE)
|
||||
Encoding(html) <- 'UTF-8'
|
||||
return(HTML(html))
|
||||
}
|
||||
|
||||
|
||||
#' Include Content Only Once
|
||||
#'
|
||||
#' Use \code{singleton} to wrap contents (tag, text, HTML, or lists) that should
|
||||
#' be included in the generated document only once, yet may appear in the
|
||||
#' document-generating code more than once. Only the first appearance of the
|
||||
#' content (in document order) will be used. Useful for custom components that
|
||||
#' have JavaScript files or stylesheets.
|
||||
#'
|
||||
#' @param x A \code{\link{tag}}, text, \code{\link{HTML}}, or list.
|
||||
#'
|
||||
#' @export
|
||||
singleton <- function(x) {
|
||||
class(x) <- c(class(x), 'shiny.singleton')
|
||||
return(x)
|
||||
}
|
||||
|
||||
renderPage <- function(ui, connection) {
|
||||
|
||||
# provide a filter so we can intercept head tag requests
|
||||
context <- new.env()
|
||||
context$head <- character()
|
||||
context$singletons <- character()
|
||||
context$filter <- function(content) {
|
||||
if (inherits(content, 'shiny.singleton')) {
|
||||
sig <- digest(content, algo='sha1')
|
||||
if (sig %in% context$singletons)
|
||||
return(FALSE)
|
||||
context$singletons <- c(sig, context$singletons)
|
||||
}
|
||||
|
||||
if (isTag(content) && identical(content$name, "head")) {
|
||||
textConn <- textConnection(NULL, "w")
|
||||
textConnWriter <- function(text) cat(text, file = textConn)
|
||||
tagWriteChildren(content, textConnWriter, 1, context)
|
||||
context$head <- append(context$head, textConnectionValue(textConn))
|
||||
close(textConn)
|
||||
return (FALSE)
|
||||
}
|
||||
else {
|
||||
return (TRUE)
|
||||
}
|
||||
}
|
||||
|
||||
# write ui HTML to a character vector
|
||||
textConn <- textConnection(NULL, "w")
|
||||
tagWrite(ui, function(text) cat(text, file = textConn), 0, context)
|
||||
uiHTML <- textConnectionValue(textConn)
|
||||
close(textConn)
|
||||
|
||||
# write preamble
|
||||
writeLines(c('<!DOCTYPE html>',
|
||||
'<html>',
|
||||
'<head>',
|
||||
' <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>',
|
||||
' <script src="shared/jquery.js" type="text/javascript"></script>',
|
||||
' <script src="shared/shiny.js" type="text/javascript"></script>',
|
||||
' <link rel="stylesheet" type="text/css" href="shared/shiny.css"/>',
|
||||
context$head,
|
||||
'</head>',
|
||||
'<body>',
|
||||
recursive=TRUE),
|
||||
con = connection)
|
||||
|
||||
# write UI html to connection
|
||||
writeLines(uiHTML, con = connection)
|
||||
|
||||
# write end document
|
||||
writeLines(c('</body>',
|
||||
'</html>'),
|
||||
con = connection)
|
||||
}
|
||||
|
||||
#' Create a Shiny UI handler
|
||||
#'
|
||||
#' Register a UI handler by providing a UI definition (created with e.g.
|
||||
#' \link{pageWithSidebar}) and web server path (typically "/", the default
|
||||
#' value).
|
||||
#'
|
||||
#' @param ui A user-interace definition
|
||||
#' @param path The web server path to server the UI from
|
||||
#' @return Called for its side-effect of registering a UI handler
|
||||
#'
|
||||
#' @examples
|
||||
#' el <- div(HTML("I like <u>turtles</u>"))
|
||||
#' cat(as.character(el))
|
||||
#'
|
||||
#' @examples
|
||||
#' # Define UI
|
||||
#' shinyUI(pageWithSidebar(
|
||||
#'
|
||||
#' # Application title
|
||||
#' headerPanel("Hello Shiny!"),
|
||||
#'
|
||||
#' # Sidebar with a slider input
|
||||
#' sidebarPanel(
|
||||
#' sliderInput("obs",
|
||||
#' "Number of observations:",
|
||||
#' min = 0,
|
||||
#' max = 1000,
|
||||
#' value = 500)
|
||||
#' ),
|
||||
#'
|
||||
#' # Show a plot of the generated distribution
|
||||
#' mainPanel(
|
||||
#' plotOutput("distPlot")
|
||||
#' )
|
||||
#' ))
|
||||
#'
|
||||
#' @export
|
||||
shinyUI <- function(ui, path='/') {
|
||||
|
||||
force(ui)
|
||||
|
||||
registerClient({
|
||||
|
||||
function(ws, header) {
|
||||
|
||||
if (header$PATH != path)
|
||||
return(NULL)
|
||||
|
||||
textConn <- textConnection(NULL, "w")
|
||||
on.exit(close(textConn))
|
||||
|
||||
renderPage(ui, textConn)
|
||||
html <- paste(textConnectionValue(textConn), collapse='\n')
|
||||
return(httpResponse(200, content=html))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
253
R/shinywrappers.R
Normal file
@@ -0,0 +1,253 @@
|
||||
suppressPackageStartupMessages({
|
||||
library(caTools)
|
||||
library(xtable)
|
||||
})
|
||||
|
||||
#' Plot Output
|
||||
#'
|
||||
#' Creates a reactive plot that is suitable for assigning to an \code{output}
|
||||
#' slot.
|
||||
#'
|
||||
#' The corresponding HTML output tag should be \code{div} or \code{img} and have
|
||||
#' the CSS class name \code{shiny-plot-output}.
|
||||
#'
|
||||
#' For output, it will try to use the following devices, in this order:
|
||||
#' quartz (via \code{\link[grDevices]{png}}), then \code{\link[Cairo]{CairoPNG}},
|
||||
#' and finally \code{\link[grDevices]{png}}. This is in order of quality of
|
||||
#' output. Notably, plain \code{png} output on Linux and Windows may not
|
||||
#' antialias some point shapes, resulting in poor quality output.
|
||||
#'
|
||||
#' @param func A function that generates a plot.
|
||||
#' @param width The width of the rendered plot, in pixels; or \code{'auto'} to
|
||||
#' use the \code{offsetWidth} of the HTML element that is bound to this plot.
|
||||
#' You can also pass in a function that returns the width in pixels or
|
||||
#' \code{'auto'}; in the body of the function you may reference reactive
|
||||
#' values and functions.
|
||||
#' @param height The height of the rendered plot, in pixels; or \code{'auto'} to
|
||||
#' use the \code{offsetHeight} of the HTML element that is bound to this plot.
|
||||
#' You can also pass in a function that returns the width in pixels or
|
||||
#' \code{'auto'}; in the body of the function you may reference reactive
|
||||
#' values and functions.
|
||||
#' @param ... Arguments to be passed through to \code{\link[grDevices]{png}}.
|
||||
#' These can be used to set the width, height, background color, etc.
|
||||
#'
|
||||
#' @export
|
||||
reactivePlot <- function(func, width='auto', height='auto', ...) {
|
||||
args <- list(...)
|
||||
|
||||
if (is.function(width))
|
||||
width <- reactive(width)
|
||||
if (is.function(height))
|
||||
height <- reactive(height)
|
||||
|
||||
return(function(shinyapp, name, ...) {
|
||||
png.file <- tempfile(fileext='.png')
|
||||
|
||||
if (is.function(width))
|
||||
width <- width()
|
||||
if (is.function(height))
|
||||
height <- height()
|
||||
|
||||
# Note that these are reactive calls. A change to the width and height
|
||||
# will inherently cause a reactive plot to redraw (unless width and
|
||||
# height were explicitly specified).
|
||||
prefix <- '.shinyout_'
|
||||
if (width == 'auto')
|
||||
width <- shinyapp$session$get(paste(prefix, name, '_width', sep=''));
|
||||
if (height == 'auto')
|
||||
height <- shinyapp$session$get(paste(prefix, name, '_height', sep=''));
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
return(NULL)
|
||||
|
||||
# If quartz is available, use png() (which will default to quartz).
|
||||
# Otherwise, if the Cairo package is installed, use CairoPNG().
|
||||
# Finally, if neither quartz nor Cairo, use png().
|
||||
if (capabilities("aqua")) {
|
||||
pngfun <- png
|
||||
} else if (nchar(system.file(package = "Cairo"))) {
|
||||
require(Cairo)
|
||||
pngfun <- CairoPNG
|
||||
} else {
|
||||
pngfun <- png
|
||||
}
|
||||
|
||||
do.call(pngfun, c(args, filename=png.file, width=width, height=height))
|
||||
on.exit(unlink(png.file))
|
||||
tryCatch(
|
||||
func(),
|
||||
finally=dev.off())
|
||||
|
||||
bytes <- file.info(png.file)$size
|
||||
if (is.na(bytes))
|
||||
return(NULL)
|
||||
|
||||
pngData <- readBin(png.file, 'raw', n=bytes)
|
||||
if (shinyapp$allowDataUriScheme) {
|
||||
b64 <- base64encode(pngData)
|
||||
return(paste("data:image/png;base64,", b64, sep=''))
|
||||
}
|
||||
else {
|
||||
imageUrl <- shinyapp$savePlot(name, pngData, 'image/png')
|
||||
return(imageUrl)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#' Table Output
|
||||
#'
|
||||
#' Creates a reactive table that is suitable for assigning to an \code{output}
|
||||
#' slot.
|
||||
#'
|
||||
#' The corresponding HTML output tag should be \code{div} and have the CSS class
|
||||
#' name \code{shiny-html-output}.
|
||||
#'
|
||||
#' @param func A function that returns an R object that can be used with
|
||||
#' \code{\link[xtable]{xtable}}.
|
||||
#' @param ... Arguments to be passed through to \code{\link[xtable]{xtable}} and
|
||||
#' \code{\link[xtable]{print.xtable}}.
|
||||
#'
|
||||
#' @export
|
||||
reactiveTable <- function(func, ...) {
|
||||
reactive(function() {
|
||||
classNames <- getOption('shiny.table.class', 'data table table-bordered table-condensed')
|
||||
data <- func()
|
||||
|
||||
if (is.null(data) || is.na(data))
|
||||
return("")
|
||||
|
||||
return(paste(
|
||||
capture.output(
|
||||
print(xtable(data, ...),
|
||||
type='html',
|
||||
html.table.attributes=paste('class="',
|
||||
htmlEscape(classNames, TRUE),
|
||||
'"',
|
||||
sep=''), ...)),
|
||||
collapse="\n"))
|
||||
})
|
||||
}
|
||||
|
||||
#' Printable Output
|
||||
#'
|
||||
#' Makes a reactive version of the given function that also turns its printable
|
||||
#' result into a string. The reactive function is suitable for assigning to an
|
||||
#' \code{output} slot.
|
||||
#'
|
||||
#' The corresponding HTML output tag can be anything (though \code{pre} is
|
||||
#' recommended if you need a monospace font and whitespace preserved) and should
|
||||
#' have the CSS class name \code{shiny-text-output}.
|
||||
#'
|
||||
#' The result of executing \code{func} will be printed inside a
|
||||
#' \code{\link[utils]{capture.output}} call.
|
||||
#'
|
||||
#' @param func A function that returns a printable R object.
|
||||
#'
|
||||
#' @export
|
||||
reactivePrint <- function(func) {
|
||||
reactive(function() {
|
||||
return(paste(capture.output(print(func())), collapse="\n"))
|
||||
})
|
||||
}
|
||||
|
||||
#' Text Output
|
||||
#'
|
||||
#' Makes a reactive version of the given function that also uses
|
||||
#' \code{\link[base]{cat}} to turn its result into a single-element character
|
||||
#' vector.
|
||||
#'
|
||||
#' The corresponding HTML output tag can be anything (though \code{pre} is
|
||||
#' recommended if you need a monospace font and whitespace preserved) and should
|
||||
#' have the CSS class name \code{shiny-text-output}.
|
||||
#'
|
||||
#' The result of executing \code{func} will passed to \code{cat}, inside a
|
||||
#' \code{\link[utils]{capture.output}} call.
|
||||
#'
|
||||
#' @param func A function that returns an R object that can be used as an
|
||||
#' argument to \code{cat}.
|
||||
#'
|
||||
#' @export
|
||||
reactiveText <- function(func) {
|
||||
reactive(function() {
|
||||
return(paste(capture.output(cat(func())), collapse="\n"))
|
||||
})
|
||||
}
|
||||
|
||||
#' UI Output
|
||||
#'
|
||||
#' \bold{Experimental feature.} Makes a reactive version of a function that
|
||||
#' generates HTML using the Shiny UI library.
|
||||
#'
|
||||
#' The corresponding HTML output tag should be \code{div} and have the CSS class
|
||||
#' name \code{shiny-html-output} (or use \code{\link{uiOutput}}).
|
||||
#'
|
||||
#' @param func A function that returns a Shiny tag object, \code{\link{HTML}},
|
||||
#' or a list of such objects.
|
||||
#'
|
||||
#' @seealso conditionalPanel
|
||||
#'
|
||||
#' @export
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' output$moreControls <- reactiveUI(function() {
|
||||
#' list(
|
||||
#'
|
||||
#' )
|
||||
#' })
|
||||
#' }
|
||||
reactiveUI <- function(func) {
|
||||
reactive(function() {
|
||||
result <- func()
|
||||
if (is.null(result) || length(result) == 0)
|
||||
return(NULL)
|
||||
# Wrap result in tagList in case it is an ordinary list
|
||||
return(as.character(tagList(result)))
|
||||
})
|
||||
}
|
||||
|
||||
#' File Downloads
|
||||
#'
|
||||
#' Allows content from the Shiny application to be made available to the user as
|
||||
#' file downloads (for example, downloading the currently visible data as a CSV
|
||||
#' file). Both filename and contents can be calculated dynamically at the time
|
||||
#' the user initiates the download. Assign the return value to a slot on
|
||||
#' \code{output} in your server function, and in the UI use
|
||||
#' \code{\link{downloadButton}} or \code{\link{downloadLink}} to make the
|
||||
#' download available.
|
||||
#'
|
||||
#' @param filename A string of the filename, including extension, that the
|
||||
#' user's web browser should default to when downloading the file; or a
|
||||
#' function that returns such a string. (Reactive values and functions may be
|
||||
#' used from this function.)
|
||||
#' @param content A function that takes a single argument \code{file} that is a
|
||||
#' file path (string) of a nonexistent temp file, and writes the content to
|
||||
#' that file path. (Reactive values and functions may be used from this
|
||||
#' function.)
|
||||
#' @param contentType A string of the download's
|
||||
#' \href{http://en.wikipedia.org/wiki/Internet_media_type}{content type}, for
|
||||
#' example \code{"text/csv"} or \code{"image/png"}. If \code{NULL} or
|
||||
#' \code{NA}, the content type will be guessed based on the filename
|
||||
#' extension, or \code{application/octet-stream} if the extension is unknown.
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' # In server.R:
|
||||
#' output$downloadData <- downloadHandler(
|
||||
#' filename = function() {
|
||||
#' paste('data-', Sys.Date(), '.csv', sep='')
|
||||
#' },
|
||||
#' content = function(file) {
|
||||
#' write.csv(data, file)
|
||||
#' }
|
||||
#' )
|
||||
#'
|
||||
#' # In ui.R:
|
||||
#' downloadLink('downloadData', 'Download')
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
downloadHandler <- function(filename, content, contentType=NA) {
|
||||
return(function(shinyapp, name, ...) {
|
||||
shinyapp$registerDownload(name, filename, contentType, content)
|
||||
})
|
||||
}
|
||||
133
R/slider.R
Normal file
@@ -0,0 +1,133 @@
|
||||
hasDecimals <- function(value) {
|
||||
truncatedValue <- round(value)
|
||||
return (!identical(value, truncatedValue))
|
||||
}
|
||||
|
||||
#' Animation Options
|
||||
#'
|
||||
#' Creates an options object for customizing animations for \link{sliderInput}.
|
||||
#'
|
||||
#' @param interval The interval, in milliseconds, between each animation step.
|
||||
#' @param loop \code{TRUE} to automatically restart the animation when it
|
||||
#' reaches the end.
|
||||
#' @param playButton Specifies the appearance of the play button. Valid values
|
||||
#' are a one-element character vector (for a simple text label), an HTML tag
|
||||
#' or list of tags (using \code{\link{tag}} and friends), or raw HTML (using
|
||||
#' \code{\link{HTML}}).
|
||||
#' @param pauseButton Similar to \code{playButton}, but for the pause button.
|
||||
#'
|
||||
#' @export
|
||||
animationOptions <- function(interval=1000,
|
||||
loop=FALSE,
|
||||
playButton=NULL,
|
||||
pauseButton=NULL) {
|
||||
list(interval=interval,
|
||||
loop=loop,
|
||||
playButton=playButton,
|
||||
pauseButton=pauseButton)
|
||||
}
|
||||
|
||||
# Create a new slider control (list of slider input element and the script
|
||||
# tag used to configure it). This is a lower level control that should
|
||||
# be wrapped in an "input" construct (e.g. sliderInput in bootstrap.R)
|
||||
#
|
||||
# this is a wrapper for: https://github.com/egorkhmelev/jslider
|
||||
# (www/shared/slider contains js, css, and img dependencies)
|
||||
slider <- function(inputId, min, max, value, step = NULL, ...,
|
||||
round=FALSE, format='#,##0.#####', locale='us',
|
||||
ticks=TRUE, animate=FALSE) {
|
||||
# validate inputId
|
||||
inputId <- as.character(inputId)
|
||||
if (!is.character(inputId))
|
||||
stop("inputId not specified")
|
||||
|
||||
# validate numeric inputs
|
||||
if (!is.numeric(value) || !is.numeric(min) || !is.numeric(max))
|
||||
stop("min, max, amd value must all be numeric values")
|
||||
else if (min(value) < min)
|
||||
stop(paste("slider initial value", value,
|
||||
"is less than the specified minimum"))
|
||||
else if (max(value) > max)
|
||||
stop(paste("slider initial value", value,
|
||||
"is greater than the specified maximum"))
|
||||
else if (min > max)
|
||||
stop(paste("slider maximum is greater than minimum"))
|
||||
else if (!is.null(step)) {
|
||||
if (!is.numeric(step))
|
||||
stop("step is not a numeric value")
|
||||
if (step > (max - min))
|
||||
stop("step is greater than range")
|
||||
}
|
||||
|
||||
# step
|
||||
range <- max - min
|
||||
if (is.null(step)) {
|
||||
# short range or decimals means continuous decimal
|
||||
if (range < 2 || hasDecimals(min) || hasDecimals(max))
|
||||
step <- range / 250 # ~ one step per pixel
|
||||
else
|
||||
step = 1
|
||||
}
|
||||
|
||||
# Default state is to not have ticks
|
||||
if (identical(ticks, TRUE)) {
|
||||
# Automatic ticks
|
||||
tickCount <- (range / step) + 1
|
||||
if (tickCount <= 26)
|
||||
ticks <- paste(rep('|', floor(tickCount)), collapse=';')
|
||||
else {
|
||||
ticks <- NULL
|
||||
# # This is a smarter auto-tick algorithm, but to be truly useful
|
||||
# # we need jslider to be able to space ticks irregularly
|
||||
# tickSize <- 10^(floor(log10(range/0.39)))
|
||||
# if ((range / tickSize) == floor(range / tickSize)) {
|
||||
# ticks <- paste(rep('|', (range / tickSize) + 1), collapse=';')
|
||||
# }
|
||||
# else {
|
||||
# ticks <- NULL
|
||||
# }
|
||||
}
|
||||
}
|
||||
else if (is.numeric(ticks) && length(ticks) == 1) {
|
||||
# Use n ticks
|
||||
ticks <- paste(rep('|', ticks), collapse=';')
|
||||
}
|
||||
else if (length(ticks) > 1 && (is.numeric(ticks) || is.character(ticks))) {
|
||||
# Explicit ticks
|
||||
ticks <- paste(ticks, collapse=';')
|
||||
}
|
||||
else {
|
||||
ticks <- NULL
|
||||
}
|
||||
|
||||
# build slider
|
||||
sliderFragment <- list(tags$input(
|
||||
id=inputId, type="slider",
|
||||
name=inputId, value=paste(value, collapse=';'), class="jslider",
|
||||
'data-from'=min, 'data-to'=max, 'data-step'=step,
|
||||
'data-skin'='plastic', 'data-round'=round, 'data-locale'=locale,
|
||||
'data-format'=format, 'data-scale'=ticks,
|
||||
'data-smooth'=FALSE))
|
||||
|
||||
if (identical(animate, TRUE))
|
||||
animate <- animationOptions()
|
||||
|
||||
if (!is.null(animate) && !identical(animate, FALSE)) {
|
||||
if (is.null(animate$playButton))
|
||||
animate$playButton <- 'Play'
|
||||
if (is.null(animate$pauseButton))
|
||||
animate$pauseButton <- 'Pause'
|
||||
|
||||
sliderFragment[[length(sliderFragment)+1]] <-
|
||||
tags$div(class='slider-animate-container',
|
||||
tags$a(href='#',
|
||||
class='slider-animate-button',
|
||||
'data-target-id'=inputId,
|
||||
'data-interval'=animate$interval,
|
||||
'data-loop'=animate$loop,
|
||||
tags$span(class='play', animate$playButton),
|
||||
tags$span(class='pause', animate$pauseButton)))
|
||||
}
|
||||
|
||||
return(sliderFragment)
|
||||
}
|
||||
380
R/tags.R
Normal file
@@ -0,0 +1,380 @@
|
||||
|
||||
|
||||
htmlEscape <- local({
|
||||
.htmlSpecials <- list(
|
||||
`&` = '&',
|
||||
`<` = '<',
|
||||
`>` = '>'
|
||||
)
|
||||
.htmlSpecialsPattern <- paste(names(.htmlSpecials), collapse='|')
|
||||
.htmlSpecialsAttrib <- c(
|
||||
.htmlSpecials,
|
||||
`'` = ''',
|
||||
`"` = '"',
|
||||
`\r` = ' ',
|
||||
`\n` = ' '
|
||||
)
|
||||
.htmlSpecialsPatternAttrib <- paste(names(.htmlSpecialsAttrib), collapse='|')
|
||||
|
||||
function(text, attribute=TRUE) {
|
||||
pattern <- if(attribute)
|
||||
.htmlSpecialsPatternAttrib
|
||||
else
|
||||
.htmlSpecialsPattern
|
||||
|
||||
# Short circuit in the common case that there's nothing to escape
|
||||
if (!grepl(pattern, text))
|
||||
return(text)
|
||||
|
||||
specials <- if(attribute)
|
||||
.htmlSpecialsAttrib
|
||||
else
|
||||
.htmlSpecials
|
||||
|
||||
for (chr in names(specials)) {
|
||||
text <- gsub(chr, specials[[chr]], text, fixed=TRUE)
|
||||
}
|
||||
|
||||
return(text)
|
||||
}
|
||||
})
|
||||
|
||||
isTag <- function(x) {
|
||||
inherits(x, "shiny.tag")
|
||||
}
|
||||
|
||||
#' @S3method print shiny.tag
|
||||
print.shiny.tag <- function(x, ...) {
|
||||
print(as.character(x), ...)
|
||||
}
|
||||
|
||||
#' @S3method format shiny.tag
|
||||
format.shiny.tag <- function(x, ...) {
|
||||
as.character.shiny.tag(x)
|
||||
}
|
||||
|
||||
#' @S3method as.character shiny.tag
|
||||
as.character.shiny.tag <- function(x, ...) {
|
||||
f = file()
|
||||
on.exit(close(f))
|
||||
textWriter <- function(text) {
|
||||
cat(text, file=f)
|
||||
}
|
||||
tagWrite(x, textWriter)
|
||||
return(HTML(paste(readLines(f), collapse='\n')))
|
||||
}
|
||||
|
||||
#' @S3method print shiny.tag.list
|
||||
print.shiny.tag.list <- print.shiny.tag
|
||||
|
||||
#' @S3method format shiny.tag.list
|
||||
format.shiny.tag.list <- format.shiny.tag
|
||||
|
||||
#' @S3method as.character shiny.tag.list
|
||||
as.character.shiny.tag.list <- as.character.shiny.tag
|
||||
|
||||
normalizeText <- function(text) {
|
||||
if (!is.null(attr(text, "html")))
|
||||
text
|
||||
else
|
||||
htmlEscape(text, attribute=FALSE)
|
||||
|
||||
}
|
||||
|
||||
#' @export
|
||||
tagList <- function(...) {
|
||||
lst <- list(...)
|
||||
class(lst) <- c("shiny.tag.list", "list")
|
||||
return(lst)
|
||||
}
|
||||
|
||||
#' @export
|
||||
tagAppendChild <- function(tag, child) {
|
||||
tag$children[[length(tag$children)+1]] <- child
|
||||
tag
|
||||
}
|
||||
|
||||
#' @export
|
||||
tag <- function(`_tag_name`, varArgs) {
|
||||
|
||||
# create basic tag data structure
|
||||
tag <- list()
|
||||
class(tag) <- "shiny.tag"
|
||||
tag$name <- `_tag_name`
|
||||
tag$attribs <- list()
|
||||
tag$children <- list()
|
||||
|
||||
# process varArgs
|
||||
varArgsNames <- names(varArgs)
|
||||
if (is.null(varArgsNames))
|
||||
varArgsNames <- character(length=length(varArgs))
|
||||
|
||||
if (length(varArgsNames) > 0) {
|
||||
for (i in 1:length(varArgsNames)) {
|
||||
# save name and value
|
||||
name <- varArgsNames[[i]]
|
||||
value <- varArgs[[i]]
|
||||
|
||||
# process attribs
|
||||
if (nzchar(name))
|
||||
tag$attribs[[name]] <- value
|
||||
|
||||
# process child tags
|
||||
else if (isTag(value)) {
|
||||
tag$children[[length(tag$children)+1]] <- value
|
||||
}
|
||||
|
||||
# recursively process lists of children
|
||||
else if (is.list(value)) {
|
||||
|
||||
tagAppendChildren <- function(tag, children) {
|
||||
for(child in children) {
|
||||
if (isTag(child))
|
||||
tag <- tagAppendChild(tag, child)
|
||||
else if (is.list(child))
|
||||
tag <- tagAppendChildren(tag, child)
|
||||
else if (is.character(child))
|
||||
tag <- tagAppendChild(tag, child)
|
||||
else
|
||||
tag <- tagAppendChild(tag, as.character(child))
|
||||
}
|
||||
return (tag)
|
||||
}
|
||||
|
||||
tag <- tagAppendChildren(tag, value)
|
||||
}
|
||||
|
||||
# add text
|
||||
else if (is.character(value)) {
|
||||
tag <- tagAppendChild(tag, value)
|
||||
}
|
||||
|
||||
# everything else treated as text
|
||||
else {
|
||||
tag <- tagAppendChild(tag, as.character(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# return the tag
|
||||
return (tag)
|
||||
}
|
||||
|
||||
tagWriteChildren <- function(tag, textWriter, indent, context) {
|
||||
for (child in tag$children) {
|
||||
if (isTag(child)) {
|
||||
tagWrite(child, textWriter, indent, context)
|
||||
}
|
||||
else {
|
||||
# first call optional filter -- exit function if it returns false
|
||||
if (is.null(context) || is.null(context$filter) || context$filter(child)) {
|
||||
child <- normalizeText(child)
|
||||
indentText <- paste(rep(" ", indent*3), collapse="")
|
||||
textWriter(paste(indentText, child, "\n", sep=""))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tagWrite <- function(tag, textWriter, indent=0, context = NULL) {
|
||||
|
||||
# optionally process a list of tags
|
||||
if (!isTag(tag) && is.list(tag)) {
|
||||
sapply(tag, function(t) tagWrite(t, textWriter, indent, context))
|
||||
return (NULL)
|
||||
}
|
||||
|
||||
# first call optional filter -- exit function if it returns false
|
||||
if (!is.null(context) && !is.null(context$filter) && !context$filter(tag))
|
||||
return (NULL)
|
||||
|
||||
# compute indent text
|
||||
indentText <- paste(rep(" ", indent*3), collapse="")
|
||||
|
||||
# write tag name
|
||||
textWriter(paste(indentText, "<", tag$name, sep=""))
|
||||
|
||||
# write attributes
|
||||
for (attrib in names(tag$attribs)) {
|
||||
attribValue <- tag$attribs[[attrib]]
|
||||
if (!is.na(attribValue)) {
|
||||
if (is.logical(attribValue))
|
||||
attribValue <- tolower(attribValue)
|
||||
text <- htmlEscape(attribValue, attribute=TRUE)
|
||||
textWriter(paste(" ", attrib,"=\"", text, "\"", sep=""))
|
||||
}
|
||||
else {
|
||||
textWriter(paste(" ", attrib, sep=""))
|
||||
}
|
||||
}
|
||||
|
||||
# write any children
|
||||
if (length(tag$children) > 0) {
|
||||
|
||||
# special case for a single child text node (skip newlines and indentation)
|
||||
if ((length(tag$children) == 1) && is.character(tag$children[[1]]) ) {
|
||||
if (is.null(context) || is.null(context$filter)
|
||||
|| context$filter(tag$children[[1]])) {
|
||||
text <- normalizeText(tag$children[[1]])
|
||||
textWriter(paste(">", text, "</", tag$name, ">\n", sep=""))
|
||||
}
|
||||
}
|
||||
else {
|
||||
textWriter(">\n")
|
||||
tagWriteChildren(tag, textWriter, indent+1, context)
|
||||
textWriter(paste(indentText, "</", tag$name, ">\n", sep=""))
|
||||
}
|
||||
}
|
||||
else {
|
||||
# only self-close void elements
|
||||
# (see: http://dev.w3.org/html5/spec/single-page.html#void-elements)
|
||||
if (tag$name %in% c("area", "base", "br", "col", "command", "embed", "hr",
|
||||
"img", "input", "keygen", "link", "meta", "param",
|
||||
"source", "track", "wbr")) {
|
||||
textWriter("/>\n")
|
||||
}
|
||||
else {
|
||||
textWriter(paste("></", tag$name, ">\n", sep=""))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
# environment used to store all available tags
|
||||
#' @export
|
||||
tags <- new.env()
|
||||
tags$a <- function(...) tag("a", list(...))
|
||||
tags$abbr <- function(...) tag("abbr", list(...))
|
||||
tags$address <- function(...) tag("address", list(...))
|
||||
tags$area <- function(...) tag("area", list(...))
|
||||
tags$article <- function(...) tag("article", list(...))
|
||||
tags$aside <- function(...) tag("aside", list(...))
|
||||
tags$audio <- function(...) tag("audio", list(...))
|
||||
tags$b <- function(...) tag("b", list(...))
|
||||
tags$base <- function(...) tag("base", list(...))
|
||||
tags$bdi <- function(...) tag("bdi", list(...))
|
||||
tags$bdo <- function(...) tag("bdo", list(...))
|
||||
tags$blockquote <- function(...) tag("blockquote", list(...))
|
||||
tags$body <- function(...) tag("body", list(...))
|
||||
tags$br <- function(...) tag("br", list(...))
|
||||
tags$button <- function(...) tag("button", list(...))
|
||||
tags$canvas <- function(...) tag("canvas", list(...))
|
||||
tags$caption <- function(...) tag("caption", list(...))
|
||||
tags$cite <- function(...) tag("cite", list(...))
|
||||
tags$code <- function(...) tag("code", list(...))
|
||||
tags$col <- function(...) tag("col", list(...))
|
||||
tags$colgroup <- function(...) tag("colgroup", list(...))
|
||||
tags$command <- function(...) tag("command", list(...))
|
||||
tags$data <- function(...) tag("data", list(...))
|
||||
tags$datalist <- function(...) tag("datalist", list(...))
|
||||
tags$dd <- function(...) tag("dd", list(...))
|
||||
tags$del <- function(...) tag("del", list(...))
|
||||
tags$details <- function(...) tag("details", list(...))
|
||||
tags$dfn <- function(...) tag("dfn", list(...))
|
||||
tags$div <- function(...) tag("div", list(...))
|
||||
tags$dl <- function(...) tag("dl", list(...))
|
||||
tags$dt <- function(...) tag("dt", list(...))
|
||||
tags$em <- function(...) tag("em", list(...))
|
||||
tags$embed <- function(...) tag("embed", list(...))
|
||||
tags$eventsource <- function(...) tag("eventsource", list(...))
|
||||
tags$fieldset <- function(...) tag("fieldset", list(...))
|
||||
tags$figcaption <- function(...) tag("figcaption", list(...))
|
||||
tags$figure <- function(...) tag("figure", list(...))
|
||||
tags$footer <- function(...) tag("footer", list(...))
|
||||
tags$form <- function(...) tag("form", list(...))
|
||||
tags$h1 <- function(...) tag("h1", list(...))
|
||||
tags$h2 <- function(...) tag("h2", list(...))
|
||||
tags$h3 <- function(...) tag("h3", list(...))
|
||||
tags$h4 <- function(...) tag("h4", list(...))
|
||||
tags$h5 <- function(...) tag("h5", list(...))
|
||||
tags$h6 <- function(...) tag("h6", list(...))
|
||||
tags$head <- function(...) tag("head", list(...))
|
||||
tags$header <- function(...) tag("header", list(...))
|
||||
tags$hgroup <- function(...) tag("hgroup", list(...))
|
||||
tags$hr <- function(...) tag("hr", list(...))
|
||||
tags$html <- function(...) tag("html", list(...))
|
||||
tags$i <- function(...) tag("i", list(...))
|
||||
tags$iframe <- function(...) tag("iframe", list(...))
|
||||
tags$img <- function(...) tag("img", list(...))
|
||||
tags$input <- function(...) tag("input", list(...))
|
||||
tags$ins <- function(...) tag("ins", list(...))
|
||||
tags$kbd <- function(...) tag("kbd", list(...))
|
||||
tags$keygen <- function(...) tag("keygen", list(...))
|
||||
tags$label <- function(...) tag("label", list(...))
|
||||
tags$legend <- function(...) tag("legend", list(...))
|
||||
tags$li <- function(...) tag("li", list(...))
|
||||
tags$link <- function(...) tag("link", list(...))
|
||||
tags$mark <- function(...) tag("mark", list(...))
|
||||
tags$map <- function(...) tag("map", list(...))
|
||||
tags$menu <- function(...) tag("menu", list(...))
|
||||
tags$meta <- function(...) tag("meta", list(...))
|
||||
tags$meter <- function(...) tag("meter", list(...))
|
||||
tags$nav <- function(...) tag("nav", list(...))
|
||||
tags$noscript <- function(...) tag("noscript", list(...))
|
||||
tags$object <- function(...) tag("object", list(...))
|
||||
tags$ol <- function(...) tag("ol", list(...))
|
||||
tags$optgroup <- function(...) tag("optgroup", list(...))
|
||||
tags$option <- function(...) tag("option", list(...))
|
||||
tags$output <- function(...) tag("output", list(...))
|
||||
tags$p <- function(...) tag("p", list(...))
|
||||
tags$param <- function(...) tag("param", list(...))
|
||||
tags$pre <- function(...) tag("pre", list(...))
|
||||
tags$progress <- function(...) tag("progress", list(...))
|
||||
tags$q <- function(...) tag("q", list(...))
|
||||
tags$ruby <- function(...) tag("ruby", list(...))
|
||||
tags$rp <- function(...) tag("rp", list(...))
|
||||
tags$rt <- function(...) tag("rt", list(...))
|
||||
tags$s <- function(...) tag("s", list(...))
|
||||
tags$samp <- function(...) tag("samp", list(...))
|
||||
tags$script <- function(...) tag("script", list(...))
|
||||
tags$section <- function(...) tag("section", list(...))
|
||||
tags$select <- function(...) tag("select", list(...))
|
||||
tags$small <- function(...) tag("small", list(...))
|
||||
tags$source <- function(...) tag("source", list(...))
|
||||
tags$span <- function(...) tag("span", list(...))
|
||||
tags$strong <- function(...) tag("strong", list(...))
|
||||
tags$style <- function(...) tag("style", list(...))
|
||||
tags$sub <- function(...) tag("sub", list(...))
|
||||
tags$summary <- function(...) tag("summary", list(...))
|
||||
tags$sup <- function(...) tag("sup", list(...))
|
||||
tags$table <- function(...) tag("table", list(...))
|
||||
tags$tbody <- function(...) tag("tbody", list(...))
|
||||
tags$td <- function(...) tag("td", list(...))
|
||||
tags$textarea <- function(...) tag("textarea", list(...))
|
||||
tags$tfoot <- function(...) tag("tfoot", list(...))
|
||||
tags$th <- function(...) tag("th", list(...))
|
||||
tags$thead <- function(...) tag("thead", list(...))
|
||||
tags$time <- function(...) tag("time", list(...))
|
||||
tags$title <- function(...) tag("title", list(...))
|
||||
tags$tr <- function(...) tag("tr", list(...))
|
||||
tags$track <- function(...) tag("track", list(...))
|
||||
tags$u <- function(...) tag("u", list(...))
|
||||
tags$ul <- function(...) tag("ul", list(...))
|
||||
tags$var <- function(...) tag("var", list(...))
|
||||
tags$video <- function(...) tag("video", list(...))
|
||||
tags$wbr <- function(...) tag("wbr", list(...))
|
||||
|
||||
#' Mark Characters as HTML
|
||||
#'
|
||||
#' Marks the given text as HTML, which means the \link{tag} functions will know
|
||||
#' not to perform HTML escaping on it.
|
||||
#'
|
||||
#' @param text The text value to mark with HTML
|
||||
#' @param ... Any additional values to be converted to character and
|
||||
#' concatenated together
|
||||
#' @return The same value, but marked as HTML.
|
||||
#'
|
||||
#' @examples
|
||||
#' el <- div(HTML("I like <u>turtles</u>"))
|
||||
#' cat(as.character(el))
|
||||
#'
|
||||
#' @export
|
||||
HTML <- function(text, ...) {
|
||||
htmlText <- c(text, as.character(list(...)))
|
||||
htmlText <- paste(htmlText, collapse=" ")
|
||||
attr(htmlText, "html") <- TRUE
|
||||
htmlText
|
||||
}
|
||||
|
||||
|
||||
191
R/tar.R
Normal file
@@ -0,0 +1,191 @@
|
||||
# This file was pulled from the R code base as of
|
||||
# Thursday, November 22, 2012 at 6:24:55 AM UTC
|
||||
# and edited to remove everything but the copyright
|
||||
# header and untar2, and to make untar2 more tolerant
|
||||
# of the 'x' and 'g' extended block indicators, the
|
||||
# latter of which is used in tar files generated by
|
||||
# GitHub.
|
||||
|
||||
|
||||
# File src/library/utils/R/tar.R
|
||||
# Part of the R package, http://www.R-project.org
|
||||
#
|
||||
# Copyright (C) 1995-2012 The R Core Team
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# A copy of the GNU General Public License is available at
|
||||
# http://www.r-project.org/Licenses/
|
||||
|
||||
untar2 <- function(tarfile, files = NULL, list = FALSE, exdir = ".")
|
||||
{
|
||||
getOct <- function(x, offset, len)
|
||||
{
|
||||
x <- 0L
|
||||
for(i in offset + seq_len(len)) {
|
||||
z <- block[i]
|
||||
if(!as.integer(z)) break; # terminate on nul
|
||||
switch(rawToChar(z),
|
||||
" " = {},
|
||||
"0"=,"1"=,"2"=,"3"=,"4"=,"5"=,"6"=,"7"=
|
||||
{x <- 8*x + (as.integer(z)-48)},
|
||||
stop("invalid octal digit")
|
||||
)
|
||||
}
|
||||
x
|
||||
}
|
||||
|
||||
mydir.create <- function(path, ...) {
|
||||
## for Windows' sake
|
||||
path <- sub("[\\/]$", "", path)
|
||||
if(file_test("-d", path)) return()
|
||||
if(!dir.create(path, showWarnings = TRUE, recursive = TRUE, ...))
|
||||
stop(gettextf("failed to create directory %s", sQuote(path)),
|
||||
domain = NA)
|
||||
}
|
||||
|
||||
warn1 <- character()
|
||||
|
||||
## A tar file is a set of 512 byte records,
|
||||
## a header record followed by file contents (zero-padded).
|
||||
## See http://en.wikipedia.org/wiki/Tar_%28file_format%29
|
||||
if(is.character(tarfile) && length(tarfile) == 1L) {
|
||||
con <- gzfile(path.expand(tarfile), "rb") # reads compressed formats
|
||||
on.exit(close(con))
|
||||
} else if(inherits(tarfile, "connection")) con <- tarfile
|
||||
else stop("'tarfile' must be a character string or a connection")
|
||||
if (!missing(exdir)) {
|
||||
mydir.create(exdir)
|
||||
od <- setwd(exdir)
|
||||
on.exit(setwd(od), add = TRUE)
|
||||
}
|
||||
contents <- character()
|
||||
llink <- lname <- NULL
|
||||
repeat{
|
||||
block <- readBin(con, "raw", n = 512L)
|
||||
if(!length(block)) break
|
||||
if(length(block) < 512L) stop("incomplete block on file")
|
||||
if(all(block == 0)) break
|
||||
ns <- max(which(block[1:100] > 0))
|
||||
name <- rawToChar(block[seq_len(ns)])
|
||||
magic <- rawToChar(block[258:262])
|
||||
if ((magic == "ustar") && block[346] > 0) {
|
||||
ns <- max(which(block[346:500] > 0))
|
||||
prefix <- rawToChar(block[345+seq_len(ns)])
|
||||
name <- file.path(prefix, name)
|
||||
}
|
||||
## mode zero-padded 8 bytes (including nul) at 101
|
||||
## Aargh: bsdtar has this one incorrectly with 6 bytes+space
|
||||
mode <- as.octmode(getOct(block, 100, 8))
|
||||
size <- getOct(block, 124, 12)
|
||||
ts <- getOct(block, 136, 12)
|
||||
ft <- as.POSIXct(as.numeric(ts), origin="1970-01-01", tz="UTC")
|
||||
csum <- getOct(block, 148, 8)
|
||||
block[149:156] <- charToRaw(" ")
|
||||
xx <- as.integer(block)
|
||||
checksum <- sum(xx) %% 2^24 # 6 bytes
|
||||
if(csum != checksum) {
|
||||
## try it with signed bytes.
|
||||
checksum <- sum(ifelse(xx > 127, xx - 128, xx)) %% 2^24 # 6 bytes
|
||||
if(csum != checksum)
|
||||
warning(gettextf("checksum error for entry '%s'", name),
|
||||
domain = NA)
|
||||
}
|
||||
type <- block[157L]
|
||||
ctype <- rawToChar(type)
|
||||
if(type == 0L || ctype == "0") {
|
||||
if(!is.null(lname)) {name <- lname; lname <- NULL}
|
||||
contents <- c(contents, name)
|
||||
remain <- size
|
||||
dothis <- !list
|
||||
if(dothis && length(files)) dothis <- name %in% files
|
||||
if(dothis) {
|
||||
mydir.create(dirname(name))
|
||||
out <- file(name, "wb")
|
||||
}
|
||||
for(i in seq_len(ceiling(size/512L))) {
|
||||
block <- readBin(con, "raw", n = 512L)
|
||||
if(length(block) < 512L)
|
||||
stop("incomplete block on file")
|
||||
if (dothis) {
|
||||
writeBin(block[seq_len(min(512L, remain))], out)
|
||||
remain <- remain - 512L
|
||||
}
|
||||
}
|
||||
if(dothis) {
|
||||
close(out)
|
||||
Sys.chmod(name, mode, FALSE) # override umask
|
||||
Sys.setFileTime(name, ft)
|
||||
}
|
||||
} else if(ctype %in% c("1", "2")) { # hard and symbolic links
|
||||
contents <- c(contents, name)
|
||||
ns <- max(which(block[158:257] > 0))
|
||||
name2 <- rawToChar(block[157L + seq_len(ns)])
|
||||
if(!is.null(lname)) {name <- lname; lname <- NULL}
|
||||
if(!is.null(llink)) {name2 <- llink; llink <- NULL}
|
||||
if(!list) {
|
||||
if(ctype == "1") {
|
||||
if (!file.link(name2, name)) { # will give a warning
|
||||
## link failed, so try a file copy
|
||||
if(file.copy(name2, name))
|
||||
warn1 <- c(warn1, "restoring hard link as a file copy")
|
||||
else
|
||||
warning(gettextf("failed to copy %s to %s", sQuote(name2), sQuote(name)), domain = NA)
|
||||
}
|
||||
} else {
|
||||
if(.Platform$OS.type == "windows") {
|
||||
## this will not work for links to dirs
|
||||
from <- file.path(dirname(name), name2)
|
||||
if (!file.copy(from, name))
|
||||
warning(gettextf("failed to copy %s to %s", sQuote(from), sQuote(name)), domain = NA)
|
||||
else
|
||||
warn1 <- c(warn1, "restoring symbolic link as a file copy")
|
||||
} else {
|
||||
if(!file.symlink(name2, name)) { # will give a warning
|
||||
## so try a file copy: will not work for links to dirs
|
||||
from <- file.path(dirname(name), name2)
|
||||
if (file.copy(from, name))
|
||||
warn1 <- c(warn1, "restoring symbolic link as a file copy")
|
||||
else
|
||||
warning(gettextf("failed to copy %s to %s", sQuote(from), sQuote(name)), domain = NA)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(ctype == "5") {
|
||||
contents <- c(contents, name)
|
||||
if(!list) {
|
||||
mydir.create(name)
|
||||
Sys.chmod(name, mode, TRUE) # FIXME: check result
|
||||
## no point is setting time, as dir will be populated later.
|
||||
}
|
||||
} else if(ctype %in% c("L", "K")) {
|
||||
## This is a GNU extension that should no longer be
|
||||
## in use, but it is.
|
||||
name_size <- 512L * ceiling(size/512L)
|
||||
block <- readBin(con, "raw", n = name_size)
|
||||
if(length(block) < name_size)
|
||||
stop("incomplete block on file")
|
||||
ns <- max(which(block > 0)) # size on file may or may not include final nul
|
||||
if(ctype == "L")
|
||||
lname <- rawToChar(block[seq_len(ns)])
|
||||
else
|
||||
llink <- rawToChar(block[seq_len(ns)])
|
||||
} else if(ctype %in% c("x", "g")) {
|
||||
readBin(con, "raw", n = 512L*ceiling(size/512L))
|
||||
} else stop("unsupported entry type ", sQuote(ctype))
|
||||
}
|
||||
if(length(warn1)) {
|
||||
warn1 <- unique(warn1)
|
||||
for (w in warn1) warning(w, domain = NA)
|
||||
}
|
||||
if(list) contents else invisible(0L)
|
||||
}
|
||||
72
R/timer.R
Normal file
@@ -0,0 +1,72 @@
|
||||
# Return the current time, in milliseconds from epoch, with
|
||||
# unspecified time zone.
|
||||
now <- function() {
|
||||
as.numeric(Sys.time()) * 1000
|
||||
}
|
||||
|
||||
TimerCallbacks <- setRefClass(
|
||||
'TimerCallbacks',
|
||||
fields = list(
|
||||
.nextId = 'integer',
|
||||
.funcs = 'Map',
|
||||
.times = 'data.frame'
|
||||
),
|
||||
methods = list(
|
||||
initialize = function() {
|
||||
.nextId <<- 0L
|
||||
},
|
||||
clear = function() {
|
||||
.nextId <<- 0L
|
||||
.funcs$clear()
|
||||
.times <<- data.frame()
|
||||
},
|
||||
schedule = function(millis, func) {
|
||||
id <- .nextId
|
||||
.nextId <<- .nextId + 1L
|
||||
|
||||
t <- now()
|
||||
|
||||
# TODO: Horribly inefficient, use a heap instead
|
||||
.times <<- rbind(.times, data.frame(time=t+millis,
|
||||
scheduled=t,
|
||||
id=id))
|
||||
.times <<- .times[order(.times$time),]
|
||||
|
||||
.funcs$set(as.character(id), func)
|
||||
|
||||
return(id)
|
||||
},
|
||||
timeToNextEvent = function() {
|
||||
if (dim(.times)[1] == 0)
|
||||
return(Inf)
|
||||
return(.times[1, 'time'] - now())
|
||||
},
|
||||
takeElapsed = function() {
|
||||
t <- now()
|
||||
elapsed <- .times$time < now()
|
||||
result <- .times[elapsed,]
|
||||
.times <<- .times[!elapsed,]
|
||||
|
||||
# TODO: Examine scheduled column to check if any funny business
|
||||
# has occurred with the system clock (e.g. if scheduled
|
||||
# is later than now())
|
||||
|
||||
return(result)
|
||||
},
|
||||
executeElapsed = function() {
|
||||
elapsed <- takeElapsed()
|
||||
if (length(elapsed) == 0)
|
||||
return(FALSE)
|
||||
|
||||
for (id in elapsed$id) {
|
||||
thisFunc <- .funcs$remove(as.character(id))
|
||||
# TODO: Catch exception, and...?
|
||||
# TODO: Detect NULL, and...?
|
||||
thisFunc()
|
||||
}
|
||||
return(TRUE)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
timerCallbacks <- TimerCallbacks$new()
|
||||
104
R/utils.R
Normal file
@@ -0,0 +1,104 @@
|
||||
#' Make a random number generator repeatable
|
||||
#'
|
||||
#' Given a function that generates random data, returns a wrapped version of
|
||||
#' that function that always uses the same seed when called. The seed to use can
|
||||
#' be passed in explicitly if desired; otherwise, a random number is used.
|
||||
#'
|
||||
#' @param rngfunc The function that is affected by the R session's seed.
|
||||
#' @param seed The seed to set every time the resulting function is called.
|
||||
#' @return A repeatable version of the function that was passed in.
|
||||
#'
|
||||
#' @note When called, the returned function attempts to preserve the R session's
|
||||
#' current seed by snapshotting and restoring
|
||||
#' \code{\link[base]{.Random.seed}}.
|
||||
#'
|
||||
#' @examples
|
||||
#' rnormA <- repeatable(rnorm)
|
||||
#' rnormB <- repeatable(rnorm)
|
||||
#' rnormA(3) # [1] 1.8285879 -0.7468041 -0.4639111
|
||||
#' 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 = runif(1, 0, .Machine$integer.max)) {
|
||||
force(seed)
|
||||
|
||||
function(...) {
|
||||
# When we exit, restore the seed to its original state
|
||||
if (exists('.Random.seed', where=globalenv())) {
|
||||
currentSeed <- get('.Random.seed', pos=globalenv())
|
||||
on.exit(assign('.Random.seed', currentSeed, pos=globalenv()))
|
||||
}
|
||||
else {
|
||||
on.exit(rm('.Random.seed', pos=globalenv()))
|
||||
}
|
||||
|
||||
set.seed(seed)
|
||||
|
||||
do.call(rngfunc, list(...))
|
||||
}
|
||||
}
|
||||
|
||||
`%OR%` <- function(x, y) {
|
||||
ifelse(is.null(x) || is.na(x), y, x)
|
||||
}
|
||||
|
||||
`%AND%` <- function(x, y) {
|
||||
if (!is.null(x) && !is.na(x))
|
||||
if (!is.null(y) && !is.na(y))
|
||||
return(y)
|
||||
return(NULL)
|
||||
}
|
||||
|
||||
`%.%` <- function(x, y) {
|
||||
paste(x, y, sep='')
|
||||
}
|
||||
|
||||
knownContentTypes <- Map$new()
|
||||
knownContentTypes$mset(
|
||||
html='text/html; charset=UTF-8',
|
||||
htm='text/html; charset=UTF-8',
|
||||
js='text/javascript',
|
||||
css='text/css',
|
||||
png='image/png',
|
||||
jpg='image/jpeg',
|
||||
jpeg='image/jpeg',
|
||||
gif='image/gif',
|
||||
svg='image/svg+xml',
|
||||
txt='text/plain',
|
||||
pdf='application/pdf',
|
||||
ps='application/postscript',
|
||||
xml='application/xml',
|
||||
m3u='audio/x-mpegurl',
|
||||
m4a='audio/mp4a-latm',
|
||||
m4b='audio/mp4a-latm',
|
||||
m4p='audio/mp4a-latm',
|
||||
mp3='audio/mpeg',
|
||||
wav='audio/x-wav',
|
||||
m4u='video/vnd.mpegurl',
|
||||
m4v='video/x-m4v',
|
||||
mp4='video/mp4',
|
||||
mpeg='video/mpeg',
|
||||
mpg='video/mpeg',
|
||||
avi='video/x-msvideo',
|
||||
mov='video/quicktime',
|
||||
ogg='application/ogg',
|
||||
swf='application/x-shockwave-flash',
|
||||
doc='application/msword',
|
||||
xls='application/vnd.ms-excel',
|
||||
ppt='application/vnd.ms-powerpoint',
|
||||
xlsx='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
xltx='application/vnd.openxmlformats-officedocument.spreadsheetml.template',
|
||||
potx='application/vnd.openxmlformats-officedocument.presentationml.template',
|
||||
ppsx='application/vnd.openxmlformats-officedocument.presentationml.slideshow',
|
||||
pptx='application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
sldx='application/vnd.openxmlformats-officedocument.presentationml.slide',
|
||||
docx='application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
dotx='application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||||
xlam='application/vnd.ms-excel.addin.macroEnabled.12',
|
||||
xlsb='application/vnd.ms-excel.sheet.binary.macroEnabled.12')
|
||||
|
||||
getContentType <- function(ext, defaultType='application/octet-stream') {
|
||||
knownContentTypes$get(tolower(ext)) %OR% defaultType
|
||||
}
|
||||
48
README.md
@@ -1,10 +1,40 @@
|
||||
# Shiny
|
||||
### A web framework for R (eventually--Ruby for now)
|
||||
# Shiny
|
||||
|
||||
```sh
|
||||
sudo apt-get install ruby1.9.1 ruby1.9.1-dev
|
||||
sudo gem install bundler
|
||||
cd shiny
|
||||
bundle install --path vendor
|
||||
./run.sh
|
||||
```
|
||||
Shiny is a new package from RStudio that makes it incredibly easy to build interactive web applications with R.
|
||||
|
||||
For an introduction and examples, visit the [Shiny homepage](http://www.rstudio.com/shiny/).
|
||||
|
||||
## Features
|
||||
|
||||
* Build useful web applications with only a few lines of code—no JavaScript required.
|
||||
* Shiny applications are automatically "live" in the same way that spreadsheets are live. Outputs change instantly as users modify inputs, without requiring a reload of the browser.
|
||||
* Shiny user interfaces can be built entirely using R, or can be written directly in HTML, CSS, and JavaScript for more flexibility.
|
||||
* Works in any R environment (Console R, Rgui for Windows or Mac, ESS, StatET, RStudio, etc.)
|
||||
* Attractive default UI theme based on [Twitter Bootstrap](http://twitter.github.com/bootstrap).
|
||||
* A highly customizable slider widget with built-in support for animation.
|
||||
* Pre-built output widgets for displaying plots, tables, and printed output of R objects.
|
||||
* Fast bidirectional communication between the web browser and R using the [websockets](http://illposed.net/websockets.html) package.
|
||||
* Uses a [reactive](http://en.wikipedia.org/wiki/Reactive_programming) programming model that eliminates messy event handling code, so you can focus on the code that really matters.
|
||||
* Develop and redistribute your own Shiny widgets that other developers can easily drop into their own applications (coming soon!).
|
||||
|
||||
## Installation
|
||||
|
||||
From an R console:
|
||||
|
||||
```r
|
||||
options(repos=c(RStudio="http://rstudio.org/_packages", getOption("repos")))
|
||||
install.packages("shiny")
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
To learn more we highly recommend you check out the [Shiny Tutorial](http://rstudio.github.com/shiny/tutorial). The tutorial explains the framework in-depth, walks you through building a simple application, and includes extensive annotated examples.
|
||||
|
||||
We hope you enjoy using Shiny. As you learn more and work with the package please [let us know](https://github.com/rstudio/shiny/issues) what problems you encounter and how you'd like to see Shiny evolve.
|
||||
|
||||
## License
|
||||
|
||||
The shiny package is licensed under the GPLv3. See these files in the inst directory for additional details:
|
||||
|
||||
- COPYING - shiny package license (GPLv3)
|
||||
- NOTICE - Copyright notices for additional included software
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
library(digest)
|
||||
|
||||
input <- Observable$new(function() {
|
||||
str <- get.shiny.input('input1')
|
||||
if (get.shiny.input('addnewline'))
|
||||
str <- paste(str, "\n", sep='')
|
||||
return(str)
|
||||
})
|
||||
|
||||
define.shiny.output('md5_hash', function() {
|
||||
digest(input$get.value(), algo='md5', serialize=F)
|
||||
})
|
||||
define.shiny.output('sha1_hash', function() {
|
||||
digest(input$get.value(), algo='sha1', serialize=F)
|
||||
})
|
||||
@@ -1,26 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<script src="shared/jquery-1.7.2.js" type="text/javascript"></script>
|
||||
<script src="shared/shiny.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" type="text/css" href="shared/shiny.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Example 2: Hash Calculation</h1>
|
||||
|
||||
<p>
|
||||
<label>Input:</label><br />
|
||||
<input name="input1" value="Hello World!"/>
|
||||
<input type="checkbox" name="addnewline" checked="checked"/> Append newline
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label>MD5:</label><br />
|
||||
<pre id="md5_hash" class="live-text"></pre>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label>SHA-1:</label><br />
|
||||
<pre id="sha1_hash" class="live-text"></pre>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,24 +0,0 @@
|
||||
data <- Observable$new(function() {
|
||||
# Choose a distribution function
|
||||
dist <- switch(get.shiny.input('dist'),
|
||||
norm = rnorm,
|
||||
unif = runif,
|
||||
lnorm = rlnorm,
|
||||
exp = rexp,
|
||||
rnorm)
|
||||
|
||||
# Generate n values from the distribution function
|
||||
dist(max(1, get.shiny.input('n')))
|
||||
})
|
||||
|
||||
define.shiny.plot('plot1', function() {
|
||||
dist <- get.shiny.input('dist')
|
||||
n <- get.shiny.input('n')
|
||||
|
||||
hist(data$get.value(),
|
||||
main=paste('r', dist, '(', n, ')', sep=''))
|
||||
}, width=600, height=300)
|
||||
|
||||
define.shiny.table('table1', function() {
|
||||
data.frame(x=data$get.value())
|
||||
})
|
||||
19
hash.rb
@@ -1,19 +0,0 @@
|
||||
require 'shiny'
|
||||
require 'digest/sha1'
|
||||
require 'digest/md5'
|
||||
|
||||
shinyapp = ShinyApp.new
|
||||
|
||||
input1 = React::ObservableValue.new {
|
||||
shinyapp.session.get('input1') + (shinyapp.session.get('addnewline') ? "\n" : '')
|
||||
}
|
||||
|
||||
shinyapp.define_output('md5_hash') do
|
||||
Digest::MD5.hexdigest(input1.value)
|
||||
end
|
||||
|
||||
shinyapp.define_output('sha1_hash') do
|
||||
Digest::SHA1.hexdigest(input1.value)
|
||||
end
|
||||
|
||||
shinyapp.run
|
||||
678
inst/COPYING
Normal file
@@ -0,0 +1,678 @@
|
||||
The shiny package is licensed to you under the GPLv3, the terms of
|
||||
which are included below. The markdown pacakge includes other open
|
||||
source software whose license terms can be found in the file NOTICE.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
264
inst/NOTICE
Normal file
@@ -0,0 +1,264 @@
|
||||
The shiny package inludes other open source software components. The following
|
||||
is a list of these components (full copies of the license agreements used by
|
||||
these components are included below):
|
||||
|
||||
- jQuery
|
||||
- Bootstrap
|
||||
- jslider
|
||||
|
||||
|
||||
jQuery License
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Copyright (c) 2012 jQuery Foundation and other contributors,
|
||||
http://jquery.com/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
Bootstrap License
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
jslider License
|
||||
----------------------------------------------------------------------
|
||||
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2012 Egor Khmelev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
20
inst/examples/01_hello/server.R
Normal file
@@ -0,0 +1,20 @@
|
||||
library(shiny)
|
||||
|
||||
# Define server logic required to generate and plot a random distribution
|
||||
shinyServer(function(input, output) {
|
||||
|
||||
# Function that generates a plot of the distribution. The function
|
||||
# is wrapped in a call to reactivePlot to indicate that:
|
||||
#
|
||||
# 1) It is "reactive" and therefore should be automatically
|
||||
# re-executed when inputs change
|
||||
# 2) Its output type is a plot
|
||||
#
|
||||
output$distPlot <- reactivePlot(function() {
|
||||
|
||||
# generate an rnorm distribution and plot it
|
||||
dist <- rnorm(input$obs)
|
||||
hist(dist)
|
||||
})
|
||||
|
||||
})
|
||||
22
inst/examples/01_hello/ui.R
Normal file
@@ -0,0 +1,22 @@
|
||||
library(shiny)
|
||||
|
||||
# Define UI for application that plots random distributions
|
||||
shinyUI(pageWithSidebar(
|
||||
|
||||
# Application title
|
||||
headerPanel("Hello Shiny!"),
|
||||
|
||||
# Sidebar with a slider input for number of observations
|
||||
sidebarPanel(
|
||||
sliderInput("obs",
|
||||
"Number of observations:",
|
||||
min = 0,
|
||||
max = 1000,
|
||||
value = 500)
|
||||
),
|
||||
|
||||
# Show a plot of the generated distribution
|
||||
mainPanel(
|
||||
plotOutput("distPlot")
|
||||
)
|
||||
))
|
||||
25
inst/examples/02_text/server.R
Normal file
@@ -0,0 +1,25 @@
|
||||
library(shiny)
|
||||
library(datasets)
|
||||
|
||||
# Define server logic required to summarize and view the selected dataset
|
||||
shinyServer(function(input, output) {
|
||||
|
||||
# Return the requested dataset
|
||||
datasetInput <- reactive(function() {
|
||||
switch(input$dataset,
|
||||
"rock" = rock,
|
||||
"pressure" = pressure,
|
||||
"cars" = cars)
|
||||
})
|
||||
|
||||
# Generate a summary of the dataset
|
||||
output$summary <- reactivePrint(function() {
|
||||
dataset <- datasetInput()
|
||||
summary(dataset)
|
||||
})
|
||||
|
||||
# Show the first "n" observations
|
||||
output$view <- reactiveTable(function() {
|
||||
head(datasetInput(), n = input$obs)
|
||||
})
|
||||
})
|
||||
25
inst/examples/02_text/ui.R
Normal file
@@ -0,0 +1,25 @@
|
||||
library(shiny)
|
||||
|
||||
# Define UI for dataset viewer application
|
||||
shinyUI(pageWithSidebar(
|
||||
|
||||
# Application title
|
||||
headerPanel("Shiny Text"),
|
||||
|
||||
# Sidebar with controls to select a dataset and specify the number
|
||||
# of observations to view
|
||||
sidebarPanel(
|
||||
selectInput("dataset", "Choose a dataset:",
|
||||
choices = c("rock", "pressure", "cars")),
|
||||
|
||||
numericInput("obs", "Number of observations to view:", 10)
|
||||
),
|
||||
|
||||
# Show a summary of the dataset and an HTML table with the requested
|
||||
# number of observations
|
||||
mainPanel(
|
||||
verbatimTextOutput("summary"),
|
||||
|
||||
tableOutput("view")
|
||||
)
|
||||
))
|
||||
50
inst/examples/03_reactivity/server.R
Normal file
@@ -0,0 +1,50 @@
|
||||
library(shiny)
|
||||
library(datasets)
|
||||
|
||||
# Define server logic required to summarize and view the selected dataset
|
||||
shinyServer(function(input, output) {
|
||||
|
||||
# By declaring databaseInput as a reactive function we ensure that:
|
||||
#
|
||||
# 1) It is only called when the inputs it depends on changes
|
||||
# 2) The computation and result are shared by all the callers (it
|
||||
# only executes a single time)
|
||||
# 3) When the inputs change and the function is re-executed, the
|
||||
# new result is compared to the previous result; if the two are
|
||||
# identical, then the callers are not notified
|
||||
#
|
||||
datasetInput <- reactive(function() {
|
||||
switch(input$dataset,
|
||||
"rock" = rock,
|
||||
"pressure" = pressure,
|
||||
"cars" = cars)
|
||||
})
|
||||
|
||||
# The output$caption is computed based on a reactive function that
|
||||
# returns input$caption. When the user changes the "caption" field:
|
||||
#
|
||||
# 1) This function is automatically called to recompute the output
|
||||
# 2) The new caption is pushed back to the browser for re-display
|
||||
#
|
||||
# Note that because the data-oriented reactive functions below don't
|
||||
# depend on input$caption, those functions are NOT called when
|
||||
# input$caption changes.
|
||||
output$caption <- reactiveText(function() {
|
||||
input$caption
|
||||
})
|
||||
|
||||
# The output$summary depends on the datasetInput reactive function,
|
||||
# so will be re-executed whenever datasetInput is re-executed
|
||||
# (i.e. whenever the input$dataset changes)
|
||||
output$summary <- reactivePrint(function() {
|
||||
dataset <- datasetInput()
|
||||
summary(dataset)
|
||||
})
|
||||
|
||||
# The output$view depends on both the databaseInput reactive function
|
||||
# and input$obs, so will be re-executed whenever input$dataset or
|
||||
# input$obs is changed.
|
||||
output$view <- reactiveTable(function() {
|
||||
head(datasetInput(), n = input$obs)
|
||||
})
|
||||
})
|
||||
32
inst/examples/03_reactivity/ui.R
Normal file
@@ -0,0 +1,32 @@
|
||||
library(shiny)
|
||||
|
||||
# Define UI for dataset viewer application
|
||||
shinyUI(pageWithSidebar(
|
||||
|
||||
# Application title
|
||||
headerPanel("Reactivity"),
|
||||
|
||||
# Sidebar with controls to provide a caption, select a dataset, and
|
||||
# specify the number of observations to view. Note that changes made
|
||||
# to the caption in the textInput control are updated in the output
|
||||
# area immediately as you type
|
||||
sidebarPanel(
|
||||
textInput("caption", "Caption:", "Data Summary"),
|
||||
|
||||
selectInput("dataset", "Choose a dataset:",
|
||||
choices = c("rock", "pressure", "cars")),
|
||||
|
||||
numericInput("obs", "Number of observations to view:", 10)
|
||||
),
|
||||
|
||||
|
||||
# Show the caption, a summary of the dataset and an HTML table with
|
||||
# the requested number of observations
|
||||
mainPanel(
|
||||
h3(textOutput("caption")),
|
||||
|
||||
verbatimTextOutput("summary"),
|
||||
|
||||
tableOutput("view")
|
||||
)
|
||||
))
|
||||
32
inst/examples/04_mpg/server.R
Normal file
@@ -0,0 +1,32 @@
|
||||
library(shiny)
|
||||
library(datasets)
|
||||
|
||||
# We tweak the "am" field to have nicer factor labels. Since this doesn't
|
||||
# rely on any user inputs we can do this once at startup and then use the
|
||||
# value throughout the lifetime of the application
|
||||
mpgData <- mtcars
|
||||
mpgData$am <- factor(mpgData$am, labels = c("Automatic", "Manual"))
|
||||
|
||||
|
||||
# Define server logic required to plot various variables against mpg
|
||||
shinyServer(function(input, output) {
|
||||
|
||||
# Compute the forumla text in a reactive function since it is
|
||||
# shared by the output$caption and output$mpgPlot functions
|
||||
formulaText <- reactive(function() {
|
||||
paste("mpg ~", input$variable)
|
||||
})
|
||||
|
||||
# Return the formula text for printing as a caption
|
||||
output$caption <- reactiveText(function() {
|
||||
formulaText()
|
||||
})
|
||||
|
||||
# Generate a plot of the requested variable against mpg and only
|
||||
# include outliers if requested
|
||||
output$mpgPlot <- reactivePlot(function() {
|
||||
boxplot(as.formula(formulaText()),
|
||||
data = mpgData,
|
||||
outline = input$outliers)
|
||||
})
|
||||
})
|
||||
26
inst/examples/04_mpg/ui.R
Normal file
@@ -0,0 +1,26 @@
|
||||
library(shiny)
|
||||
|
||||
# Define UI for miles per gallon application
|
||||
shinyUI(pageWithSidebar(
|
||||
|
||||
# Application title
|
||||
headerPanel("Miles Per Gallon"),
|
||||
|
||||
# Sidebar with controls to select the variable to plot against mpg
|
||||
# and to specify whether outliers should be included
|
||||
sidebarPanel(
|
||||
selectInput("variable", "Variable:",
|
||||
c("Cylinders" = "cyl",
|
||||
"Transmission" = "am",
|
||||
"Gears" = "gear")),
|
||||
|
||||
checkboxInput("outliers", "Show outliers", FALSE)
|
||||
),
|
||||
|
||||
# Show the caption and plot of the requested variable against mpg
|
||||
mainPanel(
|
||||
h3(textOutput("caption")),
|
||||
|
||||
plotOutput("mpgPlot")
|
||||
)
|
||||
))
|
||||
28
inst/examples/05_sliders/server.R
Normal file
@@ -0,0 +1,28 @@
|
||||
library(shiny)
|
||||
|
||||
# Define server logic for slider examples
|
||||
shinyServer(function(input, output) {
|
||||
|
||||
# Reactive function to compose a data frame containing all of the values
|
||||
sliderValues <- reactive(function() {
|
||||
|
||||
# Compose data frame
|
||||
data.frame(
|
||||
Name = c("Integer",
|
||||
"Decimal",
|
||||
"Range",
|
||||
"Custom Format",
|
||||
"Animation"),
|
||||
Value = as.character(c(input$integer,
|
||||
input$decimal,
|
||||
paste(input$range, collapse=' '),
|
||||
input$format,
|
||||
input$animation)),
|
||||
stringsAsFactors=FALSE)
|
||||
})
|
||||
|
||||
# Show the values using an HTML table
|
||||
output$values <- reactiveTable(function() {
|
||||
sliderValues()
|
||||
})
|
||||
})
|
||||
37
inst/examples/05_sliders/ui.R
Normal file
@@ -0,0 +1,37 @@
|
||||
library(shiny)
|
||||
|
||||
# Define UI for slider demo application
|
||||
shinyUI(pageWithSidebar(
|
||||
|
||||
# Application title
|
||||
headerPanel("Sliders"),
|
||||
|
||||
# Sidebar with sliders that demonstrate various available options
|
||||
sidebarPanel(
|
||||
# Simple integer interval
|
||||
sliderInput("integer", "Integer:",
|
||||
min=0, max=1000, value=500),
|
||||
|
||||
# Decimal interval with step value
|
||||
sliderInput("decimal", "Decimal:",
|
||||
min = 0, max = 1, value = 0.5, step= 0.1),
|
||||
|
||||
# Specification of range within an interval
|
||||
sliderInput("range", "Range:",
|
||||
min = 1, max = 1000, value = c(200,500)),
|
||||
|
||||
# Provide a custom currency format for value display, with basic animation
|
||||
sliderInput("format", "Custom Format:",
|
||||
min = 0, max = 10000, value = 0, step = 2500,
|
||||
format="$#,##0", locale="us", animate=TRUE),
|
||||
|
||||
# Animation with custom interval (in ms) to control speed, plus looping
|
||||
sliderInput("animation", "Looping Animation:", 1, 2000, 1, step = 10,
|
||||
animate=animationOptions(interval=300, loop=TRUE))
|
||||
),
|
||||
|
||||
# Show a table summarizing the values entered
|
||||
mainPanel(
|
||||
tableOutput("values")
|
||||
)
|
||||
))
|
||||
42
inst/examples/06_tabsets/server.R
Normal file
@@ -0,0 +1,42 @@
|
||||
library(shiny)
|
||||
|
||||
# Define server logic for random distribution application
|
||||
shinyServer(function(input, output) {
|
||||
|
||||
# Reactive function to generate the requested distribution. This is
|
||||
# called whenever the inputs change. The output functions defined
|
||||
# below then all use the value computed from this function
|
||||
data <- reactive(function() {
|
||||
dist <- switch(input$dist,
|
||||
norm = rnorm,
|
||||
unif = runif,
|
||||
lnorm = rlnorm,
|
||||
exp = rexp,
|
||||
rnorm)
|
||||
|
||||
dist(input$n)
|
||||
})
|
||||
|
||||
# Generate a plot of the data. Also uses the inputs to build the
|
||||
# plot label. Note that the dependencies on both the inputs and
|
||||
# the data reactive function are both tracked, and all functions
|
||||
# are called in the sequence implied by the dependency graph
|
||||
output$plot <- reactivePlot(function() {
|
||||
dist <- input$dist
|
||||
n <- input$n
|
||||
|
||||
hist(data(),
|
||||
main=paste('r', dist, '(', n, ')', sep=''))
|
||||
})
|
||||
|
||||
# Generate a summary of the data
|
||||
output$summary <- reactivePrint(function() {
|
||||
summary(data())
|
||||
})
|
||||
|
||||
# Generate an HTML table view of the data
|
||||
output$table <- reactiveTable(function() {
|
||||
data.frame(x=data())
|
||||
})
|
||||
|
||||
})
|
||||
36
inst/examples/06_tabsets/ui.R
Normal file
@@ -0,0 +1,36 @@
|
||||
library(shiny)
|
||||
|
||||
# Define UI for random distribution application
|
||||
shinyUI(pageWithSidebar(
|
||||
|
||||
# Application title
|
||||
headerPanel("Tabsets"),
|
||||
|
||||
# Sidebar with controls to select the random distribution type
|
||||
# and number of observations to generate. Note the use of the br()
|
||||
# element to introduce extra vertical spacing
|
||||
sidebarPanel(
|
||||
radioButtons("dist", "Distribution type:",
|
||||
c("Normal" = "norm",
|
||||
"Uniform" = "unif",
|
||||
"Log-normal" = "lnorm",
|
||||
"Exponential" = "exp")),
|
||||
br(),
|
||||
|
||||
sliderInput("n",
|
||||
"Number of observations:",
|
||||
value = 500,
|
||||
min = 1,
|
||||
max = 1000)
|
||||
),
|
||||
|
||||
# Show a tabset that includes a plot, summary, and table view
|
||||
# of the generated distribution
|
||||
mainPanel(
|
||||
tabsetPanel(
|
||||
tabPanel("Plot", plotOutput("plot")),
|
||||
tabPanel("Summary", verbatimTextOutput("summary")),
|
||||
tabPanel("Table", tableOutput("table"))
|
||||
)
|
||||
)
|
||||
))
|
||||
25
inst/examples/07_widgets/server.R
Normal file
@@ -0,0 +1,25 @@
|
||||
library(shiny)
|
||||
library(datasets)
|
||||
|
||||
# Define server logic required to summarize and view the selected dataset
|
||||
shinyServer(function(input, output) {
|
||||
|
||||
# Return the requested dataset
|
||||
datasetInput <- reactive(function() {
|
||||
switch(input$dataset,
|
||||
"rock" = rock,
|
||||
"pressure" = pressure,
|
||||
"cars" = cars)
|
||||
})
|
||||
|
||||
# Generate a summary of the dataset
|
||||
output$summary <- reactivePrint(function() {
|
||||
dataset <- datasetInput()
|
||||
summary(dataset)
|
||||
})
|
||||
|
||||
# Show the first "n" observations
|
||||
output$view <- reactiveTable(function() {
|
||||
head(datasetInput(), n = input$obs)
|
||||
})
|
||||
})
|
||||
39
inst/examples/07_widgets/ui.R
Normal file
@@ -0,0 +1,39 @@
|
||||
library(shiny)
|
||||
|
||||
# Define UI for dataset viewer application
|
||||
shinyUI(pageWithSidebar(
|
||||
|
||||
# Application title.
|
||||
headerPanel("More Widgets"),
|
||||
|
||||
# Sidebar with controls to select a dataset and specify the number
|
||||
# of observations to view. The helpText function is also used to
|
||||
# include clarifying text. Most notably, the inclusion of a
|
||||
# submitButton defers the rendering of output until the user
|
||||
# explicitly clicks the button (rather than doing it immediately
|
||||
# when inputs change). This is useful if the computations required
|
||||
# to render output are inordinately time-consuming.
|
||||
sidebarPanel(
|
||||
selectInput("dataset", "Choose a dataset:",
|
||||
choices = c("rock", "pressure", "cars")),
|
||||
|
||||
numericInput("obs", "Number of observations to view:", 10),
|
||||
|
||||
helpText("Note: while the data view will show only the specified",
|
||||
"number of observations, the summary will still be based",
|
||||
"on the full dataset."),
|
||||
|
||||
submitButton("Update View")
|
||||
),
|
||||
|
||||
# Show a summary of the dataset and an HTML table with the requested
|
||||
# number of observations. Note the use of the h4 function to provide
|
||||
# an additional header above each output section.
|
||||
mainPanel(
|
||||
h4("Summary"),
|
||||
verbatimTextOutput("summary"),
|
||||
|
||||
h4("Observations"),
|
||||
tableOutput("view")
|
||||
)
|
||||
))
|
||||
42
inst/examples/08_html/server.R
Normal file
@@ -0,0 +1,42 @@
|
||||
library(shiny)
|
||||
|
||||
# Define server logic for random distribution application
|
||||
shinyServer(function(input, output) {
|
||||
|
||||
# Reactive function to generate the requested distribution. This is
|
||||
# called whenever the inputs change. The output functions defined
|
||||
# below then all used the value computed from this function
|
||||
data <- reactive(function() {
|
||||
dist <- switch(input$dist,
|
||||
norm = rnorm,
|
||||
unif = runif,
|
||||
lnorm = rlnorm,
|
||||
exp = rexp,
|
||||
rnorm)
|
||||
|
||||
dist(input$n)
|
||||
})
|
||||
|
||||
# Generate a plot of the data. Also uses the inputs to build the
|
||||
# plot label. Note that the dependencies on both the inputs and
|
||||
# the data reactive function are both tracked, and all functions
|
||||
# are called in the sequence implied by the dependency graph
|
||||
output$plot <- reactivePlot(function() {
|
||||
dist <- input$dist
|
||||
n <- input$n
|
||||
|
||||
hist(data(),
|
||||
main=paste('r', dist, '(', n, ')', sep=''))
|
||||
})
|
||||
|
||||
# Generate a summary of the data
|
||||
output$summary <- reactivePrint(function() {
|
||||
summary(data())
|
||||
})
|
||||
|
||||
# Generate an HTML table view of the data
|
||||
output$table <- reactiveTable(function() {
|
||||
data.frame(x=data())
|
||||
})
|
||||
|
||||
})
|
||||
@@ -1,12 +1,15 @@
|
||||
<html>
|
||||
<head>
|
||||
<script src="shared/jquery-1.7.2.js" type="text/javascript"></script>
|
||||
<script src="shared/shiny.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" type="text/css" href="shared/shiny.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Example 3: Distributions</h1>
|
||||
|
||||
<head>
|
||||
<script src="shared/jquery.js" type="text/javascript"></script>
|
||||
<script src="shared/shiny.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" type="text/css" href="shared/shiny.css"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h1>HTML UI</h1>
|
||||
|
||||
<p>
|
||||
<label>Distribution type:</label><br />
|
||||
<select name="dist">
|
||||
@@ -14,17 +17,22 @@
|
||||
<option value="unif">Uniform</option>
|
||||
<option value="lnorm">Log-normal</option>
|
||||
<option value="exp">Exponential</option>
|
||||
</select>
|
||||
</select>
|
||||
</p>
|
||||
|
||||
|
||||
<p>
|
||||
<label>Number of observations:</label><br />
|
||||
<input type="numeric" name="n" value="500" />
|
||||
</p>
|
||||
|
||||
<label>Number of observations:</label><br />
|
||||
<input type="number" name="n" value="500" min="1" max="1000" />
|
||||
|
||||
<div id="plot1" class="live-plot"></div>
|
||||
</p>
|
||||
|
||||
<pre id="summary" class="shiny-text-output"></pre>
|
||||
|
||||
<div id="table1" class="live-html"></div>
|
||||
<div id="plot" class="shiny-plot-output"
|
||||
style="width: 100%; height: 400px"></div>
|
||||
|
||||
<div id="table" class="shiny-html-output"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
18
inst/examples/09_upload/server.R
Normal file
@@ -0,0 +1,18 @@
|
||||
library(shiny)
|
||||
|
||||
shinyServer(function(input, output) {
|
||||
output$contents <- reactiveTable(function() {
|
||||
|
||||
# input$file1 will be NULL initially. After the user selects and uploads a
|
||||
# file, it will be a data frame with 'name', 'size', 'type', and 'data'
|
||||
# columns. The 'data' column will contain the local filenames where the data
|
||||
# can be found.
|
||||
|
||||
inFile <- input$file1
|
||||
|
||||
if (is.null(inFile))
|
||||
return(NULL)
|
||||
|
||||
read.csv(inFile$data, header=input$header, sep=input$sep, quote=input$quote)
|
||||
})
|
||||
})
|
||||
24
inst/examples/09_upload/ui.R
Normal file
@@ -0,0 +1,24 @@
|
||||
library(shiny)
|
||||
|
||||
shinyUI(pageWithSidebar(
|
||||
headerPanel("CSV Viewer"),
|
||||
sidebarPanel(
|
||||
fileInput('file1', 'Choose CSV File',
|
||||
accept=c('text/csv', 'text/comma-separated-values,text/plain')),
|
||||
tags$hr(),
|
||||
checkboxInput('header', 'Header', TRUE),
|
||||
radioButtons('sep', 'Separator',
|
||||
c(Comma=',',
|
||||
Semicolon=';',
|
||||
Tab='\t'),
|
||||
'Comma'),
|
||||
radioButtons('quote', 'Quote',
|
||||
c(None='',
|
||||
'Double Quote'='"',
|
||||
'Single Quote'="'"),
|
||||
'Double Quote')
|
||||
),
|
||||
mainPanel(
|
||||
tableOutput('contents')
|
||||
)
|
||||
))
|
||||
19
inst/examples/10_download/server.R
Normal file
@@ -0,0 +1,19 @@
|
||||
shinyServer(function(input, output) {
|
||||
datasetInput <- reactive(function() {
|
||||
switch(input$dataset,
|
||||
"rock" = rock,
|
||||
"pressure" = pressure,
|
||||
"cars" = cars)
|
||||
})
|
||||
|
||||
output$table <- reactiveTable(function() {
|
||||
datasetInput()
|
||||
})
|
||||
|
||||
output$downloadData <- downloadHandler(
|
||||
filename = function() { paste(input$dataset, '.csv', sep='') },
|
||||
content = function(file) {
|
||||
write.csv(datasetInput(), file)
|
||||
}
|
||||
)
|
||||
})
|
||||
11
inst/examples/10_download/ui.R
Normal file
@@ -0,0 +1,11 @@
|
||||
shinyUI(pageWithSidebar(
|
||||
headerPanel('Downloading Data'),
|
||||
sidebarPanel(
|
||||
selectInput("dataset", "Choose a dataset:",
|
||||
choices = c("rock", "pressure", "cars")),
|
||||
downloadButton('downloadData', 'Download')
|
||||
),
|
||||
mainPanel(
|
||||
tableOutput('table')
|
||||
)
|
||||
))
|
||||
12
inst/www/index.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<h1>No UI defined</h1>
|
||||
<p>Shiny couldn't find any UI for this application. We looked in:</p>
|
||||
<ul>
|
||||
<li><code>www/index.html</code></li>
|
||||
<li><code>ui.R</code></li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
1040
inst/www/shared/bootstrap/css/bootstrap-responsive.css
vendored
Normal file
9
inst/www/shared/bootstrap/css/bootstrap-responsive.min.css
vendored
Normal file
5624
inst/www/shared/bootstrap/css/bootstrap.css
vendored
Normal file
9
inst/www/shared/bootstrap/css/bootstrap.min.css
vendored
Normal file
BIN
inst/www/shared/bootstrap/img/glyphicons-halflings-white.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
inst/www/shared/bootstrap/img/glyphicons-halflings.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
2027
inst/www/shared/bootstrap/js/bootstrap.js
vendored
Normal file
6
inst/www/shared/bootstrap/js/bootstrap.min.js
vendored
Normal file
60
inst/www/shared/shiny.css
Normal file
@@ -0,0 +1,60 @@
|
||||
body.disconnected {
|
||||
background-color: #999;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
table.data {
|
||||
width: auto;
|
||||
}
|
||||
table.data td[align=right] {
|
||||
font-family: monospace;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.shiny-output-error {
|
||||
color: red;
|
||||
}
|
||||
.shiny-output-error:before {
|
||||
content: 'Error: ';
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.jslider {
|
||||
/* Fix jslider running into the control above it */
|
||||
margin-top: 18px;
|
||||
}
|
||||
.jslider-value {
|
||||
/* Remove box around jslider values on colored bg */
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.recalculating {
|
||||
opacity: 0.3;
|
||||
transition: opacity 250ms ease 500ms;
|
||||
-moz-transition: opacity 250ms ease 500ms;
|
||||
-webkit-transition: opacity 250ms ease 500ms;
|
||||
-o-transition: opacity 250ms ease 500ms;
|
||||
}
|
||||
|
||||
span.jslider {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.slider-animate-container {
|
||||
text-align: right;
|
||||
margin-top: -9px;
|
||||
}
|
||||
.slider-animate-button {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.slider-animate-button .pause {
|
||||
display: none;
|
||||
}
|
||||
.slider-animate-button.playing .pause {
|
||||
display: inline;
|
||||
}
|
||||
.slider-animate-button .play {
|
||||
display: inline;
|
||||
}
|
||||
.slider-animate-button.playing .play {
|
||||
display: none;
|
||||
}
|
||||
1564
inst/www/shared/shiny.js
Normal file
1
inst/www/shared/slider/css/jquery.slider.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.jslider .jslider-bg i,.jslider .jslider-pointer{background:url(../img/jslider.png) no-repeat 0 0}.jslider{display:block;width:100%;height:1em;position:relative;top:.6em;font-family:Arial,sans-serif}.jslider table{width:100%;border-collapse:collapse;border:0}.jslider td,.jslider th{padding:0;vertical-align:top;text-align:left;border:0}.jslider table,.jslider table tr,.jslider table tr td{width:100%;vertical-align:top}.jslider .jslider-bg{position:relative}.jslider .jslider-bg i{height:5px;position:absolute;font-size:0;top:0}.jslider .jslider-bg .l{width:50%;background-position:0 0;left:0}.jslider .jslider-bg .r{width:50%;left:50%;background-position:right 0}.jslider .jslider-bg .v{position:absolute;width:60%;left:20%;top:0;height:5px;background-position:0 -20px}.jslider .jslider-pointer{width:13px;height:15px;background-position:0 -40px;position:absolute;left:20%;top:-4px;margin-left:-6px;cursor:pointer;cursor:hand}.jslider .jslider-pointer-hover{background-position:-20px -40px}.jslider .jslider-pointer-to{left:80%}.jslider .jslider-label{font-size:9px;line-height:12px;color:black;opacity:.4;white-space:nowrap;padding:0 2px;position:absolute;top:-18px;left:0}.jslider .jslider-label-to{left:auto;right:0}.jslider .jslider-value{font-size:9px;white-space:nowrap;padding:1px 2px 0;position:absolute;top:-19px;left:20%;background:white;line-height:12px;-moz-border-radius:2px;-webkit-border-radius:2px;-o-border-radius:2px;border-radius:2px}.jslider .jslider-value-to{left:80%}.jslider .jslider-label small,.jslider .jslider-value small{position:relative;top:-0.4em}.jslider .jslider-scale{position:relative;top:9px}.jslider .jslider-scale span{position:absolute;height:5px;border-left:1px solid #999;font-size:0}.jslider .jslider-scale ins{font-size:9px;text-decoration:none;position:absolute;left:0;top:5px;color:#999}.jslider-single .jslider-pointer-to,.jslider-single .jslider-value-to,.jslider-single .jslider-bg .v,.jslider-limitless .jslider-label{display:none}.jslider_blue .jslider-bg i,.jslider_blue .jslider-pointer{background-image:url(../img/jslider.blue.png)}.jslider_plastic .jslider-bg i,.jslider_plastic .jslider-pointer{background-image:url(../img/jslider.plastic.png)}.jslider_round .jslider-bg i,.jslider_round .jslider-pointer{background-image:url(../img/jslider.round.png)}.jslider_round .jslider-pointer{width:17px;height:17px;top:-6px;margin-left:-8px}.jslider_round_plastic .jslider-bg i,.jslider_round_plastic .jslider-pointer{background-image:url(../img/jslider.round.plastic.png)}.jslider_round_plastic .jslider-pointer{width:18px;height:18px;top:-7px;margin-left:-8px}
|
||||
BIN
inst/www/shared/slider/img/jslider.blue.png
Normal file
|
After Width: | Height: | Size: 1001 B |
BIN
inst/www/shared/slider/img/jslider.plastic.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
inst/www/shared/slider/img/jslider.png
Normal file
|
After Width: | Height: | Size: 999 B |
BIN
inst/www/shared/slider/img/jslider.round.plastic.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
inst/www/shared/slider/img/jslider.round.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
1
inst/www/shared/slider/js/jquery.slider.min.js
vendored
Normal file
172
lib/react.rb
@@ -1,172 +0,0 @@
|
||||
module React
|
||||
|
||||
class Context
|
||||
|
||||
private
|
||||
|
||||
@@next_id = 0
|
||||
@@current_context = nil
|
||||
@@pending_invalidate = []
|
||||
|
||||
public
|
||||
|
||||
def self.current!
|
||||
return current || raise("No current context")
|
||||
end
|
||||
|
||||
def self.current
|
||||
@@current_context
|
||||
end
|
||||
|
||||
attr_reader :id
|
||||
|
||||
def initialize
|
||||
# The ID can used to identify/sort/dedupe contexts
|
||||
@id = @@next_id += 1
|
||||
|
||||
# Indicates whether this context is invalidated, i.e. its
|
||||
# callbacks have been called or are about to be called
|
||||
@invalidated = false
|
||||
|
||||
# List of callbacks to be called after invalidation
|
||||
@callbacks = []
|
||||
end
|
||||
|
||||
# Run a block with this context as the current context. The
|
||||
# original current context will be restored after the block
|
||||
# is executed.
|
||||
def run
|
||||
old_ctx = @@current_context
|
||||
@@current_context = self
|
||||
begin
|
||||
return yield
|
||||
ensure
|
||||
@@current_context = old_ctx
|
||||
end
|
||||
end
|
||||
|
||||
def invalidate
|
||||
return if @invalidated
|
||||
|
||||
@invalidated = true
|
||||
@@pending_invalidate << self
|
||||
end
|
||||
|
||||
# Register a callback to be called after this context is
|
||||
# invalidated (or immediately if it's already invalidated).
|
||||
# The callback takes one argument, the context.
|
||||
def on_invalidate(&callback)
|
||||
if @invalidated
|
||||
callback.call(self)
|
||||
else
|
||||
@callbacks << callback
|
||||
end
|
||||
end
|
||||
|
||||
def execute_callbacks
|
||||
@callbacks.each {|callback| callback.call(self)}
|
||||
end
|
||||
|
||||
# Execute all callbacks on invalidated contexts. Will do this
|
||||
# repeatedly if the callbacks themselves cause more invalidations.
|
||||
def self.flush
|
||||
while !@@pending_invalidate.empty?
|
||||
contexts = @@pending_invalidate
|
||||
@@pending_invalidate = []
|
||||
|
||||
contexts.each {|context| context.execute_callbacks}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Session
|
||||
def initialize
|
||||
# Key is variable name, value is variable value
|
||||
@values = Hash.new
|
||||
# Key is variable name, value is { Context IDs => Contexts }
|
||||
@dependencies = Hash.new
|
||||
end
|
||||
|
||||
def get(name)
|
||||
cur_ctx = React::Context.current!
|
||||
@dependencies[name] = @dependencies[name] || Hash.new
|
||||
if !@dependencies[name].has_key?(cur_ctx.id)
|
||||
@dependencies[name][cur_ctx.id] = cur_ctx
|
||||
cur_ctx.on_invalidate do
|
||||
@dependencies[name].delete(cur_ctx.id)
|
||||
end
|
||||
end
|
||||
|
||||
return @values[name]
|
||||
end
|
||||
|
||||
def set(name, value)
|
||||
if @values.has_key?(name) && @values[name] == value
|
||||
return
|
||||
end
|
||||
|
||||
@values[name] = value
|
||||
if @dependencies[name]
|
||||
@dependencies[name].each_value {|ctx| ctx.invalidate}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Stores (and caches) a single dependent value in a context
|
||||
class ObservableValue
|
||||
def initialize(&valueProc)
|
||||
@valueProc = valueProc
|
||||
|
||||
@dependencies = Hash.new
|
||||
@initialized = false
|
||||
end
|
||||
|
||||
def value
|
||||
if !@initialized
|
||||
@initialized = true
|
||||
update_value
|
||||
end
|
||||
|
||||
cur_ctx = React::Context.current!
|
||||
@dependencies[cur_ctx.id] = cur_ctx
|
||||
cur_ctx.on_invalidate do
|
||||
@dependencies.delete cur_ctx.id
|
||||
end
|
||||
@value
|
||||
end
|
||||
|
||||
private
|
||||
def update_value
|
||||
old_value = @value
|
||||
|
||||
ctx = Context.new
|
||||
ctx.on_invalidate do
|
||||
update_value
|
||||
end
|
||||
ctx.run do
|
||||
@value = @valueProc.call
|
||||
end
|
||||
|
||||
if old_value != @value
|
||||
@dependencies.each_value {|dep_ctx| dep_ctx.invalidate}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Runs the given proc whenever its dependencies change
|
||||
class Observer
|
||||
def initialize(&proc)
|
||||
@proc = proc
|
||||
run
|
||||
end
|
||||
|
||||
def run
|
||||
ctx = React::Context.new
|
||||
ctx.on_invalidate do
|
||||
run
|
||||
end
|
||||
ctx.run &@proc
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
156
lib/shiny.rb
@@ -1,156 +0,0 @@
|
||||
require 'eventmachine'
|
||||
require 'evma_httpserver'
|
||||
require 'em-websocket'
|
||||
require 'pathname'
|
||||
require 'json'
|
||||
require 'react'
|
||||
|
||||
|
||||
class WebServer < EM::Connection
|
||||
include EM::HttpServer
|
||||
|
||||
def post_init
|
||||
super
|
||||
no_environment_strings
|
||||
|
||||
@basepath = File.join(Dir.pwd, 'www')
|
||||
end
|
||||
|
||||
def resolve_path(path)
|
||||
# It's not a valid path if it doesn't start with /
|
||||
return nil if path !~ /^\//
|
||||
|
||||
abspath = File.join(@basepath, "./#{path}")
|
||||
# Resolves '..', etc.
|
||||
abspath = Pathname.new(abspath).cleanpath.to_s
|
||||
|
||||
return false if abspath[0...(@basepath.size + 1)] != @basepath + '/'
|
||||
return false if !File.exist?(abspath)
|
||||
|
||||
return abspath
|
||||
end
|
||||
|
||||
def process_http_request
|
||||
# the http request details are available via the following instance variables:
|
||||
# @http_protocol
|
||||
# @http_request_method
|
||||
# @http_cookie
|
||||
# @http_if_none_match
|
||||
# @http_content_type
|
||||
# @http_path_info
|
||||
# @http_request_uri
|
||||
# @http_query_string
|
||||
# @http_post_content
|
||||
# @http_headers
|
||||
|
||||
response = EM::DelegatedHttpResponse.new(self)
|
||||
|
||||
path = @http_path_info
|
||||
path = '/index.html' if path == '/'
|
||||
|
||||
resolved_path = resolve_path(path)
|
||||
|
||||
if !resolved_path
|
||||
response.status = 404
|
||||
response.content_type 'text/html'
|
||||
response.content = '<h1>404 Not Found</h1>'
|
||||
else
|
||||
response.status = 200
|
||||
response.content_type case resolved_path
|
||||
when /\.html?$/
|
||||
'text/html'
|
||||
when /\.js$/
|
||||
'text/javascript'
|
||||
when /\.css$/
|
||||
'text/css'
|
||||
when /\.png$/
|
||||
'image/png'
|
||||
when /\.jpg$/
|
||||
'image/jpeg'
|
||||
when /\.gif$/
|
||||
'image/gif'
|
||||
end
|
||||
response.content = File.read(resolved_path)
|
||||
end
|
||||
response.send_response
|
||||
end
|
||||
end
|
||||
|
||||
def run_shiny_app(shinyapp)
|
||||
EventMachine.run do
|
||||
EventMachine.start_server '0.0.0.0', 8100, WebServer
|
||||
puts "Listening on port 8100"
|
||||
|
||||
EventMachine::WebSocket.start(:host => '0.0.0.0', :port => 8101) do |ws|
|
||||
shinyapp.websocket = ws
|
||||
ws.onclose { exit(0) }
|
||||
ws.onmessage do |msg|
|
||||
begin
|
||||
puts "RECV: #{msg}"
|
||||
|
||||
msg_obj = JSON.parse(msg)
|
||||
case msg_obj['method']
|
||||
when 'init'
|
||||
msg_obj['data'].each do |k, v|
|
||||
shinyapp.session.set(k, v)
|
||||
end
|
||||
React::Context.flush
|
||||
shinyapp.instantiate_outputs
|
||||
when 'update'
|
||||
msg_obj['data'].each do |k, v|
|
||||
shinyapp.session.set(k, v)
|
||||
end
|
||||
end
|
||||
|
||||
React::Context.flush
|
||||
shinyapp.flush_output
|
||||
rescue Exception => e
|
||||
puts "ERROR: #{e}"
|
||||
puts e.backtrace.collect {|x| "\t#{x}"}
|
||||
raise
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ShinyApp
|
||||
attr_reader :session
|
||||
|
||||
def initialize
|
||||
@session = React::Session.new
|
||||
@outputs = {}
|
||||
@invalidated_output_values = Hash.new
|
||||
end
|
||||
|
||||
def websocket=(value)
|
||||
@websocket = value
|
||||
end
|
||||
|
||||
def define_output(name, &proc)
|
||||
@outputs[name] = proc
|
||||
end
|
||||
|
||||
def instantiate_outputs
|
||||
@outputs.keys.each do |name|
|
||||
proc = @outputs.delete(name)
|
||||
React::Observer.new do
|
||||
value = proc.call
|
||||
@invalidated_output_values[name] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def flush_output
|
||||
return if @invalidated_output_values.empty?
|
||||
|
||||
data = @invalidated_output_values
|
||||
@invalidated_output_values = Hash.new
|
||||
puts "SEND: #{JSON.generate(data)}"
|
||||
@websocket.send(JSON.generate(data))
|
||||
end
|
||||
|
||||
def run
|
||||
run_shiny_app self
|
||||
end
|
||||
end
|
||||
24
man/HTML.Rd
Normal file
@@ -0,0 +1,24 @@
|
||||
\name{HTML}
|
||||
\alias{HTML}
|
||||
\title{Mark Characters as HTML}
|
||||
\usage{
|
||||
HTML(text, ...)
|
||||
}
|
||||
\arguments{
|
||||
\item{text}{The text value to mark with HTML}
|
||||
|
||||
\item{...}{Any additional values to be converted to
|
||||
character and concatenated together}
|
||||
}
|
||||
\value{
|
||||
The same value, but marked as HTML.
|
||||
}
|
||||
\description{
|
||||
Marks the given text as HTML, which means the \link{tag}
|
||||
functions will know not to perform HTML escaping on it.
|
||||
}
|
||||
\examples{
|
||||
el <- div(HTML("I like <u>turtles</u>"))
|
||||
cat(as.character(el))
|
||||
}
|
||||
|
||||
36
man/addResourcePath.Rd
Normal file
@@ -0,0 +1,36 @@
|
||||
\name{addResourcePath}
|
||||
\alias{addResourcePath}
|
||||
\title{Resource Publishing}
|
||||
\usage{
|
||||
addResourcePath(prefix, directoryPath)
|
||||
}
|
||||
\arguments{
|
||||
\item{prefix}{The URL prefix (without slashes). Valid
|
||||
characters are a-z, A-Z, 0-9, hyphen, and underscore; and
|
||||
must begin with a-z or A-Z. For example, a value of 'foo'
|
||||
means that any request paths that begin with '/foo' will
|
||||
be mapped to the given directory.}
|
||||
|
||||
\item{directoryPath}{The directory that contains the
|
||||
static resources to be served.}
|
||||
}
|
||||
\description{
|
||||
Adds a directory of static resources to Shiny's web
|
||||
server, with the given path prefix. Primarily intended
|
||||
for package authors to make supporting JavaScript/CSS
|
||||
files available to their components.
|
||||
}
|
||||
\details{
|
||||
You can call \code{addResourcePath} multiple times for a
|
||||
given \code{prefix}; only the most recent value will be
|
||||
retained. If the normalized \code{directoryPath} is
|
||||
different than the directory that's currently mapped to
|
||||
the \code{prefix}, a warning will be issued.
|
||||
}
|
||||
\examples{
|
||||
addResourcePath('datasets', system.file('data', package='datasets'))
|
||||
}
|
||||
\seealso{
|
||||
\code{\link{singleton}}
|
||||
}
|
||||
|
||||
28
man/animationOptions.Rd
Normal file
@@ -0,0 +1,28 @@
|
||||
\name{animationOptions}
|
||||
\alias{animationOptions}
|
||||
\title{Animation Options}
|
||||
\usage{
|
||||
animationOptions(interval = 1000, loop = FALSE,
|
||||
playButton = NULL, pauseButton = NULL)
|
||||
}
|
||||
\arguments{
|
||||
\item{interval}{The interval, in milliseconds, between
|
||||
each animation step.}
|
||||
|
||||
\item{loop}{\code{TRUE} to automatically restart the
|
||||
animation when it reaches the end.}
|
||||
|
||||
\item{playButton}{Specifies the appearance of the play
|
||||
button. Valid values are a one-element character vector
|
||||
(for a simple text label), an HTML tag or list of tags
|
||||
(using \code{\link{tag}} and friends), or raw HTML (using
|
||||
\code{\link{HTML}}).}
|
||||
|
||||
\item{pauseButton}{Similar to \code{playButton}, but for
|
||||
the pause button.}
|
||||
}
|
||||
\description{
|
||||
Creates an options object for customizing animations for
|
||||
\link{sliderInput}.
|
||||
}
|
||||
|
||||
26
man/bootstrapPage.Rd
Normal file
@@ -0,0 +1,26 @@
|
||||
\name{bootstrapPage}
|
||||
\alias{bootstrapPage}
|
||||
\title{Create a Twitter Bootstrap page}
|
||||
\usage{
|
||||
bootstrapPage(...)
|
||||
}
|
||||
\arguments{
|
||||
\item{...}{The contents of the document body.}
|
||||
}
|
||||
\value{
|
||||
A UI defintion that can be passed to the \link{shinyUI}
|
||||
function.
|
||||
}
|
||||
\description{
|
||||
Create a Shiny UI page that loads the CSS and JavaScript
|
||||
for \href{http://getbootstrap.com}{Twitter Bootstrap},
|
||||
and has no content in the page body (other than what you
|
||||
provide).
|
||||
}
|
||||
\details{
|
||||
This function is primarily intended for users who are
|
||||
proficient in HTML/CSS, and know how to lay out pages in
|
||||
Bootstrap. Most users should use template functions like
|
||||
\code{\link{pageWithSidebar}}.
|
||||
}
|
||||
|
||||
79
man/builder.Rd
Normal file
@@ -0,0 +1,79 @@
|
||||
\name{builder}
|
||||
\alias{p}
|
||||
\alias{h1}
|
||||
\alias{h2}
|
||||
\alias{h3}
|
||||
\alias{h4}
|
||||
\alias{h5}
|
||||
\alias{h6}
|
||||
\alias{a}
|
||||
\alias{br}
|
||||
\alias{div}
|
||||
\alias{span}
|
||||
\alias{pre}
|
||||
\alias{code}
|
||||
\alias{img}
|
||||
\alias{strong}
|
||||
\alias{em}
|
||||
\alias{tags}
|
||||
|
||||
\usage{
|
||||
p(...)
|
||||
h1(...)
|
||||
h2(...)
|
||||
h3(...)
|
||||
h4(...)
|
||||
h5(...)
|
||||
h6(...)
|
||||
a(...)
|
||||
br(...)
|
||||
div(...)
|
||||
span(...)
|
||||
pre(...)
|
||||
code(...)
|
||||
img(...)
|
||||
strong(...)
|
||||
em(...)
|
||||
|
||||
tags
|
||||
}
|
||||
|
||||
\title{HTML Builder Functions}
|
||||
|
||||
\arguments{
|
||||
\item{...}{Attributes and children of the element. Named arguments
|
||||
become attributes, and positional arguments become children. Valid
|
||||
children are tags, single-character character vectors (which become
|
||||
text nodes), and raw HTML (see \code{\link{HTML}}). You can also
|
||||
pass lists that contain tags, text nodes, and HTML.}
|
||||
}
|
||||
\description{
|
||||
Simple functions for constructing HTML documents.
|
||||
}
|
||||
\details{
|
||||
The \code{tags} environment contains convenience functions for all
|
||||
valid HTML5 tags. To generate tags that are not part of the HTML5
|
||||
specification, you can use the \link{tag} function.
|
||||
|
||||
Dedicated functions are available for the most common HTML tags
|
||||
that do not conflict with common R functions.
|
||||
|
||||
The result from these functions is a tag object, which can be
|
||||
converted using \code{as.character}.
|
||||
}
|
||||
\examples{
|
||||
doc <- tags$html(
|
||||
tags$head(
|
||||
tags$title('My first page')
|
||||
),
|
||||
tags$body(
|
||||
h1('My first heading'),
|
||||
p('My first paragraph, with some ',
|
||||
strong('bold'),
|
||||
' text.'),
|
||||
div(id='myDiv', class='simpleDiv',
|
||||
'Here is a div with some attributes.')
|
||||
)
|
||||
)
|
||||
cat(as.character(doc))
|
||||
}
|
||||
39
man/checkboxGroupInput.Rd
Normal file
@@ -0,0 +1,39 @@
|
||||
\name{checkboxGroupInput}
|
||||
\alias{checkboxGroupInput}
|
||||
\title{Checkbox Group Input Control}
|
||||
\usage{
|
||||
checkboxGroupInput(inputId, label, choices,
|
||||
selected = NULL)
|
||||
}
|
||||
\arguments{
|
||||
\item{inputId}{Input variable to assign the control's
|
||||
value to.}
|
||||
|
||||
\item{label}{Display label for the control.}
|
||||
|
||||
\item{choices}{List of values to show checkboxes for. If
|
||||
elements of the list are named then that name rather than
|
||||
the value is displayed to the user.}
|
||||
|
||||
\item{selected}{Names of items that should be initially
|
||||
selected, if any.}
|
||||
}
|
||||
\value{
|
||||
A list of HTML elements that can be added to a UI
|
||||
definition.
|
||||
}
|
||||
\description{
|
||||
Create a group of checkboxes that can be used to toggle
|
||||
multiple choices independently. The server will receive
|
||||
the input as a character vector of the selected values.
|
||||
}
|
||||
\examples{
|
||||
checkboxGroupInput("variable", "Variable:",
|
||||
c("Cylinders" = "cyl",
|
||||
"Transmission" = "am",
|
||||
"Gears" = "gear"))
|
||||
}
|
||||
\seealso{
|
||||
\code{\link{checkboxInput}}
|
||||
}
|
||||
|
||||
29
man/checkboxInput.Rd
Normal file
@@ -0,0 +1,29 @@
|
||||
\name{checkboxInput}
|
||||
\alias{checkboxInput}
|
||||
\title{Checkbox Input Control}
|
||||
\usage{
|
||||
checkboxInput(inputId, label, value = FALSE)
|
||||
}
|
||||
\arguments{
|
||||
\item{inputId}{Input variable to assign the control's
|
||||
value to.}
|
||||
|
||||
\item{label}{Display label for the control.}
|
||||
|
||||
\item{value}{Initial value (\code{TRUE} or
|
||||
\code{FALSE}).}
|
||||
}
|
||||
\value{
|
||||
A checkbox control that can be added to a UI definition.
|
||||
}
|
||||
\description{
|
||||
Create a checkbox that can be used to specify logical
|
||||
values.
|
||||
}
|
||||
\examples{
|
||||
checkboxInput("outliers", "Show outliers", FALSE)
|
||||
}
|
||||
\seealso{
|
||||
\code{\link{checkboxGroupInput}}
|
||||
}
|
||||
|
||||
54
man/conditionalPanel.Rd
Normal file
@@ -0,0 +1,54 @@
|
||||
\name{conditionalPanel}
|
||||
\alias{conditionalPanel}
|
||||
\title{Conditional Panel}
|
||||
\usage{
|
||||
conditionalPanel(condition, ...)
|
||||
}
|
||||
\arguments{
|
||||
\item{condition}{A JavaScript expression that will be
|
||||
evaluated repeatedly to determine whether the panel
|
||||
should be displayed.}
|
||||
|
||||
\item{...}{Elements to include in the panel.}
|
||||
}
|
||||
\description{
|
||||
Creates a panel that is visible or not, depending on the
|
||||
value of a JavaScript expression. The JS expression is
|
||||
evaluated once at startup and whenever Shiny detects a
|
||||
relevant change in input/output.
|
||||
}
|
||||
\details{
|
||||
In the JS expression, you can refer to \code{input} and
|
||||
\code{output} JavaScript objects that contain the current
|
||||
values of input and output. For example, if you have an
|
||||
input with an id of \code{foo}, then you can use
|
||||
\code{input.foo} to read its value. (Be sure not to
|
||||
modify the input/output objects, as this may cause
|
||||
unpredictable behavior.)
|
||||
}
|
||||
\examples{
|
||||
sidebarPanel(
|
||||
selectInput(
|
||||
"plotType", "Plot Type",
|
||||
c(Scatter = "scatter",
|
||||
Histogram = "hist")),
|
||||
|
||||
# Only show this panel if the plot type is a histogram
|
||||
conditionalPanel(
|
||||
condition = "input.plotType == 'hist'",
|
||||
selectInput(
|
||||
"breaks", "Breaks",
|
||||
c("Sturges",
|
||||
"Scott",
|
||||
"Freedman-Diaconis",
|
||||
"[Custom]" = "custom")),
|
||||
|
||||
# Only show this panel if Custom is selected
|
||||
conditionalPanel(
|
||||
condition = "input.breaks == 'custom'",
|
||||
sliderInput("breakCount", "Break Count", min=1, max=1000, value=10)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
46
man/downloadButton.Rd
Normal file
@@ -0,0 +1,46 @@
|
||||
\name{downloadButton}
|
||||
\alias{downloadButton}
|
||||
\alias{downloadLink}
|
||||
\title{Create a download button or link}
|
||||
\usage{
|
||||
downloadButton(outputId, label = "Download",
|
||||
class = NULL)
|
||||
|
||||
downloadLink(outputId, label = "Download", class = NULL)
|
||||
}
|
||||
\arguments{
|
||||
\item{outputId}{The name of the output slot that the
|
||||
\code{downloadHandler} is assigned to.}
|
||||
|
||||
\item{label}{The label that should appear on the button.}
|
||||
|
||||
\item{class}{Additional CSS classes to apply to the tag,
|
||||
if any.}
|
||||
}
|
||||
\description{
|
||||
Use these functions to create a download button or link;
|
||||
when clicked, it will initiate a browser download. The
|
||||
filename and contents are specified by the corresponding
|
||||
\code{\link{downloadHandler}} defined in the server
|
||||
function.
|
||||
}
|
||||
\examples{
|
||||
\dontrun{
|
||||
# In server.R:
|
||||
output$downloadData <- downloadHandler(
|
||||
filename = function() {
|
||||
paste('data-', Sys.Date(), '.csv', sep='')
|
||||
},
|
||||
content = function(con) {
|
||||
write.csv(data, con)
|
||||
}
|
||||
)
|
||||
|
||||
# In ui.R:
|
||||
downloadLink('downloadData', 'Download')
|
||||
}
|
||||
}
|
||||
\seealso{
|
||||
downloadHandler
|
||||
}
|
||||
|
||||
55
man/downloadHandler.Rd
Normal file
@@ -0,0 +1,55 @@
|
||||
\name{downloadHandler}
|
||||
\alias{downloadHandler}
|
||||
\title{File Downloads}
|
||||
\usage{
|
||||
downloadHandler(filename, content, contentType = NA)
|
||||
}
|
||||
\arguments{
|
||||
\item{filename}{A string of the filename, including
|
||||
extension, that the user's web browser should default to
|
||||
when downloading the file; or a function that returns
|
||||
such a string. (Reactive values and functions may be used
|
||||
from this function.)}
|
||||
|
||||
\item{content}{A function that takes a single argument
|
||||
\code{file} that is a file path (string) of a nonexistent
|
||||
temp file, and writes the content to that file path.
|
||||
(Reactive values and functions may be used from this
|
||||
function.)}
|
||||
|
||||
\item{contentType}{A string of the download's
|
||||
\href{http://en.wikipedia.org/wiki/Internet_media_type}{content
|
||||
type}, for example \code{"text/csv"} or
|
||||
\code{"image/png"}. If \code{NULL} or \code{NA}, the
|
||||
content type will be guessed based on the filename
|
||||
extension, or \code{application/octet-stream} if the
|
||||
extension is unknown.}
|
||||
}
|
||||
\description{
|
||||
Allows content from the Shiny application to be made
|
||||
available to the user as file downloads (for example,
|
||||
downloading the currently visible data as a CSV file).
|
||||
Both filename and contents can be calculated dynamically
|
||||
at the time the user initiates the download. Assign the
|
||||
return value to a slot on \code{output} in your server
|
||||
function, and in the UI use \code{\link{downloadButton}}
|
||||
or \code{\link{downloadLink}} to make the download
|
||||
available.
|
||||
}
|
||||
\examples{
|
||||
\dontrun{
|
||||
# In server.R:
|
||||
output$downloadData <- downloadHandler(
|
||||
filename = function() {
|
||||
paste('data-', Sys.Date(), '.csv', sep='')
|
||||
},
|
||||
content = function(file) {
|
||||
write.csv(data, file)
|
||||
}
|
||||
)
|
||||
|
||||
# In ui.R:
|
||||
downloadLink('downloadData', 'Download')
|
||||
}
|
||||
}
|
||||
|
||||
27
man/fileInput.Rd
Normal file
@@ -0,0 +1,27 @@
|
||||
\name{fileInput}
|
||||
\alias{fileInput}
|
||||
\title{File Upload Control}
|
||||
\usage{
|
||||
fileInput(inputId, label, multiple = FALSE,
|
||||
accept = NULL)
|
||||
}
|
||||
\arguments{
|
||||
\item{inputId}{Input variable to assign the control's
|
||||
value to.}
|
||||
|
||||
\item{label}{Display label for the control.}
|
||||
|
||||
\item{multiple}{Whether the user should be allowed to
|
||||
select and upload multiple files at once.}
|
||||
|
||||
\item{accept}{A character vector of MIME types; gives the
|
||||
browser a hint of what kind of files the server is
|
||||
expecting.}
|
||||
}
|
||||
\description{
|
||||
Create a file upload control that can be used to upload
|
||||
one or more files. \bold{Experimental feature. Only works
|
||||
in some browsers (primarily tested on Chrome and
|
||||
Firefox).}
|
||||
}
|
||||
|
||||
24
man/headerPanel.Rd
Normal file
@@ -0,0 +1,24 @@
|
||||
\name{headerPanel}
|
||||
\alias{headerPanel}
|
||||
\title{Create a header panel}
|
||||
\usage{
|
||||
headerPanel(title, windowTitle = title)
|
||||
}
|
||||
\arguments{
|
||||
\item{title}{An application title to display}
|
||||
|
||||
\item{windowTitle}{The title that should be displayed by
|
||||
the browser window. Useful if \code{title} is not a
|
||||
string.}
|
||||
}
|
||||
\value{
|
||||
A headerPanel that can be passed to
|
||||
\link{pageWithSidebar}
|
||||
}
|
||||
\description{
|
||||
Create a header panel containing an application title.
|
||||
}
|
||||
\examples{
|
||||
headerPanel("Hello Shiny!")
|
||||
}
|
||||
|
||||
23
man/helpText.Rd
Normal file
@@ -0,0 +1,23 @@
|
||||
\name{helpText}
|
||||
\alias{helpText}
|
||||
\title{Create a help text element}
|
||||
\usage{
|
||||
helpText(...)
|
||||
}
|
||||
\arguments{
|
||||
\item{...}{One or more help text strings (or other inline
|
||||
HTML elements)}
|
||||
}
|
||||
\value{
|
||||
A help text element that can be added to a UI definition.
|
||||
}
|
||||
\description{
|
||||
Create help text which can be added to an input form to
|
||||
provide additional explanation or context.
|
||||
}
|
||||
\examples{
|
||||
helpText("Note: while the data view will show only",
|
||||
"the specified number of observations, the",
|
||||
"summary will be based on the full dataset.")
|
||||
}
|
||||
|
||||
30
man/htmlOutput.Rd
Normal file
@@ -0,0 +1,30 @@
|
||||
\name{htmlOutput}
|
||||
\alias{htmlOutput}
|
||||
\alias{uiOutput}
|
||||
\title{Create an HTML output element}
|
||||
\usage{
|
||||
htmlOutput(outputId)
|
||||
|
||||
uiOutput(outputId)
|
||||
}
|
||||
\arguments{
|
||||
\item{outputId}{output variable to read the value from}
|
||||
}
|
||||
\value{
|
||||
An HTML output element that can be included in a panel
|
||||
}
|
||||
\description{
|
||||
Render a reactive output variable as HTML within an
|
||||
application page. The text will be included within an
|
||||
HTML \code{div} tag, and is presumed to contain HTML
|
||||
content which should not be escaped.
|
||||
}
|
||||
\details{
|
||||
\code{uiOutput} is intended to be used with
|
||||
\code{reactiveUI} on the server side. It is currently
|
||||
just an alias for \code{htmlOutput}.
|
||||
}
|
||||
\examples{
|
||||
htmlOutput("summary")
|
||||
}
|
||||
|
||||
54
man/include.Rd
Normal file
@@ -0,0 +1,54 @@
|
||||
\name{includeHTML}
|
||||
\alias{includeHTML}
|
||||
\alias{includeText}
|
||||
\alias{includeMarkdown}
|
||||
|
||||
\usage{
|
||||
includeHTML(path)
|
||||
includeText(path)
|
||||
includeMarkdown(path)
|
||||
}
|
||||
|
||||
\title{Include Content From a File}
|
||||
|
||||
\arguments{
|
||||
\item{path}{
|
||||
The path of the file to be included. It is highly recommended to
|
||||
use a relative path (the base path being the Shiny application
|
||||
directory), not an absolute path.
|
||||
}
|
||||
}
|
||||
\description{
|
||||
Include HTML, text, or rendered Markdown into a \link[=shinyUI]{Shiny UI}.
|
||||
}
|
||||
\details{
|
||||
These functions provide a convenient way to include an extensive amount
|
||||
of HTML, textual, or Markdown content, rather than using a large literal R
|
||||
string.
|
||||
}
|
||||
\note{
|
||||
\code{includeText} escapes its contents, but does no other processing. This
|
||||
means that hard breaks and multiple spaces will be rendered as they usually
|
||||
are in HTML: as a single space character. If you are looking for
|
||||
preformatted text, wrap the call with \code{\link{pre}}, or consider using
|
||||
\code{includeMarkdown} instead.
|
||||
}
|
||||
\note{
|
||||
The \code{includeMarkdown} function requires the \code{markdown} package.
|
||||
}
|
||||
\examples{
|
||||
doc <- tags$html(
|
||||
tags$head(
|
||||
tags$title('My first page')
|
||||
),
|
||||
tags$body(
|
||||
h1('My first heading'),
|
||||
p('My first paragraph, with some ',
|
||||
strong('bold'),
|
||||
' text.'),
|
||||
div(id='myDiv', class='simpleDiv',
|
||||
'Here is a div with some attributes.')
|
||||
)
|
||||
)
|
||||
cat(as.character(doc))
|
||||
}
|
||||
15
man/invalidateLater.Rd
Normal file
@@ -0,0 +1,15 @@
|
||||
\name{invalidateLater}
|
||||
\alias{invalidateLater}
|
||||
\title{Scheduled Invalidation}
|
||||
\usage{
|
||||
invalidateLater(millis)
|
||||
}
|
||||
\arguments{
|
||||
\item{millis}{Approximate milliseconds to wait before
|
||||
invalidating the current reactive context.}
|
||||
}
|
||||
\description{
|
||||
Schedules the current reactive context to be invalidated
|
||||
in the given number of milliseconds.
|
||||
}
|
||||
|
||||
24
man/mainPanel.Rd
Normal file
@@ -0,0 +1,24 @@
|
||||
\name{mainPanel}
|
||||
\alias{mainPanel}
|
||||
\title{Create a main panel}
|
||||
\usage{
|
||||
mainPanel(...)
|
||||
}
|
||||
\arguments{
|
||||
\item{...}{Ouput elements to include in the main panel}
|
||||
}
|
||||
\value{
|
||||
A main panel that can be passed to \link{pageWithSidebar}
|
||||
}
|
||||
\description{
|
||||
Create a main panel containing output elements that can
|
||||
in turn be passed to \link{pageWithSidebar}.
|
||||
}
|
||||
\examples{
|
||||
# Show the caption and plot of the requested variable against mpg
|
||||
mainPanel(
|
||||
h3(textOutput("caption")),
|
||||
plotOutput("mpgPlot")
|
||||
)
|
||||
}
|
||||
|
||||
34
man/numericInput.Rd
Normal file
@@ -0,0 +1,34 @@
|
||||
\name{numericInput}
|
||||
\alias{numericInput}
|
||||
\title{Create a numeric input control}
|
||||
\usage{
|
||||
numericInput(inputId, label, value, min = NA, max = NA,
|
||||
step = NA)
|
||||
}
|
||||
\arguments{
|
||||
\item{inputId}{Input variable to assign the control's
|
||||
value to}
|
||||
|
||||
\item{label}{Display label for the control}
|
||||
|
||||
\item{value}{Initial value}
|
||||
|
||||
\item{min}{Minimum allowed value}
|
||||
|
||||
\item{max}{Maximum allowed value}
|
||||
|
||||
\item{step}{Interval to use when stepping between min and
|
||||
max}
|
||||
}
|
||||
\value{
|
||||
A numeric input control that can be added to a UI
|
||||
definition.
|
||||
}
|
||||
\description{
|
||||
Create an input control for entry of numeric values
|
||||
}
|
||||
\examples{
|
||||
numericInput("obs", "Observations:", 10,
|
||||
min = 1, max = 100)
|
||||
}
|
||||
|
||||
33
man/observe.Rd
Normal file
@@ -0,0 +1,33 @@
|
||||
\name{observe}
|
||||
\alias{observe}
|
||||
\title{Create a reactive observer}
|
||||
\usage{
|
||||
observe(func)
|
||||
}
|
||||
\arguments{
|
||||
\item{func}{The function to observe. It must not have any
|
||||
parameters. Any return value from this function will be
|
||||
ignored.}
|
||||
}
|
||||
\description{
|
||||
Creates an observer from the given function. An observer
|
||||
is like a reactive function in that it can read reactive
|
||||
values and call reactive functions, and will
|
||||
automatically re-execute when those dependencies change.
|
||||
But unlike reactive functions, it doesn't yield a result
|
||||
and can't be used as an input to other reactive
|
||||
functions. Thus, observers are only useful for their side
|
||||
effects (for example, performing I/O).
|
||||
}
|
||||
\details{
|
||||
Another contrast between reactive functions and observers
|
||||
is their execution strategy. Reactive functions use lazy
|
||||
evaluation; that is, when their dependencies change, they
|
||||
don't re-execute right away but rather wait until they
|
||||
are called by someone else. Indeed, if they are not
|
||||
called then they will never re-execute. In contrast,
|
||||
observers use eager evaluation; as soon as their
|
||||
dependencies change, they schedule themselves to
|
||||
re-execute.
|
||||
}
|
||||
|
||||
47
man/pageWithSidebar.Rd
Normal file
@@ -0,0 +1,47 @@
|
||||
\name{pageWithSidebar}
|
||||
\alias{pageWithSidebar}
|
||||
\title{Create a page with a sidebar}
|
||||
\usage{
|
||||
pageWithSidebar(headerPanel, sidebarPanel, mainPanel)
|
||||
}
|
||||
\arguments{
|
||||
\item{headerPanel}{The \link{headerPanel} with the
|
||||
application title}
|
||||
|
||||
\item{sidebarPanel}{The \link{sidebarPanel} containing
|
||||
input controls}
|
||||
|
||||
\item{mainPanel}{The \link{mainPanel} containing outputs}
|
||||
}
|
||||
\value{
|
||||
A UI defintion that can be passed to the \link{shinyUI}
|
||||
function
|
||||
}
|
||||
\description{
|
||||
Create a Shiny UI that contains a header with the
|
||||
application title, a sidebar for input controls, and a
|
||||
main area for output.
|
||||
}
|
||||
\examples{
|
||||
# Define UI
|
||||
shinyUI(pageWithSidebar(
|
||||
|
||||
# Application title
|
||||
headerPanel("Hello Shiny!"),
|
||||
|
||||
# Sidebar with a slider input
|
||||
sidebarPanel(
|
||||
sliderInput("obs",
|
||||
"Number of observations:",
|
||||
min = 0,
|
||||
max = 1000,
|
||||
value = 500)
|
||||
),
|
||||
|
||||
# Show a plot of the generated distribution
|
||||
mainPanel(
|
||||
plotOutput("distPlot")
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
26
man/plotOutput.Rd
Normal file
@@ -0,0 +1,26 @@
|
||||
\name{plotOutput}
|
||||
\alias{plotOutput}
|
||||
\title{Create a plot output element}
|
||||
\usage{
|
||||
plotOutput(outputId, width = "100\%", height = "400px")
|
||||
}
|
||||
\arguments{
|
||||
\item{outputId}{output variable to read the plot from}
|
||||
|
||||
\item{width}{Plot width}
|
||||
|
||||
\item{height}{Plot height}
|
||||
}
|
||||
\value{
|
||||
A plot output element that can be included in a panel
|
||||
}
|
||||
\description{
|
||||
Render a \link{reactivePlot} within an application page.
|
||||
}
|
||||
\examples{
|
||||
# Show a plot of the generated distribution
|
||||
mainPanel(
|
||||
plotOutput("distPlot")
|
||||
)
|
||||
}
|
||||
|
||||
35
man/radioButtons.Rd
Normal file
@@ -0,0 +1,35 @@
|
||||
\name{radioButtons}
|
||||
\alias{radioButtons}
|
||||
\title{Create radio buttons}
|
||||
\usage{
|
||||
radioButtons(inputId, label, choices, selected = NULL)
|
||||
}
|
||||
\arguments{
|
||||
\item{inputId}{Input variable to assign the control's
|
||||
value to}
|
||||
|
||||
\item{label}{Display label for the control}
|
||||
|
||||
\item{choices}{List of values to select from (if elements
|
||||
of the list are named then that name rather than the
|
||||
value is displayed to the user)}
|
||||
|
||||
\item{selected}{Name of initially selected item (if not
|
||||
specified then defaults to the first item)}
|
||||
}
|
||||
\value{
|
||||
A set of radio buttons that can be added to a UI
|
||||
definition.
|
||||
}
|
||||
\description{
|
||||
Create a set of radio buttons used to select an item from
|
||||
a list.
|
||||
}
|
||||
\examples{
|
||||
radioButtons("dist", "Distribution type:",
|
||||
c("Normal" = "norm",
|
||||
"Uniform" = "unif",
|
||||
"Log-normal" = "lnorm",
|
||||
"Exponential" = "exp"))
|
||||
}
|
||||
|
||||
35
man/reactive.Rd
Normal file
@@ -0,0 +1,35 @@
|
||||
\name{reactive}
|
||||
\alias{reactive}
|
||||
\title{Create a Reactive Function}
|
||||
\usage{
|
||||
reactive(x)
|
||||
}
|
||||
\arguments{
|
||||
\item{x}{The value or function to make reactive. The
|
||||
function must not have any parameters.}
|
||||
}
|
||||
\value{
|
||||
A reactive function. (Note that reactive functions can
|
||||
only be called from within other reactive functions.)
|
||||
}
|
||||
\description{
|
||||
Wraps a normal function to create a reactive function.
|
||||
Conceptually, a reactive function is a function whose
|
||||
result will change over time.
|
||||
}
|
||||
\details{
|
||||
Reactive functions are functions that can read reactive
|
||||
values and call other reactive functions. Whenever a
|
||||
reactive value changes, any reactive functions that
|
||||
depended on it are marked as "invalidated" and will
|
||||
automatically re-execute if necessary. If a reactive
|
||||
function is marked as invalidated, any other reactive
|
||||
functions that recently called it are also marked as
|
||||
invalidated. In this way, invalidations ripple through
|
||||
the functions that depend on each other.
|
||||
|
||||
See the
|
||||
\href{http://rstudio.github.com/shiny/tutorial/}{Shiny
|
||||
tutorial} for more information about reactive functions.
|
||||
}
|
||||
|
||||
45
man/reactivePlot.Rd
Normal file
@@ -0,0 +1,45 @@
|
||||
\name{reactivePlot}
|
||||
\alias{reactivePlot}
|
||||
\title{Plot Output}
|
||||
\usage{
|
||||
reactivePlot(func, width = "auto", height = "auto", ...)
|
||||
}
|
||||
\arguments{
|
||||
\item{func}{A function that generates a plot.}
|
||||
|
||||
\item{width}{The width of the rendered plot, in pixels;
|
||||
or \code{'auto'} to use the \code{offsetWidth} of the
|
||||
HTML element that is bound to this plot. You can also
|
||||
pass in a function that returns the width in pixels or
|
||||
\code{'auto'}; in the body of the function you may
|
||||
reference reactive values and functions.}
|
||||
|
||||
\item{height}{The height of the rendered plot, in pixels;
|
||||
or \code{'auto'} to use the \code{offsetHeight} of the
|
||||
HTML element that is bound to this plot. You can also
|
||||
pass in a function that returns the width in pixels or
|
||||
\code{'auto'}; in the body of the function you may
|
||||
reference reactive values and functions.}
|
||||
|
||||
\item{...}{Arguments to be passed through to
|
||||
\code{\link[grDevices]{png}}. These can be used to set
|
||||
the width, height, background color, etc.}
|
||||
}
|
||||
\description{
|
||||
Creates a reactive plot that is suitable for assigning to
|
||||
an \code{output} slot.
|
||||
}
|
||||
\details{
|
||||
The corresponding HTML output tag should be \code{div} or
|
||||
\code{img} and have the CSS class name
|
||||
\code{shiny-plot-output}.
|
||||
|
||||
For output, it will try to use the following devices, in
|
||||
this order: quartz (via \code{\link[grDevices]{png}}),
|
||||
then \code{\link[Cairo]{CairoPNG}}, and finally
|
||||
\code{\link[grDevices]{png}}. This is in order of quality
|
||||
of output. Notably, plain \code{png} output on Linux and
|
||||
Windows may not antialias some point shapes, resulting in
|
||||
poor quality output.
|
||||
}
|
||||
|
||||
26
man/reactivePrint.Rd
Normal file
@@ -0,0 +1,26 @@
|
||||
\name{reactivePrint}
|
||||
\alias{reactivePrint}
|
||||
\title{Printable Output}
|
||||
\usage{
|
||||
reactivePrint(func)
|
||||
}
|
||||
\arguments{
|
||||
\item{func}{A function that returns a printable R
|
||||
object.}
|
||||
}
|
||||
\description{
|
||||
Makes a reactive version of the given function that also
|
||||
turns its printable result into a string. The reactive
|
||||
function is suitable for assigning to an \code{output}
|
||||
slot.
|
||||
}
|
||||
\details{
|
||||
The corresponding HTML output tag can be anything (though
|
||||
\code{pre} is recommended if you need a monospace font
|
||||
and whitespace preserved) and should have the CSS class
|
||||
name \code{shiny-text-output}.
|
||||
|
||||
The result of executing \code{func} will be printed
|
||||
inside a \code{\link[utils]{capture.output}} call.
|
||||
}
|
||||
|
||||
23
man/reactiveTable.Rd
Normal file
@@ -0,0 +1,23 @@
|
||||
\name{reactiveTable}
|
||||
\alias{reactiveTable}
|
||||
\title{Table Output}
|
||||
\usage{
|
||||
reactiveTable(func, ...)
|
||||
}
|
||||
\arguments{
|
||||
\item{func}{A function that returns an R object that can
|
||||
be used with \code{\link[xtable]{xtable}}.}
|
||||
|
||||
\item{...}{Arguments to be passed through to
|
||||
\code{\link[xtable]{xtable}} and
|
||||
\code{\link[xtable]{print.xtable}}.}
|
||||
}
|
||||
\description{
|
||||
Creates a reactive table that is suitable for assigning
|
||||
to an \code{output} slot.
|
||||
}
|
||||
\details{
|
||||
The corresponding HTML output tag should be \code{div}
|
||||
and have the CSS class name \code{shiny-html-output}.
|
||||
}
|
||||
|
||||
26
man/reactiveText.Rd
Normal file
@@ -0,0 +1,26 @@
|
||||
\name{reactiveText}
|
||||
\alias{reactiveText}
|
||||
\title{Text Output}
|
||||
\usage{
|
||||
reactiveText(func)
|
||||
}
|
||||
\arguments{
|
||||
\item{func}{A function that returns an R object that can
|
||||
be used as an argument to \code{cat}.}
|
||||
}
|
||||
\description{
|
||||
Makes a reactive version of the given function that also
|
||||
uses \code{\link[base]{cat}} to turn its result into a
|
||||
single-element character vector.
|
||||
}
|
||||
\details{
|
||||
The corresponding HTML output tag can be anything (though
|
||||
\code{pre} is recommended if you need a monospace font
|
||||
and whitespace preserved) and should have the CSS class
|
||||
name \code{shiny-text-output}.
|
||||
|
||||
The result of executing \code{func} will passed to
|
||||
\code{cat}, inside a \code{\link[utils]{capture.output}}
|
||||
call.
|
||||
}
|
||||
|
||||
35
man/reactiveTimer.Rd
Normal file
@@ -0,0 +1,35 @@
|
||||
\name{reactiveTimer}
|
||||
\alias{reactiveTimer}
|
||||
\title{Timer}
|
||||
\usage{
|
||||
reactiveTimer(intervalMs = 1000)
|
||||
}
|
||||
\arguments{
|
||||
\item{intervalMs}{How often to fire, in milliseconds}
|
||||
}
|
||||
\value{
|
||||
A no-parameter function that can be called from a
|
||||
reactive context, in order to cause that context to be
|
||||
invalidated the next time the timer interval elapses.
|
||||
Calling the returned function also happens to yield the
|
||||
current time (as in \code{\link{Sys.time}}).
|
||||
}
|
||||
\description{
|
||||
Creates a reactive timer with the given interval. A
|
||||
reactive timer is like a reactive value, except reactive
|
||||
values are triggered when they are set, while reactive
|
||||
timers are triggered simply by the passage of time.
|
||||
}
|
||||
\details{
|
||||
\link[=reactive]{Reactive functions} and observers that
|
||||
want to be invalidated by the timer need to call the
|
||||
timer function that \code{reactiveTimer} returns, even if
|
||||
the current time value is not actually needed.
|
||||
|
||||
See \code{\link{invalidateLater}} as a safer and simpler
|
||||
alternative.
|
||||
}
|
||||
\seealso{
|
||||
invalidateLater
|
||||
}
|
||||
|
||||
33
man/reactiveUI.Rd
Normal file
@@ -0,0 +1,33 @@
|
||||
\name{reactiveUI}
|
||||
\alias{reactiveUI}
|
||||
\title{UI Output}
|
||||
\usage{
|
||||
reactiveUI(func)
|
||||
}
|
||||
\arguments{
|
||||
\item{func}{A function that returns a Shiny tag object,
|
||||
\code{\link{HTML}}, or a list of such objects.}
|
||||
}
|
||||
\description{
|
||||
\bold{Experimental feature.} Makes a reactive version of
|
||||
a function that generates HTML using the Shiny UI
|
||||
library.
|
||||
}
|
||||
\details{
|
||||
The corresponding HTML output tag should be \code{div}
|
||||
and have the CSS class name \code{shiny-html-output} (or
|
||||
use \code{\link{uiOutput}}).
|
||||
}
|
||||
\examples{
|
||||
\dontrun{
|
||||
output$moreControls <- reactiveUI(function() {
|
||||
list(
|
||||
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
\seealso{
|
||||
conditionalPanel
|
||||
}
|
||||
|
||||
38
man/repeatable.Rd
Normal file
@@ -0,0 +1,38 @@
|
||||
\name{repeatable}
|
||||
\alias{repeatable}
|
||||
\title{Make a random number generator repeatable}
|
||||
\usage{
|
||||
repeatable(rngfunc,
|
||||
seed = runif(1, 0, .Machine$integer.max))
|
||||
}
|
||||
\arguments{
|
||||
\item{rngfunc}{The function that is affected by the R
|
||||
session's seed.}
|
||||
|
||||
\item{seed}{The seed to set every time the resulting
|
||||
function is called.}
|
||||
}
|
||||
\value{
|
||||
A repeatable version of the function that was passed in.
|
||||
}
|
||||
\description{
|
||||
Given a function that generates random data, returns a
|
||||
wrapped version of that function that always uses the
|
||||
same seed when called. The seed to use can be passed in
|
||||
explicitly if desired; otherwise, a random number is
|
||||
used.
|
||||
}
|
||||
\note{
|
||||
When called, the returned function attempts to preserve
|
||||
the R session's current seed by snapshotting and
|
||||
restoring \code{\link[base]{.Random.seed}}.
|
||||
}
|
||||
\examples{
|
||||
rnormA <- repeatable(rnorm)
|
||||
rnormB <- repeatable(rnorm)
|
||||
rnormA(3) # [1] 1.8285879 -0.7468041 -0.4639111
|
||||
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
|
||||
}
|
||||
|
||||
26
man/runApp.Rd
Normal file
@@ -0,0 +1,26 @@
|
||||
\name{runApp}
|
||||
\alias{runApp}
|
||||
\title{Run Shiny Application}
|
||||
\usage{
|
||||
runApp(appDir = getwd(), port = 8100L,
|
||||
launch.browser = getOption("shiny.launch.browser", interactive()))
|
||||
}
|
||||
\arguments{
|
||||
\item{appDir}{The directory of the application. Should
|
||||
contain \code{server.R}, plus, either \code{ui.R} or a
|
||||
\code{www} directory that contains the file
|
||||
\code{index.html}. Defaults to the working directory.}
|
||||
|
||||
\item{port}{The TCP port that the application should
|
||||
listen on. Defaults to port 8100.}
|
||||
|
||||
\item{launch.browser}{If true, the system's default web
|
||||
browser will be launched automatically after the app is
|
||||
started. Defaults to true in interactive sessions only.}
|
||||
}
|
||||
\description{
|
||||
Runs a Shiny application. This function normally does not
|
||||
return; interrupt R to stop the application (usually by
|
||||
pressing Ctrl+C or Esc).
|
||||
}
|
||||
|
||||