Compare commits

...

596 Commits

Author SHA1 Message Date
Joe Cheng
fb4da933d4 Update version number in NEWS 2013-10-29 12:31:58 -07:00
Yihui Xie
7483900db2 fixes #288: moving shinyCallingHandlers() to a lower-level so that the shiny.error handler can be applied to observe() and isolate() as well 2013-10-28 23:17:02 -05:00
Yihui Xie
ef9b9bdd6d prepare the v0.8 release in the RC branch 2013-10-24 23:48:48 -05:00
Yihui Xie
1937aa43ba Ah, brain poisoned by R's return() syntax; check if data is empty, then check data.colnames to make sure column names are passed in 2013-10-23 21:26:47 -05:00
Yihui Xie
293ea66784 fixes #286: if the data passed to renderDataTable() does not have dim==2, return an empty object 2013-10-23 21:14:18 -05:00
Yihui Xie
e98d8f4ced Merge pull request #284 from jcheng5/shiny-server-creds
Make Shiny Server credentials available on session object
2013-10-22 11:35:20 -07:00
Joe Cheng
418d2afb2a Make Shiny Server credentials available on session object 2013-10-22 10:42:29 -07:00
Yihui Xie
a4c1a6187f roxygenize and sync doc 2013-10-22 12:33:07 -05:00
Yihui Xie
123ca34040 tweak the roxygen doc for installExprFunction() (otherwise the second paragraph is treated as \description{}) 2013-10-22 12:33:07 -05:00
Joe Cheng
6b3224116c Merge pull request #279 from yihui/datatables
DataTables
2013-10-22 09:43:13 -07:00
Yihui Xie
635e0c9788 news for shiny v0.8 2013-10-22 10:40:29 -05:00
Yihui Xie
dd33a0e0ec the error object may not be interesting at all; just call the handler without arguments
this makes it easier to set options(shiny.error = browser/recover/traceback/...), otherwise will have to do options(shiny.error = function(e) traceback()), which seems awkward
2013-10-22 10:33:01 -05:00
Yihui Xie
191deeaba6 a hack to remove the scrollbars in tab panels 2013-10-22 02:07:06 -05:00
Yihui Xie
245072f7a2 make sure colnames is an array, even when there is only one column in the data 2013-10-22 02:07:06 -05:00
Yihui Xie
6b858512b6 must empty the data table first 2013-10-22 02:07:06 -05:00
Yihui Xie
b857a01c30 save $(el) in $el 2013-10-22 02:07:06 -05:00
Yihui Xie
94c9a3e05b should not have assigned installExprFunction() to func 2013-10-22 02:07:06 -05:00
Yihui Xie
8928d2c488 use the new installExprFunction() instead of exprToFunction() 2013-10-22 02:07:06 -05:00
Yihui Xie
25bd5654aa display 25 rows by default, again per suggestion of JJ 2013-10-22 02:07:06 -05:00
Yihui Xie
83d5b96adf no CSS classes for sorted columns, per suggestion of JJ 2013-10-22 02:07:06 -05:00
Yihui Xie
7eb90c5718 roxygenize 2013-10-22 02:07:06 -05:00
Yihui Xie
4b1af75724 debouncing is done single-handedly, thanks to Joe's smart debounce() function 2013-10-22 02:07:06 -05:00
Yihui Xie
8d07ab6527 roxygenize 2013-10-22 02:07:06 -05:00
Yihui Xie
ce4ea7e7a9 allow users to pass initialization options to datatables 2013-10-22 02:07:06 -05:00
Yihui Xie
50ab5e7517 BSD license for DataTables 2013-10-22 02:07:06 -05:00
Yihui Xie
431c1d7f66 css for the processing indicator 2013-10-22 02:07:06 -05:00
Yihui Xie
a55090dc2f the searching should use intersection instead of union 2013-10-22 02:07:06 -05:00
Yihui Xie
d76cdb73b0 remove the ColumnFilter plugin; it is too heavy, and I just added the <input> by myself and implemented searching by individual columns 2013-10-22 02:07:05 -05:00
Yihui Xie
2594664330 use the nicer bootstrap style 2013-10-22 02:07:05 -05:00
Yihui Xie
f9ed075db6 write a datatable output binding; the column names and action url are passed from renderDataTable() to the output binding 2013-10-22 02:07:05 -05:00
Yihui Xie
099ced4f94 implement searching by columns 2013-10-22 02:07:05 -05:00
Yihui Xie
13d2513930 index from 0, sigh 2013-10-22 02:07:05 -05:00
Yihui Xie
2211b1c65e now we can sort multiple columns: press Shift and click the column headers 2013-10-22 02:07:05 -05:00
Yihui Xie
1fd37ca2b2 implement sorting; the very basic features are there now, but this still needs a lot of improvement in terms of details 2013-10-22 02:07:05 -05:00
Yihui Xie
7070e3748d disable default sorting 2013-10-22 02:07:05 -05:00
Yihui Xie
dfaef908c2 make sure the data has two dimensions 2013-10-22 02:07:05 -05:00
Yihui Xie
67540c763b a simple implementation of global searching 2013-10-22 02:07:05 -05:00
Yihui Xie
14269bd4d9 document renderDataTable() and dataTableOutput() 2013-10-22 02:07:05 -05:00
Yihui Xie
131663032c the ... argument is not really used 2013-10-22 02:07:05 -05:00
Yihui Xie
8ac71165e9 add dataTableOutput() and renderDataTable() for the DataTables library
not yet done, but at least paging is working now
2013-10-22 02:07:05 -05:00
Yihui Xie
346758d3f0 white spaces 2013-10-21 21:40:28 -05:00
Yihui Xie
aef8837b5d add .csv to fileInput() to make it consistent with the tutorial (#280) 2013-10-21 21:25:42 -05:00
Yihui Xie
dc0832adba should pass a session object to invalidateLater() 2013-10-21 21:20:41 -05:00
Joe Cheng
0ad3ff655e Merge remote-tracking branch 'origin/pr/276'
Conflicts:
	R/shiny.R
2013-10-21 10:11:05 -07:00
Yihui Xie
ef45a62cc9 Merge pull request #282 from rstudio/feature/httpuv-version
bump the required version of httpuv to v1.2
2013-10-20 09:56:17 -07:00
JJ Allaire
b79abbdea9 bump the required version of httpuv to v1.2
this is to pull the changes to httpuv that enable shiny applications to run inside the rstudio viewer pane
2013-10-20 06:34:52 -04:00
Yihui Xie
930e2d1d9d closes #272: the argument ws_env was not used anywhere 2013-10-18 22:01:17 -05:00
Joe Cheng
ec2992cd2d Merge pull request #264 from rstudio/feature/debug-hooks
Export installExprFunction and add supporting documentation
2013-10-17 09:44:03 -07:00
Joe Cheng
619208565b Merge pull request #273 from yihui/feature/callinghandlers
use withCallingHandlers() so that users can do error handling like recover()
2013-10-17 09:39:01 -07:00
Joe Cheng
dcd689d2ea Don't clear timers when runApp returns
The lifetimes of reactive expressions and observers can span multiple
runApp calls, so timers should as well.
2013-10-16 15:34:25 -07:00
Yihui Xie
e94de15f83 well, we still have to use do.call($) on .self; closes #274
Winston reported the issue at https://stat.ethz.ch/pipermail/r-devel/2013-October/067744.html

partially reverted 86d61e0b44
2013-10-16 15:42:36 -05:00
Yihui Xie
6af7de51a5 another attempt to close #249, using withCallingHandlers() instead of modifying tryCatch()
also closes #217 if everyone agrees with this approach
2013-10-16 00:57:54 -05:00
Joe Cheng
f4a4af0fa4 Merge pull request #265 from yihui/bug/offset
a more reliable solution for e.offsetX/Y in Firefox
2013-10-14 11:02:20 -07:00
Joe Cheng
6934838974 Merge pull request #268 from rstudio/feature/parent-notify-disconnected
send disconnected message to parent frame when running on localhost
2013-10-11 07:51:34 -07:00
JJ Allaire
1aadd25cb5 notify iframe parent of disconnect when on the same domain 2013-10-10 06:20:26 -04:00
Winston Chang
0caf944668 Fix typo 2013-10-09 16:04:14 -05:00
Jonathan McPherson
6452f62b88 use a check hint (globalVariables()) in favor of modifying code in renderImage 2013-10-08 23:24:27 -07:00
Yihui Xie
e061dfd808 roxygenize 2013-10-08 23:42:19 -05:00
Yihui Xie
4da53ef219 a better solution for e.offsetX/Y in Firefox based on http://stackoverflow.com/q/12704686/559676
which fixes the bug reported by Greg D: https://groups.google.com/forum/#!topic/shiny-discuss/E6oYvyvx0oU
2013-10-08 23:34:34 -05:00
Jonathan McPherson
347e44f04d look up function by name (for R CMD check --as-cran) 2013-10-08 15:29:35 -07:00
Joe Cheng
8997fa7242 Merge pull request #252 from jcheng5/random-ports2
Try up to 20 random ports if necessary
2013-10-08 13:58:17 -07:00
JJ Allaire
19ba6efb82 allow a custom function for the launch.browser parameter 2013-10-08 13:56:14 -07:00
Jonathan McPherson
d10cbc9984 export and add docs for installExprFunction 2013-10-08 10:36:31 -07:00
Jonathan McPherson
6c7d9ded00 simplify syntax for creating new debuggable expressions 2013-10-07 11:11:59 -07:00
Yihui Xie
6d04e89d7d Merge pull request #262 from yihui/r-check
synchronize doc
2013-10-06 15:50:01 -07:00
Yihui Xie
2beb24147d roxygenize 2013-10-06 17:47:09 -05:00
Yihui Xie
16c5f4e377 no need to expose the documentation to users; expose registerShinyDebugHook to R so that R CMD check does not complain
per discussion in #258, and closes #259
2013-10-06 17:46:45 -05:00
Joe Cheng
03a6f1753c Merge pull request #258 from rstudio/feature/debug-hooks
Add debug hook (when present) for functions generated by exprToFunction
2013-10-02 20:06:46 -07:00
Jonathan McPherson
9fb61d8446 tag Shiny server function for special debug treatment if needed 2013-10-02 12:27:44 -07:00
Jonathan McPherson
bc3322d3c9 allow debugging Shiny server function itself 2013-10-02 10:47:38 -07:00
Jonathan McPherson
06c7bf7514 invoke debug hook function if present after instantiating app functions 2013-10-02 08:53:45 -07:00
Yihui Xie
4c89a000e4 Merge pull request #257 from yihui/trycatch
rewrite some instances of do.call()
2013-10-01 23:33:44 -07:00
Yihui Xie
86d61e0b44 we do not really need do.call() in these cases 2013-10-02 01:30:31 -05:00
Joe Cheng
6407390d72 Try up to 20 random ports if necessary 2013-09-30 16:13:46 -07:00
Yihui Xie
648120cabf Merge pull request #243 from jcheng5/master
Conditional panel expressions were broken for typed inputs
2013-09-28 21:30:42 -07:00
Joe Cheng
ce5b3f290a Merge pull request #245 from rstudio/feature/random-port
choose a random port for runApp
2013-09-27 12:36:49 -07:00
JJ Allaire
5308ca1806 use .globals rather than .runContext 2013-09-27 15:36:02 -04:00
JJ Allaire
6df6d408d2 choose a random port for runApp and continue to use it for the duration of the session unless explicitly overridden 2013-09-27 07:59:44 -04:00
Joe Cheng
b60d6ccdd8 Conditional panel expressions were broken for typed inputs 2013-09-26 15:31:14 -07:00
Joe Cheng
de01c9685e Merge pull request #235 from crtahlin/patch-2
Typo fix.
2013-09-26 10:37:56 -07:00
Joe Cheng
31d2ecc9fd Merge pull request #234 from crtahlin/patch-1
Typo fix.
2013-09-26 10:37:31 -07:00
Yihui Xie
2f8502aec6 save coordmap in $el.data() so it can be dynamically retrieved
6161eaa was an inappropriate fix

(jcheng: cherry-picked from @yihui/master)
2013-09-26 10:33:37 -07:00
Joe Cheng
d377b04dad Make websocket path relative to Shiny page path
This is necessary for proxy situations that don't override the
Shiny.createSocket function (so, not including Shiny Server,
but more like Nginx, HAProxy, and a future version of RStudio
Server).

Without the path being preserved, it's impossible for these
proxies to know that the URL should be forwarded to the host
and port that belongs to Shiny.
2013-09-19 22:53:11 -07:00
crtahlin
40cc78ae1e Typo fix. 2013-09-14 23:12:45 +02:00
crtahlin
268f1e8472 Typo fix. 2013-09-14 22:54:20 +02:00
Winston Chang
004b7c782d Add mailing list information to README 2013-09-11 11:09:18 -05:00
Winston Chang
33b293f0aa Merge pull request #226 from crtahlin/patch-2
Fixed typo.
2013-09-05 17:53:51 -07:00
crtahlin
ad584a98ad Fixed typo. 2013-09-05 17:25:27 +02:00
Jeff Allen
e2509eddb2 Merge pull request #225 from crtahlin/patch-1
Fixed typo.
2013-09-05 08:09:03 -07:00
crtahlin
b9f72d0e78 Fixed typo. 2013-09-05 16:35:04 +02:00
Joe Cheng
c839bb2db3 Merge pull request #224 from yihui/patch-1
websockets has been changed to httpuv
2013-09-04 10:38:04 -07:00
Yihui Xie
30161369a8 websockets has been changed to httpuv 2013-09-03 17:24:05 -05:00
Winston Chang
c65aa9732e Bump version to 0.7.0.99 for development 2013-08-29 11:44:30 -05:00
Joe Cheng
bc72b8fd1c Merge pull request #214 from hadley/doc-tweaks
Doc tweaks
2013-08-27 16:50:00 -07:00
Winston Chang
f089531bd1 Put man-roxygen in .Rbuildignore 2013-08-27 10:08:03 -05:00
Winston Chang
8d8ea53804 Fixes to imports for R-devel 2013-08-26 14:19:50 -05:00
Winston Chang
89e405e927 Bump version to 0.7.0 2013-08-26 12:03:02 -05:00
Joe Cheng
ca984a6630 Implement shiny.sharedSecret option 2013-08-25 22:20:38 -07:00
Joe Cheng
fa39a55eca Wow, IE10 is *really* picky about websocket URLs 2013-08-23 15:42:19 -07:00
Joe Cheng
c3a1ba2f2d Make websocket URL work with IE10
See https://github.com/einaros/ws/issues/131#issuecomment-15715373
2013-08-23 15:38:12 -07:00
Joe Cheng
86e291f250 Add docs for showReactLog. Un-export writeReactLog. 2013-08-23 13:11:04 -07:00
Jeff Allen
dd1d4439a9 Merge pull request #218 from wch/fix-log
Cleanups to reactive logging code for R CMD check
2013-08-23 12:16:23 -07:00
Joe Cheng
cbfde18f8c More updates to NEWS 2013-08-23 12:03:11 -07:00
Winston Chang
e2c2e23d2a Cleanups to reactive logging code for R CMD check
* Fix 'env' partial argument match for get().
* Fix unbound variable access of .shiny__stdout.
* Restructure if statement so test is only done once.
2013-08-23 12:16:18 -05:00
Winston Chang
40cc5d5242 Doc fixes for R CMD check 2013-08-23 12:07:34 -05:00
Winston Chang
9765194ace Bump httpuv version dependency to 1.1.0 2013-08-23 10:16:47 -05:00
Winston Chang
628465e6b5 Update NEWS 2013-08-23 10:16:12 -05:00
Winston Chang
58706df120 Update sliderInput docs 2013-08-23 10:16:05 -05:00
Joe Cheng
b19225c747 Don't send websocket subprotocol
Some time recently, Google Chrome started actually caring what the
client sends for this and what the server replies with. httpuv
doesn't currently have any logic for subprotocol selection, so
it always replies with no Sec-WebSocket-Protocol header, which
now Google Chrome reacts to by closing the websocket connection.

This problem goes away if we just don't send a subprotocol at all.
2013-08-23 02:15:54 -07:00
Joe Cheng
c304889e61 Merge pull request #208 from trestletech/master
Add reactive timing logging, if the necessary variables are available.
2013-08-22 13:55:11 -07:00
trestletech
05a9204678 Added timestamp to reactive log. 2013-08-22 11:20:26 -05:00
hadley
ed8537bb0b Redue duplication in docs betwee date/dateRange 2013-08-14 13:37:14 -05:00
hadley
6a9ae10fcf Add cross-references between input types 2013-08-14 13:33:44 -05:00
hadley
05358904bf Re-document 2013-08-14 13:31:16 -05:00
hadley wickham
1a6901c3e3 Document range slider 2013-08-14 13:25:42 -05:00
Winston Chang
7aaba8244b Add is.reactivevalues function 2013-08-05 14:02:50 -05:00
Winston Chang
8c45dcde88 Merge pull request #205 from hadley/master
Class output of reactive.
2013-08-05 12:02:14 -07:00
Winston Chang
6c155b04b2 Merge pull request #197 from wch/fix-style
Add compatibility wrapper for getComputedStyle in IE8
2013-07-27 17:43:01 -07:00
Winston Chang
cd8ad9a2ec Add compatibility wrapper for getComputedStyle in IE8 2013-07-27 19:41:56 -05:00
hadley
a5db7d0246 Class output of reactive.
Also add print method and test
2013-07-27 11:06:35 -05:00
trestletech
b84b467b96 Added logging of start/stop flushing of reactives using a param provided via HTTP headers. 2013-07-24 17:40:24 -04:00
trestletech
0812aaac88 Merge remote-tracking branch 'rstudio/master' 2013-07-24 08:59:34 -04:00
Joe Cheng
194d2f911e Changes to public-facing session object
- Change from list to environment. This enables the next feature:
- Make $request a promise. websocket$request breaks on some (earlier?) versions
  of httpuv. I'm not sure why this is but in the few hours since I submitted
  the websocket$request change we've had a number of complaints. This way the
  error only occurs if the app actually asks for session$request.
- Make the private session object accessible via `.impl`. Obviously any data or
  methods on the private session object are unsupported but they are there if
  you are desperate and don't mind possible future breakage.
2013-07-23 15:39:27 -07:00
Joe Cheng
e360b36b8a Make WS request available on session object 2013-07-22 15:15:27 -07:00
trestletech
b6f66dd287 Moved print out so we can put it where it belongs. 2013-07-16 08:01:22 -07:00
trestletech
0a4bb48cd3 Added output of process ID on app start. 2013-07-15 14:26:25 -07:00
Joe Cheng
15d62d4a91 Add instructions for installing from GitHub 2013-07-12 01:32:49 +02:00
Joe Cheng
5b13c44ef9 Fix isolate return value bug (issue #200) 2013-07-12 01:30:07 +02:00
Joe Cheng
0a4250f3b4 Merge pull request #193 from jcheng5/reactlog
React log
2013-07-09 00:58:53 -07:00
Joe Cheng
f79223ed58 Merge remote-tracking branch 'origin/master' into reactlog 2013-07-07 01:20:59 -07:00
Joe Cheng
2d28218a2a Allow Cmd+F3 to launch reactlog 2013-07-07 01:20:40 -07:00
Joe Cheng
35974f2ee1 Firefox scrubbing fix 2013-07-06 23:13:25 -07:00
Joe Cheng
1f73323fb9 reactlog: Support arbitrary temporal movement 2013-07-06 23:06:38 -07:00
Joe Cheng
a3d0736eec Use more obscure keyboard shortcut for reactlog 2013-07-06 18:27:10 -07:00
Joe Cheng
4bdd486c00 reactlog: Firefox compatibility; visual tweaks 2013-07-06 18:22:40 -07:00
Joe Cheng
c3895c9bd7 Configurable hover delay type (debounce/throttle) 2013-07-05 17:29:57 -07:00
Joe Cheng
e9ddd89b32 Simpler math 2013-07-05 15:08:42 -07:00
Joe Cheng
88a8f2d609 Fix locator Retina compatibility. Again.
Was working locally on Macs but not on spark.rstudio.com accessed via rMBP.
2013-07-05 13:52:56 -07:00
Joe Cheng
a5dc5c89e8 Firefox locator compat 2013-07-05 12:30:14 -07:00
Joe Cheng
3a15a35137 Merge pull request #192 from jcheng5/flush-callbacks
Add session$onFlush and session$onFlushed
2013-07-05 02:10:02 -07:00
Joe Cheng
b644640804 Add session$onFlush and session$onFlushed 2013-07-05 02:07:51 -07:00
Joe Cheng
aaa4f66671 Merge pull request #183 from jcheng5/plot-mouse-events
Click and hover on static plots. Also, fix retina compatibility and make hover delay configurable
2013-07-04 23:43:54 -07:00
Joe Cheng
07e021199e Use crosshair cursor when plot supports hover/click 2013-07-04 23:42:44 -07:00
Joe Cheng
6b2ca7dc80 Merge pull request #182 from jcheng5/reactive-poll
Implement reactivePoll and reactiveFileReader
2013-07-04 23:31:16 -07:00
Joe Cheng
091d62803e Merge pull request #191 from trestletech/master
Restrict the number of observations to a valid, positive number.
2013-07-04 12:15:57 -07:00
trestletech
547999bae0 Restrict the number of observations to a valid, positive number. 2013-07-03 23:06:42 -05:00
Joe Cheng
99013f7998 Launch reactlog from Shiny app with F3 2013-07-03 12:17:36 -07:00
Joe Cheng
fc396800db reactlog: Automatically run first step 2013-07-03 08:56:28 -07:00
Joe Cheng
6d03ae57ac reactlog: Show value changes 2013-07-03 00:05:28 -07:00
Joe Cheng
4a0aa57355 reactlog node shapes, visible labels 2013-07-02 17:48:09 -07:00
Joe Cheng
7db737494c Reverse reactlog arrow orientation 2013-07-02 13:21:39 -07:00
Joe Cheng
b285501c44 reactlog code cleanup 2013-07-02 08:38:24 -07:00
Joe Cheng
2f9b29994f Add showReactLog function 2013-07-02 03:17:30 -07:00
Joe Cheng
917434cb6b Introduce shiny.reactlog function 2013-07-02 03:03:29 -07:00
Joe Cheng
28a52bb658 More visual improvements to reactlog 2013-07-02 02:59:37 -07:00
Joe Cheng
82bc19374c Improve appearance of reactlog 2013-07-02 02:12:07 -07:00
Joe Cheng
0b23f30bb7 Work in progress 2013-07-02 01:29:33 -07:00
Joe Cheng
64a62d7aed Add doc for workerId param 2013-06-28 13:55:45 -07:00
Joe Cheng
de31cf8e7d Merge pull request #175 from trestletech/master
Add workerID to upload, download, and file URLs.
2013-06-28 13:52:38 -07:00
Winston Chang
3484f9afb3 Merge pull request #179 from wch/faster-tags
Faster tags
2013-06-25 09:57:34 -07:00
Winston Chang
81df0ff390 Fix typo 2013-06-25 11:40:54 -05:00
Winston Chang
d403ec7399 Make hover delay configurable 2013-06-24 16:39:04 -05:00
Winston Chang
6ac77835df Fix Retina compatibility (revert b113119) 2013-06-24 16:31:43 -05:00
Joe Cheng
b113119a9a Retina display compatibility 2013-06-21 21:38:23 -07:00
Joe Cheng
b713057614 Implement click and hover events on static plots
plotOutput now takes clickId and hoverId params that tell Shiny
where to send click and hover events for that plot. The server.R
file can listen on input$<clickId> and/or input$<hoverId>. In
both cases, the resulting value will have numeric x and y elements
that indicate the mouse position in user coordinates. In the case
of hover events, it's also possible to have a NULL value which
means the mouse is not currently hovering over the plot.
2013-06-21 16:58:43 -07:00
Winston Chang
4268570166 Add tests for escaping in tags 2013-06-20 14:13:11 -05:00
Winston Chang
ead508c0d0 Preserve attributes in child tags 2013-06-20 12:16:51 -05:00
Joe Cheng
f8e1be8565 Update shiny-package 2013-06-19 13:09:48 -07:00
Winston Chang
360f1af32f Export tagSetChildren and tagAppendChildren 2013-06-19 15:01:45 -05:00
Joe Cheng
d897df6a30 Implement reactivePoll and reactiveFileReader 2013-06-19 09:16:04 -07:00
Winston Chang
ba4f3a1553 Speed up input update functions 2013-06-19 00:34:57 -05:00
Winston Chang
6ba9534da4 In tag functions, drop NULL attributes 2013-06-19 00:25:46 -05:00
Winston Chang
c16ef96754 Don't use named list items for selectInput and radioButtons
The names aren't used anyway, and this matches previous behavior (Shiny 0.6.0)
2013-06-18 23:53:30 -05:00
Winston Chang
e728491aa2 Refactor checkboxGroupInput 2013-06-18 23:52:41 -05:00
Winston Chang
ce356fa266 Fix handling of empty tags 2013-06-18 23:52:13 -05:00
Winston Chang
5e46323ca3 Refactor tag()
This is much faster when there are large lists of children (and the code is
much simpler!)
2013-06-18 23:33:28 -05:00
Winston Chang
0a7d047246 Add tests for creating nested tags 2013-06-18 22:40:17 -05:00
Winston Chang
3fa534a3eb Add tests for adding children 2013-06-18 22:39:34 -05:00
Winston Chang
c6405f70d3 Add tagSetChildren() and tagAppendChildren() 2013-06-18 20:12:04 -05:00
Joe Cheng
acae6c2c49 Expose session$input and session$output
This makes it possible for packaged Shiny components to only ask
for the session variable to get access to all inputs and outputs
(along with the other good stuff on session).
2013-06-18 17:08:48 -07:00
Joe Cheng
141fdc2197 Do away with dependsOnFile error
This error is causing more problems than it solves.
2013-06-18 17:07:42 -07:00
Joe Cheng
a7ed8a006f includeText should be HTML escaped 2013-06-18 17:07:08 -07:00
Joe Cheng
b1a0ebd531 Add includeCSS and includeScript functions 2013-06-18 17:06:34 -07:00
Winston Chang
e8021acccd Speed up radioButtons when there are many choices 2013-06-17 23:49:50 -05:00
Winston Chang
39b0da2a3f Speed up selectInput when there are many choices 2013-06-17 23:44:13 -05:00
Joe Cheng
fd3d18f6c5 Add stopApp function, for returning a value from runApp 2013-06-14 22:27:58 -07:00
trestletech
ecc27d1674 Incorporated a worker ID specification.
Accept this as a parameter from the runApp() function and pass it through into the shinysession object so that it can be used in file uploads, downloads, and HTTP image fallbacks on non-websocket browsers.
2013-06-13 21:34:10 -07:00
Joe Cheng
7d0514ab36 Merge pull request #172 from wch/cairo-option
Add option for not using Cairo
2013-06-12 10:10:14 -07:00
Winston Chang
44c3024c00 Add option for not using Cairo 2013-06-12 10:56:21 -05:00
Winston Chang
253c92bab7 Bump version to 0.6.0.99 for development 2013-06-12 10:53:51 -05:00
Joe Cheng
c10850118d Merge pull request #170 from hadley/master
Fix typo
2013-06-11 13:26:14 -07:00
Joe Cheng
4f017e9173 Remove annoying title="foo" tooltip on all tabset tabs 2013-06-11 09:19:49 -07:00
Joe Cheng
5ed46c82cb Document observer methods 2013-06-11 09:18:57 -07:00
hadley wickham
64391e906d Update reactives.R
Add newline (guessing that's how it's supposed to be)
2013-06-11 10:37:02 -05:00
Joe Cheng
47b4ee07ab Merge pull request #165 from trestletech/master
Export validateCSSUnit function
2013-06-10 11:32:01 -07:00
trestletech
3000cbf763 Reorder namespace using latest roxygen2 code. 2013-06-07 16:33:19 -05:00
trestletech
76b3d314a8 Exported validateCSSUnit function. 2013-06-05 15:15:54 -05:00
Winston Chang
ba646de0ad Bump version to 0.6.0 2013-06-05 12:12:46 -05:00
Winston Chang
395f746a05 Update NEWS 2013-06-04 22:20:07 -05:00
Winston Chang
f7e57cd398 Update NEWS 2013-06-04 21:21:26 -05:00
Winston Chang
3ea6d97ed2 Documentation updates 2013-06-04 21:21:26 -05:00
Winston Chang
affc0d8b67 Remove sendJavascript 2013-05-31 10:48:39 -05:00
Joe Cheng
c637e310e9 Merge pull request #164 from jcheng5/suspend-bugfix
Fix several bugs relating to suspendWhenHidden
2013-05-28 08:58:52 -07:00
Joe Cheng
6ee7dcdd51 Fix several bugs relating to suspendWhenHidden
- If an output is bound in the UI before it exists on the server, the
  server will suspend the new output until something else causes
  manageHiddenOutputs to be triggered. The refactor in shiny.R sets
  the suspended flag to .shouldSuspend.
- As part of the previous refactor, we don't set suspendOnHidden in
  defineOutput; instead we leave it empty and .shouldSuspend knows
  what the default should be.
- In shiny.js, unbound controls were not being suspended. This was
  fixed by having sendOutputHiddenState be stateful; anything in
  a visible state that disappears on the next iteration is marked
  as hidden. Also unbindAll now calls sendOutputHiddenState.
2013-05-27 12:52:33 -07:00
Joe Cheng
23470267fe Merge pull request #163 from jcheng5/app-object
Run apps without creating files on disk
2013-05-24 22:07:36 -07:00
Winston Chang
4a92bb91df Initialize slider at correct time 2013-05-24 18:58:47 -05:00
Joe Cheng
69522c422c Fix rendering issues when slider gets too wide
Synced to jslider commit (from rstudio/jslider fork):
6de6ef7b2c788cbcec74dd51e0008e12247e6638
2013-05-23 12:02:43 -07:00
Joe Cheng
bc5e3524eb Export basicPage 2013-05-23 11:56:55 -07:00
Joe Cheng
479297fc35 Add basicPage function; like bootstrapPage with padding 2013-05-23 11:23:28 -07:00
Joe Cheng
516feafcfb Run apps without creating files on disk
With this commit, runApp can now accept a list instead of a path.
This list should have elements named "ui" and "server" that contain
what would normally go in shinyUI and shinyServer, respectively.
(Note that there is no equivalent to global.R, nor should there
need to be since you can just directly execute in the global env
before calling runApp.)

Example:

runApp(list(
  ui = bootstrapPage(
    numericInput('n', 'Number of obs', 100),
    plotOutput('plot')
  ),
  server = function(input, output) {
    output$plot <- renderPlot({ hist(runif(input$n)) })
  }
))
2013-05-23 10:00:38 -07:00
Joe Cheng
a135c82ab5 Merge pull request #160 from wch/reactive-timer
reactiveTimer: don't invalidate when session closed
2013-05-15 14:08:56 -07:00
Winston Chang
10996f1cbd reactiveTimer: don't invalidate when session closed 2013-05-15 15:50:37 -05:00
Joe Cheng
23b060e1f5 Merge pull request #159 from wch/session-invalidate
invalidateLater: don't invalidate when session closed
2013-05-15 11:32:09 -07:00
Winston Chang
622ff3a256 invalidateLater: don't invalidate when session closed 2013-05-15 12:41:07 -05:00
Winston Chang
5d457b6834 Bump httpuv version dependency 2013-05-15 12:09:21 -05:00
Winston Chang
f10f76d127 Fixes for R CMD check 2013-05-13 10:54:54 -05:00
Winston Chang
58f3382daf Update NEWS 2013-05-10 14:09:51 -05:00
Winston Chang
0e1139446e Merge pull request #149 from jcheng5/observer-priorities
Add priority levels to observers
2013-05-10 12:03:28 -07:00
Winston Chang
f433216fae Merge pull request #156 from jcheng5/fix-submit-button
Fix submit button interactions with tabs, plot sizes
2013-05-10 12:02:14 -07:00
Winston Chang
ed680baaac selectInput: correctly handle choices with duplicate names. Fixes #157 2013-05-08 18:32:05 -05:00
Winston Chang
e0a9d908ed Update NEWS 2013-05-06 11:14:42 -05:00
Winston Chang
bfa4a46bd5 Merge pull request #154 from wch/tag
Add withTags function
2013-05-06 08:46:09 -07:00
Winston Chang
03f3ff991e Update NEWS 2013-05-06 10:45:41 -05:00
Winston Chang
619b4824f0 Add tests for withTags 2013-05-06 10:45:41 -05:00
Winston Chang
021af0186b Add withTags function 2013-05-06 10:45:41 -05:00
Winston Chang
d3caad8b8d Add comment in test 2013-05-01 01:17:07 -04:00
Winston Chang
ec6bec3326 Fix selected items in checkboxGroupInput 2013-05-01 01:12:03 -04:00
Joe Cheng
dd54740d36 Fix submit button interactions with tabs, plot sizes 2013-04-29 13:08:04 -07:00
Winston Chang
8f65156bda Add Makefile for building web assets 2013-04-24 12:29:46 -05:00
Winston Chang
96c7df5afa Documentation fixes 2013-04-24 12:27:35 -05:00
Winston Chang
0c19105fbf Add dateInput and dateRangeInput
Also:
* add initialize() method for input bindings
* cleanups for JShint
2013-04-24 12:18:20 -05:00
Joe Cheng
4145d83248 Merge remote-tracking branch 'jcheng5/session-close-callbacks'
Conflicts:
	R/shiny.R
2013-04-23 15:40:38 -07:00
Winston Chang
6490705e2a Clarification of tagList help 2013-04-23 12:06:22 -05:00
Joe Cheng
10d2432df5 Merge pull request #148 from wch/sendmessage
Add support for sending arbitrary messages to client
2013-04-22 16:40:01 -07:00
Joe Cheng
815db72671 Add callback for session end 2013-04-22 16:29:40 -07:00
Joe Cheng
6d0ba61c54 Add priority to outputOption 2013-04-19 15:48:56 -07:00
Joe Cheng
5f61267f75 Allow integers or numerics for priority 2013-04-19 15:42:18 -07:00
Joe Cheng
94ee42cebb Add priority levels to observers
Observers can now take priority levels, which allow the user to control
the order of execution. Note that reactive expressions do not have
priority levels; since they are lazily evaluated, it wouldn't make any
sense to speak of priorities.

Another commit will be needed to add an API for changing the priorities
of outputs (probably in outputOptions?).
2013-04-19 10:34:34 -07:00
Winston Chang
b6795e5c63 Move actionbutton into Shiny proper 2013-04-18 15:11:13 -05:00
Winston Chang
ef85d063c2 Make external message handlers use 'custom' 2013-04-18 13:40:32 -05:00
Winston Chang
59755971e5 Add message handlers 2013-04-18 12:40:19 -05:00
Winston Chang
c5ab831a87 Fixes for jshint
Conflicts:
	inst/www/shared/shiny.js
2013-04-18 11:41:52 -05:00
Winston Chang
6715dc2a5d Merge pull request #139 from wch/set-input
Add ability to set inputs from server
2013-04-17 14:58:14 -07:00
Winston Chang
af6de64ec0 Add clientData to session object 2013-04-10 13:02:48 -05:00
Winston Chang
1ac2448f90 Split sendMessage into type-specific functions 2013-04-10 12:30:58 -05:00
Winston Chang
b5f34b30d3 Add documentation for input updater functions 2013-04-10 12:13:53 -05:00
Winston Chang
01f4e080df Workaround for Cairo resolution bug 2013-04-09 17:13:26 -05:00
Winston Chang
d55335e70b Make updateFooInput functions behave like fooInput functions 2013-04-08 18:38:16 -05:00
Winston Chang
a8c1dc4bc6 Add dropNulls function 2013-04-08 18:13:26 -05:00
Winston Chang
2897059503 Allow setting labels for all input objects 2013-04-08 18:09:40 -05:00
Winston Chang
d491f9df5a updateCheckboxGroupInput: clean up and allow setting labels 2013-04-08 17:01:00 -05:00
Winston Chang
bc40318e40 Wrap slider in div so that it can be manipulated more easily 2013-04-08 16:01:31 -05:00
Joe Cheng
3935434f04 Merge branch 'domain-sockets' 2013-04-06 20:44:40 -07:00
Joe Cheng
4cf1f2de94 Fix bug where multiple file uploads get same path
https://groups.google.com/d/msg/shiny-discuss/Km_pQL87paM/4V0AiIoTgwIJ

The problem is that length(.files) gives the number of columns in the
data frame, not the number of rows. So it's always either 0 or 4.
2013-04-06 18:14:21 -07:00
Winston Chang
73156c6780 Add update-input functions 2013-04-06 09:32:01 -05:00
Winston Chang
bc52bafa8d Add methods for setting bootstrapTabInputBinding 2013-04-06 01:00:18 -05:00
Winston Chang
5c9007b242 Streamline tests 2013-04-06 00:28:26 -05:00
Winston Chang
5857e3f75e Allow setting value of sliders when they have range 2013-04-05 22:56:12 -05:00
Winston Chang
e202831013 Add empty message tests 2013-04-05 21:57:45 -05:00
Winston Chang
4cbbfccb6d Make sure not to bind new-style multi-inputs 2013-04-05 21:57:45 -05:00
Winston Chang
21f3b1cf34 Add checkboxGroupInputBinding 2013-04-05 21:57:45 -05:00
Winston Chang
f7b384e9b6 Add radioInputBinding 2013-04-05 21:57:45 -05:00
Winston Chang
1e6ab47ee4 Fixes to tests for jshint 2013-04-05 21:57:45 -05:00
Winston Chang
78341ea2f1 Trigger change when message sent to input bindings 2013-04-05 21:57:45 -05:00
Winston Chang
3f8a4d4273 Add getState and receiveMessage support to selectInputBinding 2013-04-05 21:57:45 -05:00
Winston Chang
bae517c9f8 Align checkboxes with labels 2013-04-05 21:57:45 -05:00
Winston Chang
c88ccbf9bc Put inputMessage in a queue 2013-04-05 21:57:45 -05:00
Joe Cheng
5e40f5d509 Allow listening on domain sockets 2013-04-02 15:24:24 -07:00
Winston Chang
46389131bc Add functions for sending messages to client 2013-03-30 21:07:22 -05:00
Joe Cheng
c6a344d0d9 Add selected argument to tabsetPanel 2013-03-29 23:14:01 -07:00
Winston Chang
bcc2c377a0 Add messenge-sending support 2013-03-29 14:32:34 -05:00
Winston Chang
bb6afc847e Add input binding tests 2013-03-29 14:30:22 -05:00
Winston Chang
e0193151db shiny.js: allow setting inputs and getting input state 2013-03-28 16:15:24 -05:00
Winston Chang
42a80bad8e Undo license change 2013-03-28 10:45:26 -05:00
Joe Cheng
6e3e77f65d Code review feedback for version checking stuff 2013-03-25 11:26:16 -07:00
Winston Chang
e155f022a0 Bump version to 0.5.0 2013-03-22 22:49:55 -05:00
Joe Cheng
db65aab347 Give warning message on obsolete Shiny Server version 2013-03-22 14:33:44 -07:00
Winston Chang
a180c5f357 renderPlot: just return if width or height is NULL 2013-03-22 12:52:43 -05:00
Winston Chang
1c0279f17c Undo commit 'Remove redundant code in shiny.js'
The code wasn't actually redundant.
2013-03-22 12:51:29 -05:00
Winston Chang
8866eb292b Add section breaks in shiny.js 2013-03-21 21:09:16 -05:00
Winston Chang
6fdda3391e Send initial value of URL hash 2013-03-20 16:53:40 -05:00
Winston Chang
fdb8dd4e5b Remove redundant code in shiny.js 2013-03-20 13:22:21 -05:00
Joe Cheng
9a1d3783ee Unbind controls in htmlOutput before displaying error 2013-03-19 13:32:14 -07:00
Winston Chang
3841d9e322 Update license information 2013-03-18 15:33:12 -07:00
Winston Chang
e392eadf8a Update NEWS 2013-03-18 11:22:27 -07:00
Winston Chang
f743d5d0b5 New method for detecting hidden outputs 2013-03-17 11:08:28 -07:00
Winston Chang
4a76bf59ef Fix access to .clientData 2013-03-17 09:54:18 -07:00
Winston Chang
205b29e2f5 Update NEWS 2013-03-15 16:44:42 -05:00
Winston Chang
d511b82264 Add imageOutput function 2013-03-15 16:44:18 -05:00
Winston Chang
aaae112e60 Add to on.exit() instead of replace 2013-03-15 16:26:05 -05:00
Winston Chang
955fd6207f Change license to GPL>=2 2013-03-15 12:32:05 -05:00
Joe Cheng
4e56c96612 Fix allowDataUriScheme 2013-03-13 11:13:26 -07:00
Joe Cheng
dd046f3442 Merge remote-tracking branch 'jcheng5/master'
Conflicts:
	R/shiny.R
2013-03-13 10:47:06 -07:00
Winston Chang
5a947f83a1 Separate private and public fields for input and clientData 2013-03-12 21:41:38 -05:00
Winston Chang
b87b8b54fd Update NEWS 2013-03-12 16:14:28 -05:00
Winston Chang
233c0537a1 Merge pull request #122 from wch/url
Send URL components in clientData
2013-03-12 13:42:05 -07:00
Winston Chang
63d4798a50 Add tests for parseQueryString 2013-03-12 14:32:52 -05:00
Winston Chang
6c47517684 Move allowDataUriScheme into .clientdata 2013-03-12 14:24:48 -05:00
Winston Chang
c58b1a0143 Add parseQueryString function 2013-03-12 14:24:48 -05:00
Joe Cheng
f489d9131b File uploads failed when no content type was provided
The simple fix for this would've been to just guess the content
type on the server (or use empty string or something), but by
doing the fix this way we're also set up to handle uploads by
servers that don't allow custom headers on AJAX calls.
2013-03-12 09:50:01 -07:00
Winston Chang
f0109c5588 Send URL in clientdata 2013-03-11 18:44:58 -05:00
Winston Chang
c16becba56 renderTable: check for empty data frame. Fixes #55 2013-03-11 18:43:57 -05:00
Winston Chang
4605788696 Add informative comments 2013-03-11 18:43:03 -05:00
Joe Cheng
87908313cc Merge remote-tracking branch 'jcheng5/httpuv'
Conflicts:
	R/shiny.R
2013-03-11 09:36:47 -07:00
Winston Chang
9cc2eba7b8 Merge pull request #109 from wch/retina
Add clientdata channel for sending data

This branch includes support for Retina-resolution displays.
2013-03-11 07:45:24 -07:00
Winston Chang
2459cee57b Add renderImage function 2013-03-11 09:39:39 -05:00
Winston Chang
0bf6ce57ed renderPlot: send height and width along with image 2013-03-11 09:39:38 -05:00
Winston Chang
7041424f96 Add plotPNG function 2013-03-07 17:12:24 -06:00
Winston Chang
9509285c16 Rename ShinySession to saveFile 2013-03-07 17:12:24 -06:00
Winston Chang
e55ee0e65d Create ShinySession$sendFile() and use from renderPlot() 2013-03-07 17:12:18 -06:00
Winston Chang
9ea70497c2 Bump version to 0.4.1.99 for development 2013-03-06 17:26:35 -06:00
Joe Cheng
3389b9e9fd Memory-efficient file downloads 2013-03-05 23:06:13 -08:00
Winston Chang
76d4d54639 Allow shinyServer() to take clientData argument 2013-03-05 19:32:10 -06:00
Winston Chang
1b692b6c37 Rename shinyapp to shinysession, and .shinyout_xx to .clientdata_output_xx 2013-03-05 19:07:36 -06:00
Winston Chang
40d8cef1a2 Rename .metadata to clientData 2013-03-05 16:21:02 -06:00
Winston Chang
23550c0062 Add manageInputs() to handle metadata and normal inputs 2013-03-05 16:07:28 -06:00
Winston Chang
949bd940ee Partial change to reactiveValues 2013-03-04 20:33:29 -06:00
Winston Chang
79bdb9eed5 Add shiny metadata channel and send pixel ratio
This adds support for retina-resolution displays
2013-03-04 20:33:29 -06:00
Winston Chang
a141f08298 Bump version to 0.4.1 2013-03-04 20:27:48 -06:00
Joe Cheng
dee43a3911 Don't animate when showing file upload error 2013-03-04 14:47:26 -08:00
Joe Cheng
ef227d0139 More memory-efficient file uploading 2013-03-04 11:14:39 -08:00
Winston Chang
cbcf9ce645 reactivePlot: fix infinite recursion when height/width is function 2013-03-04 11:20:14 -06:00
Joe Cheng
0e5af2b16c Check for excessively large uploads before they begin
The onHeaders callback is supposed to be able to stop large uploads before
they begin, but do not appear to be having the desired effect. The browsers
continue uploading until completion, before noticing the response. To work
around this for now, upload the sizes explicitly when the job begins and
let Shiny pre-emptively reject the whole thing. This is also beneficial
in cases where multiple files are being uploaded and not all of them
exceed the maximum upload size.
2013-03-01 19:31:18 -08:00
Joe Cheng
85ca3a3b27 Update upload docs 2013-03-01 15:54:19 -08:00
Joe Cheng
fc5f5f3b6c Don't initiate file upload if no files were chosen 2013-02-28 21:14:06 -08:00
Joe Cheng
716fd8c0b9 File upload improvements
- Add "shiny.maxRequestSize" option
- Show upload progress
2013-02-28 21:06:06 -08:00
Joe Cheng
a517393c43 Remove dead upload code 2013-02-28 07:46:31 -08:00
Joe Cheng
c2311faffe httpuv-style file uploading
Use HTTP POST to upload files rather than sending 4K chunks
one at a time over the websocket. This is massively faster and
also means no binary websocket support is needed. In theory
this approach should be compatible with Shiny Server.

Currently the client side code still uses File API which means
IE8 and 9 users are out of luck.
2013-02-27 16:47:18 -08:00
Joe Cheng
fe453b0d66 Restore filter functionality 2013-02-26 16:59:56 -08:00
Joe Cheng
7e75b0fc02 eventloop package renamed to httpuv 2013-02-26 16:27:26 -08:00
Joe Cheng
11b0a0a73d Conform to API changes in eventloop package 2013-02-25 20:06:21 -08:00
Joe Cheng
82fdb5c3eb Greatly improve responsiveness of interruption on Windows 2013-02-25 15:14:24 -08:00
Joe Cheng
3f1d532c8b Restore startApp/serviceApp division of labor 2013-02-25 15:14:23 -08:00
Joe Cheng
f258b00aa7 Initial implementation on eventloop
Timers don't work yet
2013-02-25 15:13:30 -08:00
Winston Chang
4e71b9576d Update NEWS 2013-02-25 15:11:24 -06:00
Winston Chang
f36567a5cd reactivePlot: correctly pass width and height to renderPlot 2013-02-25 12:35:39 -06:00
Winston Chang
924ebb6c7f Bump version to 0.4.0.99 for development 2013-02-22 15:30:04 -06:00
Winston Chang
6e7e8eb44a Bump version to 0.4.0 2013-02-21 16:50:01 -06:00
Winston Chang
308c583254 setRatePolicy based on effectiveId. Fixes #110
Previously when getType() was defined for a type of object, shiny.js would
send updates immediately instead of applying the rate policy.
2013-02-20 11:39:22 -06:00
Winston Chang
97b2f7e5ca Fix call to manageHiddenOutputs in timer callback. Fixes #112 2013-02-19 12:20:49 -06:00
Winston Chang
3ea88a07d9 sliderInputBinding inherits from text instead of number. Fixes #110 2013-02-18 22:25:38 -06:00
Winston Chang
588f8bb96a Merge pull request #107 from wch/numeric-na
Empty numericInput gets converted to NA
2013-02-18 14:01:04 -08:00
Winston Chang
c93c0dd721 Update NEWS 2013-02-18 16:00:25 -06:00
Winston Chang
fc59c254fd Merge pull request #108 from wch/unused-hidden
Treat unused outputs as hidden
2013-02-18 13:56:49 -08:00
Winston Chang
2f8b6a150f Treat unused outputs as hidden 2013-02-18 15:53:31 -06:00
Winston Chang
db60ac5c17 Empty numericInput gets converted to NA 2013-02-18 15:11:41 -06:00
Winston Chang
e1f09853c5 Make shiny.deprecation.messages option actually work 2013-02-17 16:17:41 -06:00
Winston Chang
24656713a5 Remove unnecessary function() in renderXX 2013-02-17 12:02:00 -06:00
Winston Chang
7dd0269292 Update NEWS 2013-02-14 14:09:42 -06:00
Winston Chang
8b87cea7aa Merge pull request #104 from wch/reactive-exp
Change reactive() and observe() to take expressions
2013-02-14 12:08:18 -08:00
Winston Chang
c7559a6946 Suspend overwritten output objects 2013-02-14 12:14:08 -06:00
Winston Chang
945c6080ad Export exprToFunction 2013-02-14 11:48:01 -06:00
Winston Chang
44590965d1 Add renderXX Rd files 2013-02-14 11:48:01 -06:00
Winston Chang
7ab64d678f reactivePlot: call height and width properly 2013-02-14 11:48:01 -06:00
Winston Chang
e406a76b62 Update documentation for renderXX 2013-02-14 11:48:01 -06:00
Winston Chang
e26f175a8f Change reactiveXX to renderXX 2013-02-13 12:11:39 -06:00
Winston Chang
d4ab84745d Make function for expr-to-function conversion 2013-02-12 15:55:51 -06:00
Winston Chang
32dbc3101e Add shinyDeprecated function 2013-02-12 15:24:50 -06:00
Winston Chang
0a924eb718 Fix deprecation message for observe() 2013-02-12 15:24:50 -06:00
Winston Chang
a284327bfc Re-roxygenize 2013-02-12 15:24:50 -06:00
Winston Chang
2ea38d6ecc Clean up instances of reactive() and observe() 2013-02-12 15:24:50 -06:00
Winston Chang
6a34bbfddd Add label argument to reactive and observe 2013-02-12 15:24:50 -06:00
Winston Chang
58323ada4b Change references of reactive 'functions' to 'expressions' 2013-02-12 15:24:49 -06:00
Winston Chang
5fd723cb80 reactive() and observe() now take expressions 2013-02-12 15:24:49 -06:00
Winston Chang
5c626e6957 Documentation fixes 2013-02-12 15:24:39 -06:00
Winston Chang
5d949842eb Add garbage collection tests 2013-02-11 20:26:23 -06:00
Winston Chang
b595c17d78 observe: add option to start suspended 2013-02-11 19:48:22 -06:00
Winston Chang
b84973ba2b Remove leftover testing string 2013-02-11 19:36:06 -06:00
Winston Chang
61be49e7b2 Merge pull request #97 from wch/suspend-hidden
Suspend hidden outputs. Fixes #24
2013-02-11 16:48:39 -08:00
Winston Chang
8faf5659ee Re-roxygenize 2013-02-11 18:47:53 -06:00
Winston Chang
cc9267a646 manageHiddenOutputs: check that output object exists 2013-02-11 18:45:45 -06:00
Winston Chang
55838bb032 Call manageHiddenOutputs after timer callbacks 2013-02-11 18:37:18 -06:00
Winston Chang
67619ac5e8 Don't allow another flush if currently in one 2013-02-11 18:35:32 -06:00
Winston Chang
952b342859 Better checks for hidden output objects 2013-02-11 18:31:44 -06:00
Winston Chang
c7149c460d Add documentation for suspendWhenHidden option 2013-02-11 16:08:30 -06:00
Winston Chang
fd0613ea0e Call manageHiddenOutputs when suspendWhenHidden is set 2013-02-11 15:16:04 -06:00
Winston Chang
36d2dddc59 Run manageHiddenOutputs on app init 2013-02-09 00:02:52 -06:00
Joe Cheng
63c5b05584 Stop extra update message from occurring on startup 2013-02-08 16:37:55 -08:00
Winston Chang
4b235e5b87 Send output hidden state on init 2013-02-07 14:29:03 -06:00
Winston Chang
6c51fffdaa Fix tests 2013-02-07 14:29:03 -06:00
Winston Chang
5d6d638c85 Clarify suspend description 2013-02-07 14:29:03 -06:00
Winston Chang
90eb515167 Observer: .flushCallbacks to .invalidateCallbacks 2013-02-07 14:29:03 -06:00
Joe Cheng
17526711a2 Change resume behavior for Observer
Eliminate multiple runs when resumed multiple times
2013-02-07 14:29:03 -06:00
Winston Chang
cf0118e090 Add tests for suspended observers 2013-02-07 14:29:03 -06:00
Winston Chang
868d6fec42 Add suspended option to Observer 2013-02-07 14:29:03 -06:00
Winston Chang
851f5854bf Add outputOptions function 2013-02-07 14:29:03 -06:00
Winston Chang
eb5428c971 Suspend hidden outputs 2013-02-07 14:29:03 -06:00
Winston Chang
81188df7ef Update runUrl help and re-document 2013-02-07 10:46:20 -06:00
Winston Chang
9fd365cc41 isolate help: mention debugging use and fix typos 2013-02-06 14:38:12 -06:00
Winston Chang
999df6e40f httpResponse: make sure headers is a list. Fixes #102 2013-02-06 12:29:24 -06:00
Winston Chang
076d069568 runGist: accept new URL format with username 2013-02-06 12:06:14 -06:00
Joe Cheng
2738648197 Merge pull request #101 from jcheng5/chrome-frame
Chrome Frame compatibility
2013-02-05 15:18:03 -08:00
Joe Cheng
36013009a1 Chrome Frame compatibility 2013-02-05 15:15:03 -08:00
Winston Chang
1b60233862 Fix closing brace in isolate help 2013-02-05 10:56:54 -06:00
Winston Chang
2cba10dd05 Follow redirects with curl for http
The previous logic added the -L option to curl when downloading https, but
    not for http.
2013-02-04 13:06:15 -06:00
Winston Chang
b3944127ea Add note about using local() with isolate() 2013-02-01 15:16:33 -05:00
Winston Chang
f1674378ca Remove unneeded reactive() wrappers 2013-01-31 15:47:02 -05:00
Winston Chang
6f0191e1cf Block some operators for shinyoutput objects 2013-01-31 15:45:31 -05:00
Winston Chang
1848844be6 Cleaner method for creating objects with class 2013-01-30 15:06:17 -05:00
Winston Chang
8b6362c749 Add section markers 2013-01-30 15:04:55 -05:00
Winston Chang
d860d13361 Add comments to test 2013-01-30 15:04:50 -05:00
Winston Chang
4b077dbf4c Observers can be suspended/resumed 2013-01-30 14:47:19 -05:00
Winston Chang
40f73bbfe2 Bump version to 3.1.99 for development 2013-01-30 13:51:54 -05:00
Winston Chang
f455706d7c Bump version to 0.3.1 2013-01-29 21:16:44 -05:00
Winston Chang
23e9672476 Update NEWS 2013-01-26 13:16:42 -06:00
Winston Chang
36f992f95f Add [[<-.shinyoutput operator 2013-01-26 13:08:40 -06:00
Joe Cheng
b2c6d526ab Merge pull request #92 from wch/fix-download
Use correct default label for contexts. Fixes #91
2013-01-25 13:17:04 -08:00
Winston Chang
fe1e833677 Use correct default label for contexts. Fixes #91
NULL apparently is not a valid value for a field in a reference class.
2013-01-25 14:57:05 -06:00
Joe Cheng
8df1b9e8e5 Merge pull request #85 from jcheng5/flush-all
Flush all shinyapp instances
2013-01-25 08:52:51 -08:00
Joe Cheng
38b0f71b01 Merge pull request #89 from wch/reactive-invisible
Store visibility state of functions called from Observable
2013-01-25 00:47:42 -08:00
Winston Chang
29d2f115f8 Better reactiveText test 2013-01-24 23:10:02 -06:00
Winston Chang
0f677b4891 Add tests for reactive function return visibility 2013-01-24 22:45:07 -06:00
Winston Chang
2f7dd04168 Observable: save visibility state of function 2013-01-24 21:57:49 -06:00
Winston Chang
ed3b667985 Remove unneeded eval.parent 2013-01-24 21:38:25 -06:00
Joe Cheng
6ae1d8c158 Flush all shinyapp instances
Allows reactivity to affect all app instances at once.
(It already does but the outputs don't currently update)
2013-01-24 13:48:05 -08:00
Winston Chang
404bced97b Bump version to .99 for development 2013-01-24 13:58:58 -06:00
Winston Chang
5af49c8a82 Bump version and update NEWS 2013-01-23 14:54:39 -06:00
Winston Chang
85aa98e8e2 Fixes for R CMD check 2013-01-23 14:30:11 -06:00
Joe Cheng
330d102f62 Fix test on Linux (sort locale) 2013-01-23 12:17:45 -08:00
Joe Cheng
32b33a7910 Add res dir to .Rbuildignore 2013-01-23 12:13:40 -08:00
Joe Cheng
17c6a0f28a Merge branch 'reactivePrint-invisible'
Conflicts:
	man/plotOutput.Rd
2013-01-23 12:09:53 -08:00
Joe Cheng
7341eed1cf Merge pull request #80 from wch/run-github
Add functions runGithub and runUrl
2013-01-23 12:06:46 -08:00
Joe Cheng
ff99fbfbc9 Fix #64: Hitting Enter in textbox causes form submit 2013-01-23 11:54:06 -08:00
Winston Chang
9f67fdc771 Re-document 2013-01-23 13:44:18 -06:00
Winston Chang
521143a16b Add subdir argument for runGitHub and runUrl 2013-01-23 13:44:17 -06:00
Winston Chang
2622a25b12 Add runGitHub and runUrl functions 2013-01-23 13:44:17 -06:00
Joe Cheng
a91e925221 Remove failure comment 2013-01-23 11:33:06 -08:00
Joe Cheng
6c3289d5a5 Documentation and examples for reactivePrint/reactiveText 2013-01-23 11:32:13 -08:00
Joe Cheng
988a91ac06 reactiveText shouldn't capture print output 2013-01-23 11:31:51 -08:00
Winston Chang
aa7c913e9a Escape percent sign in documentation 2013-01-23 09:42:08 -06:00
Joe Cheng
56db9feaa4 reactivePrint should not display invisibles 2013-01-22 23:36:51 -08:00
Winston Chang
5ace0f13c9 Move validateCssUnit to separate function 2013-01-23 00:02:16 -06:00
Winston Chang
076e6c9479 Re-roxgenize 2013-01-22 23:25:36 -06:00
Winston Chang
8277b1192e Update NEWS 2013-01-22 23:23:02 -06:00
Winston Chang
150b978b0e Fix tests with reactiveValuesToList 2013-01-22 23:22:41 -06:00
Winston Chang
6c72096bfe Better CSS unit validation 2013-01-22 19:18:18 -06:00
Winston Chang
87c18cea80 Merge pull request #79 from wch/better-deps
Finer grained dependencies when converting reactiveValues to list
2013-01-22 17:15:38 -08:00
Winston Chang
e658734084 Rename reactivevalues_to_list to reactiveValuesToList 2013-01-22 19:14:30 -06:00
Winston Chang
ec4f350baa reactivevalues_to_list: add all.names option 2013-01-22 14:53:14 -06:00
Winston Chang
095f583211 Deprecate as.list.reactivevalues and add reactivevalues_to_list 2013-01-22 14:51:43 -06:00
Winston Chang
3c864cf6d2 reactiveValues(): improved check for unnamed arguments 2013-01-22 13:59:31 -06:00
Joe Cheng
eb4b21ce9f Fix #77: tagWriteChildren error 2013-01-21 22:40:08 -08:00
Joe Cheng
ff5349fd90 Fix #65: tagWrite doesn't expect strings except as direct children of tags 2013-01-21 16:31:09 -08:00
Winston Chang
1f34ffa85d plotOutput: check that height has proper format 2013-01-18 19:16:50 -06:00
Winston Chang
e98cab1f7c Fix test 2013-01-17 00:11:38 -06:00
Winston Chang
aabc9659a2 Update NEWS
Some news items were under the wrong version heading. Those have also been
fixed.
2013-01-16 23:00:07 -06:00
Winston Chang
8d8d308f7a Rename 'dependencies' to 'dependents' 2013-01-16 22:42:03 -06:00
Winston Chang
3ebd4595c6 Add read-write wrapper class for ReactiveValues 2013-01-16 19:02:26 -06:00
Winston Chang
7e1168946f Re-roxygenize 2013-01-16 16:08:12 -06:00
Joe Cheng
134689d8aa Remove subsetting operators from Map and Values
The correct operators would be [[/[[<- but since we don't use them I
just removed them instead.
2013-01-16 13:48:50 -08:00
Winston Chang
56282f9cbb Merge branch 'lazy' 2013-01-16 12:32:32 -06:00
Joe Cheng
b4713741b1 Two new recursion/circularity tests 2013-01-16 10:27:20 -08:00
Joe Cheng
e42fe3bd61 Fix problem with circular dependencies
The first of the included tests did not pass without the changes to
Observable. The problem occurred when a function read a reactive value
and then wrote it. Any dependents on the function would not receive
any invalidations, then or ever after.

The first problem was that the dirty state was unilaterally set to FALSE
after the function finished executing, which might not be accurate if
the function's newly created was invalidated during its own execution.
Instead we set dirty state to FALSE before executing. But to prevent
reentrant calls from thinking the cached value can be used, we add
a .running field that is also consulted during getValue.

The second problem was that Observable$getValue didn't register the
dependent until after updateValue. That is a problem if updateValue
creates *and* invalidates a context before returning. So now we
register the dependent before calling updateValue.
2013-01-15 17:37:26 -08:00
Winston Chang
4fd2dade60 reactiveTable: don't return blank if first element is NA. Fixes #71 2013-01-15 16:04:18 -06:00
Joe Cheng
e12b03504c Fix bad calls to on.exit
I didn't realize on.exit replaces previous calls to on.exit by default.
2013-01-15 12:07:27 -08:00
Winston Chang
153156c1fa Add back onInvalidate to Observer class 2013-01-15 11:13:46 -06:00
Winston Chang
3ecc69da2b Un-export execCount 2013-01-15 11:13:46 -06:00
Winston Chang
07ad29da41 Clarify isolation test 2013-01-15 11:13:46 -06:00
Winston Chang
7d0de0b26f Remove onInvalidateHint
The recent changes to onInvalidate make it do almost exactly the same thing.
2013-01-15 11:13:46 -06:00
Winston Chang
77fab9c78f Remove all pendingInvalidate 2013-01-15 11:13:46 -06:00
Winston Chang
3a8f3272c7 Don't call observers until flushReact() 2013-01-15 11:13:46 -06:00
Joe Cheng
2d44cbac1b Failing overreactivity test 2013-01-08 14:06:10 -06:00
Joe Cheng
893d72677b Try LIFO pendingInvalidates? 2013-01-08 14:06:10 -06:00
Joe Cheng
979eca4066 Add execCount 2013-01-08 14:06:10 -06:00
Joe Cheng
258d13e746 Add ctx$.label to help with debugging
Shows the code that the context "belongs" to.
2013-01-08 14:06:10 -06:00
Winston Chang
779531da5d Use lazy evaluation of reactive functions 2013-01-08 14:06:10 -06:00
Winston Chang
31d71006d7 Add tests for isolate() 2013-01-08 14:06:10 -06:00
Winston Chang
64ca66c062 Add test for reactive evaluation order 2013-01-08 14:06:10 -06:00
Winston Chang
6e1a2b3427 reactive tests: count number of times observers are run 2013-01-08 14:06:10 -06:00
Winston Chang
f585235192 Add reactivity tests 2013-01-08 14:06:10 -06:00
Winston Chang
9355643554 Update NEWS 2013-01-08 14:03:23 -06:00
Winston Chang
ccc6055926 Fix reactivity for empty checkbox groups. Fixes #58 2013-01-08 13:57:10 -06:00
Joe Cheng
6639446bb8 Update README.md 2013-01-07 22:39:07 -08:00
Joe Cheng
e2925c585f Add isolate function for accessing reactives non-reactively 2013-01-03 12:16:50 -08:00
Joe Cheng
6c76b0473c Add implementation of reactive values 2013-01-02 16:00:21 -08:00
Joe Cheng
e1e19632a5 Update URL in DESCRIPTION 2012-12-21 14:46:52 -08:00
Winston Chang
3e5364d5c0 Bump version number to .99 for development 2012-12-18 11:17:12 -06:00
Winston Chang
6c98de4c8b Update NEWS 2012-12-17 16:24:40 -06:00
Winston Chang
9613dde4d2 Increment version to 0.2.4 2012-12-17 15:30:08 -06:00
Winston Chang
d47df2e538 Re-roxygenize 2012-12-17 15:23:59 -06:00
Winston Chang
6fcacd5159 Use different method of accessing CairoPNG
R CMD check didn't like Cairo::CairoPNG. With this method, check wants
Cairo to be imported in NAMESPACE, but it shouldn't be - Cairo should
be optional.
2012-12-17 15:23:08 -06:00
Winston Chang
11b39cb020 Change maintainer 2012-12-17 14:30:47 -06:00
Winston Chang
d81f132db6 Update NEWS 2012-12-17 13:40:50 -06:00
Winston Chang
095697e789 Use new URL for runGist. Fixes #57 2012-12-17 12:18:19 -06:00
Joe Cheng
62d98c3137 Revert "Run invalidated hints only once per context"
This reverts commit e80d5dc172.

The original commit could cause under-reporting of progress.
2012-12-14 16:41:12 -08:00
jeffreyhorner
e80d5dc172 Run invalidated hints only once per context 2012-12-13 16:02:47 -06:00
jeffreyhorner
421e29db2d Suppress base64 output when tracing websocket messages 2012-12-13 16:00:58 -06:00
Joe Cheng
9e6e53583c Merge pull request #49 from wch/png-cairo
For png output, try quartz and CairoPNG before plain png
2012-12-05 09:56:11 -08:00
Joe Cheng
3f59a7d84e Fix bug where reactiveUI doesn't accept plain lists 2012-12-05 09:54:31 -08:00
Winston Chang
21ffd788ab For png output, try quartz and CairoPNG before plain png 2012-12-03 12:06:31 -06:00
Joe Cheng
8dadfea724 Separate request parameters from path; version 0.2.3 2012-11-30 09:31:09 -08:00
Joe Cheng
00ce52ecf7 Fix CRAN warning; version 0.2.2 2012-11-30 09:05:20 -08:00
Joe Cheng
50ac13d3fd [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.
2012-11-29 17:14:44 -08:00
Joe Cheng
58318fec46 Update package metadata for v0.2.0 2012-11-27 16:32:27 -08:00
Joe Cheng
a49941113e Require Shiny at app startup
Some of our examples omit library(shiny) from the top of ui.R and server.R,
which worked fine before but not with the namespace fix from yesterday.
Requiring shiny at startup fixes the problem.
2012-11-27 16:29:01 -08:00
Joe Cheng
595801cb99 Trivial style copy edits to example 10_download 2012-11-26 21:48:12 -08:00
Joe Cheng
0b469f09df Fix subtle name resolution bugs
See in particular:
http://stackoverflow.com/questions/13575353/how-does-the-shiny-r-package-deal-with-data-frames

Also reported at different times by Dirk Eddelbuettel and Jay Emerson.

The observed behavior is that S3/S4 method dispatch does not always seem to
work; the desired methods are not invoked despite appearing to be in the
search path.

The problem was that sourcing files with local=TRUE creates a new environment
based on the parent frame, which in our case is Shiny's package environment.
What we really want is to read from the global environment but write to a
throwaway environment. The correct way to do that is to make a new environment
with .GlobalEnv as the parent.
2012-11-26 21:45:28 -08:00
Joe Cheng
1e1f4e4a47 Update metadata for 0.1.14 2012-11-24 01:47:47 -08:00
Joe Cheng
c63e2ae7c8 Fix slider animation controls 2012-11-24 00:30:44 -08:00
Joe Cheng
d3d3fa990e Update version metadata 2012-11-23 23:47:27 -08:00
Joe Cheng
21980b7e71 Clean up PNG file when no longer needed 2012-11-23 22:44:37 -08:00
Joe Cheng
844ca0d387 I am stupid. 2012-11-21 23:02:40 -08:00
Joe Cheng
972ae35300 Update metadata for 0.1.12 2012-11-21 22:44:19 -08:00
Joe Cheng
57bfb8eb96 Bring untar operations in-house
Very simple tweak to R's untar2 code was all that was
required to fix the built-in untar's problems with
gists. Seemed best to just fork it and start using
the forked version directly, regardless of what is
installed on your machine.
2012-11-21 22:37:47 -08:00
Joe Cheng
ed6e6a9fb2 Squash another cygwin warning 2012-11-21 21:43:32 -08:00
Joe Cheng
ed402267b6 Fix runGist cygwin warning bug 2012-11-21 21:39:16 -08:00
Joe Cheng
6eec570828 Add CSS hooks for app-wide busy indicators 2012-11-21 00:04:16 -08:00
Joe Cheng
22fc1e3f0b Add param docs 2012-11-20 18:08:59 -08:00
Joe Cheng
ae9bd868f1 Implement arbitrary file downloads 2012-11-20 17:42:34 -08:00
Joe Cheng
a887012aca Update metadata for v0.1.11 2012-11-19 17:22:57 -08:00
Joe Cheng
bc73048ab9 Fix IE8 slice bug
IE8 doesn't like slice(0, undefined)--rather than interpreting it as slice(0),
it returns an array of length 0.
2012-11-19 17:19:51 -08:00
Joe Cheng
c89dd6c379 Fix issue #41: reactiveTable should allow print options too 2012-11-19 15:26:34 -08:00
Joe Cheng
9662debe5e Dynamic plot sizing 2012-11-19 15:26:02 -08:00
Joe Cheng
057262d917 Update metadata for v0.1.10 2012-11-19 13:11:07 -08:00
Joe Cheng
b6723a6219 Add per-session GET infrastructure. Allow IE8/9 to avoid data URIs. 2012-11-19 13:08:09 -08:00
Joe Cheng
068f3e0a43 Merge pull request #32 from edwindj/master
small bug: checkboxInputGroup sets html attribute "selected" in stead of "checked"
2012-11-15 23:30:28 -08:00
Joe Cheng
95635a8c47 Issue #37: headerPanel HTML argument shows up in title 2012-11-13 01:52:33 -08:00
Joe Cheng
3ec2071820 Address issue #35: Allow modification of untar args 2012-11-13 00:09:27 -08:00
Joe Cheng
1696db3044 Fix issue #36: reactiveUI does not always correctly render sliders
There is a deeper problem here, that reactiveUI output that renders stuff to the <head> will generally not work. We're not in a position to fix that yet and this problem has been reported twice, so we'll just fix this instance by making the slider dependencies built into the framework.
2012-11-11 18:32:34 -08:00
Joe Cheng
e1a1eab2b3 More MIME types 2012-11-10 15:18:29 -08:00
Edwin de Jonge
f7865f3358 changed html attribute of checkboxInputGroup from "selected" into "checked" 2012-11-08 23:09:08 +01:00
Joe Cheng
6d5f8ed5f3 Pointer to Shiny homepage 2012-11-08 03:29:23 -08:00
Joe Cheng
96a737379f Add linked example 2012-11-07 10:36:42 -08:00
Joe Cheng
d73feec013 Turns out GitHub doesn't like iframes 2012-11-07 10:28:47 -08:00
Joe Cheng
2ccead1da5 Add example to README 2012-11-07 10:28:06 -08:00
Joe Cheng
8885f2717e Update version 2012-11-06 13:53:53 -08:00
Joe Cheng
4448ffc777 Add methods for including text, HTML, and Markdown files in UI 2012-11-06 13:38:52 -08:00
Joe Cheng
022d10c598 Export and document observe function 2012-11-06 10:03:11 -08:00
Joe Cheng
8e6b7043bd Shut down timer callbacks before runApp returns 2012-11-06 09:36:49 -08:00
Joe Cheng
66eaaff598 More customizable error display 2012-11-02 09:49:17 -07:00
Joe Cheng
478c6c134f Much less flicker when updating plots 2012-11-02 09:48:36 -07:00
Joe Cheng
b5d333ba6c Rev downloader code 2012-10-31 15:36:52 -07:00
Joe Cheng
81723d55ac Change T and F to TRUE and FALSE
TRUE and FALSE are keywords whereas T and F are just predefined variables that can be reassigned
2012-10-31 11:35:41 -07:00
Joe Cheng
fb784ce962 Merge pull request #28 from rstudio/list-to-vec
Change lists to vectors in UI elements
2012-10-31 10:00:21 -07:00
Winston Chang
5a37380900 Capture stderr in download() 2012-10-30 16:19:14 -05:00
Joe Cheng
b6300f3a5c More robust setInternet2 workaround 2012-10-30 12:31:36 -07:00
Winston Chang
a3e8a2d623 Re-roxygenize 2012-10-30 10:49:55 -05:00
Winston Chang
7b3a4bdc39 Use vectors instead of lists in UI elements 2012-10-30 10:47:05 -05:00
Joe Cheng
cc0b5e5e0f Remove problematic link (fails R CMD check)
I first attempted to remove \code, but I couldn't figure out how to get the # in the URL to work right under both web-based help and PDF.
2012-10-29 11:56:59 -07:00
Joe Cheng
5c3f7d8f94 Update NEWS 2012-10-29 11:47:27 -07:00
Joe Cheng
8c3f8cd450 Add wellPanel and bootstrapPage functions 2012-10-29 11:45:58 -07:00
Joe Cheng
046582711a Update NEWS 2012-10-29 11:22:30 -07:00
Joe Cheng
15756ec92d Case insensitive probing for server.R, ui.R, et al 2012-10-29 11:19:23 -07:00
Joe Cheng
fc49abc9fb Fix issue #27: Warnings cause reactive functions to stop executing 2012-10-29 11:09:13 -07:00
Winston Chang
4a9ff27f3e Download gists in binary mode 2012-10-26 16:38:18 -05:00
Joe Cheng
790e6f370f Remove RCurl dependency 2012-10-26 14:07:07 -07:00
Joe Cheng
16ccc1321d Update docs 2012-10-26 10:46:42 -07:00
Joe Cheng
8648c94dd4 Update version to 0.1.8 2012-10-26 10:43:53 -07:00
Joe Cheng
dc4eb720ae Introduce input type hints
These allow the server to use custom deserialization code on a per-type basis.
2012-10-26 10:28:40 -07:00
Joe Cheng
0b891ad557 Run a GitHub gist 2012-10-25 20:41:52 -07:00
Joe Cheng
e96193ae28 Do .Random.seed restoring correctly 2012-10-24 23:19:13 -07:00
Joe Cheng
3ff9075959 Update NEWS 2012-10-24 21:11:56 -07:00
Joe Cheng
c03842056c Convert JSON to UTF-8
If reactivePrint or reactiveText return non-ASCII characters on
Windows, it causes invalid UTF-8 strings to be received by the
browser which closes the websocket connection.

I'm not sure this is the right place to do encoding, but it seems
to me like this approach is likely to work best for the most
users (especially those who just aren't thinking about encoding).
If you want to handle encoding in the reactives themselves (for
example), use `options(shiny.transcode.json=F)`.
2012-10-24 21:10:09 -07:00
Joe Cheng
6df226b21c Add repeatable utility function to stabilize RNGs 2012-10-24 16:12:08 -07:00
Joe Cheng
7dfa7d7426 Fix issue #26: Shiny.OutputBindings not correctly exported 2012-10-24 14:41:32 -07:00
Joe Cheng
b8b1a891cf Add custom message handler support, console logging
If the server sends a message with a "custom" field, that field's value will
be passed to a custom window.Shiny.oncustommessage function, if it is defined.

Also add support for messages like so:
{
  console: [
    'line one',
    'line two'
  ]
}

This will cause "line one" and "line two" to be printed at the browser console.
2012-10-04 17:45:20 -07:00
Joe Cheng
7df0e8b0f9 Update docs for 0.1.6 2012-09-25 03:08:31 -07:00
Joe Cheng
ff072ae9d9 bindAll should send initial values to server 2012-09-25 01:29:52 -07:00
Joe Cheng
f81ca39741 Add uiOutput. Tweak comments. 2012-09-25 00:33:00 -07:00
Joe Cheng
3db1f2a98c Don't animate showing/hiding of conditionalPanel 2012-09-21 19:51:24 -07:00
Joe Cheng
4865df9be1 Mark fileInput and reactiveUI as experimental. 2012-09-21 19:50:50 -07:00
Joe Cheng
0c16f2c334 Fix broken imports 2012-09-21 14:00:03 -07:00
Joe Cheng
d01149620f Fix issue #19: Checkboxes and radios can't be added dynamically 2012-09-19 11:48:28 -05:00
Joe Cheng
ab9401f390 Fix issue #20: DESCRIPTION file should use Imports instead of Depends 2012-09-19 11:47:12 -05:00
Joe Cheng
3223c17b74 Update websockets dependency version 2012-09-15 00:52:04 -07:00
Joe Cheng
404035bcf0 Bump version number 2012-09-14 19:16:03 -07:00
Joe Cheng
a0185bb0b4 Introduce shiny.http.response.filter option
Allows post-processing of HTTP responses
2012-09-14 13:15:58 -07:00
Joe Cheng
1a591cd9f1 conditionalPanel now triggers show/shown/hide/hidden event 2012-09-07 00:44:20 -07:00
Joe Cheng
e9b81b2033 [BREAKING] Simplify input binding callbacks
InputBinding.subscribe used to have to call callbacks with at least two arguments,
now there is only one optional argument (allowDeferred). The binding argument in
particular was problematic because it required "var self=this;".
2012-09-06 12:06:15 -07:00
Joe Cheng
cbfc1e8ed1 Add reactiveUI output type 2012-09-05 15:22:34 -07:00
Joe Cheng
cb63338805 Allow htmlOutput to contain inputs/outputs 2012-09-05 11:17:39 -07:00
Joe Cheng
bcdc82ccee Add conditionalPanel; JS API changes
- bindAll/unbindAll added
- bindInput/bindOutput/unbindInput/unbindOutput removed
2012-09-05 09:40:40 -07:00
Joe Cheng
76a4cf6c34 Update NEWS 2012-08-31 23:21:04 -07:00
Joe Cheng
872f23b0f0 Improvements for output binding/unbinding
- When bound, outputs receive cached error/value
- On binding, (potentially all) output plot sizes are resent
2012-08-31 23:12:20 -07:00
Joe Cheng
e61f7405fd Upload example app should accept text/plain files 2012-08-31 22:39:45 -07:00
Joe Cheng
0714871b56 Improve blob handling browser compatibility 2012-08-31 22:39:26 -07:00
Joe Cheng
8a89fb2a1a Expose and fix Shiny.unbindOutputs 2012-08-31 18:29:42 -07:00
Joe Cheng
036544e3ed Eagerly evaluate output name 2012-08-31 12:33:13 -07:00
Joe Cheng
7a6784d809 Add missing param to prototype method 2012-08-31 11:48:21 -07:00
Joe Cheng
ed9301705b Refactor JS to use more consistent OOP style
(function() { }).call(Foo.prototype) for extending prototypes manually, and
$.extend for extending objects manually or prototypes inheriting from each
other.
2012-08-31 10:00:20 -07:00
Joe Cheng
21f9694574 Add NEWS for file upload 2012-08-30 22:10:16 -07:00
Joe Cheng
3a0b11b89d Add file upload feature
This feature is currently pretty rough. It only works in the most modern
browsers (depends on HTML5 File API, including Blob.slice) and doesn't
show upload progress.
2012-08-30 22:07:00 -07:00
Joe Cheng
d5272e3e74 Update version 2012-08-30 12:27:05 -07:00
Joe Cheng
b5197869db Update NEWS 2012-08-30 12:18:46 -07:00
Joe Cheng
5f775db40a Enhancements to Shiny transport
- JS can now do remote procedure calls (with return value or exception), not just message passing
- RPC calls can include non-JSON-compatible binary data (not compatible with IE)
2012-08-30 12:16:12 -07:00
Joe Cheng
9b84b83627 Allow binding and unbinding of Shiny input/output 2012-08-30 12:04:17 -07:00
Joe Cheng
b0d9b5762a Don't use WebSocket constant, it's not on IE8 2012-08-24 11:28:46 -07:00
Joe Cheng
8d9fd402be Check inheritance properly 2012-08-23 18:07:09 -07:00
Joe Cheng
73a44a4f8e Packages can register their own URL namespace
Helpful for serving up custom stylesheets, CSS, images, etc.
2012-08-23 13:08:08 -07:00
Joe Cheng
a7dd62249e Add checkboxGroupInput control 2012-08-22 13:39:19 -07:00
Joe Cheng
42fac871fb Extensible websocket creation 2012-08-22 12:32:33 -07:00
Joe Cheng
2782bf6735 Execute sendPlotSize when anything is shown/hidden 2012-08-21 14:18:00 -07:00
Joe Cheng
f2a1ce4977 Update NEWS 2012-08-21 14:16:25 -07:00
Joe Cheng
c8969c4cc0 Upgrade to Bootstrap 2.1 2012-08-21 11:35:37 -07:00
Joe Cheng
cfefb4a07c Update NEWS 2012-08-20 17:16:23 -07:00
Joe Cheng
653509368b Let Bootstrap tabset send its selected tab as input 2012-08-20 17:01:41 -07:00
207 changed files with 23804 additions and 2554 deletions

View File

@@ -7,3 +7,6 @@
^shiny\.cmd$
^run\.R$
^\.gitignore$
^res$
^tools$
^man-roxygen$

View File

@@ -1,27 +1,51 @@
Package: shiny
Type: Package
Title: Web Application Framework for R
Version: 0.1.3
Date: 2012-08-20
Version: 0.8.0
Date: 2013-10-26
Author: RStudio, Inc.
Maintainer: Joe Cheng <joe@rstudio.org>
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
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), methods, websockets (>= 1.1.4), caTools, RJSONIO, xtable
Imports: stats, tools, utils, datasets
URL: https://github.com/rstudio/shiny, http://rstudio.github.com/shiny/tutorial
Depends:
R (>= 2.14.1)
Imports:
stats,
tools,
utils,
methods,
httpuv (>= 1.2.0),
caTools,
RJSONIO,
xtable,
digest
Suggests:
datasets,
markdown,
Cairo,
testthat
URL: http://www.rstudio.com/shiny/
BugReports: https://github.com/rstudio/shiny/issues
Collate:
'map.R'
'priorityqueue.R'
'utils.R'
'tar.R'
'timer.R'
'tags.R'
'cache.R'
'graph.R'
'react.R'
'reactives.R'
'fileupload.R'
'shiny.R'
'shinywrappers.R'
'shinyui.R'
'slider.R'
'bootstrap.R'
'run-url.R'
'imageutils.R'
'update-input.R'

104
NAMESPACE
View File

@@ -1,10 +1,47 @@
S3method("$",reactivevalues)
S3method("$",shinyoutput)
S3method("$<-",reactivevalues)
S3method("$<-",shinyoutput)
S3method("[",reactivevalues)
S3method("[",shinyoutput)
S3method("[<-",reactivevalues)
S3method("[<-",shinyoutput)
S3method("[[",reactivevalues)
S3method("[[",shinyoutput)
S3method("[[<-",reactivevalues)
S3method("[[<-",shinyoutput)
S3method("names<-",reactivevalues)
S3method(as.character,shiny.tag)
S3method(as.character,shiny.tag.list)
S3method(as.list,reactivevalues)
S3method(format,shiny.tag)
S3method(format,shiny.tag.list)
S3method(names,reactivevalues)
S3method(print,reactive)
S3method(print,shiny.tag)
S3method(print,shiny.tag.list)
export(HTML)
export(a)
export(actionButton)
export(addResourcePath)
export(animationOptions)
export(basicPage)
export(bootstrapPage)
export(br)
export(checkboxGroupInput)
export(checkboxInput)
export(code)
export(conditionalPanel)
export(dataTableOutput)
export(dateInput)
export(dateRangeInput)
export(div)
export(downloadButton)
export(downloadHandler)
export(downloadLink)
export(em)
export(exprToFunction)
export(fileInput)
export(h1)
export(h2)
export(h3)
@@ -13,49 +50,94 @@ export(h5)
export(h6)
export(headerPanel)
export(helpText)
export(HTML)
export(htmlOutput)
export(imageOutput)
export(img)
export(includeCSS)
export(includeHTML)
export(includeMarkdown)
export(includeScript)
export(includeText)
export(installExprFunction)
export(invalidateLater)
export(is.reactive)
export(is.reactivevalues)
export(isolate)
export(mainPanel)
export(numericInput)
export(observe)
export(outputOptions)
export(p)
export(pageWithSidebar)
export(parseQueryString)
export(plotOutput)
export(plotPNG)
export(pre)
export(radioButtons)
export(reactive)
export(reactiveFileReader)
export(reactivePlot)
export(reactivePoll)
export(reactivePrint)
export(reactiveTable)
export(reactiveText)
export(reactiveTimer)
export(reactiveUI)
export(reactiveValues)
export(reactiveValuesToList)
export(renderDataTable)
export(renderImage)
export(renderPlot)
export(renderPrint)
export(renderTable)
export(renderText)
export(renderUI)
export(repeatable)
export(runApp)
export(runExample)
export(runGist)
export(runGitHub)
export(runUrl)
export(selectInput)
export(shinyServer)
export(shinyUI)
export(showReactLog)
export(sidebarPanel)
export(singleton)
export(sliderInput)
export(span)
export(stopApp)
export(strong)
export(submitButton)
export(tableOutput)
export(tabPanel)
export(tableOutput)
export(tabsetPanel)
export(tag)
export(tagAppendChild)
export(tagAppendChildren)
export(tagList)
export(tagSetChildren)
export(tags)
export(textInput)
export(textOutput)
export(uiOutput)
export(updateCheckboxGroupInput)
export(updateCheckboxInput)
export(updateDateInput)
export(updateDateRangeInput)
export(updateNumericInput)
export(updateRadioButtons)
export(updateSelectInput)
export(updateSliderInput)
export(updateTabsetPanel)
export(updateTextInput)
export(validateCssUnit)
export(verbatimTextOutput)
S3method(as.character,shiny.tag)
S3method(as.list,reactvaluesreader)
S3method(format,shiny.tag)
S3method(names,reactvaluesreader)
S3method(print,shiny.tag)
S3method(reactive,default)
S3method(reactive,"function")
S3method("$",reactvaluesreader)
S3method("$<-",shinyoutput)
export(wellPanel)
export(withTags)
import(RJSONIO)
import(caTools)
import(digest)
import(httpuv)
import(methods)
import(xtable)

375
NEWS
View File

@@ -1,12 +1,385 @@
shiny 0.8.0
--------------------------------------------------------------------------------
* Debug hooks are registered on all user-provided functions and (reactive)
expressions (e.g., in renderPlot()), which makes it possible to set
breakpoints in these functions using the latest version of the RStudio
IDE, and the RStudio visual debugging tools can be used to debug Shiny
apps. Internally, the registration is done via installExprFunction(),
which is a new function introduced in this version to replace
exprToFunction() so that the registration can be automatically done.
* Added a new function renderDataTable() to display tables using the
JavaScript library DataTables. It includes basic features like pagination,
searching (global search or search by individual columns), sorting (by
single or multiple columns). All these features are implemented on the R
side; for example, we can use R regular expressions for searching.
Besides, it also uses the Twitter Bootstrap CSS style. See the full
documentation and examples in the tutorial:
http://rstudio.github.io/shiny/tutorial/#datatables
* Added a new option `shiny.error` which can take a function as an error
handler. It is called when an error occurs in an app (in user-provided
code), e.g., after we set options(shiny.error = recover), we can enter a
specified environment in the call stack to debug our code after an error
occurs.
* The argument `launch.browser` in runApp() can also be a function,
which takes the URL of the shiny app as its input value.
* runApp() uses a random port between 3000 and 8000 instead of 8100 now. It
will try up to 20 ports in case certain ports are not available.
* Fixed a bug for conditional panels: the value `input.id` in the condition
was not correctly retrieved when the input widget had a type, such as
numericInput(). (reported by Jason Bryer)
* Fixed two bugs in plotOutput(); clickId and hoverId did not give correct
coordinates in Firefox, or when the axis limits of the plot were changed.
(reported by Chris Warth and Greg D)
* The minimal required version for the httpuv package was increased to 1.2
(on CRAN now).
shiny 0.7.0
--------------------------------------------------------------------------------
* Stopped sending websocket subprotocol. This fixes a compatibility issue with
Google Chrome 30.
* The `input` and `output` objects are now also accessible via `session$input`
and `session$output`.
* Added click and hover events for static plots; see `?plotOutput` for details.
* Added optional logging of the execution states of a reactive program, and
tools for visualizing the log data. To use, start a new R session and call
`options(shiny.reactlog=TRUE)`. Then launch a Shiny app and interact with it.
Press Ctrl+F3 (or for Mac, Cmd+F3) in the browser to launch an interactive
visualization of the reactivity that has occurred. See `?showReactLog` for
more information.
* Added `includeScript()` and `includeCSS()` functions.
* Reactive expressions now have class="reactive" attribute. Also added
`is.reactive()` and `is.reactivevalues()` functions.
* New `stopApp()` function, which stops an app and returns a value to the caller
of `runApp()`.
* Added the `shiny.usecairo` option, which can be used to tell Shiny not to use
Cairo for PNG output even when it is installed. (Defaults to `TRUE`.)
* Speed increases for `selectInput()` and `radioButtons()`, and their
corresponding updater functions, for when they have many options.
* Added `tagSetChildren()` and `tagAppendChildren()` functions.
* The HTTP request object that created the websocket is now accessible from the
`session` object, as `session$request`. This is a Rook-like request
environment that can be used to access HTTP headers, among other things.
(Note: When running in a Shiny Server environment, the request will reflect
the proxy HTTP request that was made from the Shiny Server process to the R
process, not the request that was made from the web browser to Shiny Server.)
* Fix `getComputedStyle` issue, for IE8 browser compatibility (#196). Note:
Shiny Server is still required for IE8/9 compatibility.
* Add shiny.sharedSecret option, to require the HTTP header Shiny-Shared-Secret
to be set to the given value.
shiny 0.6.0
--------------------------------------------------------------------------------
* `tabsetPanel()` can be directed to start with a specific tab selected.
* Fix bug where multiple file uploads with 3 or more files result in incorrect
data.
* Add `withTags()` function.
* Add dateInput and dateRangeInput.
* `shinyServer()` now takes an optional `session` argument, which is used for
communication with the session object.
* Add functions to update values of existing inputs on a page, instead of
replacing them entirely.
* Allow listening on domain sockets.
* Added `actionButton()` to Shiny.
* The server can now send custom JSON messages to the client. On the client
side, functions can be registered to handle these messages.
* Callbacks can be registered to be called at the end of a client session.
* Add ability to set priority of observers and outputs. Each priority level
gets its own queue.
* Fix bug where the presence of a submit button would prevent sending of
metadata until the button was clicked.
* `reactiveTimer()` and `invalidateLater()` by default no longer invalidate
reactive objects after the client session has closed.
* Shiny apps can be run without a server.r and ui.r file.
shiny 0.5.0
--------------------------------------------------------------------------------
* Switch from websockets package for handling websocket connections to httpuv.
* New method for detecting hidden output objects. Instead of checking that
height and width are 0, it checks that the object or any ancestor in the DOM
has style display:none.
* Add `clientData` reactive values object, which carries information about the
client. This includes the hidden status of output objects, height/width plot
output objects, and the URL of the browser.
* Add `parseQueryString()` function.
* Add `renderImage()` function for sending arbitrary image files to the client,
and its counterpart, `imageOutput()`.
* Add support for high-resolution (Retina) displays.
* Fix bug #55, where `renderTable()` would throw error with an empty data frame.
shiny 0.4.1
--------------------------------------------------------------------------------
* Fix bug where width and height weren't passed along properly from
`reactivePlot` to `renderPlot`.
* Fix bug where infinite recursion would happen when `reactivePlot` was passed
a function for width or height.
shiny 0.4.0
--------------------------------------------------------------------------------
* Added suspend/resume capability to observers.
* Output objects are automatically suspended when they are hidden on the user's
web browser.
* `runGist()` accepts GitHub's new URL format, which includes the username.
* `reactive()` and `observe()` now take expressions instead of functions.
* `reactiveText()`, `reactivePlot()`, and so on, have been renamed to
`renderText()`, `renderPlot()`, etc. They also now take expressions instead
of functions.
* Fixed a bug where empty values in a numericInput were sent to the R process
as 0. They are now sent as NA.
shiny 0.3.1
--------------------------------------------------------------------------------
* Fix issue #91: bug where downloading files did not work.
* Add [[<- operator for shinyoutput object, making it possible to assign values
with `output[['plot1']] <- ...`.
* Reactive functions now preserve the visible/invisible state of their returned
values.
shiny 0.3.0
--------------------------------------------------------------------------------
* Reactive functions are now evaluated lazily.
* Add `reactiveValues()`.
* Using `as.list()` to convert a reactivevalues object (like `input`) to a list
is deprecated. The new function `reactiveValuesToList()` should be used
instead.
* Add `isolate()`. This function is used for accessing reactive functions,
without them invalidating their parent contexts.
* Fix issue #58: bug where reactive functions are not re-run when all items in
a checkboxGroup are unchecked.
* Fix issue #71, where `reactiveTable()` would return blank if the first
element of a data frame was NA.
* In `plotOutput`, better validation for CSS units when specifying width and
height.
* `reactivePrint()` no longer displays invisible output.
* `reactiveText()` no longer displays printed output, only the return value
from a function.
* The `runGitHub()` and `runUrl()` functions have been added, for running
Shiny apps from GitHub repositories and zip/tar files at remote URLs.
* Fix issue #64, where pressing Enter in a textbox would cause a form to
submit.
shiny 0.2.4
--------------------------------------------------------------------------------
* `runGist` has been updated to use the new download URLs from
https://gist.github.com.
* Shiny now uses `CairoPNG()` for output, when the Cairo package is available.
This provides better-looking output on Linux and Windows.
shiny 0.2.3
--------------------------------------------------------------------------------
* Ignore request variables for routing purposes
shiny 0.2.2
--------------------------------------------------------------------------------
* Fix CRAN warning (assigning to global environment)
shiny 0.2.1
--------------------------------------------------------------------------------
* [BREAKING] Modify API of `downloadHandler`: The `content` function now takes
a file path, not writable connection, as an argument. This makes it much
easier to work with APIs that only write to file paths, not connections.
shiny 0.2.0
--------------------------------------------------------------------------------
* Fix subtle name resolution bug--the usual symptom being S4 methods not being
invoked correctly when called from inside of ui.R or server.R
shiny 0.1.14
--------------------------------------------------------------------------------
* Fix slider animator, which broke in 0.1.10
shiny 0.1.13
--------------------------------------------------------------------------------
* Fix temp file leak in reactivePlot
shiny 0.1.12
--------------------------------------------------------------------------------
* Fix problems with runGist on Windows
* Add feature for on-the-fly file downloads (e.g. CSV data, PDFs)
* Add CSS hooks for app-wide busy indicators
shiny 0.1.11
--------------------------------------------------------------------------------
* Fix input binding with IE8 on Shiny Server
* Fix issue #41: reactiveTable should allow print options too
* Allow dynamic sizing of reactivePlot (i.e. using a function instead of a fixed
value)
shiny 0.1.10
--------------------------------------------------------------------------------
* Support more MIME types when serving out of www
* Fix issue #35: Allow modification of untar args
* headerPanel can take an explicit window title parameter
* checkboxInput uses correct attribute `checked` instead of `selected`
* Fix plot rendering with IE8 on Shiny Server
shiny 0.1.9
--------------------------------------------------------------------------------
* Much less flicker when updating plots
* More customizable error display
* Add `includeText`, `includeHTML`, and `includeMarkdown` functions for putting
text, HTML, and Markdown content from external files in the application's UI.
shiny 0.1.8
--------------------------------------------------------------------------------
* Add `runGist` function for conveniently running a Shiny app that is published
on gist.github.com.
* Fix issue #27: Warnings cause reactive functions to stop executing.
* The server.R and ui.R filenames are now case insensitive.
* Add `wellPanel` function for creating inset areas on the page.
* Add `bootstrapPage` function for creating new 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!
Initial private beta release!

File diff suppressed because it is too large Load Diff

80
R/cache.R Normal file
View 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))
return()
if (is.null(filepath) || is.na(filepath))
.currentCacheContext$cc$forceDirty()
else
.currentCacheContext$cc$addDependencyFile(filepath)
}

109
R/fileupload.R Normal file
View File

@@ -0,0 +1,109 @@
# 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',
.pendingFileInfos = 'list'
),
methods = list(
initialize = function(parent, id, dir, fileInfos) {
.parent <<- parent
.id <<- id
.files <<- data.frame(name=character(),
size=numeric(),
type=character(),
datapath=character(),
stringsAsFactors=FALSE)
.dir <<- dir
.pendingFileInfos <<- fileInfos
},
fileBegin = function() {
if (length(.pendingFileInfos) < 1)
stop("fileBegin called too many times")
file <- .pendingFileInfos[[1]]
.currentFileInfo <<- file
.pendingFileInfos <<- tail(.pendingFileInfos, -1)
filename <- file.path(.dir, as.character(length(.files$name)))
row <- data.frame(name=file$name, size=file$size, type=file$type,
datapath=filename, stringsAsFactors=FALSE)
if (length(.files$name) == 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() {
if (length(.pendingFileInfos) > 0)
stop("File upload job was stopped prematurely")
.parent$onJobFinished(.id)
return(.files)
}
)
)
FileUploadContext <- setRefClass(
'FileUploadContext',
fields = list(
.basedir = 'character',
.operations = 'Map'
),
methods = list(
initialize = function(dir=tempdir()) {
.basedir <<- dir
},
createUploadOperation = function(fileInfos) {
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, fileInfos)
.operations$set(id, op)
return(id)
}
},
getUploadOperation = function(jobId) {
.operations$get(jobId)
},
onJobFinished = function(jobId) {
.operations$remove(jobId)
}
)
)

105
R/graph.R Normal file
View File

@@ -0,0 +1,105 @@
writeReactLog <- function(file=stdout()) {
cat(RJSONIO::toJSON(.graphEnv$log, pretty=TRUE), file=file)
}
#' Reactive Log Visualizer
#'
#' Provides an interactive browser-based tool for visualizing reactive
#' dependencies and execution in your application.
#'
#' To use the reactive log visualizer, start with a fresh R session and
#' run the command \code{options(shiny.reactlog=TRUE)}; then launch your
#' application in the usual way (e.g. using \code{\link{runApp}}). At
#' any time you can hit Ctrl+F3 (or for Mac users, Command+F3) in your
#' web browser to launch the reactive log visualization.
#'
#' The reactive log visualization only includes reactive activity up
#' until the time the report was loaded. If you want to see more recent
#' activity, refresh the browser.
#'
#' Note that Shiny does not distinguish between reactive dependencies
#' that "belong" to one Shiny user session versus another, so the
#' visualization will include all reactive activity that has taken place
#' in the process, not just for a particular application or session.
#'
#' As an alternative to pressing Ctrl/Command+F3--for example, if you
#' are using reactives outside of the context of a Shiny
#' application--you can run the \code{showReactLog} function, which will
#' generate the reactive log visualization as a static HTML file and
#' launch it in your default browser. In this case, refreshing your
#' browser will not load new activity into the report; you will need to
#' call \code{showReactLog()} explicitly.
#'
#' For security and performance reasons, do not enable
#' \code{shiny.reactlog} in production environments. When the option is
#' enabled, it's possible for any user of your app to see at least some
#' of the source code of your reactive expressions and observers.
#'
#' @export
showReactLog <- function() {
browseURL(renderReactLog())
}
renderReactLog <- function() {
templateFile <- system.file('www/reactive-graph.html', package='shiny')
html <- paste(readLines(templateFile, warn=FALSE), collapse='\r\n')
tc <- textConnection(NULL, 'w')
on.exit(close(tc))
writeReactLog(tc)
cat('\n', file=tc)
flush(tc)
html <- sub('__DATA__', paste(textConnectionValue(tc), collapse='\r\n'), html, fixed=TRUE)
file <- tempfile(fileext = '.html')
writeLines(html, file)
return(file)
}
.graphAppend <- function(logEntry) {
if (isTRUE(getOption('shiny.reactlog', FALSE)))
.graphEnv$log <- c(.graphEnv$log, list(logEntry))
}
.graphDependsOn <- function(id, label) {
if (isTRUE(getOption('shiny.reactlog', FALSE)))
.graphAppend(list(action='dep', id=id, dependsOn=label))
}
.graphDependsOnId <- function(id, dependee) {
if (isTRUE(getOption('shiny.reactlog', FALSE)))
.graphAppend(list(action='depId', id=id, dependsOn=dependee))
}
.graphCreateContext <- function(id, label, type, prevId) {
if (isTRUE(getOption('shiny.reactlog', FALSE)))
.graphAppend(list(
action='ctx', id=id, label=paste(label, collapse='\n'), type=type, prevId=prevId
))
}
.graphEnterContext <- function(id) {
if (isTRUE(getOption('shiny.reactlog', FALSE)))
.graphAppend(list(action='enter', id=id))
}
.graphExitContext <- function(id) {
if (isTRUE(getOption('shiny.reactlog', FALSE)))
.graphAppend(list(action='exit', id=id))
}
.graphValueChange <- function(label, value) {
if (isTRUE(getOption('shiny.reactlog', FALSE))) {
.graphAppend(list(
action = 'valueChange',
id = label,
value = paste(capture.output(str(value)), collapse='\n')
))
}
}
.graphInvalidate <- function(id) {
if (isTRUE(getOption('shiny.reactlog', FALSE)))
.graphAppend(list(action='invalidate', id=id))
}
.graphEnv <- new.env()
.graphEnv$log <- list()

55
R/imageutils.R Normal file
View File

@@ -0,0 +1,55 @@
#' Run a plotting function and save the output as a PNG
#'
#' This function returns the name of the PNG file that it generates. In
#' essence, it calls \code{png()}, then \code{func()}, then \code{dev.off()}.
#' So \code{func} must be a function that will generate a plot when used this
#' way.
#'
#' 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.
#'
#' In some cases, \code{Cairo()} provides output that looks worse than
#' \code{png()}. To disable Cairo output for an app, use
#' \code{options(shiny.usecairo=FALSE)}.
#'
#' @param func A function that generates a plot.
#' @param filename The name of the output file. Defaults to a temp file with
#' extension \code{.png}.
#' @param width Width in pixels.
#' @param height Height in pixels.
#' @param res Resolution in pixels per inch. This value is passed to
#' \code{\link{png}}. Note that this affects the resolution of PNG rendering in
#' R; it won't change the actual ppi of the browser.
#' @param ... Arguments to be passed through to \code{\link[grDevices]{png}}.
#' These can be used to set the width, height, background color, etc.
#'
#' @export
plotPNG <- function(func, filename=tempfile(fileext='.png'),
width=400, height=400, res=72, ...) {
# 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 (getOption('shiny.usecairo', TRUE) &&
nchar(system.file(package = "Cairo"))) {
# Workaround for issue #140: Cairo ignores res and dpi settings. Need to
# use regular png function.
if (res == 72) {
pngfun <- Cairo::CairoPNG
} else {
pngfun <- png
}
} else {
pngfun <- png
}
pngfun(filename=filename, width=width, height=height, res=res, ...)
dv <- dev.cur()
tryCatch(shinyCallingHandlers(func()), finally = dev.off(dv))
filename
}

29
R/map.R
View File

@@ -20,30 +20,36 @@ Map <- setRefClass(
},
get = function(key) {
if (.self$containsKey(key))
return(base::get(key, pos=.env, inherits=F))
return(base::get(key, pos=.env, inherits=FALSE))
else
return(NULL)
},
set = function(key, value) {
assign(key, value, pos=.env, inherits=F)
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=F)
rm(list = key, pos=.env, inherits=FALSE)
return(result)
}
return(NULL)
},
containsKey = function(key) {
exists(key, where=.env, inherits=F)
exists(key, where=.env, inherits=FALSE)
},
keys = function() {
ls(envir=.env, all.names=T)
ls(envir=.env, all.names=TRUE)
},
values = function() {
mget(.self$keys(), envir=.env, inherits=F)
mget(.self$keys(), envir=.env, inherits=FALSE)
},
clear = function() {
.env <<- new.env(parent=emptyenv())
@@ -55,19 +61,10 @@ Map <- setRefClass(
)
)
`[.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=F)
simplify=FALSE)
}
length.Map <- function(map) {
map$size()

110
R/priorityqueue.R Normal file
View File

@@ -0,0 +1,110 @@
# "...like a regular queue or stack data structure, but where additionally each
# element has a "priority" associated with it. In a priority queue, an element
# with high priority is served before an element with low priority. If two
# elements have the same priority, they are served according to their order in
# the queue." (http://en.wikipedia.org/wiki/Priority_queue)
PriorityQueue <- setRefClass(
'PriorityQueue',
fields = list(
# Keys are priorities, values are subqueues (implemented as list)
.itemsByPriority = 'Map',
# Sorted vector (largest first)
.priorities = 'numeric'
),
methods = list(
# Enqueue an item, with the given priority level (must be integer). Higher
# priority numbers are dequeued earlier than lower.
enqueue = function(item, priority) {
priority <- normalizePriority(priority)
if (!(priority %in% .priorities)) {
.priorities <<- c(.priorities, priority)
.priorities <<- sort(.priorities, decreasing=TRUE)
.itemsByPriority$set(.key(priority), list(item))
} else {
.itemsByPriority$set(
.key(priority),
c(.itemsByPriority$get(.key(priority)), item)
)
}
return(invisible())
},
# Retrieve a single item by 1) priority number (highest first) and then 2)
# insertion order (first in, first out). If there are no items to be
# dequeued, then NULL is returned. If it is necessary to distinguish between
# a NULL value and the empty case, call isEmpty() before dequeue().
dequeue = function() {
if (length(.priorities) == 0)
return(NULL)
maxPriority <- .priorities[[1]]
items <- .itemsByPriority$get(.key(maxPriority))
firstItem <- items[[1]]
if (length(items) == 1) {
# This is the last item at this priority. Remove both the list and the
# priority level.
.itemsByPriority$remove(.key(maxPriority))
.priorities <<- .priorities[-1]
} else {
# There are still items at this priority. Remove the current item from
# the list, and save it.
items <- items[-1]
.itemsByPriority$set(.key(maxPriority), items)
}
return(firstItem)
},
# Returns TRUE if no items are in the queue, otherwise FALSE.
isEmpty = function() {
length(.priorities) == 0
},
# Translates a priority integer to a character that is suitable for using as
# a key.
.key = function(priority) {
sprintf('%a', priority)
}
)
)
normalizePriority <- function(priority) {
if (is.null(priority))
priority <- 0
# Cast integers to numeric to prevent any inconsistencies
if (is.integer(priority))
priority <- as.numeric(priority)
if (!is.numeric(priority))
stop('priority must be an integer or numeric')
# Check length
if (length(priority) == 0) {
warning('Zero-length priority vector was passed; using 0')
priority <- 0
} else if (length(priority) > 1) {
warning('Priority has length > 1 and only the first element will be used')
priority <- priority[1]
}
# NA == 0
if (is.na(priority))
priority <- 0
return(priority)
}
# pq <- PriorityQueue$new()
# pq$enqueue('a', 1)
# pq$enqueue('b', 1L)
# pq$enqueue('c', 1)
# pq$enqueue('A', 2)
# pq$enqueue('B', 2L)
# pq$enqueue('C', 2)
# pq$enqueue('d', 1)
# pq$enqueue('e', 1L)
# pq$enqueue('f', 1)
# pq$enqueue('D', 2)
# pq$enqueue('E', 2L)
# pq$enqueue('F', 2)
# # Expect ABCDEFabcdef

View File

@@ -2,38 +2,38 @@ Context <- setRefClass(
'Context',
fields = list(
id = 'character',
.label = 'character', # For debug purposes
.invalidated = 'logical',
.callbacks = 'list',
.hintCallbacks = 'list'
.invalidateCallbacks = 'list',
.flushCallbacks = 'list'
),
methods = list(
initialize = function() {
initialize = function(label='', type='other', prevId='') {
id <<- .getReactiveEnvironment()$nextId()
.invalidated <<- F
.callbacks <<- list()
.hintCallbacks <<- list()
.invalidated <<- FALSE
.invalidateCallbacks <<- list()
.flushCallbacks <<- list()
.label <<- label
.graphCreateContext(id, label, type, prevId)
},
run = function(func) {
"Run the provided function under this context."
env <- .getReactiveEnvironment()
.graphEnterContext(id)
on.exit(.graphExitContext(id))
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}}."
"Invalidate this context. It will immediately call the callbacks
that have been registered with onInvalidate()."
if (.invalidated)
return()
.invalidated <<- T
.getReactiveEnvironment()$addPendingInvalidate(.self)
.invalidated <<- TRUE
.graphInvalidate(id)
lapply(.invalidateCallbacks, function(func) {
func()
})
NULL
},
onInvalidate = function(func) {
@@ -43,23 +43,27 @@ Context <- setRefClass(
if (.invalidated)
func()
else
.callbacks <<- c(.callbacks, func)
.invalidateCallbacks <<- c(.invalidateCallbacks, func)
NULL
},
onInvalidateHint = function(func) {
.hintCallbacks <<- c(.hintCallbacks, func)
addPendingFlush = function(priority) {
"Tell the reactive environment that this context should be flushed the
next time flushReact() called."
.getReactiveEnvironment()$addPendingFlush(.self, priority)
},
executeCallbacks = function() {
onFlush = function(func) {
"Register a function to be called when this context is flushed."
.flushCallbacks <<- c(.flushCallbacks, func)
},
executeFlushCallbacks = function() {
"For internal use only."
lapply(.callbacks, function(func) {
tryCatch({
lapply(.flushCallbacks, function(func) {
withCallingHandlers({
func()
}, warning = function(e) {
# TODO: Callbacks in app
print(e)
}, error = function(e) {
# TODO: Callbacks in app
print(e)
})
})
}
@@ -68,51 +72,57 @@ Context <- setRefClass(
ReactiveEnvironment <- setRefClass(
'ReactiveEnvironment',
fields = c('.currentContext', '.nextId', '.pendingInvalidate'),
fields = list(
.currentContext = 'ANY',
.nextId = 'integer',
.pendingFlush = 'PriorityQueue',
.inFlush = 'logical'
),
methods = list(
initialize = function() {
.currentContext <<- NULL
.nextId <<- 0L
.pendingInvalidate <<- list()
.pendingFlush <<- PriorityQueue$new()
.inFlush <<- FALSE
},
nextId = function() {
.nextId <<- .nextId + 1L
return(as.character(.nextId))
},
currentContext = function() {
if (is.null(.currentContext))
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)
},
runWith = function(ctx, func) {
old.ctx <- .currentContext
.currentContext <<- ctx
on.exit(.currentContext <<- old.ctx)
func()
shinyCallingHandlers(func())
},
addPendingInvalidate = function(ctx) {
.pendingInvalidate <<- c(.pendingInvalidate, ctx)
addPendingFlush = function(ctx, priority) {
.pendingFlush$enqueue(ctx, priority)
},
flush = function() {
while (length(.pendingInvalidate) > 0) {
contexts <- .pendingInvalidate
.pendingInvalidate <<- list()
lapply(contexts, function(ctx) {
ctx$executeCallbacks()
NULL
})
# If already in a flush, don't start another one
if (.inFlush) return()
.inFlush <<- TRUE
on.exit(.inFlush <<- FALSE)
while (!.pendingFlush$isEmpty()) {
ctx <- .pendingFlush$dequeue()
ctx$executeFlushCallbacks()
}
}
)
)
.reactiveEnvironment <- ReactiveEnvironment$new()
.getReactiveEnvironment <- function() {
if (!exists('.ReactiveEnvironment', envir=.GlobalEnv, inherits=F)) {
assign('.ReactiveEnvironment', ReactiveEnvironment$new(), envir=.GlobalEnv)
}
get('.ReactiveEnvironment', envir=.GlobalEnv, inherits=F)
.reactiveEnvironment
}
# Causes any pending invalidations to run.

File diff suppressed because it is too large Load Diff

164
R/run-url.R Normal file
View File

@@ -0,0 +1,164 @@
#' Run a Shiny application from https://gist.github.com
#'
#' Download and launch a Shiny application that is hosted on GitHub as a gist.
#'
#' @param gist The identifier of the gist. For example, if the gist is
#' https://gist.github.com/jcheng5/3239667, then \code{3239667},
#' \code{'3239667'}, and \code{'https://gist.github.com/jcheng5/3239667'}
#' are all valid values.
#' @param port The TCP port that the application should listen on. Defaults to
#' choosing a random port.
#' @param 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.
#'
#' @examples
#' \dontrun{
#' runGist(3239667)
#' runGist("https://gist.github.com/jcheng5/3239667")
#'
#' # Old URL format without username
#' runGist("https://gist.github.com/3239667")
#' }
#'
#' @export
runGist <- function(gist,
port=NULL,
launch.browser=getOption('shiny.launch.browser',
interactive())) {
gistUrl <- if (is.numeric(gist) || grepl('^[0-9a-f]+$', gist)) {
sprintf('https://gist.github.com/%s/download', gist)
} else if(grepl('^https://gist.github.com/([^/]+/)?([0-9a-f]+)$', gist)) {
paste(gist, '/download', sep='')
} else {
stop('Unrecognized gist identifier format')
}
runUrl(gistUrl, filetype=".tar.gz", subdir=NULL, port=port,
launch.browser=launch.browser)
}
#' Run a Shiny application from a GitHub repository
#'
#' Download and launch a Shiny application that is hosted in a GitHub repository.
#'
#' @param repo Name of the repository
#' @param username GitHub username
#' @param ref Desired git reference. Could be a commit, tag, or branch
#' name. Defaults to \code{"master"}.
#' @param subdir A subdirectory in the repository that contains the app. By
#' default, this function will run an app from the top level of the repo, but
#' you can use a path such as `\code{"inst/shinyapp"}.
#' @param port The TCP port that the application should listen on. Defaults to
#' choosing a random port.
#' @param 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.
#'
#' @examples
#' \dontrun{
#' runGitHub("shiny_example", "rstudio")
#'
#' # Can run an app from a subdirectory in the repo
#' runGitHub("shiny_example", "rstudio", subdir = "inst/shinyapp/")
#' }
#'
#' @export
runGitHub <- function(repo, username = getOption("github.user"),
ref = "master", subdir = NULL, port = NULL,
launch.browser = getOption('shiny.launch.browser', interactive())) {
if (is.null(ref)) {
stop("Must specify either a ref. ")
}
message("Downloading github repo(s) ",
paste(repo, ref, sep = "/", collapse = ", "),
" from ",
paste(username, collapse = ", "))
name <- paste(username, "-", repo, sep = "")
url <- paste("https://github.com/", username, "/", repo, "/archive/",
ref, ".tar.gz", sep = "")
runUrl(url, subdir=subdir, port=port, launch.browser=launch.browser)
}
#' Run a Shiny application from a URL
#'
#' Download and launch a Shiny application that is hosted at a downloadable
#' URL. The Shiny application must be saved in a .zip, .tar, or .tar.gz file.
#' The Shiny application files must be contained in a subdirectory in the
#' archive. For example, the files might be \code{myapp/server.r} and
#' \code{myapp/ui.r}.
#'
#' @param url URL of the application.
#' @param filetype The file type (\code{".zip"}, \code{".tar"}, or
#' \code{".tar.gz"}. Defaults to the file extension taken from the url.
#' @param subdir A subdirectory in the repository that contains the app. By
#' default, this function will run an app from the top level of the repo, but
#' you can use a path such as `\code{"inst/shinyapp"}.
#' @param port The TCP port that the application should listen on. Defaults to
#' choosing a random port.
#' @param 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.
#'
#' @examples
#' \dontrun{
#' runUrl('https://github.com/rstudio/shiny_example/archive/master.tar.gz')
#'
#' # Can run an app from a subdirectory in the archive
#' runUrl("https://github.com/rstudio/shiny_example/archive/master.zip",
#' subdir = "inst/shinyapp/")
#' }
#'
#' @export
runUrl <- function(url, filetype = NULL, subdir = NULL, port = NULL,
launch.browser = getOption('shiny.launch.browser', interactive())) {
if (!is.null(subdir) && ".." %in% strsplit(subdir, '/')[[1]])
stop("'..' not allowed in subdir")
if (is.null(filetype))
filetype <- basename(url)
if (grepl("\\.tar\\.gz$", filetype))
fileext <- ".tar.gz"
else if (grepl("\\.tar$", filetype))
fileext <- ".tar"
else if (grepl("\\.zip$", filetype))
fileext <- ".zip"
else
stop("Unknown file extension.")
message("Downloading ", url)
filePath <- tempfile('shinyapp', fileext=fileext)
if (download(url, filePath, mode = "wb", quiet = TRUE) != 0)
stop("Failed to download URL ", url)
on.exit(unlink(filePath))
if (fileext %in% c(".tar", ".tar.gz")) {
# Regular untar commonly causes two problems on Windows with github tarballs:
# 1) If RTools' tar.exe is in the path, you get cygwin path warnings which
# throw list=TRUE off;
# 2) If the internal untar implementation is used, it chokes on the 'g'
# type flag that github uses (to stash their commit hash info).
# By using our own forked/modified untar2 we sidestep both issues.
dirname <- untar2(filePath, list=TRUE)[1]
untar2(filePath, exdir = dirname(filePath))
} else if (fileext == ".zip") {
dirname <- as.character(unzip(filePath, list=TRUE)$Name[1])
unzip(filePath, exdir = dirname(filePath))
}
appdir <- file.path(dirname(filePath), dirname)
on.exit(unlink(appdir, recursive = TRUE), add = TRUE)
appsubdir <- ifelse(is.null(subdir), appdir, file.path(appdir, subdir))
runApp(appsubdir, port=port, launch.browser=launch.browser)
}

1423
R/shiny.R

File diff suppressed because it is too large Load Diff

View File

@@ -47,6 +47,76 @@ strong <- function(...) tags$strong(...)
#' @export
em <- function(...) tags$em(...)
#' Include Content From a File
#'
#' Include HTML, text, or rendered Markdown into a \link[=shinyUI]{Shiny UI}.
#'
#' These functions provide a convenient way to include an extensive amount of
#' HTML, textual, Markdown, CSS, or JavaScript 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.
#'
#' @param 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.
#'
#' @rdname include
#' @export
includeHTML <- function(path) {
dependsOnFile(path)
lines <- readLines(path, warn=FALSE, encoding='UTF-8')
return(HTML(paste(lines, collapse='\r\n')))
}
#' @rdname include
#' @export
includeText <- function(path) {
dependsOnFile(path)
lines <- readLines(path, warn=FALSE, encoding='UTF-8')
return(paste(lines, collapse='\r\n'))
}
#' @rdname include
#' @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))
}
#' @param ... Any additional attributes to be applied to the generated tag.
#' @rdname include
#' @export
includeCSS <- function(path, ...) {
dependsOnFile(path)
lines <- readLines(path, warn=FALSE, encoding='UTF-8')
args <- list(...)
if (is.null(args$type))
args$type <- 'text/css'
return(do.call(tags$style,
c(list(HTML(paste(lines, collapse='\r\n'))), args)))
}
#' @rdname include
#' @export
includeScript <- function(path, ...) {
dependsOnFile(path)
lines <- readLines(path, warn=FALSE, encoding='UTF-8')
return(tags$script(HTML(paste(lines, collapse='\r\n')), ...))
}
#' Include Content Only Once
#'
@@ -81,7 +151,7 @@ renderPage <- function(ui, connection) {
if (isTag(content) && identical(content$name, "head")) {
textConn <- textConnection(NULL, "w")
textConnWriter <- function(text) cat(text, file = textConn)
tagWriteChildren(content, textConnWriter, 1, context)
tagWrite(content$children, textConnWriter, 1, context)
context$head <- append(context$head, textConnectionValue(textConn))
close(textConn)
return (FALSE)
@@ -101,10 +171,10 @@ renderPage <- function(ui, connection) {
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"/>',
' <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>',
@@ -159,10 +229,15 @@ renderPage <- function(ui, connection) {
#' @export
shinyUI <- function(ui, path='/') {
force(ui)
registerClient({
function(ws, header) {
if (header$RESOURCE != path)
function(req) {
if (!identical(req$REQUEST_METHOD, 'GET'))
return(NULL)
if (req$PATH_INFO != path)
return(NULL)
textConn <- textConnection(NULL, "w")
@@ -170,7 +245,7 @@ shinyUI <- function(ui, path='/') {
renderPage(ui, textConn)
html <- paste(textConnectionValue(textConn), collapse='\n')
return(http_response(ws, 200, content=html))
return(httpResponse(200, content=html))
}
})
}

View File

@@ -2,56 +2,251 @@ suppressPackageStartupMessages({
library(caTools)
library(xtable)
})
globalVariables('func')
#' Plot Output
#'
#' Creates a reactive plot that is suitable for assigning to an \code{output}
#' Renders 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}.
#'
#' @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.
#' @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.
#'
#' @seealso For more details on how the plots are generated, and how to control
#' the output, see \code{\link{plotPNG}}.
#'
#' @param expr An expression 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 res Resolution of resulting plot, in pixels per inch. This value is
#' passed to \code{\link{png}}. Note that this affects the resolution of PNG
#' rendering in R; it won't change the actual ppi of the browser.
#' @param ... Arguments to be passed through to \code{\link[grDevices]{png}}.
#' These can be used to set the width, height, background color, etc.
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' is useful if you want to save an expression in a variable.
#' @param func A function that generates a plot (deprecated; use \code{expr}
#' instead).
#'
#' @export
reactivePlot <- function(func, width='auto', height='auto', ...) {
renderPlot <- function(expr, width='auto', height='auto', res=72, ...,
env=parent.frame(), quoted=FALSE, func=NULL) {
if (!is.null(func)) {
shinyDeprecated(msg="renderPlot: argument 'func' is deprecated. Please use 'expr' instead.")
} else {
installExprFunction(expr, "func", env, quoted)
}
args <- list(...)
return(function(shinyapp, name, ...) {
png.file <- tempfile(fileext='.png')
if (is.function(width))
widthWrapper <- reactive({ width() })
else
widthWrapper <- NULL
if (is.function(height))
heightWrapper <- reactive({ height() })
else
heightWrapper <- NULL
return(function(shinysession, name, ...) {
if (!is.null(widthWrapper))
width <- widthWrapper()
if (!is.null(heightWrapper))
height <- heightWrapper()
# Note that these are reactive calls. A change to the width and height
# will inherently cause a reactive plot to redraw (unless width and
# height were explicitly specified).
prefix <- '.shinyout_'
prefix <- 'output_'
if (width == 'auto')
width <- shinyapp$session$get(paste(prefix, name, '_width', sep=''));
width <- shinysession$clientData[[paste(prefix, name, '_width', sep='')]];
if (height == 'auto')
height <- shinyapp$session$get(paste(prefix, name, '_height', sep=''));
height <- shinysession$clientData[[paste(prefix, name, '_height', sep='')]];
if (width <= 0 || height <= 0)
if (is.null(width) || is.null(height) || width <= 0 || height <= 0)
return(NULL)
# Resolution multiplier
pixelratio <- shinysession$clientData$pixelratio
if (is.null(pixelratio))
pixelratio <- 1
do.call(png, c(args, filename=png.file, width=width, height=height))
tryCatch(
func(),
finally=dev.off())
coordmap <- NULL
plotFunc <- function() {
# Actually perform the plotting
func()
# Now capture some graphics device info before we close it
usrCoords <- par('usr')
usrBounds <- usrCoords
if (par('xlog')) {
usrBounds[c(1,2)] <- 10 ^ usrBounds[c(1,2)]
}
if (par('ylog')) {
usrBounds[c(3,4)] <- 10 ^ usrBounds[c(3,4)]
}
coordmap <<- list(
usr = c(
left = usrCoords[1],
right = usrCoords[2],
bottom = usrCoords[3],
top = usrCoords[4]
),
# The bounds of the plot area, in DOM pixels
bounds = c(
left = grconvertX(usrBounds[1], 'user', 'nfc') * width,
right = grconvertX(usrBounds[2], 'user', 'nfc') * width,
bottom = (1-grconvertY(usrBounds[3], 'user', 'nfc')) * height,
top = (1-grconvertY(usrBounds[4], 'user', 'nfc')) * height
),
log = c(
x = par('xlog'),
y = par('ylog')
),
pixelratio = pixelratio
)
}
outfile <- do.call(plotPNG, c(plotFunc, width=width*pixelratio,
height=height*pixelratio, res=res*pixelratio, args))
on.exit(unlink(outfile))
bytes <- file.info(png.file)$size
if (is.na(bytes))
return(NULL)
b64 <- base64encode(readBin(png.file, 'raw', n=bytes))
return(paste("data:image/png;base64,", b64, sep=''))
# Return a list of attributes for the img
return(list(
src=shinysession$fileUrl(name, outfile, contentType='image/png'),
width=width, height=height, coordmap=coordmap
))
})
}
#' Image file output
#'
#' Renders a reactive image that is suitable for assigning to an \code{output}
#' slot.
#'
#' The expression \code{expr} must return a list containing the attributes for
#' the \code{img} object on the client web page. For the image to display,
#' properly, the list must have at least one entry, \code{src}, which is the
#' path to the image file. It may also useful to have a \code{contentType}
#' entry specifying the MIME type of the image. If one is not provided,
#' \code{renderImage} will try to autodetect the type, based on the file
#' extension.
#'
#' Other elements such as \code{width}, \code{height}, \code{class}, and
#' \code{alt}, can also be added to the list, and they will be used as
#' attributes in the \code{img} object.
#'
#' The corresponding HTML output tag should be \code{div} or \code{img} and have
#' the CSS class name \code{shiny-image-output}.
#'
#' @seealso For more details on how the images are generated, and how to control
#' the output, see \code{\link{plotPNG}}.
#'
#' @param expr An expression that returns a list.
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' is useful if you want to save an expression in a variable.
#' @param deleteFile Should the file in \code{func()$src} be deleted after
#' it is sent to the client browser? Generally speaking, if the image is a
#' temp file generated within \code{func}, then this should be \code{TRUE};
#' if the image is not a temp file, this should be \code{FALSE}.
#'
#' @export
#'
#' @examples
#' \dontrun{
#'
#' shinyServer(function(input, output, clientData) {
#'
#' # A plot of fixed size
#' output$plot1 <- renderImage({
#' # A temp file to save the output. It will be deleted after renderImage
#' # sends it, because deleteFile=TRUE.
#' outfile <- tempfile(fileext='.png')
#'
#' # Generate a png
#' png(outfile, width=400, height=400)
#' hist(rnorm(input$n))
#' dev.off()
#'
#' # Return a list
#' list(src = outfile,
#' alt = "This is alternate text")
#' }, deleteFile = TRUE)
#'
#' # A dynamically-sized plot
#' output$plot2 <- renderImage({
#' # Read plot2's width and height. These are reactive values, so this
#' # expression will re-run whenever these values change.
#' width <- clientData$output_plot2_width
#' height <- clientData$output_plot2_height
#'
#' # A temp file to save the output.
#' outfile <- tempfile(fileext='.png')
#'
#' png(outfile, width=width, height=height)
#' hist(rnorm(input$obs))
#' dev.off()
#'
#' # Return a list containing the filename
#' list(src = outfile,
#' width = width,
#' height = height,
#' alt = "This is alternate text")
#' }, deleteFile = TRUE)
#'
#' # Send a pre-rendered image, and don't delete the image after sending it
#' output$plot3 <- renderImage({
#' # When input$n is 1, filename is ./images/image1.jpeg
#' filename <- normalizePath(file.path('./images',
#' paste('image', input$n, '.jpeg', sep='')))
#'
#' # Return a list containing the filename
#' list(src = filename)
#' }, deleteFile = FALSE)
#' })
#'
#' }
renderImage <- function(expr, env=parent.frame(), quoted=FALSE,
deleteFile=TRUE) {
installExprFunction(expr, "func", env, quoted)
return(function(shinysession, name, ...) {
imageinfo <- func()
# Should the file be deleted after being sent? If .deleteFile not set or if
# TRUE, then delete; otherwise don't delete.
if (deleteFile) {
on.exit(unlink(imageinfo$src))
}
# If contentType not specified, autodetect based on extension
if (is.null(imageinfo$contentType)) {
contentType <- getContentType(sub('^.*\\.', '', basename(imageinfo$src)))
} else {
contentType <- imageinfo$contentType
}
# Extra values are everything in imageinfo except 'src' and 'contentType'
extra_attr <- imageinfo[!names(imageinfo) %in% c('src', 'contentType')]
# Return a list with src, and other img attributes
c(src = shinysession$fileUrl(name, file=imageinfo$src, contentType=contentType),
extra_attr)
})
}
#' Table Output
#'
#' Creates a reactive table that is suitable for assigning to an \code{output}
@@ -60,32 +255,49 @@ reactivePlot <- function(func, width='auto', height='auto', ...) {
#' 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
#' @param expr An expression that returns an R object that can be used with
#' \code{\link[xtable]{xtable}}.
#' @param ... Arguments to be passed through to \code{\link[xtable]{xtable}}.
#' @param ... Arguments to be passed through to \code{\link[xtable]{xtable}} and
#' \code{\link[xtable]{print.xtable}}.
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' is useful if you want to save an expression in a variable.
#' @param func A function that returns an R object that can be used with
#' \code{\link[xtable]{xtable}} (deprecated; use \code{expr} instead).
#'
#' @export
reactiveTable <- function(func, ...) {
reactive(function() {
renderTable <- function(expr, ..., env=parent.frame(), quoted=FALSE, func=NULL) {
if (!is.null(func)) {
shinyDeprecated(msg="renderTable: argument 'func' is deprecated. Please use 'expr' instead.")
} else {
installExprFunction(expr, "func", env, quoted)
}
function() {
classNames <- getOption('shiny.table.class', 'data table table-bordered table-condensed')
data <- func()
if (is.null(data) || identical(data, data.frame()))
return("")
return(paste(
capture.output(
print(xtable(data, ...),
type='html',
html.table.attributes=paste('class="',
htmlEscape(classNames, T),
htmlEscape(classNames, TRUE),
'"',
sep=''))),
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.
#' Makes a reactive version of the given function that captures any printed
#' output, and also captures its printable result (unless
#' \code{\link{invisible}}), into a string. The resulting function is suitable
#' 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
@@ -94,13 +306,37 @@ reactiveTable <- function(func, ...) {
#' 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.
#' Note that unlike most other Shiny output functions, if the given function
#' returns \code{NULL} then \code{NULL} will actually be visible in the output.
#' To display nothing, make your function return \code{\link{invisible}()}.
#'
#' @param expr An expression that may print output and/or return a printable R
#' object.
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' @param func A function that may print output and/or return a printable R
#' object (deprecated; use \code{expr} instead).
#'
#' @seealso \code{\link{renderText}} for displaying the value returned from a
#' function, instead of the printed output.
#'
#' @example res/text-example.R
#'
#' @export
reactivePrint <- function(func) {
reactive(function() {
return(paste(capture.output(print(func())), collapse="\n"))
})
renderPrint <- function(expr, env=parent.frame(), quoted=FALSE, func=NULL) {
if (!is.null(func)) {
shinyDeprecated(msg="renderPrint: argument 'func' is deprecated. Please use 'expr' instead.")
} else {
installExprFunction(expr, "func", env, quoted)
}
function() {
return(paste(capture.output({
result <- withVisible(func())
if (result$visible)
print(result$value)
}), collapse="\n"))
}
}
#' Text Output
@@ -116,12 +352,202 @@ reactivePrint <- function(func) {
#' 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
#' @param expr An expression that returns an R object that can be used as an
#' argument to \code{cat}.
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' is useful if you want to save an expression in a variable.
#' @param func A function that returns an R object that can be used as an
#' argument to \code{cat}.(deprecated; use \code{expr} instead).
#'
#' @seealso \code{\link{renderPrint}} for capturing the print output of a
#' function, rather than the returned text value.
#'
#' @example res/text-example.R
#'
#' @export
reactiveText <- function(func) {
reactive(function() {
return(paste(capture.output(cat(func())), collapse="\n"))
renderText <- function(expr, env=parent.frame(), quoted=FALSE, func=NULL) {
if (!is.null(func)) {
shinyDeprecated(msg="renderText: argument 'func' is deprecated. Please use 'expr' instead.")
} else {
installExprFunction(expr, "func", env, quoted)
}
function() {
value <- func()
return(paste(capture.output(cat(value)), 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 expr An expression that returns a Shiny tag object, \code{\link{HTML}},
#' or a list of such objects.
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' is useful if you want to save an expression in a variable.
#' @param func A function that returns a Shiny tag object, \code{\link{HTML}},
#' or a list of such objects (deprecated; use \code{expr} instead).
#'
#' @seealso conditionalPanel
#'
#' @export
#' @examples
#' \dontrun{
#' output$moreControls <- renderUI({
#' list(
#'
#' )
#' })
#' }
renderUI <- function(expr, env=parent.frame(), quoted=FALSE, func=NULL) {
if (!is.null(func)) {
shinyDeprecated(msg="renderUI: argument 'func' is deprecated. Please use 'expr' instead.")
} else {
installExprFunction(expr, "func", env, quoted)
}
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(shinysession, name, ...) {
shinysession$registerDownload(name, filename, contentType, content)
})
}
}
#' Table output with the JavaScript library DataTables
#'
#' Makes a reactive version of the given function that returns a data frame (or
#' matrix), which will be rendered with the DataTables library. Paging,
#' searching, filtering, and sorting can be done on the R side using Shiny as
#' the server infrastructure.
#' @param expr An expression that returns a data frame or a matrix.
#' @param options A list of initialization options to be passed to DataTables.
#' @param searchDelay The delay for searching, in milliseconds (to avoid too
#' frequent search requests).
#' @references \url{http://datatables.net}
#' @export
#' @inheritParams renderPlot
renderDataTable <- function(expr, options = NULL, searchDelay = 500,
env=parent.frame(), quoted=FALSE) {
installExprFunction(expr, "func", env, quoted)
function(shinysession, name, ...) {
data <- func()
if (length(dim(data)) != 2) return() # expects a rectangular data object
action <- shinysession$registerDataTable(name, data)
list(colnames = colnames(data), action = action, options = options,
searchDelay = searchDelay)
}
}
# Deprecated functions ------------------------------------------------------
#' Plot output (deprecated)
#'
#' See \code{\link{renderPlot}}.
#' @param func A function.
#' @param width Width.
#' @param height Height.
#' @param ... Other arguments to pass on.
#' @export
reactivePlot <- function(func, width='auto', height='auto', ...) {
shinyDeprecated(new="renderPlot")
renderPlot({ func() }, width=width, height=height, ...)
}
#' Table output (deprecated)
#'
#' See \code{\link{renderTable}}.
#' @param func A function.
#' @param ... Other arguments to pass on.
#' @export
reactiveTable <- function(func, ...) {
shinyDeprecated(new="renderTable")
renderTable({ func() })
}
#' Print output (deprecated)
#'
#' See \code{\link{renderPrint}}.
#' @param func A function.
#' @export
reactivePrint <- function(func) {
shinyDeprecated(new="renderPrint")
renderPrint({ func() })
}
#' UI output (deprecated)
#'
#' See \code{\link{renderUI}}.
#' @param func A function.
#' @export
reactiveUI <- function(func) {
shinyDeprecated(new="renderUI")
renderUI({ func() })
}
#' Text output (deprecated)
#'
#' See \code{\link{renderText}}.
#' @param func A function.
#' @export
reactiveText <- function(func) {
shinyDeprecated(new="renderText")
renderText({ func() })
}

View File

@@ -43,7 +43,7 @@ slider <- function(inputId, min, max, value, step = NULL, ...,
# validate numeric inputs
if (!is.numeric(value) || !is.numeric(min) || !is.numeric(max))
stop("min, max, amd value must all be numeric values")
stop("min, max, and value must all be numeric values")
else if (min(value) < min)
stop(paste("slider initial value", value,
"is less than the specified minimum"))
@@ -70,7 +70,7 @@ slider <- function(inputId, min, max, value, step = NULL, ...,
}
# Default state is to not have ticks
if (identical(ticks, T)) {
if (identical(ticks, TRUE)) {
# Automatic ticks
tickCount <- (range / step) + 1
if (tickCount <= 26)
@@ -101,28 +101,18 @@ slider <- function(inputId, min, max, value, step = NULL, ...,
}
# build slider
sliderFragment <- list(
singleton(
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$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, T))
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, F)) {
if (!is.null(animate) && !identical(animate, FALSE)) {
if (is.null(animate$playButton))
animate$playButton <- 'Play'
if (is.null(animate$pauseButton))

440
R/tags.R
View File

@@ -16,7 +16,7 @@ htmlEscape <- local({
)
.htmlSpecialsPatternAttrib <- paste(names(.htmlSpecialsAttrib), collapse='|')
function(text, attribute=T) {
function(text, attribute=TRUE) {
pattern <- if(attribute)
.htmlSpecialsPatternAttrib
else
@@ -32,7 +32,7 @@ htmlEscape <- local({
.htmlSpecials
for (chr in names(specials)) {
text <- gsub(chr, specials[[chr]], text, fixed=T)
text <- gsub(chr, specials[[chr]], text, fixed=TRUE)
}
return(text)
@@ -61,9 +61,18 @@ as.character.shiny.tag <- function(x, ...) {
cat(text, file=f)
}
tagWrite(x, textWriter)
return(HTML(paste(readLines(f), collapse='\n')))
return(HTML(paste(readLines(f, warn=FALSE), 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
@@ -72,95 +81,57 @@ normalizeText <- function(text) {
}
#' @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
tagAppendChildren <- function(tag, ..., list = NULL) {
tag$children <- c(tag$children, c(list(...), list))
tag
}
#' @export
tagSetChildren <- function(tag, ..., list = NULL) {
tag$children <- c(list(...), list)
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
# Get arg names; if not a named list, use vector of empty strings
varArgsNames <- names(varArgs)
if (is.null(varArgsNames))
varArgsNames <- character(length=length(varArgs))
# Named arguments become attribs, dropping NULL values
named_idx <- nzchar(varArgsNames)
attribs <- dropNulls(varArgs[named_idx])
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)
# Unnamed arguments are flattened and added as children.
# Use unname() to remove the names attribute from the list, which would
# consist of empty strings anyway.
children <- flattenTags(unname(varArgs[!named_idx]))
# Return tag data structure
structure(
list(name = `_tag_name`,
attribs = attribs,
children = children),
class = "shiny.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) {
tagWrite <- function(tag, textWriter, indent=0, context = NULL, eol = "\n") {
# optionally process a list of tags
if (!isTag(tag) && is.list(tag)) {
@@ -173,7 +144,13 @@ tagWrite <- function(tag, textWriter, indent=0, context = NULL) {
return (NULL)
# compute indent text
indentText <- paste(rep(" ", indent*3), collapse="")
indentText <- paste(rep(" ", indent*2), collapse="")
# Check if it's just text (may either be plain-text or HTML)
if (is.character(tag)) {
textWriter(paste(indentText, normalizeText(tag), eol, sep=""))
return (NULL)
}
# write tag name
textWriter(paste(indentText, "<", tag$name, sep=""))
@@ -194,19 +171,18 @@ tagWrite <- function(tag, textWriter, indent=0, context = NULL) {
# write any children
if (length(tag$children) > 0) {
textWriter(">")
# 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=""))
}
tagWrite(tag$children[[1]], textWriter, 0, context, "")
textWriter(paste("</", tag$name, ">", eol, sep=""))
}
else {
textWriter(">\n")
tagWriteChildren(tag, textWriter, indent+1, context)
textWriter(paste(indentText, "</", tag$name, ">\n", sep=""))
textWriter("\n")
for (child in tag$children)
tagWrite(child, textWriter, indent+1, context)
textWriter(paste(indentText, "</", tag$name, ">", eol, sep=""))
}
}
else {
@@ -215,129 +191,129 @@ tagWrite <- function(tag, textWriter, indent=0, context = NULL) {
if (tag$name %in% c("area", "base", "br", "col", "command", "embed", "hr",
"img", "input", "keygen", "link", "meta", "param",
"source", "track", "wbr")) {
textWriter("/>\n")
textWriter(paste("/>", eol, sep=""))
}
else {
textWriter(paste("></", tag$name, ">\n", sep=""))
textWriter(paste("></", tag$name, ">", eol, 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(...))
tags <- list(
a = function(...) tag("a", list(...)),
abbr = function(...) tag("abbr", list(...)),
address = function(...) tag("address", list(...)),
area = function(...) tag("area", list(...)),
article = function(...) tag("article", list(...)),
aside = function(...) tag("aside", list(...)),
audio = function(...) tag("audio", list(...)),
b = function(...) tag("b", list(...)),
base = function(...) tag("base", list(...)),
bdi = function(...) tag("bdi", list(...)),
bdo = function(...) tag("bdo", list(...)),
blockquote = function(...) tag("blockquote", list(...)),
body = function(...) tag("body", list(...)),
br = function(...) tag("br", list(...)),
button = function(...) tag("button", list(...)),
canvas = function(...) tag("canvas", list(...)),
caption = function(...) tag("caption", list(...)),
cite = function(...) tag("cite", list(...)),
code = function(...) tag("code", list(...)),
col = function(...) tag("col", list(...)),
colgroup = function(...) tag("colgroup", list(...)),
command = function(...) tag("command", list(...)),
data = function(...) tag("data", list(...)),
datalist = function(...) tag("datalist", list(...)),
dd = function(...) tag("dd", list(...)),
del = function(...) tag("del", list(...)),
details = function(...) tag("details", list(...)),
dfn = function(...) tag("dfn", list(...)),
div = function(...) tag("div", list(...)),
dl = function(...) tag("dl", list(...)),
dt = function(...) tag("dt", list(...)),
em = function(...) tag("em", list(...)),
embed = function(...) tag("embed", list(...)),
eventsource = function(...) tag("eventsource", list(...)),
fieldset = function(...) tag("fieldset", list(...)),
figcaption = function(...) tag("figcaption", list(...)),
figure = function(...) tag("figure", list(...)),
footer = function(...) tag("footer", list(...)),
form = function(...) tag("form", list(...)),
h1 = function(...) tag("h1", list(...)),
h2 = function(...) tag("h2", list(...)),
h3 = function(...) tag("h3", list(...)),
h4 = function(...) tag("h4", list(...)),
h5 = function(...) tag("h5", list(...)),
h6 = function(...) tag("h6", list(...)),
head = function(...) tag("head", list(...)),
header = function(...) tag("header", list(...)),
hgroup = function(...) tag("hgroup", list(...)),
hr = function(...) tag("hr", list(...)),
html = function(...) tag("html", list(...)),
i = function(...) tag("i", list(...)),
iframe = function(...) tag("iframe", list(...)),
img = function(...) tag("img", list(...)),
input = function(...) tag("input", list(...)),
ins = function(...) tag("ins", list(...)),
kbd = function(...) tag("kbd", list(...)),
keygen = function(...) tag("keygen", list(...)),
label = function(...) tag("label", list(...)),
legend = function(...) tag("legend", list(...)),
li = function(...) tag("li", list(...)),
link = function(...) tag("link", list(...)),
mark = function(...) tag("mark", list(...)),
map = function(...) tag("map", list(...)),
menu = function(...) tag("menu", list(...)),
meta = function(...) tag("meta", list(...)),
meter = function(...) tag("meter", list(...)),
nav = function(...) tag("nav", list(...)),
noscript = function(...) tag("noscript", list(...)),
object = function(...) tag("object", list(...)),
ol = function(...) tag("ol", list(...)),
optgroup = function(...) tag("optgroup", list(...)),
option = function(...) tag("option", list(...)),
output = function(...) tag("output", list(...)),
p = function(...) tag("p", list(...)),
param = function(...) tag("param", list(...)),
pre = function(...) tag("pre", list(...)),
progress = function(...) tag("progress", list(...)),
q = function(...) tag("q", list(...)),
ruby = function(...) tag("ruby", list(...)),
rp = function(...) tag("rp", list(...)),
rt = function(...) tag("rt", list(...)),
s = function(...) tag("s", list(...)),
samp = function(...) tag("samp", list(...)),
script = function(...) tag("script", list(...)),
section = function(...) tag("section", list(...)),
select = function(...) tag("select", list(...)),
small = function(...) tag("small", list(...)),
source = function(...) tag("source", list(...)),
span = function(...) tag("span", list(...)),
strong = function(...) tag("strong", list(...)),
style = function(...) tag("style", list(...)),
sub = function(...) tag("sub", list(...)),
summary = function(...) tag("summary", list(...)),
sup = function(...) tag("sup", list(...)),
table = function(...) tag("table", list(...)),
tbody = function(...) tag("tbody", list(...)),
td = function(...) tag("td", list(...)),
textarea = function(...) tag("textarea", list(...)),
tfoot = function(...) tag("tfoot", list(...)),
th = function(...) tag("th", list(...)),
thead = function(...) tag("thead", list(...)),
time = function(...) tag("time", list(...)),
title = function(...) tag("title", list(...)),
tr = function(...) tag("tr", list(...)),
track = function(...) tag("track", list(...)),
u = function(...) tag("u", list(...)),
ul = function(...) tag("ul", list(...)),
var = function(...) tag("var", list(...)),
video = function(...) tag("video", list(...)),
wbr = function(...) tag("wbr", list(...))
)
#' Mark Characters as HTML
#'
@@ -361,4 +337,66 @@ HTML <- function(text, ...) {
htmlText
}
#' Evaluate an expression using the \code{tags}
#'
#' This function makes it simpler to write HTML-generating code. Instead of
#' needing to specify \code{tags} each time a tag function is used, as in
#' \code{tags$div()} and \code{tags$p()}, code inside \code{withTags} is
#' evaluated with \code{tags} searched first, so you can simply use
#' \code{div()} and \code{p()}.
#'
#' If your code uses an object which happens to have the same name as an
#' HTML tag function, such as \code{source()} or \code{summary()}, it will call
#' the tag function. To call the intended (non-tags function), specify the
#' namespace, as in \code{base::source()} or \code{base::summary()}.
#'
#' @param code A set of tags.
#'
#' @examples
#' # Using tags$ each time
#' tags$div(class = "myclass",
#' tags$h3("header"),
#' tags$p("text")
#' )
#'
#' # Equivalent to above, but using withTags
#' withTags(
#' div(class = "myclass",
#' h3("header"),
#' p("text")
#' )
#' )
#'
#'
#' @export
withTags <- function(code) {
eval(substitute(code), envir = as.list(tags), enclos = parent.frame())
}
# Given a list of tags, lists, and other items, return a flat list, where the
# items from the inner, nested lists are pulled to the top level, recursively.
flattenTags <- function(x) {
if (isTag(x)) {
# For tags, wrap them into a list (which will be unwrapped by caller)
list(x)
} else if (is.list(x)) {
if (length(x) == 0) {
# Empty lists are simply returned
x
} else {
# For items that are lists (but not tags), recurse
unlist(lapply(x, flattenTags), recursive = FALSE)
}
} else if (is.character(x)){
# This will preserve attributes if x is a character with attribute,
# like what HTML() produces
list(x)
} else {
# For other items, coerce to character and wrap them into a list (which
# will be unwrapped by caller). Note that this will strip attributes.
list(as.character(x))
}
}

191
R/tar.R Normal file
View 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)
}

View File

@@ -15,6 +15,11 @@ TimerCallbacks <- setRefClass(
initialize = function() {
.nextId <<- 0L
},
clear = function() {
.nextId <<- 0L
.funcs$clear()
.times <<- data.frame()
},
schedule = function(millis, func) {
id <- .nextId
.nextId <<- .nextId + 1L
@@ -51,7 +56,7 @@ TimerCallbacks <- setRefClass(
executeElapsed = function() {
elapsed <- takeElapsed()
if (length(elapsed) == 0)
return(F)
return(FALSE)
for (id in elapsed$id) {
thisFunc <- .funcs$remove(as.character(id))
@@ -59,7 +64,7 @@ TimerCallbacks <- setRefClass(
# TODO: Detect NULL, and...?
thisFunc()
}
return(T)
return(TRUE)
}
)
)

405
R/update-input.R Normal file
View File

@@ -0,0 +1,405 @@
#' Change the value of a text input on the client
#'
#' @template update-input
#' @param value The value to set for the input object.
#'
#' @seealso \code{\link{textInput}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#'
#' observe({
#' # We'll use the input$controller variable multiple times, so save it as x
#' # for convenience.
#' x <- input$controller
#'
#' # This will change the value of input$inText, based on x
#' updateTextInput(session, "inText", value = paste("New text", x))
#'
#' # Can also set the label, this time for input$inText2
#' updateTextInput(session, "inText2",
#' label = paste("New label", x),
#' value = paste("New text", x))
#' })
#' })
#' }
#' @export
updateTextInput <- function(session, inputId, label = NULL, value = NULL) {
message <- dropNulls(list(label=label, value=value))
session$sendInputMessage(inputId, message)
}
#' Change the value of a checkbox input on the client
#'
#' @template update-input
#' @param value The value to set for the input object.
#'
#' @seealso \code{\link{checkboxInput}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#'
#' observe({
#' # TRUE if input$controller is even, FALSE otherwise.
#' x_even <- input$controller %% 2 == 0
#'
#' updateCheckboxInput(session, "inCheckbox", value = x_even)
#' })
#' })
#' }
#' @export
updateCheckboxInput <- updateTextInput
#' Change the value of a slider input on the client
#'
#' @template update-input
#' @param value The value to set for the input object.
#'
#' @seealso \code{\link{sliderInput}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#'
#' observe({
#' # We'll use the input$controller variable multiple times, so save it as x
#' # for convenience.
#' x <- input$controller
#'
#' # Similar to number and text. only label and value can be set for slider
#' updateSliderInput(session, "inSlider",
#' label = paste("Slider label", x),
#' value = x)
#'
#' # For sliders that pick out a range, pass in a vector of 2 values.
#' updateSliderInput(session, "inSlider2", value = c(x-1, x+1))
#'
#' # An NA means to not change that value (the low or high one)
#' updateSliderInput(session, "inSlider3", value = c(NA, x+2))
#' })
#' })
#' }
#' @export
updateSliderInput <- updateTextInput
#' Change the value of a date input on the client
#'
#' @template update-input
#' @param value The desired date value. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#' @param min The minimum allowed date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#' @param max The maximum allowed date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#'
#' @seealso \code{\link{dateInput}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#'
#' observe({
#' # We'll use the input$controller variable multiple times, so save it as x
#' # for convenience.
#' x <- input$controller
#'
#' updateDateInput(session, "inDate",
#' label = paste("Date label", x),
#' value = paste("2013-04-", x, sep=""),
#' min = paste("2013-04-", x-1, sep=""),
#' max = paste("2013-04-", x+1, sep="")
#' )
#' })
#' })
#' }
#' @export
updateDateInput <- function(session, inputId, label = NULL, value = NULL,
min = NULL, max = NULL) {
# If value is a date object, convert it to a string with yyyy-mm-dd format
# Same for min and max
if (inherits(value, "Date")) value <- format(value, "%Y-%m-%d")
if (inherits(min, "Date")) min <- format(min, "%Y-%m-%d")
if (inherits(max, "Date")) max <- format(max, "%Y-%m-%d")
message <- dropNulls(list(label=label, value=value, min=min, max=max))
session$sendInputMessage(inputId, message)
}
#' Change the start and end values of a date range input on the client
#'
#' @template update-input
#' @param start The start date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#' @param end The end date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#' @param min The minimum allowed date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#' @param max The maximum allowed date. Either a Date object, or a string in
#' \code{yyyy-mm-dd} format.
#'
#' @seealso \code{\link{dateRangeInput}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#'
#' observe({
#' # We'll use the input$controller variable multiple times, so save it as x
#' # for convenience.
#' x <- input$controller
#'
#' updateDateRangeInput(session, "inDateRange",
#' label = paste("Date range label", x),
#' start = paste("2013-01-", x, sep=""))
#' end = paste("2013-12-", x, sep=""))
#' })
#' })
#' }
#' @export
updateDateRangeInput <- function(session, inputId, label = NULL,
start = NULL, end = NULL, min = NULL, max = NULL) {
# Make sure start and end are strings, not date objects. This is for
# consistency across different locales.
if (inherits(start, "Date")) start <- format(start, '%Y-%m-%d')
if (inherits(end, "Date")) end <- format(end, '%Y-%m-%d')
if (inherits(min, "Date")) min <- format(min, '%Y-%m-%d')
if (inherits(max, "Date")) max <- format(max, '%Y-%m-%d')
message <- dropNulls(list(
label = label,
value = c(start, end),
min = min,
max = max
))
session$sendInputMessage(inputId, message)
}
#' Change the selected tab on the client
#'
#' @param session The \code{session} object passed to function given to
#' \code{shinyServer}.
#' @param inputId The id of the tabset panel object.
#' @param selected The name of the tab to make active.
#'
#' @seealso \code{\link{tabsetPanel}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#'
#' observe({
#' # TRUE if input$controller is even, FALSE otherwise.
#' x_even <- input$controller %% 2 == 0
#'
#' # Change the selected tab.
#' # Note that the tabsetPanel must have been created with an 'id' argument
#' if (x_even) {
#' updateTabsetPanel(session, "inTabset", selected = "panel2")
#' } else {
#' updateTabsetPanel(session, "inTabset", selected = "panel1")
#' }
#' })
#' })
#' }
#' @export
updateTabsetPanel <- function(session, inputId, selected = NULL) {
message <- dropNulls(list(value = selected))
session$sendInputMessage(inputId, message)
}
#' Change the value of a number input on the client
#'
#' @template update-input
#' @param value The value to set for the input object.
#' @param min Minimum value.
#' @param max Maximum value.
#' @param step Step size.
#'
#' @seealso \code{\link{numericInput}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#'
#' observe({
#' # We'll use the input$controller variable multiple times, so save it as x
#' # for convenience.
#' x <- input$controller
#'
#' updateNumericInput(session, "inNumber", value = x)
#'
#' updateNumericInput(session, "inNumber2",
#' label = paste("Number label ", x),
#' value = x, min = x-10, max = x+10, step = 5)
#' })
#' })
#' }
#' @export
updateNumericInput <- function(session, inputId, label = NULL, value = NULL,
min = NULL, max = NULL, step = NULL) {
message <- dropNulls(list(label=label, value=value, min=min, max=max, step=step))
session$sendInputMessage(inputId, message)
}
#' Change the value of a checkbox group input on the client
#'
#' @template update-input
#' @param choices A named vector or named list of options. For each item, the
#' name will be used as the label, and the value will be used as the value.
#' @param selected A vector or list of options which will be selected.
#'
#' @seealso \code{\link{checkboxGroupInput}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#'
#' observe({
#' # We'll use the input$controller variable multiple times, so save it as x
#' # for convenience.
#' x <- input$controller
#'
#' # Create a list of new options, where the name of the items is something
#' # like 'option label x 1', and the values are 'option-x-1'.
#' cb_options <- list()
#' cb_options[[sprintf("option label %d 1", x)]] <- sprintf("option-%d-1", x)
#' cb_options[[sprintf("option label %d 2", x)]] <- sprintf("option-%d-2", x)
#'
#' # Change values for input$inCheckboxGroup
#' updateCheckboxGroupInput(session, "inCheckboxGroup", choices = cb_options)
#'
#' # Can also set the label and select items
#' updateCheckboxGroupInput(session, "inCheckboxGroup2",
#' label = paste("checkboxgroup label", x),
#' choices = cb_options,
#' selected = sprintf("option label %d 2", x)
#' )
#' })
#' })
#' }
#' @export
updateCheckboxGroupInput <- function(session, inputId, label = NULL,
choices = NULL, selected = NULL) {
choices <- choicesWithNames(choices)
options <- mapply(choices, names(choices),
SIMPLIFY = FALSE, USE.NAMES = FALSE,
FUN = function(value, name) {
list(value = value,
label = name,
checked = name %in% selected)
}
)
message <- dropNulls(list(label = label, options = options))
session$sendInputMessage(inputId, message)
}
#' Change the value of a radio input on the client
#'
#' @template update-input
#' @param choices A named vector or named list of options. For each item, the
#' name will be used as the label, and the value will be used as the value.
#' @param selected A vector or list of options which will be selected.
#'
#' @seealso \code{\link{radioButtons}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#'
#' observe({
#' # We'll use the input$controller variable multiple times, so save it as x
#' # for convenience.
#' x <- input$controller
#'
#' r_options <- list()
#' r_options[[sprintf("option label %d 1", x)]] <- sprintf("option-%d-1", x)
#' r_options[[sprintf("option label %d 2", x)]] <- sprintf("option-%d-2", x)
#'
#' # Change values for input$inRadio
#' updateRadioButtons(session, "inRadio", choices = r_options)
#'
#' # Can also set the label and select an item
#' updateRadioButtons(session, "inRadio2",
#' label = paste("Radio label", x),
#' choices = r_options,
#' selected = sprintf("option label %d 2", x)
#' )
#' })
#' })
#' }
#' @export
updateRadioButtons <- updateCheckboxGroupInput
#' Change the value of a select input on the client
#'
#' @template update-input
#' @param choices A named vector or named list of options. For each item, the
#' name will be used as the label, and the value will be used as the value.
#' @param selected A vector or list of options which will be selected.
#'
#' @seealso \code{\link{selectInput}}
#'
#' @examples
#' \dontrun{
#' shinyServer(function(input, output, session) {
#'
#' observe({
#' # We'll use the input$controller variable multiple times, so save it as x
#' # for convenience.
#' x <- input$controller
#'
#' # Create a list of new options, where the name of the items is something
#' # like 'option label x 1', and the values are 'option-x-1'.
#' s_options <- list()
#' s_options[[sprintf("option label %d 1", x)]] <- sprintf("option-%d-1", x)
#' s_options[[sprintf("option label %d 2", x)]] <- sprintf("option-%d-2", x)
#'
#' # Change values for input$inSelect
#' updateSelectInput(session, "inSelect", choices = s_options)
#'
#' # Can also set the label and select an item (or more than one if it's a
#' # multi-select)
#' updateSelectInput(session, "inSelect2",
#' label = paste("Select label", x),
#' choices = s_options,
#' selected = sprintf("option label %d 2", x)
#' )
#' })
#' })
#' }
#' @export
updateSelectInput <- function(session, inputId, label = NULL, choices = NULL,
selected = NULL) {
choices <- choicesWithNames(choices)
options <- mapply(choices, names(choices),
SIMPLIFY = FALSE, USE.NAMES = FALSE,
FUN = function(value, name) {
list(value = value,
label = name,
selected = name %in% selected)
}
)
message <- dropNulls(list(label = label, options = options))
session$sendInputMessage(inputId, message)
}

423
R/utils.R Normal file
View File

@@ -0,0 +1,423 @@
#' 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)
rngfunc(...)
}
}
`%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='')
}
# Given a vector or list, drop all the NULL items in it
dropNulls <- function(x) {
x[!vapply(x, is.null, FUN.VALUE=logical(1))]
}
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
}
# Create a zero-arg function from a quoted expression and environment
# @examples
# makeFunction(body=quote(print(3)))
makeFunction <- function(args = pairlist(), body, env = parent.frame()) {
eval(call("function", args, body), env)
}
#' Convert an expression or quoted expression to a function
#'
#' This is to be called from another function, because it will attempt to get
#' an unquoted expression from two calls back.
#'
#' If expr is a quoted expression, then this just converts it to a function.
#' If expr is a function, then this simply returns expr (and prints a
#' deprecation message).
#' If expr was a non-quoted expression from two calls back, then this will
#' quote the original expression and convert it to a function.
#
#' @param expr A quoted or unquoted expression, or a function.
#' @param env The desired environment for the function. Defaults to the
#' calling environment two steps back.
#' @param quoted Is the expression quoted?
#' @param caller_offset If specified, the offset in the callstack of the
#' functiont to be treated as the caller.
#'
#' @examples
#' # Example of a new renderer, similar to renderText
#' # This is something that toolkit authors will do
#' renderTriple <- function(expr, env=parent.frame(), quoted=FALSE) {
#' # Convert expr to a function
#' func <- shiny::exprToFunction(expr, env, quoted)
#'
#' function() {
#' value <- func()
#' paste(rep(value, 3), collapse=", ")
#' }
#' }
#'
#'
#' # Example of using the renderer.
#' # This is something that app authors will do.
#' values <- reactiveValues(A="text")
#'
#' \dontrun{
#' # Create an output object
#' output$tripleA <- renderTriple({
#' values$A
#' })
#' }
#'
#' # At the R console, you can experiment with the renderer using isolate()
#' tripleA <- renderTriple({
#' values$A
#' })
#'
#' isolate(tripleA())
#' # "text, text, text"
#'
#' @export
exprToFunction <- function(expr, env=parent.frame(2), quoted=FALSE,
caller_offset=1) {
# Get the quoted expr from two calls back
expr_sub <- eval(substitute(substitute(expr)), parent.frame(caller_offset))
# Check if expr is a function, making sure not to evaluate expr, in case it
# is actually an unquoted expression.
# If expr is a single token, then indexing with [[ will error; if it has multiple
# tokens, then [[ works. In the former case it will be a name object; in the
# latter, it will be a language object.
if (!is.name(expr_sub) && expr_sub[[1]] == as.name('function')) {
# Get name of function that called this function
called_fun <- sys.call(-1 * caller_offset)[[1]]
shinyDeprecated(msg = paste("Passing functions to '", called_fun,
"' is deprecated. Please use expressions instead. See ?", called_fun,
" for more information.", sep=""))
return(expr)
}
if (quoted) {
# expr is a quoted expression
makeFunction(body=expr, env=env)
} else {
# expr is an unquoted expression
makeFunction(body=expr_sub, env=env)
}
}
#' Installs an expression in the given environment as a function, and registers
#' debug hooks so that breakpoints may be set in the function.
#'
#' This function can replace \code{exprToFunction} as follows: we may use
#' \code{func <- exprToFunction(expr)} if we do not want the debug hooks, or
#' \code{installExprFunction(expr, "func")} if we do. Both approaches create a
#' function named \code{func} in the current environment.
#'
#' @seealso Wraps \code{exprToFunction}; see that method's documentation for
#' more documentation and examples.
#'
#' @param expr A quoted or unquoted expression
#' @param name The name the function should be given
#' @param eval.env The desired environment for the function. Defaults to the
#' calling environment two steps back.
#' @param quoted Is the expression quoted?
#' @param assign.env The environment in which the function should be assigned.
#' @param label A label for the object to be shown in the debugger. Defaults
#' to the name of the calling function.
#'
#' @export
installExprFunction <- function(expr, name, eval.env = parent.frame(2),
quoted = FALSE,
assign.env = parent.frame(1),
label = as.character(sys.call(-1)[[1]])) {
func <- exprToFunction(expr, eval.env, quoted, 2)
assign(name, func, envir = assign.env)
registerDebugHook(name, assign.env, label)
}
#' Parse a GET query string from a URL
#'
#' Returns a named character vector of key-value pairs.
#'
#' @param str The query string. It can have a leading \code{"?"} or not.
#' @export
#' @examples
#' parseQueryString("?foo=1&bar=b%20a%20r")
#'
#' \dontrun{
#' # Example of usage within a Shiny app
#' shinyServer(function(input, output, clientData) {
#'
#' output$queryText <- renderText({
#' query <- parseQueryString(clientData$url_search)
#'
#' # Ways of accessing the values
#' if (as.numeric(query$foo) == 1) {
#' # Do something
#' }
#' if (query[["bar"]] == "targetstring") {
#' # Do something else
#' }
#'
#' # Return a string with key-value pairs
#' paste(names(query), query, sep = "=", collapse=", ")
#' })
#' })
#' }
#'
parseQueryString <- function(str) {
if (is.null(str) || nchar(str) == 0)
return(list())
# Remove leading ?
if (substr(str, 1, 1) == '?')
str <- substr(str, 2, nchar(str))
pairs <- strsplit(str, '&', fixed = TRUE)[[1]]
pairs <- strsplit(pairs, '=', fixed = TRUE)
keys <- vapply(pairs, function(x) x[1], FUN.VALUE = character(1))
values <- vapply(pairs, function(x) x[2], FUN.VALUE = character(1))
# Replace NA with '', so they don't get converted to 'NA' by URLdecode
values[is.na(values)] <- ''
# Convert "+" to " ", since URLdecode doesn't do it
keys <- gsub('+', ' ', keys, fixed = TRUE)
values <- gsub('+', ' ', values, fixed = TRUE)
keys <- vapply(keys, function(x) URLdecode(x), FUN.VALUE = character(1))
values <- vapply(values, function(x) URLdecode(x), FUN.VALUE = character(1))
setNames(as.list(values), keys)
}
# decide what to do in case of errors; it is customizable using the shiny.error
# option (e.g. we can set options(shiny.error = recover))
shinyCallingHandlers <- function(expr) {
withCallingHandlers(expr, error = function(e) {
handle <- getOption('shiny.error')
if (is.function(handle)) handle()
})
}
#' Print message for deprecated functions in Shiny
#'
#' To disable these messages, use \code{options(shiny.deprecation.messages=FALSE)}.
#'
#' @param new Name of replacement function.
#' @param msg Message to print. If used, this will override the default message.
#' @param old Name of deprecated function.
shinyDeprecated <- function(new=NULL, msg=NULL,
old=as.character(sys.call(sys.parent()))[1L]) {
if (getOption("shiny.deprecation.messages", default=TRUE) == FALSE)
return(invisible())
if (is.null(msg)) {
msg <- paste(old, "is deprecated.")
if (!is.null(new))
msg <- paste(msg, "Please use", new, "instead.",
"To disable this message, run options(shiny.deprecation.messages=FALSE)")
}
# Similar to .Deprecated(), but print a message instead of warning
message(msg)
}
#' Register a function with the debugger (if one is active).
#'
#' Call this function after exprToFunction to give any active debugger a hook
#' to set and clear breakpoints in the function. A debugger may implement
#' registerShinyDebugHook to receive callbacks when Shiny functions are
#' instantiated at runtime.
#'
#' @param name Name of the field or object containing the function.
#' @param where The reference object or environment containing the function.
#' @param label A label to display on the function in the debugger.
#' @noRd
registerDebugHook <- function(name, where, label) {
if (exists("registerShinyDebugHook", mode = "function")) {
registerShinyDebugHook <- get("registerShinyDebugHook", mode = "function")
params <- new.env(parent = emptyenv())
params$name <- name
params$where <- where
params$label <- label
registerShinyDebugHook(params)
}
}
Callbacks <- setRefClass(
'Callbacks',
fields = list(
.nextId = 'integer',
.callbacks = 'Map'
),
methods = list(
initialize = function() {
.nextId <<- as.integer(.Machine$integer.max)
},
register = function(callback) {
id <- as.character(.nextId)
.nextId <<- .nextId - 1L
.callbacks$set(id, callback)
return(function() {
.callbacks$remove(id)
})
},
invoke = function(..., onError=NULL) {
for (callback in .callbacks$values()) {
if (is.null(onError)) {
callback(...)
} else {
tryCatch(callback(...), error = onError)
}
}
},
count = function() {
.callbacks$size()
}
)
)
# convert a data frame to JSON as required by DataTables request
dataTablesJSON <- function(data, query) {
n <- nrow(data)
with(parseQueryString(query), {
# global searching
i <- seq_len(n)
if (nzchar(sSearch)) {
i0 <- apply(data, 2, function(x) grep(sSearch, as.character(x)))
i <- intersect(i, unique(unlist(i0)))
}
# search by columns
if (length(i)) for (j in seq_len(as.integer(iColumns)) - 1) {
if (is.null(k <- get_exists(sprintf('sSearch_%d', j), 'character'))) next
if (nzchar(k)) i <- intersect(grep(k, as.character(data[, j + 1])), i)
if (length(i) == 0) break
}
if (length(i) != n) data <- data[i, , drop = FALSE]
# sorting
oList <- list()
for (j in seq_len(as.integer(iSortingCols)) - 1) {
if (is.null(k <- get_exists(sprintf('iSortCol_%d', j), 'character'))) break
desc = get_exists(sprintf('sSortDir_%d', j), 'character')
if (is.character(desc)) {
col <- data[, as.integer(k) + 1]
oList[[length(oList) + 1]] <- (if (desc == 'asc') identity else `-`)(
if (is.numeric(col)) col else xtfrm(col)
)
}
}
if (length(oList)) {
i <- do.call(order, oList)
data <- data[i, , drop = FALSE]
}
# paging
i <- seq(as.integer(iDisplayStart) + 1L, length.out = as.integer(iDisplayLength))
i <- i[i <= n]
fdata <- data[i, , drop = FALSE] # filtered data
fdata <- unname(as.matrix(fdata))
toJSON(list(
sEcho = as.integer(sEcho),
iTotalRecords = n,
iTotalDisplayRecords = nrow(data),
aaData = fdata
))
})
}
get_exists = function(x, mode) {
if (exists(x, envir = parent.frame(), mode = mode, inherits = FALSE))
get(x, envir = parent.frame(), mode = mode, inherits = FALSE)
}

View File

@@ -2,6 +2,8 @@
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&mdash;no JavaScript required.
@@ -11,24 +13,31 @@ Shiny is a new package from RStudio that makes it incredibly easy to build inter
* 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.
* Fast bidirectional communication between the web browser and R using the [httpuv](https://github.com/rstudio/httpuv) 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:
To install the stable version from CRAN, simply run the following from an R console:
```r
options(repos=c(RStudio="http://rstudio.org/_packages", getOption("repos")))
install.packages("shiny")
```
To install the latest development builds directly from GitHub, run this instead:
```r
if (!require("devtools"))
install.packages("devtools")
devtools::install_github("shiny", "rstudio")
```
## 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.
We hope you enjoy using Shiny. If you have general questions about using Shiny, please use the Shiny [mailing list](https://groups.google.com/forum/#!forum/shiny-discuss). For bug reports, please use the [issue tracker](https://github.com/rstudio/shiny/issues).
## License

View File

@@ -4,13 +4,14 @@ these components are included below):
- jQuery
- Bootstrap
- bootstrap-datepicker, from https://github.com/eternicode/bootstrap-datepicker
- jslider
- DataTables
jQuery License
----------------------------------------------------------------------
----------------------------------------------------------------------
Copyright (c) 2012 jQuery Foundation and other contributors,
Copyright (c) 2012 jQuery Foundation and other contributors,
http://jquery.com/
Permission is hereby granted, free of charge, to any person obtaining
@@ -33,8 +34,8 @@ 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
----------------------------------------------------------------------
Bootstrap and bootstrap-datepicker License
----------------------------------------------------------------------
Apache License
Version 2.0, January 2004
@@ -240,7 +241,7 @@ Bootstrap License
jslider License
----------------------------------------------------------------------
----------------------------------------------------------------------
The MIT License (MIT)
Copyright (c) 2012 Egor Khmelev
@@ -262,3 +263,35 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
DataTables License
----------------------------------------------------------------------
Copyright (c) 2008-2010, Allan Jardine
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Allan Jardine nor SpryMedia UK may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -3,14 +3,14 @@ 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:
# Expression that generates a plot of the distribution. The expression
# is wrapped in a call to renderPlot 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() {
output$distPlot <- renderPlot({
# generate an rnorm distribution and plot it
dist <- rnorm(input$obs)

View File

@@ -10,7 +10,7 @@ shinyUI(pageWithSidebar(
sidebarPanel(
sliderInput("obs",
"Number of observations:",
min = 0,
min = 1,
max = 1000,
value = 500)
),

View File

@@ -5,7 +5,7 @@ library(datasets)
shinyServer(function(input, output) {
# Return the requested dataset
datasetInput <- reactive(function() {
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
@@ -13,13 +13,13 @@ shinyServer(function(input, output) {
})
# Generate a summary of the dataset
output$summary <- reactivePrint(function() {
output$summary <- renderPrint({
dataset <- datasetInput()
summary(dataset)
})
# Show the first "n" observations
output$view <- reactiveTable(function() {
output$view <- renderTable({
head(datasetInput(), n = input$obs)
})
})

View File

@@ -4,47 +4,47 @@ 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:
# By declaring databaseInput as a reactive expression 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
# 3) When the inputs change and the expression 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() {
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
# The output$caption is computed based on a reactive function that
# The output$caption is computed based on a reactive expression 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
# Note that because the data-oriented reactive expressions below don't
# depend on input$caption, those expressions are NOT called when
# input$caption changes.
output$caption <- reactiveText(function() {
output$caption <- renderText({
input$caption
})
# The output$summary depends on the datasetInput reactive function,
# The output$summary depends on the datasetInput reactive expression,
# so will be re-executed whenever datasetInput is re-executed
# (i.e. whenever the input$dataset changes)
output$summary <- reactivePrint(function() {
output$summary <- renderPrint({
dataset <- datasetInput()
summary(dataset)
})
# The output$view depends on both the databaseInput reactive function
# The output$view depends on both the databaseInput reactive expression
# and input$obs, so will be re-executed whenever input$dataset or
# input$obs is changed.
output$view <- reactiveTable(function() {
output$view <- renderTable({
head(datasetInput(), n = input$obs)
})
})

View File

@@ -11,20 +11,20 @@ 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
# Compute the forumla text in a reactive expression since it is
# shared by the output$caption and output$mpgPlot functions
formulaText <- reactive(function() {
formulaText <- reactive({
paste("mpg ~", input$variable)
})
# Return the formula text for printing as a caption
output$caption <- reactiveText(function() {
output$caption <- renderText({
formulaText()
})
# Generate a plot of the requested variable against mpg and only
# include outliers if requested
output$mpgPlot <- reactivePlot(function() {
output$mpgPlot <- renderPlot({
boxplot(as.formula(formulaText()),
data = mpgData,
outline = input$outliers)

View File

@@ -10,10 +10,10 @@ shinyUI(pageWithSidebar(
# and to specify whether outliers should be included
sidebarPanel(
selectInput("variable", "Variable:",
list("Cylinders" = "cyl",
"Transmission" = "am",
"Gears" = "gear")),
c("Cylinders" = "cyl",
"Transmission" = "am",
"Gears" = "gear")),
checkboxInput("outliers", "Show outliers", FALSE)
),

View File

@@ -3,8 +3,8 @@ 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() {
# Reactive expression to compose a data frame containing all of the values
sliderValues <- reactive({
# Compose data frame
data.frame(
@@ -22,7 +22,7 @@ shinyServer(function(input, output) {
})
# Show the values using an HTML table
output$values <- reactiveTable(function() {
output$values <- renderTable({
sliderValues()
})
})

View File

@@ -27,7 +27,7 @@ shinyUI(pageWithSidebar(
# 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=T))
animate=animationOptions(interval=300, loop=TRUE))
),
# Show a table summarizing the values entered

View File

@@ -3,10 +3,10 @@ library(shiny)
# Define server logic for random distribution application
shinyServer(function(input, output) {
# Reactive function to generate the requested distribution. This is
# Reactive expression 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() {
# below then all use the value computed from this expression
data <- reactive({
dist <- switch(input$dist,
norm = rnorm,
unif = runif,
@@ -19,9 +19,9 @@ shinyServer(function(input, output) {
# 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
# the data reactive expression are both tracked, and all expressions
# are called in the sequence implied by the dependency graph
output$plot <- reactivePlot(function() {
output$plot <- renderPlot({
dist <- input$dist
n <- input$n
@@ -30,12 +30,12 @@ shinyServer(function(input, output) {
})
# Generate a summary of the data
output$summary <- reactivePrint(function() {
output$summary <- renderPrint({
summary(data())
})
# Generate an HTML table view of the data
output$table <- reactiveTable(function() {
output$table <- renderTable({
data.frame(x=data())
})

View File

@@ -11,10 +11,10 @@ shinyUI(pageWithSidebar(
# element to introduce extra vertical spacing
sidebarPanel(
radioButtons("dist", "Distribution type:",
list("Normal" = "norm",
"Uniform" = "unif",
"Log-normal" = "lnorm",
"Exponential" = "exp")),
c("Normal" = "norm",
"Uniform" = "unif",
"Log-normal" = "lnorm",
"Exponential" = "exp")),
br(),
sliderInput("n",

View File

@@ -5,7 +5,7 @@ library(datasets)
shinyServer(function(input, output) {
# Return the requested dataset
datasetInput <- reactive(function() {
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
@@ -13,13 +13,13 @@ shinyServer(function(input, output) {
})
# Generate a summary of the dataset
output$summary <- reactivePrint(function() {
output$summary <- renderPrint({
dataset <- datasetInput()
summary(dataset)
})
# Show the first "n" observations
output$view <- reactiveTable(function() {
output$view <- renderTable({
head(datasetInput(), n = input$obs)
})
})

View File

@@ -3,10 +3,10 @@ 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() {
# Reactive expression to generate the requested distribution. This is
# called whenever the inputs change. The output expressions defined
# below then all used the value computed from this expression
data <- reactive({
dist <- switch(input$dist,
norm = rnorm,
unif = runif,
@@ -19,9 +19,9 @@ shinyServer(function(input, output) {
# 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
# the data reactive expression are both tracked, and all expressions
# are called in the sequence implied by the dependency graph
output$plot <- reactivePlot(function() {
output$plot <- renderPlot({
dist <- input$dist
n <- input$n
@@ -30,12 +30,12 @@ shinyServer(function(input, output) {
})
# Generate a summary of the data
output$summary <- reactivePrint(function() {
output$summary <- renderPrint({
summary(data())
})
# Generate an HTML table view of the data
output$table <- reactiveTable(function() {
output$table <- renderTable({
data.frame(x=data())
})

View File

@@ -0,0 +1,18 @@
library(shiny)
shinyServer(function(input, output) {
output$contents <- renderTable({
# input$file1 will be NULL initially. After the user selects and uploads a
# file, it will be a data frame with 'name', 'size', 'type', and 'datapath'
# columns. The 'datapath' column will contain the local filenames where the
# data can be found.
inFile <- input$file1
if (is.null(inFile))
return(NULL)
read.csv(inFile$datapath, header=input$header, sep=input$sep, quote=input$quote)
})
})

View File

@@ -0,0 +1,24 @@
library(shiny)
shinyUI(pageWithSidebar(
headerPanel("Uploading Files"),
sidebarPanel(
fileInput('file1', 'Choose CSV File',
accept=c('text/csv', 'text/comma-separated-values,text/plain', '.csv')),
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')
)
))

View File

@@ -0,0 +1,19 @@
shinyServer(function(input, output) {
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
output$table <- renderTable({
datasetInput()
})
output$downloadData <- downloadHandler(
filename = function() { paste(input$dataset, '.csv', sep='') },
content = function(file) {
write.csv(datasetInput(), file)
}
)
})

View 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')
)
))

View File

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

View File

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

View File

@@ -0,0 +1,95 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Jasmine Spec Runner</title>
<link rel="shortcut icon" type="image/png" href="lib/jasmine-1.3.1/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="lib/jasmine-1.3.1/jasmine.css">
<script type="text/javascript" src="lib/jasmine-1.3.1/jasmine.js"></script>
<script type="text/javascript" src="lib/jasmine-1.3.1/jasmine-html.js"></script>
<!-- include source files here... -->
<!-- All of these includes are copied out of the HTML file generated by
shinyUI() -->
<script src="../www/shared/jquery.js" type="text/javascript"></script>
<script src="../www/shared/shiny.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="../www/shared/shiny.css"/>
<link rel="stylesheet" type="text/css" href="../www/shared/slider/css/jquery.slider.min.css"/>
<script src="../www/shared/slider/js/jquery.slider.min.js"></script>
<link rel="stylesheet" type="text/css" href="../www/shared/bootstrap/css/bootstrap.min.css"/>
<script src="../www/shared/bootstrap/js/bootstrap.min.js"></script>
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"/> -->
<link rel="stylesheet" type="text/css" href="../www/shared/bootstrap/css/bootstrap-responsive.min.css"/>
<script src="../www/shared/datepicker/js/bootstrap-datepicker.min.js"></script>
<link rel="stylesheet" type="text/css" href="../www/shared/datepicker/css/datepicker.css"/>
<script src="../www/shared/bootstrap-daterangepicker/date.js"></script>
<script src="../www/shared/bootstrap-daterangepicker/daterangepicker.js"></script>
<link rel="stylesheet" type="text/css" href="../www/shared/bootstrap-daterangepicker/daterangepicker.css"/>
<!-- include spec files here... -->
<!-- <script type="text/javascript" src="spec/SpecHelper.js"></script>
<script type="text/javascript" src="spec/PlayerSpec.js"></script>
-->
<script type="text/javascript" src="spec/inputBindingSpec.js"></script>
<script type="text/javascript">
(function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.specFilter = function(spec) {
return htmlReporter.specFilter(spec);
};
// var currentWindowOnload = window.onload;
// window.onload = function() {
// if (currentWindowOnload) {
// currentWindowOnload();
// }
// execJasmine();
// };
// Add a slight delay before running tests, so that Shiny has time to
// do setup stuff.
$(document).ready(function() {
setTimeout(function() {
execJasmine();
},
50
)
});
function execJasmine() {
jasmineEnv.execute();
}
})();
// Clear the Shiny disconnected gray screen shortly after loading
$(document).ready(function() {
setTimeout(function() {
$('body').removeClass('disconnected');
},
100
)
});
</script>
</head>
<body>
</body>
</html>

View File

@@ -0,0 +1,2 @@
<label>Text input:</label>
<input id="in_text" type="text" value="starting value"/>

View File

@@ -0,0 +1,20 @@
Copyright (c) 2008-2011 Pivotal Labs
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.

View File

@@ -0,0 +1,681 @@
jasmine.HtmlReporterHelpers = {};
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
var el = document.createElement(type);
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i];
if (typeof child === 'string') {
el.appendChild(document.createTextNode(child));
} else {
if (child) {
el.appendChild(child);
}
}
}
for (var attr in attrs) {
if (attr == "className") {
el[attr] = attrs[attr];
} else {
el.setAttribute(attr, attrs[attr]);
}
}
return el;
};
jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
var results = child.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.skipped) {
status = 'skipped';
}
return status;
};
jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
var parentDiv = this.dom.summary;
var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
var parent = child[parentSuite];
if (parent) {
if (typeof this.views.suites[parent.id] == 'undefined') {
this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
}
parentDiv = this.views.suites[parent.id].element;
}
parentDiv.appendChild(childElement);
};
jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
for(var fn in jasmine.HtmlReporterHelpers) {
ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
}
};
jasmine.HtmlReporter = function(_doc) {
var self = this;
var doc = _doc || window.document;
var reporterView;
var dom = {};
// Jasmine Reporter Public Interface
self.logRunningSpecs = false;
self.reportRunnerStarting = function(runner) {
var specs = runner.specs() || [];
if (specs.length == 0) {
return;
}
createReporterDom(runner.env.versionString());
doc.body.appendChild(dom.reporter);
setExceptionHandling();
reporterView = new jasmine.HtmlReporter.ReporterView(dom);
reporterView.addSpecs(specs, self.specFilter);
};
self.reportRunnerResults = function(runner) {
reporterView && reporterView.complete();
};
self.reportSuiteResults = function(suite) {
reporterView.suiteComplete(suite);
};
self.reportSpecStarting = function(spec) {
if (self.logRunningSpecs) {
self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
}
};
self.reportSpecResults = function(spec) {
reporterView.specComplete(spec);
};
self.log = function() {
var console = jasmine.getGlobal().console;
if (console && console.log) {
if (console.log.apply) {
console.log.apply(console, arguments);
} else {
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
}
}
};
self.specFilter = function(spec) {
if (!focusedSpecName()) {
return true;
}
return spec.getFullName().indexOf(focusedSpecName()) === 0;
};
return self;
function focusedSpecName() {
var specName;
(function memoizeFocusedSpec() {
if (specName) {
return;
}
var paramMap = [];
var params = jasmine.HtmlReporter.parameters(doc);
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
}
specName = paramMap.spec;
})();
return specName;
}
function createReporterDom(version) {
dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
dom.banner = self.createDom('div', { className: 'banner' },
self.createDom('span', { className: 'title' }, "Jasmine "),
self.createDom('span', { className: 'version' }, version)),
dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
dom.alert = self.createDom('div', {className: 'alert'},
self.createDom('span', { className: 'exceptions' },
self.createDom('label', { className: 'label', 'for': 'no_try_catch' }, 'No try/catch'),
self.createDom('input', { id: 'no_try_catch', type: 'checkbox' }))),
dom.results = self.createDom('div', {className: 'results'},
dom.summary = self.createDom('div', { className: 'summary' }),
dom.details = self.createDom('div', { id: 'details' }))
);
}
function noTryCatch() {
return window.location.search.match(/catch=false/);
}
function searchWithCatch() {
var params = jasmine.HtmlReporter.parameters(window.document);
var removed = false;
var i = 0;
while (!removed && i < params.length) {
if (params[i].match(/catch=/)) {
params.splice(i, 1);
removed = true;
}
i++;
}
if (jasmine.CATCH_EXCEPTIONS) {
params.push("catch=false");
}
return params.join("&");
}
function setExceptionHandling() {
var chxCatch = document.getElementById('no_try_catch');
if (noTryCatch()) {
chxCatch.setAttribute('checked', true);
jasmine.CATCH_EXCEPTIONS = false;
}
chxCatch.onclick = function() {
window.location.search = searchWithCatch();
};
}
};
jasmine.HtmlReporter.parameters = function(doc) {
var paramStr = doc.location.search.substring(1);
var params = [];
if (paramStr.length > 0) {
params = paramStr.split('&');
}
return params;
}
jasmine.HtmlReporter.sectionLink = function(sectionName) {
var link = '?';
var params = [];
if (sectionName) {
params.push('spec=' + encodeURIComponent(sectionName));
}
if (!jasmine.CATCH_EXCEPTIONS) {
params.push("catch=false");
}
if (params.length > 0) {
link += params.join("&");
}
return link;
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);
jasmine.HtmlReporter.ReporterView = function(dom) {
this.startedAt = new Date();
this.runningSpecCount = 0;
this.completeSpecCount = 0;
this.passedCount = 0;
this.failedCount = 0;
this.skippedCount = 0;
this.createResultsMenu = function() {
this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
' | ',
this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
this.summaryMenuItem.onclick = function() {
dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
};
this.detailsMenuItem.onclick = function() {
showDetails();
};
};
this.addSpecs = function(specs, specFilter) {
this.totalSpecCount = specs.length;
this.views = {
specs: {},
suites: {}
};
for (var i = 0; i < specs.length; i++) {
var spec = specs[i];
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
if (specFilter(spec)) {
this.runningSpecCount++;
}
}
};
this.specComplete = function(spec) {
this.completeSpecCount++;
if (isUndefined(this.views.specs[spec.id])) {
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
}
var specView = this.views.specs[spec.id];
switch (specView.status()) {
case 'passed':
this.passedCount++;
break;
case 'failed':
this.failedCount++;
break;
case 'skipped':
this.skippedCount++;
break;
}
specView.refresh();
this.refresh();
};
this.suiteComplete = function(suite) {
var suiteView = this.views.suites[suite.id];
if (isUndefined(suiteView)) {
return;
}
suiteView.refresh();
};
this.refresh = function() {
if (isUndefined(this.resultsMenu)) {
this.createResultsMenu();
}
// currently running UI
if (isUndefined(this.runningAlert)) {
this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "runningAlert bar" });
dom.alert.appendChild(this.runningAlert);
}
this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
// skipped specs UI
if (isUndefined(this.skippedAlert)) {
this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "skippedAlert bar" });
}
this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
if (this.skippedCount === 1 && isDefined(dom.alert)) {
dom.alert.appendChild(this.skippedAlert);
}
// passing specs UI
if (isUndefined(this.passedAlert)) {
this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: "passingAlert bar" });
}
this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
// failing specs UI
if (isUndefined(this.failedAlert)) {
this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
}
this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
if (this.failedCount === 1 && isDefined(dom.alert)) {
dom.alert.appendChild(this.failedAlert);
dom.alert.appendChild(this.resultsMenu);
}
// summary info
this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
};
this.complete = function() {
dom.alert.removeChild(this.runningAlert);
this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
if (this.failedCount === 0) {
dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
} else {
showDetails();
}
dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
};
return this;
function showDetails() {
if (dom.reporter.className.search(/showDetails/) === -1) {
dom.reporter.className += " showDetails";
}
}
function isUndefined(obj) {
return typeof obj === 'undefined';
}
function isDefined(obj) {
return !isUndefined(obj);
}
function specPluralizedFor(count) {
var str = count + " spec";
if (count > 1) {
str += "s"
}
return str;
}
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
this.spec = spec;
this.dom = dom;
this.views = views;
this.symbol = this.createDom('li', { className: 'pending' });
this.dom.symbolSummary.appendChild(this.symbol);
this.summary = this.createDom('div', { className: 'specSummary' },
this.createDom('a', {
className: 'description',
href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()),
title: this.spec.getFullName()
}, this.spec.description)
);
this.detail = this.createDom('div', { className: 'specDetail' },
this.createDom('a', {
className: 'description',
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
title: this.spec.getFullName()
}, this.spec.getFullName())
);
};
jasmine.HtmlReporter.SpecView.prototype.status = function() {
return this.getSpecStatus(this.spec);
};
jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
this.symbol.className = this.status();
switch (this.status()) {
case 'skipped':
break;
case 'passed':
this.appendSummaryToSuiteDiv();
break;
case 'failed':
this.appendSummaryToSuiteDiv();
this.appendFailureDetail();
break;
}
};
jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
this.summary.className += ' ' + this.status();
this.appendToSummary(this.spec, this.summary);
};
jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
this.detail.className += ' ' + this.status();
var resultItems = this.spec.results().getItems();
var messagesDiv = this.createDom('div', { className: 'messages' });
for (var i = 0; i < resultItems.length; i++) {
var result = resultItems[i];
if (result.type == 'log') {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
} else if (result.type == 'expect' && result.passed && !result.passed()) {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
if (result.trace.stack) {
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
}
}
}
if (messagesDiv.childNodes.length > 0) {
this.detail.appendChild(messagesDiv);
this.dom.details.appendChild(this.detail);
}
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
this.suite = suite;
this.dom = dom;
this.views = views;
this.element = this.createDom('div', { className: 'suite' },
this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description)
);
this.appendToSummary(this.suite, this.element);
};
jasmine.HtmlReporter.SuiteView.prototype.status = function() {
return this.getSpecStatus(this.suite);
};
jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
this.element.className += " " + this.status();
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
/* @deprecated Use jasmine.HtmlReporter instead
*/
jasmine.TrivialReporter = function(doc) {
this.document = doc || document;
this.suiteDivs = {};
this.logRunningSpecs = false;
};
jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
var el = document.createElement(type);
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i];
if (typeof child === 'string') {
el.appendChild(document.createTextNode(child));
} else {
if (child) { el.appendChild(child); }
}
}
for (var attr in attrs) {
if (attr == "className") {
el[attr] = attrs[attr];
} else {
el.setAttribute(attr, attrs[attr]);
}
}
return el;
};
jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
var showPassed, showSkipped;
this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
this.createDom('div', { className: 'banner' },
this.createDom('div', { className: 'logo' },
this.createDom('span', { className: 'title' }, "Jasmine"),
this.createDom('span', { className: 'version' }, runner.env.versionString())),
this.createDom('div', { className: 'options' },
"Show ",
showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
)
),
this.runnerDiv = this.createDom('div', { className: 'runner running' },
this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
);
this.document.body.appendChild(this.outerDiv);
var suites = runner.suites();
for (var i = 0; i < suites.length; i++) {
var suite = suites[i];
var suiteDiv = this.createDom('div', { className: 'suite' },
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
this.suiteDivs[suite.id] = suiteDiv;
var parentDiv = this.outerDiv;
if (suite.parentSuite) {
parentDiv = this.suiteDivs[suite.parentSuite.id];
}
parentDiv.appendChild(suiteDiv);
}
this.startedAt = new Date();
var self = this;
showPassed.onclick = function(evt) {
if (showPassed.checked) {
self.outerDiv.className += ' show-passed';
} else {
self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
}
};
showSkipped.onclick = function(evt) {
if (showSkipped.checked) {
self.outerDiv.className += ' show-skipped';
} else {
self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
}
};
};
jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
var results = runner.results();
var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
this.runnerDiv.setAttribute("class", className);
//do it twice for IE
this.runnerDiv.setAttribute("className", className);
var specs = runner.specs();
var specCount = 0;
for (var i = 0; i < specs.length; i++) {
if (this.specFilter(specs[i])) {
specCount++;
}
}
var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
};
jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
var results = suite.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.totalCount === 0) { // todo: change this to check results.skipped
status = 'skipped';
}
this.suiteDivs[suite.id].className += " " + status;
};
jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
if (this.logRunningSpecs) {
this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
}
};
jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
var results = spec.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.skipped) {
status = 'skipped';
}
var specDiv = this.createDom('div', { className: 'spec ' + status },
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
this.createDom('a', {
className: 'description',
href: '?spec=' + encodeURIComponent(spec.getFullName()),
title: spec.getFullName()
}, spec.description));
var resultItems = results.getItems();
var messagesDiv = this.createDom('div', { className: 'messages' });
for (var i = 0; i < resultItems.length; i++) {
var result = resultItems[i];
if (result.type == 'log') {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
} else if (result.type == 'expect' && result.passed && !result.passed()) {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
if (result.trace.stack) {
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
}
}
}
if (messagesDiv.childNodes.length > 0) {
specDiv.appendChild(messagesDiv);
}
this.suiteDivs[spec.suite.id].appendChild(specDiv);
};
jasmine.TrivialReporter.prototype.log = function() {
var console = jasmine.getGlobal().console;
if (console && console.log) {
if (console.log.apply) {
console.log.apply(console, arguments);
} else {
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
}
}
};
jasmine.TrivialReporter.prototype.getLocation = function() {
return this.document.location;
};
jasmine.TrivialReporter.prototype.specFilter = function(spec) {
var paramMap = {};
var params = this.getLocation().search.substring(1).split('&');
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
}
if (!paramMap.spec) {
return true;
}
return spec.getFullName().indexOf(paramMap.spec) === 0;
};

View File

@@ -0,0 +1,82 @@
body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
#HTMLReporter a { text-decoration: none; }
#HTMLReporter a:hover { text-decoration: underline; }
#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
#HTMLReporter .version { color: #aaaaaa; }
#HTMLReporter .banner { margin-top: 14px; }
#HTMLReporter .duration { color: #aaaaaa; float: right; }
#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
#HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
#HTMLReporter .runningAlert { background-color: #666666; }
#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
#HTMLReporter .passingAlert { background-color: #a6b779; }
#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
#HTMLReporter .failingAlert { background-color: #cf867e; }
#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
#HTMLReporter .results { margin-top: 14px; }
#HTMLReporter #details { display: none; }
#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
#HTMLReporter.showDetails .summary { display: none; }
#HTMLReporter.showDetails #details { display: block; }
#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
#HTMLReporter .summary { margin-top: 14px; }
#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
#HTMLReporter .description + .suite { margin-top: 0; }
#HTMLReporter .suite { margin-top: 14px; }
#HTMLReporter .suite a { color: #333333; }
#HTMLReporter #details .specDetail { margin-bottom: 28px; }
#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
#HTMLReporter .resultMessage span.result { display: block; }
#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
#TrivialReporter .runner.running { background-color: yellow; }
#TrivialReporter .options { text-align: right; font-size: .8em; }
#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
#TrivialReporter .suite .suite { margin: 5px; }
#TrivialReporter .suite.passed { background-color: #dfd; }
#TrivialReporter .suite.failed { background-color: #fdd; }
#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
#TrivialReporter .spec.skipped { background-color: #bbb; }
#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
#TrivialReporter .passed { background-color: #cfc; display: none; }
#TrivialReporter .failed { background-color: #fbb; }
#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
#TrivialReporter .resultMessage .mismatch { color: black; }
#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
context("bootstrap")
test_that("CSS unit validation", {
# On error, return NA; on success, return result
validateCssUnit_wrap <- function(x) {
tryCatch(validateCssUnit(x), error = function(e) { NA_character_ })
}
# Test strings and expected results
strings <- c("100x", "10px", "10.4px", ".4px", "1px0", "px", "5", "%", "5%", "auto", "1auto", "")
expected <- c(NA, "10px", "10.4px", ".4px", NA, NA, NA, NA, "5%", "auto", NA, NA)
results <- vapply(strings, validateCssUnit_wrap, character(1), USE.NAMES = FALSE)
expect_equal(results, expected)
# Numbers should return string with "px"
expect_equal(validateCssUnit(100), "100px")
})
test_that("Repeated names for selectInput and radioButtons choices", {
# These test might be a bit too closely tied to the exact structure of the
# tag object, but they get the job done for now.
# Select input
x <- selectInput('id','label', choices = c(a='x1', a='x2', b='x3'))
choices <- x[[2]]$children
expect_equal(choices[[1]]$children[[1]], 'a')
expect_equal(choices[[1]]$attribs$value, 'x1')
expect_equal(choices[[1]]$attribs$selected, 'selected')
expect_equal(choices[[2]]$children[[1]], 'a')
expect_equal(choices[[2]]$attribs$value, 'x2')
# This one actually should be NULL, but with the syntax of selectInput, it
# must be 'selected'.
expect_equal(choices[[2]]$attribs$selected, 'selected')
expect_equal(choices[[3]]$children[[1]], 'b')
expect_equal(choices[[3]]$attribs$value, 'x3')
expect_equal(choices[[3]]$attribs$selected, NULL)
# Radio buttons
x <- radioButtons('id','label', choices = c(a='x1', a='x2', b='x3'))
choices <- x$children
expect_equal(choices[[2]]$children[[2]]$children[[1]], 'a')
expect_equal(choices[[2]]$children[[1]]$attribs$value, 'x1')
expect_equal(choices[[2]]$children[[1]]$attribs$checked, 'checked')
expect_equal(choices[[3]]$children[[2]]$children[[1]], 'a')
expect_equal(choices[[3]]$children[[1]]$attribs$value, 'x2')
# This one actually should be NULL, but with the syntax of radioButtons, it
# must be 'checked'.
expect_equal(choices[[3]]$children[[1]]$attribs$checked, 'checked')
expect_equal(choices[[4]]$children[[2]]$children[[1]], 'b')
expect_equal(choices[[4]]$children[[1]]$attribs$value, 'x3')
expect_equal(choices[[4]]$children[[1]]$attribs$checked, NULL)
})

73
inst/tests/test-gc.r Normal file
View File

@@ -0,0 +1,73 @@
context("garbage collection")
test_that("unreferenced observers are garbage collected", {
vals_removed <- FALSE
obs_removed <- FALSE
vals <- reactiveValues(A=1)
obs <- observe({ vals$A })
# These are called when the objects are garbage-collected
reg.finalizer(attr(.subset2(vals,'impl'), ".xData"),
function(e) vals_removed <<- TRUE)
reg.finalizer(attr(obs, ".xData"),
function(e) obs_removed <<- TRUE)
flushReact()
# Removing this reference to obs doesn't delete it because vals still has a
# reference to it
rm(obs)
invisible(gc())
expect_equal(c(vals_removed, obs_removed), c(FALSE, FALSE))
# Updating vals$A and flushing won't make obs go away because it creates a new
# context, and vals$A's context tracks obs's context as a dependent
vals$A <- 2
flushReact()
invisible(gc())
expect_equal(c(vals_removed, obs_removed), c(FALSE, FALSE))
# Removing vals will result in vals and obs being garbage collected since
# there are no other references to them
rm(vals)
invisible(gc())
expect_equal(c(vals_removed, obs_removed), c(TRUE, TRUE))
})
test_that("suspended observers are garbage collected", {
vals_removed <- FALSE
obs_removed <- FALSE
vals <- reactiveValues(A=1)
obs <- observe({ vals$A })
# These are called when the objects are garbage-collected
reg.finalizer(attr(.subset2(vals,'impl'), ".xData"),
function(e) vals_removed <<- TRUE)
reg.finalizer(attr(obs, ".xData"),
function(e) obs_removed <<- TRUE)
flushReact()
vals$A <- 2
flushReact()
invisible(gc())
# Simply suspending and removing our reference to obs doesn't result in GC,
# because vals's context still has a reference to obs's context, as a dependent
obs$suspend()
rm(obs)
invisible(gc())
expect_equal(c(vals_removed, obs_removed), c(FALSE, FALSE))
# Next time we update vals$A and flush, there's no more reference to obs
vals$A <- 3
flushReact()
invisible(gc())
expect_equal(c(vals_removed, obs_removed), c(FALSE, TRUE))
# Deleting vals should work immediately now
rm(vals)
invisible(gc()) # Removes vals object
expect_equal(c(vals_removed, obs_removed), c(TRUE, TRUE))
})

View File

@@ -0,0 +1,697 @@
context("reactivity")
# Test for correct behavior of ReactiveValues
test_that("ReactiveValues", {
# Creation and indexing into ReactiveValues -------------------------------
values <- reactiveValues()
# $ indexing
values$a <- 3
expect_equal(isolate(values$a), 3)
# [[ indexing
values[['a']] <- 4
expect_equal(isolate(values[['a']]), 4)
# Create with initialized values
values <- reactiveValues(a=1, b=2)
expect_equal(isolate(values$a), 1)
expect_equal(isolate(values[['b']]), 2)
# NULL values -------------------------------------------------------------
# Initializing with NULL value
values <- reactiveValues(a=NULL, b=2)
# a should exist and be NULL
expect_equal(isolate(names(values)), c("a", "b"))
expect_true(is.null(isolate(values$a)))
# Assigning NULL should keep object (not delete it), and set value to NULL
values$b <- NULL
expect_equal(isolate(names(values)), c("a", "b"))
expect_true(is.null(isolate(values$b)))
# Errors -----------------------------------------------------------------
# Error: indexing with non-string
expect_error(isolate(values[[1]]))
expect_error(isolate(values[[NULL]]))
expect_error(isolate(values[[list('a')]]))
# Error: [ indexing shouldn't work
expect_error(isolate(values['a']))
expect_error(isolate(values['a'] <- 1))
# Error: unnamed arguments
expect_error(reactiveValues(1))
expect_error(reactiveValues(1, b=2))
# Error: assignment to readonly values
values <- .createReactiveValues(ReactiveValues$new(), readonly = TRUE)
expect_error(values$a <- 1)
})
# Test for overreactivity. funcB has an indirect dependency on valueA (via
# funcA) and also a direct dependency on valueA. When valueA changes, funcB
# should only execute once.
test_that("Functions are not over-reactive", {
values <- reactiveValues(A=10)
funcA <- reactive({
values$A
})
funcB <- reactive({
funcA()
values$A
})
obsC <- observe({
funcB()
})
flushReact()
expect_equal(execCount(funcB), 1)
expect_equal(execCount(obsC), 1)
values$A <- 11
flushReact()
expect_equal(execCount(funcB), 2)
expect_equal(execCount(obsC), 2)
})
## "foo => bar" is defined as "foo is a dependency of bar"
##
## vA => fB
## (fB, vA) => obsE
## (fB, vA) => obsF
##
## obsE and obsF should each execute once when vA changes.
test_that("overreactivity2", {
# ----------------------------------------------
# Test 1
# B depends on A, and observer depends on A and B. The observer uses A and
# B, in that order.
# This is to store the value from observe()
observed_value1 <- NA
observed_value2 <- NA
values <- reactiveValues(A=1)
funcB <- reactive({
values$A + 5
})
obsC <- observe({
observed_value1 <<- funcB() * values$A
})
obsD <- observe({
observed_value2 <<- funcB() * values$A
})
flushReact()
expect_equal(observed_value1, 6) # Should be 1 * (1 + 5) = 6
expect_equal(observed_value2, 6) # Should be 1 * (1 + 5) = 6
expect_equal(execCount(funcB), 1)
expect_equal(execCount(obsC), 1)
expect_equal(execCount(obsD), 1)
values$A <- 2
flushReact()
expect_equal(observed_value1, 14) # Should be 2 * (2 + 5) = 14
expect_equal(observed_value2, 14) # Should be 2 * (2 + 5) = 14
expect_equal(execCount(funcB), 2)
expect_equal(execCount(obsC), 2)
expect_equal(execCount(obsD), 2)
})
## Test for isolation. funcB depends on funcA depends on valueA. When funcA
## is invalidated, if its new result is not different than its old result,
## then it doesn't invalidate its dependents. This is done by adding an observer
## (valueB) between obsA and funcC.
##
## valueA => obsB => valueC => funcD => obsE
test_that("isolation", {
values <- reactiveValues(A=10, C=NULL)
obsB <- observe({
values$C <- values$A > 0
})
funcD <- reactive({
values$C
})
obsE <- observe({
funcD()
})
flushReact()
countD <- execCount(funcD)
values$A <- 11
flushReact()
expect_equal(execCount(funcD), countD)
})
## Test for laziness. With lazy evaluation, the observers should "pull" values
## from their dependent functions. In contrast, eager evaluation would have
## reactive values and functions "push" their changes down to their descendents.
test_that("laziness", {
values <- reactiveValues(A=10)
funcA <- reactive({
values$A > 0
})
funcB <- reactive({
funcA()
})
obsC <- observe({
if (values$A > 10)
return()
funcB()
})
flushReact()
expect_equal(execCount(funcA), 1)
expect_equal(execCount(funcB), 1)
expect_equal(execCount(obsC), 1)
values$A <- 11
flushReact()
expect_equal(execCount(funcA), 1)
expect_equal(execCount(funcB), 1)
expect_equal(execCount(obsC), 2)
})
## Suppose B depends on A and C depends on A and B. Then when A is changed,
## the evaluation order should be A, B, C. Also, each time A is changed, B and
## C should be run once, if we want to be maximally efficient.
test_that("order of evaluation", {
# ----------------------------------------------
# Test 1
# B depends on A, and observer depends on A and B. The observer uses A and
# B, in that order.
# This is to store the value from observe()
observed_value <- NA
values <- reactiveValues(A=1)
funcB <- reactive({
values$A + 5
})
obsC <- observe({
observed_value <<- values$A * funcB()
})
flushReact()
expect_equal(observed_value, 6) # Should be 1 * (1 + 5) = 6
expect_equal(execCount(funcB), 1)
expect_equal(execCount(obsC), 1)
values$A <- 2
flushReact()
expect_equal(observed_value, 14) # Should be 2 * (2 + 5) = 14
expect_equal(execCount(funcB), 2)
expect_equal(execCount(obsC), 2)
# ----------------------------------------------
# Test 2:
# Same as Test 1, except the observer uses A and B in reversed order.
# Resulting values should be the same.
observed_value <- NA
values <- reactiveValues(A=1)
funcB <- reactive({
values$A + 5
})
obsC <- observe({
observed_value <<- funcB() * values$A
})
flushReact()
# Should be 1 * (1 + 5) = 6
expect_equal(observed_value, 6)
expect_equal(execCount(funcB), 1)
expect_equal(execCount(obsC), 1)
values$A <- 2
flushReact()
# Should be 2 * (2 + 5) = 14
expect_equal(observed_value, 14)
expect_equal(execCount(funcB), 2)
expect_equal(execCount(obsC), 2)
})
## Expressions in isolate() should not invalidate the parent context.
test_that("isolate() blocks invalidations from propagating", {
obsC_value <- NA
obsD_value <- NA
values <- reactiveValues(A=1, B=10)
funcB <- reactive({
values$B + 100
})
# References to valueB and funcB are isolated
obsC <- observe({
obsC_value <<-
values$A + isolate(values$B) + isolate(funcB())
})
# In contrast with obsC, this has a non-isolated reference to funcB
obsD <- observe({
obsD_value <<-
values$A + isolate(values$B) + funcB()
})
flushReact()
expect_equal(obsC_value, 121)
expect_equal(execCount(obsC), 1)
expect_equal(obsD_value, 121)
expect_equal(execCount(obsD), 1)
# Changing A should invalidate obsC and obsD
values$A <- 2
flushReact()
expect_equal(obsC_value, 122)
expect_equal(execCount(obsC), 2)
expect_equal(obsD_value, 122)
expect_equal(execCount(obsD), 2)
# Changing B shouldn't invalidate obsC becuause references to B are in isolate()
# But it should invalidate obsD.
values$B <- 20
flushReact()
expect_equal(obsC_value, 122)
expect_equal(execCount(obsC), 2)
expect_equal(obsD_value, 142)
expect_equal(execCount(obsD), 3)
# Changing A should invalidate obsC and obsD, and they should see updated
# values for valueA, valueB, and funcB
values$A <- 3
flushReact()
expect_equal(obsC_value, 143)
expect_equal(execCount(obsC), 3)
expect_equal(obsD_value, 143)
expect_equal(execCount(obsD), 4)
})
test_that("isolate() evaluates expressions in calling environment", {
outside <- 1
inside <- 1
loc <- 1
outside <- isolate(2) # Assignment outside isolate
isolate(inside <- 2) # Assignment inside isolate
# Should affect vars in the calling environment
expect_equal(outside, 2)
expect_equal(inside, 2)
isolate(local(loc <<- 2)) # <<- inside isolate(local)
isolate(local(loc <- 3)) # <- inside isolate(local) - should have no effect
expect_equal(loc, 2)
})
test_that("Circular refs/reentrancy in reactive functions work", {
values <- reactiveValues(A=3)
funcB <- reactive({
# Each time fB executes, it reads and then writes valueA,
# effectively invalidating itself--until valueA becomes 0.
if (values$A == 0)
return()
values$A <- values$A - 1
return(values$A)
})
obsC <- observe({
funcB()
})
flushReact()
expect_equal(execCount(obsC), 4)
values$A <- 3
flushReact()
expect_equal(execCount(obsC), 8)
})
test_that("Simple recursion", {
values <- reactiveValues(A=5)
funcB <- reactive({
if (values$A == 0)
return(0)
values$A <- values$A - 1
funcB()
})
obsC <- observe({
funcB()
})
flushReact()
expect_equal(execCount(obsC), 2)
expect_equal(execCount(funcB), 6)
})
test_that("Non-reactive recursion", {
nonreactiveA <- 3
outputD <- NULL
funcB <- reactive({
if (nonreactiveA == 0)
return(0)
nonreactiveA <<- nonreactiveA - 1
return(funcB())
})
obsC <- observe({
outputD <<- funcB()
})
flushReact()
expect_equal(execCount(funcB), 4)
expect_equal(outputD, 0)
})
test_that("Circular dep with observer only", {
values <- reactiveValues(A=3)
obsB <- observe({
if (values$A == 0)
return()
values$A <- values$A - 1
})
flushReact()
expect_equal(execCount(obsB), 4)
})
test_that("Writing then reading value is not circular", {
values <- reactiveValues(A=3)
funcB <- reactive({
values$A <- isolate(values$A) - 1
values$A
})
obsC <- observe({
funcB()
})
flushReact()
expect_equal(execCount(obsC), 1)
values$A <- 10
flushReact()
expect_equal(execCount(obsC), 2)
})
test_that("names() and reactiveValuesToList()", {
values <- reactiveValues(A=1, .B=2)
# Dependent on names
depNames <- observe({
names(values)
})
# Dependent on all non-hidden objects
depValues <- observe({
reactiveValuesToList(values)
})
# Dependent on all objects, including hidden
depAllValues <- observe({
reactiveValuesToList(values, all.names = TRUE)
})
# names() returns all names
expect_equal(sort(isolate(names(values))), sort(c(".B", "A")))
# Assigning names fails
expect_error(isolate(names(v) <- c('x', 'y')))
expect_equal(isolate(reactiveValuesToList(values)), list(A=1))
expect_equal(isolate(reactiveValuesToList(values, all.names=TRUE)), list(A=1, .B=2))
flushReact()
expect_equal(execCount(depNames), 1)
expect_equal(execCount(depValues), 1)
expect_equal(execCount(depAllValues), 1)
# Update existing variable
values$A <- 2
flushReact()
expect_equal(execCount(depNames), 1)
expect_equal(execCount(depValues), 2)
expect_equal(execCount(depAllValues), 2)
# Update existing hidden variable
values$.B <- 3
flushReact()
expect_equal(execCount(depNames), 1)
expect_equal(execCount(depValues), 2)
expect_equal(execCount(depAllValues), 3)
# Add new variable
values$C <- 1
flushReact()
expect_equal(execCount(depNames), 2)
expect_equal(execCount(depValues), 3)
expect_equal(execCount(depAllValues), 4)
# Add new hidden variable
values$.D <- 1
flushReact()
expect_equal(execCount(depNames), 3)
expect_equal(execCount(depValues), 3)
expect_equal(execCount(depAllValues), 5)
})
test_that("Observer pausing works", {
values <- reactiveValues(a=1)
funcA <- reactive({
values$a
})
obsB <- observe({
funcA()
})
# Important: suspend() only affects observer at invalidation time
# Observers are invalidated at creation time, so it will run once regardless
# of being suspended
obsB$suspend()
flushReact()
expect_equal(execCount(funcA), 1)
expect_equal(execCount(obsB), 1)
# When resuming, if nothing changed, don't do anything
obsB$resume()
flushReact()
expect_equal(execCount(funcA), 1)
expect_equal(execCount(obsB), 1)
# Make sure suspended observers do not flush, but do invalidate
obsB_invalidated <- FALSE
obsB$onInvalidate(function() {obsB_invalidated <<- TRUE})
obsB$suspend()
values$a <- 2
flushReact()
expect_equal(obsB_invalidated, TRUE)
expect_equal(execCount(funcA), 1)
expect_equal(execCount(obsB), 1)
obsB$resume()
values$a <- 2.5
obsB$suspend()
flushReact()
expect_equal(execCount(funcA), 2)
expect_equal(execCount(obsB), 2)
values$a <- 3
flushReact()
expect_equal(execCount(funcA), 2)
expect_equal(execCount(obsB), 2)
# If onInvalidate() is added _after_ obsB is suspended and the values$a
# changes, then it shouldn't get run (onInvalidate runs on invalidation, not
# on flush)
values$a <- 4
obsB_invalidated2 <- FALSE
obsB$onInvalidate(function() {obsB_invalidated2 <<- TRUE})
obsB$resume()
flushReact()
expect_equal(execCount(funcA), 3)
expect_equal(execCount(obsB), 3)
expect_equal(obsB_invalidated2, FALSE)
})
test_that("suspended/resumed observers run at most once", {
values <- reactiveValues(A=1)
obs <- observe(function() {
values$A
})
expect_equal(execCount(obs), 0)
# First flush should run obs once
flushReact()
expect_equal(execCount(obs), 1)
# Modify the dependency at each stage of suspend/resume/flush should still
# only result in one run of obs()
values$A <- 2
obs$suspend()
values$A <- 3
obs$resume()
values$A <- 4
flushReact()
expect_equal(execCount(obs), 2)
})
test_that("reactive() accepts quoted and unquoted expressions", {
vals <- reactiveValues(A=1)
# Unquoted expression, with curly braces
fun <- reactive({ vals$A + 1 })
expect_equal(isolate(fun()), 2)
# Unquoted expression, no curly braces
fun <- reactive(vals$A + 1)
expect_equal(isolate(fun()), 2)
# Quoted expression
fun <- reactive(quote(vals$A + 1), quoted = TRUE)
expect_equal(isolate(fun()), 2)
# Quoted expression, saved in a variable
q_expr <- quote(vals$A + 1)
fun <- reactive(q_expr, quoted = TRUE)
expect_equal(isolate(fun()), 2)
# If function is used, work, but print message
expect_message(fun <- reactive(function() { vals$A + 1 }))
expect_equal(isolate(fun()), 2)
# Check that environment is correct - parent environment should be this one
this_env <- environment()
fun <- reactive(environment())
expect_identical(isolate(parent.env(fun())), this_env)
# Sanity check: environment structure for a reactive() should be the same as for
# a normal function
fun <- function() environment()
expect_identical(parent.env(fun()), this_env)
})
test_that("observe() accepts quoted and unquoted expressions", {
vals <- reactiveValues(A=0)
valB <- 0
# Unquoted expression, with curly braces
observe({ valB <<- vals$A + 1})
flushReact()
expect_equal(valB, 1)
# Unquoted expression, no curly braces
observe({ valB <<- vals$A + 2})
flushReact()
expect_equal(valB, 2)
# Quoted expression
observe(quote(valB <<- vals$A + 3), quoted = TRUE)
flushReact()
expect_equal(valB, 3)
# Quoted expression, saved in a variable
q_expr <- quote(valB <<- vals$A + 4)
fun <- observe(q_expr, quoted = TRUE)
flushReact()
expect_equal(valB, 4)
# If function is used, work, but print message
expect_message(observe(function() { valB <<- vals$A + 5 }))
flushReact()
expect_equal(valB, 5)
# Check that environment is correct - parent environment should be this one
this_env <- environment()
inside_env <- NULL
fun <- observe(inside_env <<- environment())
flushReact()
expect_identical(parent.env(inside_env), this_env)
})
test_that("Observer priorities are respected", {
results <- c()
observe(results <<- c(results, 10), priority=10)
observe(results <<- c(results, 30), priority=30)
observe(results <<- c(results, 20), priority=20L)
observe(results <<- c(results, 21), priority=20)
observe(results <<- c(results, 22), priority=20L)
flushReact()
expect_identical(results, c(30, 20, 21, 22, 10))
})
test_that("reactivePoll and reactiveFileReader", {
path <- tempfile('file')
on.exit(unlink(path))
write.csv(cars, file=path, row.names=FALSE)
rfr <- reactiveFileReader(100, NULL, path, read.csv)
expect_equal(isolate(rfr()), cars)
write.csv(rbind(cars, cars), file=path, row.names=FALSE)
Sys.sleep(0.15)
timerCallbacks$executeElapsed()
expect_equal(isolate(rfr()), cars)
flushReact()
expect_equal(isolate(rfr()), rbind(cars, cars))
})
test_that("classes of reactive object", {
v <- reactiveValues(a = 1)
r <- reactive({ v$a + 1 })
o <- observe({ print(r()) })
expect_false(is.reactivevalues(12))
expect_true(is.reactivevalues(v))
expect_false(is.reactivevalues(r))
expect_false(is.reactivevalues(o))
expect_false(is.reactive(12))
expect_false(is.reactive(v))
expect_true(is.reactive(r))
expect_false(is.reactive(o))
})

300
inst/tests/test-tags.r Normal file
View File

@@ -0,0 +1,300 @@
context("tags")
test_that("Basic tag writing works", {
expect_equal(as.character(tagList("hi")), HTML("hi"))
expect_equal(
as.character(tagList("one", "two", tagList("three"))),
HTML("one\ntwo\nthree"))
expect_equal(
as.character(tags$b("one")),
HTML("<b>one</b>"))
expect_equal(
as.character(tags$b("one", "two")),
HTML("<b>\n one\n two\n</b>"))
expect_equal(
as.character(tagList(list("one"))),
HTML("one"))
expect_equal(
as.character(tagList(list(tagList("one")))),
HTML("one"))
expect_equal(
as.character(tagList(tags$br(), "one")),
HTML("<br/>\none"))
})
test_that("withTags works", {
output_tags <- tags$div(class = "myclass",
tags$h3("header"),
tags$p("text here")
)
output_withhtml <- withTags(
div(class = "myclass",
h3("header"),
p("text here")
)
)
expect_identical(output_tags, output_withhtml)
# Check that current environment is searched
x <- 100
expect_identical(tags$p(x), withTags(p(x)))
# Just to make sure, run it in a function, which has its own environment
foo <- function() {
y <- 100
withTags(p(y))
}
expect_identical(tags$p(100), foo())
})
test_that("HTML escaping in tags", {
# Regular text is escaped
expect_equivalent(format(div("<a&b>")), "<div>&lt;a&amp;b&gt;</div>")
# Text in HTML() isn't escaped
expect_equivalent(format(div(HTML("<a&b>"))), "<div><a&b></div>")
# Text in a property is escaped
expect_equivalent(format(div(class = "<a&b>", "text")),
'<div class="&lt;a&amp;b&gt;">text</div>')
# HTML() has no effect in a property like 'class'
expect_equivalent(format(div(class = HTML("<a&b>"), "text")),
'<div class="&lt;a&amp;b&gt;">text</div>')
})
test_that("Adding child tags", {
tag_list <- list(tags$p("tag1"), tags$b("tag2"), tags$i("tag3"))
# Creating nested tags by calling the tag$div function and passing a list
t1 <- tags$div(class="foo", tag_list)
expect_equal(length(t1$children), 3)
expect_equal(t1$children[[1]]$name, "p")
expect_equal(t1$children[[1]]$children[[1]], "tag1")
expect_equal(t1$children[[2]]$name, "b")
expect_equal(t1$children[[2]]$children[[1]], "tag2")
expect_equal(t1$children[[3]]$name, "i")
expect_equal(t1$children[[3]]$children[[1]], "tag3")
# div tag used as starting point for tests below
div_tag <- tags$div(class="foo")
# Appending each child
t2 <- tagAppendChild(div_tag, tag_list[[1]])
t2 <- tagAppendChild(t2, tag_list[[2]])
t2 <- tagAppendChild(t2, tag_list[[3]])
expect_identical(t1, t2)
# tagSetChildren, using list argument
t2 <- tagSetChildren(div_tag, list = tag_list)
expect_identical(t1, t2)
# tagSetChildren, using ... arguments
t2 <- tagSetChildren(div_tag, tag_list[[1]], tag_list[[2]], tag_list[[3]])
expect_identical(t1, t2)
# tagSetChildren, using ... and list arguments
t2 <- tagSetChildren(div_tag, tag_list[[1]], list = tag_list[2:3])
expect_identical(t1, t2)
# tagSetChildren overwrites existing children
t2 <- tagAppendChild(div_tag, p("should replace this tag"))
t2 <- tagSetChildren(div_tag, list = tag_list)
expect_identical(t1, t2)
# tagAppendChildren, using list argument
t2 <- tagAppendChild(div_tag, tag_list[[1]])
t2 <- tagAppendChildren(t2, list = tag_list[2:3])
expect_identical(t1, t2)
# tagAppendChildren, using ... arguments
t2 <- tagAppendChild(div_tag, tag_list[[1]])
t2 <- tagAppendChildren(t2, tag_list[[2]], tag_list[[3]])
expect_identical(t1, t2)
# tagAppendChildren, using ... and list arguments
t2 <- tagAppendChild(div_tag, tag_list[[1]])
t2 <- tagAppendChildren(t2, tag_list[[2]], list = list(tag_list[[3]]))
expect_identical(t1, t2)
# tagAppendChildren can start with no children
t2 <- tagAppendChildren(div_tag, list = tag_list)
expect_identical(t1, t2)
# tagSetChildren preserves attributes
x <- tagSetChildren(div(), HTML("text"))
expect_identical(attr(x$children[[1]], "html"), TRUE)
# tagAppendChildren preserves attributes
x <- tagAppendChildren(div(), HTML("text"))
expect_identical(attr(x$children[[1]], "html"), TRUE)
})
test_that("Creating simple tags", {
# Empty tag
expect_identical(
div(),
structure(
list(name = "div", attribs = list(), children = list()),
.Names = c("name", "attribs", "children"),
class = "shiny.tag"
)
)
# Tag with text
expect_identical(
div("text"),
structure(
list(name = "div", attribs = list(), children = list("text")),
.Names = c("name", "attribs", "children"),
class = "shiny.tag"
)
)
# NULL attributes are dropped
expect_identical(
div(a = NULL, b = "value"),
div(b = "value")
)
# Numbers are coerced to strings
expect_identical(
div(1234),
structure(
list(name = "div", attribs = list(), children = list("1234")),
.Names = c("name", "attribs", "children"),
class = "shiny.tag"
)
)
})
test_that("Creating nested tags", {
# Simple version
# Note that the $children list should not have a names attribute
expect_identical(
div(class="foo", list("a", "b")),
structure(
list(name = "div",
attribs = structure(list(class = "foo"), .Names = "class"),
children = list("a", "b")),
.Names = c("name", "attribs", "children"),
class = "shiny.tag"
)
)
# More complex version
t1 <- withTags(
div(class = "foo",
p("child tag"),
list(
p("in-list child tag 1"),
"in-list character string",
p(),
p("in-list child tag 2")
),
"character string",
1234
)
)
# t1 should be identical to this data structure.
# The nested list should be flattened, and non-tag, non-strings should be
# converted to strings
t1_full <- structure(
list(
name = "div",
attribs = list(class = "foo"),
children = list(
structure(list(name = "p",
attribs = list(),
children = list("child tag")),
class = "shiny.tag"
),
structure(list(name = "p",
attribs = list(),
children = list("in-list child tag 1")),
class = "shiny.tag"
),
"in-list character string",
structure(list(name = "p",
attribs = list(),
children = list()),
class = "shiny.tag"
),
structure(list(name = "p",
attribs = list(),
children = list("in-list child tag 2")),
class = "shiny.tag"
),
"character string",
"1234"
)
),
class = "shiny.tag"
)
expect_identical(t1, t1_full)
})
test_that("Attributes are preserved", {
# HTML() adds an attribute to the data structure (note that this is
# different from the 'attribs' field in the list)
x <- HTML("<tag>&&</tag>")
expect_identical(attr(x, "html"), TRUE)
expect_equivalent(format(x), "<tag>&&</tag>")
# Make sure attributes are preserved when wrapped in other tags
x <- div(HTML("<tag>&&</tag>"))
expect_equivalent(x$children[[1]], "<tag>&&</tag>")
expect_identical(attr(x$children[[1]], "html"), TRUE)
expect_equivalent(format(x), "<div><tag>&&</tag></div>")
# Deeper nesting
x <- div(p(HTML("<tag>&&</tag>")))
expect_equivalent(x$children[[1]]$children[[1]], "<tag>&&</tag>")
expect_identical(attr(x$children[[1]]$children[[1]], "html"), TRUE)
expect_equivalent(format(x), "<div>\n <p><tag>&&</tag></p>\n</div>")
})
test_that("Flattening a list of tags", {
# Flatten a nested list
nested <- list(
"a1",
list(
"b1",
list("c1", "c2"),
list(),
"b2",
list("d1", "d2")
),
"a2"
)
flat <- list("a1", "b1", "c1", "c2", "b2", "d1", "d2", "a2")
expect_identical(flattenTags(nested), flat)
# no-op for flat lists
expect_identical(flattenTags(list(a="1", "b")), list(a="1", "b"))
# numbers are coerced to character
expect_identical(flattenTags(list(a=1, "b")), list(a="1", "b"))
# empty list results in empty list
expect_identical(flattenTags(list()), list())
# preserve attributes
nested <- list("txt1", list(structure("txt2", prop="prop2")))
flat <- list("txt1",
structure("txt2", prop="prop2"))
expect_identical(flattenTags(nested), flat)
})

56
inst/tests/test-text.R Normal file
View File

@@ -0,0 +1,56 @@
context("text")
test_that("renderPrint and renderText behavior is correct", {
expect_equal(isolate(renderPrint({ "foo" })()),
'[1] "foo"')
expect_equal(isolate(renderPrint({ invisible("foo") })()),
'')
expect_equal(isolate(renderPrint({ print("foo"); "bar"})()),
'[1] "foo"\n[1] "bar"')
expect_equal(isolate(renderPrint({ NULL })()),
'NULL')
expect_equal(isolate(renderPrint({ invisible() })()),
'')
expect_equal(isolate(renderPrint({ 1:5 })()),
'[1] 1 2 3 4 5')
expect_equal(isolate(renderText({ "foo" })()),
'foo')
expect_equal(isolate(renderText({ invisible("foo") })()),
'foo')
# Capture the print output so it's not shown on console during test, and
# also check that it is correct
print_out <- capture.output(ret <- isolate(renderText({ print("foo"); "bar"})()))
expect_equal(ret, 'bar')
expect_equal(print_out, '[1] "foo"')
expect_equal(isolate(renderText({ NULL })()),
'')
expect_equal(isolate(renderText({ invisible() })()),
'')
expect_equal(isolate(renderText({ 1:5 })()),
'1 2 3 4 5')
})
test_that("reactive functions save visibility state", {
# Call each function twice - should be no change in state with second call
# invisible NULL
f <- reactive({ invisible() })
expect_identical(withVisible(isolate(f())), list(value=NULL, visible=FALSE))
expect_identical(withVisible(isolate(f())), list(value=NULL, visible=FALSE))
# visible NULL
f <- reactive({ NULL })
expect_identical(withVisible(isolate(f())), list(value=NULL, visible=TRUE))
expect_identical(withVisible(isolate(f())), list(value=NULL, visible=TRUE))
# invisible non-NULL value
f <- reactive({ invisible(10)})
expect_identical(withVisible(isolate(f())), list(value=10, visible=FALSE))
expect_identical(withVisible(isolate(f())), list(value=10, visible=FALSE))
# visible non-NULL value
f <- reactive({ 10 })
expect_identical(withVisible(isolate(f())), list(value=10, visible=TRUE))
expect_identical(withVisible(isolate(f())), list(value=10, visible=TRUE))
})

20
inst/tests/test-url.R Normal file
View File

@@ -0,0 +1,20 @@
context("URL")
test_that("Query string parsing", {
expect_identical(
parseQueryString("?foo=1&bar=b+a%20r&b+a%20z=baz&=nokey&novalue=&=&noequal&end=end"),
list(
foo = '1',
bar = 'b a r',
`b a z` = 'baz',
'nokey',
novalue = '',
'',
noequal = '',
end = 'end'
)
)
# Should be the same with or without leading question mark
expect_identical(parseQueryString("?foo=1&bar=b"), parseQueryString("foo=1&bar=b"))
})

View File

@@ -0,0 +1,818 @@
<!DOCTYPE html>
<html>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<link href='http://fonts.googleapis.com/css?family=Source+Sans+Pro:200,400,600' rel='stylesheet' type='text/css'>
<style type="text/css">
html, body {
font-family: 'Source Sans Pro', sans-serif;
font-weight: 400;
overflow: hidden;
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
div {
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
-o-user-select: none;
cursor: default;
}
#instructions, #ended {
position: relative;
font-weight: 200;
color: #444;
top: 20px;
font-size: 30px;
text-align: center;
}
#ended strong {
font-weight: 600;
}
svg {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.node {
cursor: pointer;
}
.node text {
font-family: 'Source Code Pro', monospace;
font-weight: normal;
text-anchor: start;
fill: #999;
user-select: none;
transition: fill 0.75s ease;
}
.node.running text {
fill: black;
}
.node.changed text {
fill: red;
}
.node text tspan {
white-space: pre;
}
.node path {
fill: white;
stroke: #777;
stroke-width: 7.5px;
transition: fill 0.75s ease;
}
.node.observer path {
}
.node.observable path {
}
.node.value path {
}
.node.invalidated path {
fill: #E0E0E0;
/*fill: url(#diagonalHatch);*/
}
.node.running path {
fill: #61B97E;
}
#legend {
font-size: 22px;
position: fixed;
bottom: 10px;
right: 20px;
}
.color {
display: inline-block;
border: 1px solid #777;
height: 14px;
width: 14px;
}
.color.normal {
background-color: #white;
}
.color.invalidated {
background-color: #E0E0E0;
}
.color.running {
background-color: #61B97E;
}
#triangle {
fill: #CCC;
}
.link {
fill: none;
stroke: #CCC;
stroke-width: 0.5px;
}
#description {
position: fixed;
width: 300px;
left: 630px;
top: 36px;
height: auto;
display: none;
}
#timeline {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 20px;
transition: height 500ms;
}
#timeline, #timeline * {
cursor: pointer;
}
#timeline:hover {
height: 32px;
}
#timeline-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 12px;
background-color: silver;
}
#timeline-fill {
background-color: #28A3F2;
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 0;
transition: width 500ms;
}
</style>
<script>
var log = [
{ "action" : "valueChange", "id" : "names(input)", "value" : " chr \"dataset\"" },
{ "action" : "valueChange", "id" : "input (all)", "value" : "List of 1\n $ dataset: chr \"rock\"" },
{ "action" : "valueChange", "id" : "input$dataset", "value" : " chr \"rock\"" },
{ "action" : "valueChange", "id" : "names(input)", "value" : " chr [1:2] \"caption\" \"dataset\"" },
{ "action" : "valueChange", "id" : "input (all)", "value" : "List of 2\n $ caption: chr \"Data Summary\"\n $ dataset: chr \"rock\"" },
{ "action" : "valueChange", "id" : "input$caption", "value" : " chr \"Data Summary\"" },
{ "action" : "valueChange", "id" : "names(input)", "value" : " chr [1:3] \"caption\" \"dataset\" \"obs\"" },
{ "action" : "valueChange", "id" : "input (all)", "value" : "List of 3\n $ caption: chr \"Data Summary\"\n $ obs : num 10\n $ dataset: chr \"rock\"" },
{ "action" : "valueChange", "id" : "input$obs", "value" : " num 10" },
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr \"output_caption_hidden\"" },
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 1\n $ output_caption_hidden: logi FALSE" },
{ "action" : "valueChange", "id" : "clientData$output_caption_hidden", "value" : " logi FALSE" },
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:2] \"output_caption_hidden\" \"output_summary_hidden\"" },
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 2\n $ output_caption_hidden: logi FALSE\n $ output_summary_hidden: logi FALSE" },
{ "action" : "valueChange", "id" : "clientData$output_summary_hidden", "value" : " logi FALSE" },
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:3] \"output_caption_hidden\" \"output_summary_hidden\" \"output_view_hidden\"" },
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 3\n $ output_view_hidden : logi FALSE\n $ output_caption_hidden: logi FALSE\n $ output_summary_hidden: logi FALSE" },
{ "action" : "valueChange", "id" : "clientData$output_view_hidden", "value" : " logi FALSE" },
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:4] \"output_caption_hidden\" \"output_summary_hidden\" \"output_view_hidden\" ..." },
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 4\n $ output_view_hidden : logi FALSE\n $ output_caption_hidden: logi FALSE\n $ pixelratio : num 2\n $ output_summary_hidden: logi FALSE" },
{ "action" : "valueChange", "id" : "clientData$pixelratio", "value" : " num 2" },
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:5] \"output_caption_hidden\" \"output_summary_hidden\" \"output_view_hidden\" ..." },
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 5\n $ output_view_hidden : logi FALSE\n $ output_caption_hidden: logi FALSE\n $ pixelratio : num 2\n $ output_summary_hidden: logi FALSE\n $ url_protocol : chr \"http:\"" },
{ "action" : "valueChange", "id" : "clientData$url_protocol", "value" : " chr \"http:\"" },
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:6] \"output_caption_hidden\" \"output_summary_hidden\" \"output_view_hidden\" ..." },
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 6\n $ output_view_hidden : logi FALSE\n $ output_caption_hidden: logi FALSE\n $ pixelratio : num 2\n $ url_hostname : chr \"localhost\"\n $ output_summary_hidden: logi FALSE\n $ url_protocol : chr \"http:\"" },
{ "action" : "valueChange", "id" : "clientData$url_hostname", "value" : " chr \"localhost\"" },
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:7] \"output_caption_hidden\" \"output_summary_hidden\" \"output_view_hidden\" ..." },
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 7\n $ output_view_hidden : logi FALSE\n $ url_port : chr \"8100\"\n $ output_caption_hidden: logi FALSE\n $ pixelratio : num 2\n $ url_hostname : chr \"localhost\"\n $ output_summary_hidden: logi FALSE\n $ url_protocol : chr \"http:\"" },
{ "action" : "valueChange", "id" : "clientData$url_port", "value" : " chr \"8100\"" },
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:8] \"output_caption_hidden\" \"output_summary_hidden\" \"output_view_hidden\" ..." },
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 8\n $ url_pathname : chr \"/\"\n $ output_view_hidden : logi FALSE\n $ url_port : chr \"8100\"\n $ output_caption_hidden: logi FALSE\n $ pixelratio : num 2\n $ url_hostname : chr \"localhost\"\n $ output_summary_hidden: logi FALSE\n $ url_protocol : chr \"http:\"" },
{ "action" : "valueChange", "id" : "clientData$url_pathname", "value" : " chr \"/\"" },
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:9] \"output_caption_hidden\" \"output_summary_hidden\" \"output_view_hidden\" ..." },
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 9\n $ url_pathname : chr \"/\"\n $ output_view_hidden : logi FALSE\n $ url_port : chr \"8100\"\n $ output_caption_hidden: logi FALSE\n $ pixelratio : num 2\n $ url_hostname : chr \"localhost\"\n $ output_summary_hidden: logi FALSE\n $ url_search : chr \"\"\n $ url_protocol : chr \"http:\"" },
{ "action" : "valueChange", "id" : "clientData$url_search", "value" : " chr \"\"" },
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:10] \"output_caption_hidden\" \"output_summary_hidden\" \"output_view_hidden\" ..." },
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 10\n $ url_pathname : chr \"/\"\n $ output_view_hidden : logi FALSE\n $ url_port : chr \"8100\"\n $ output_caption_hidden: logi FALSE\n $ url_hash_initial : chr \"\"\n $ pixelratio : num 2\n $ url_hostname : chr \"localhost\"\n $ output_summary_hidden: logi FALSE\n $ url_search : chr \"\"\n $ url_protocol : chr \"http:\"" },
{ "action" : "valueChange", "id" : "clientData$url_hash_initial", "value" : " chr \"\"" },
{ "action" : "valueChange", "id" : "names(clientData)", "value" : " chr [1:11] \"allowDataUriScheme\" \"output_caption_hidden\" \"output_summary_hidden\" ..." },
{ "action" : "valueChange", "id" : "clientData (all)", "value" : "List of 11\n $ allowDataUriScheme : logi TRUE\n $ url_pathname : chr \"/\"\n $ output_view_hidden : logi FALSE\n $ url_port : chr \"8100\"\n $ output_caption_hidden: logi FALSE\n $ url_hash_initial : chr \"\"\n $ pixelratio : num 2\n $ url_hostname : chr \"localhost\"\n $ output_summary_hidden: logi FALSE\n $ url_search : chr \"\"\n $ url_protocol : chr \"http:\"" },
{ "action" : "valueChange", "id" : "clientData$allowDataUriScheme", "value" : " logi TRUE" },
{ "action" : "ctx", "id" : "1", "label" : "output$caption <- renderText({ \n input$caption\n})", "type" : "observer", "prevId" : "" },
{ "action" : "invalidate", "id" : "1" },
{ "action" : "ctx", "id" : "2", "label" : "output$summary <- renderPrint({ \n dataset <- datasetInput()\n summary(dataset)\n})", "type" : "observer", "prevId" : "" },
{ "action" : "invalidate", "id" : "2" },
{ "action" : "ctx", "id" : "3", "label" : "output$view <- renderTable({ \n head(datasetInput(), n = input$obs)\n})", "type" : "observer", "prevId" : "" },
{ "action" : "invalidate", "id" : "3" },
{ "action" : "ctx", "id" : "4", "label" : "output$caption <- renderText({ \n input$caption\n})", "type" : "observer", "prevId" : "1" },
{ "action" : "enter", "id" : "4" },
{ "action" : "dep", "id" : "4", "dependsOn" : "input$caption" },
{ "action" : "exit", "id" : "4" },
{ "action" : "ctx", "id" : "5", "label" : "output$summary <- renderPrint({ \n dataset <- datasetInput()\n summary(dataset)\n})", "type" : "observer", "prevId" : "2" },
{ "action" : "enter", "id" : "5" },
{ "action" : "ctx", "id" : "6", "label" : "reactive({ \n switch(input$dataset, rock = rock, pressure = pressure, cars = cars)\n})", "type" : "observable", "prevId" : "" },
{ "action" : "enter", "id" : "6" },
{ "action" : "dep", "id" : "6", "dependsOn" : "input$dataset" },
{ "action" : "exit", "id" : "6" },
{ "action" : "depId", "id" : "5", "dependsOn" : "6" },
{ "action" : "exit", "id" : "5" },
{ "action" : "ctx", "id" : "7", "label" : "output$view <- renderTable({ \n head(datasetInput(), n = input$obs)\n})", "type" : "observer", "prevId" : "3" },
{ "action" : "enter", "id" : "7" },
{ "action" : "depId", "id" : "7", "dependsOn" : "6" },
{ "action" : "dep", "id" : "7", "dependsOn" : "input$obs" },
{ "action" : "exit", "id" : "7" },
{ "action" : "valueChange", "id" : "names(input)", "value" : " chr [1:3] \"caption\" \"dataset\" \"obs\"" },
{ "action" : "valueChange", "id" : "input (all)", "value" : "List of 3\n $ caption: chr \"Pressure Summary\"\n $ obs : num 10\n $ dataset: chr \"rock\"" },
{ "action" : "valueChange", "id" : "input$caption", "value" : " chr \"Pressure Summary\"" },
{ "action" : "invalidate", "id" : "4" },
{ "action" : "ctx", "id" : "8", "label" : "output$caption <- renderText({ \n input$caption\n})", "type" : "observer", "prevId" : "4" },
{ "action" : "enter", "id" : "8" },
{ "action" : "dep", "id" : "8", "dependsOn" : "input$caption" },
{ "action" : "exit", "id" : "8" },
{ "action" : "valueChange", "id" : "names(input)", "value" : " chr [1:3] \"caption\" \"dataset\" \"obs\"" },
{ "action" : "valueChange", "id" : "input (all)", "value" : "List of 3\n $ caption: chr \"Pressure Summary\"\n $ obs : num 10\n $ dataset: chr \"pressure\"" },
{ "action" : "valueChange", "id" : "input$dataset", "value" : " chr \"pressure\"" },
{ "action" : "invalidate", "id" : "6" },
{ "action" : "invalidate", "id" : "5" },
{ "action" : "invalidate", "id" : "7" },
{ "action" : "ctx", "id" : "9", "label" : "output$summary <- renderPrint({ \n dataset <- datasetInput()\n summary(dataset)\n})", "type" : "observer", "prevId" : "5" },
{ "action" : "enter", "id" : "9" },
{ "action" : "ctx", "id" : "10", "label" : "reactive({ \n switch(input$dataset, rock = rock, pressure = pressure, cars = cars)\n})", "type" : "observable", "prevId" : "6" },
{ "action" : "enter", "id" : "10" },
{ "action" : "dep", "id" : "10", "dependsOn" : "input$dataset" },
{ "action" : "exit", "id" : "10" },
{ "action" : "depId", "id" : "9", "dependsOn" : "10" },
{ "action" : "exit", "id" : "9" },
{ "action" : "ctx", "id" : "11", "label" : "output$view <- renderTable({ \n head(datasetInput(), n = input$obs)\n})", "type" : "observer", "prevId" : "7" },
{ "action" : "enter", "id" : "11" },
{ "action" : "depId", "id" : "11", "dependsOn" : "10" },
{ "action" : "dep", "id" : "11", "dependsOn" : "input$obs" },
{ "action" : "exit", "id" : "11" },
{ "action" : "valueChange", "id" : "names(input)", "value" : " chr [1:3] \"caption\" \"dataset\" \"obs\"" },
{ "action" : "valueChange", "id" : "input (all)", "value" : "List of 3\n $ caption: chr \"Pressure Summary\"\n $ obs : num 15\n $ dataset: chr \"pressure\"" },
{ "action" : "valueChange", "id" : "input$obs", "value" : " num 15" },
{ "action" : "invalidate", "id" : "11" },
{ "action" : "ctx", "id" : "12", "label" : "output$view <- renderTable({ \n head(datasetInput(), n = input$obs)\n})", "type" : "observer", "prevId" : "11" },
{ "action" : "enter", "id" : "12" },
{ "action" : "depId", "id" : "12", "dependsOn" : "10" },
{ "action" : "dep", "id" : "12", "dependsOn" : "input$obs" },
{ "action" : "exit", "id" : "12" }
];
try {
log = __DATA__;
} catch (e) {}
var nodes = {};
var nodeList = [];
var nodeSelection = null;
var links = [];
var linkSelection = null;
var node, link; // d3 selections
var MAX_LINES = 6;
var force = d3.layout.force()
.charge(-100)
.nodes(nodeList)
.links(links);
force.on('tick', onTick);
function pathDataForNode(node) {
switch (node.type) {
case 'observer':
return 'M -25,-50 c -75,0 -75,100 0,100 l 100,0 l 0,-100 Z';
case 'observable':
return 'M -25,-50 c -75,0 -75,100 0,100 l 60,0 l 50,-50 l -50,-50 Z';
case 'value':
return 'M -50,-50 l 0,100 l 100,0 l 50,-50 l -50,-50 Z';
}
}
function getSourceCoords(node) {
switch (node.type) {
case 'observer':
case 'observable':
return {x: node.x - 5, y: node.y};
default:
return {x: node.x, y: node.y};
}
}
function getTargetCoords(node) {
switch (node.type) {
case 'observable':
return {x: node.x + 7, y: node.y};
case 'value':
return {x: node.x + 8, y: node.y};
default:
return {x: node.x, y: node.y};
}
}
function multilineTextNode(node) {
var MAX_LINES = 6;
var fade = false;
var el = d3.select(this);
var lines = el.text().split('\n');
if (lines.length > MAX_LINES) {
lines.splice(MAX_LINES);
fade = true;
}
el.text('');
var tspan = el.selectAll('tspan').data(lines);
tspan.enter().append('tspan');
tspan
.attr('x', 8)
.attr('dy', function(line, i) { return i > 0 ? '1em' : 0})
.attr('opacity', function(line, i) {
if (!fade)
return 1;
return Math.min(1, (MAX_LINES - i) * 0.25 - 0.15);
})
.text(function(line) { return line; });
}
function update() {
force.size([document.documentElement.clientWidth / 4,
document.documentElement.clientHeight / 4]);
var layoutDirty = false;
node = d3.select('#nodes').selectAll('.node').data(nodeList);
layoutDirty = layoutDirty || !node.enter().empty() || !node.exit().empty();
var newG = node.enter().append('g')
.attr('class', function(n) {return 'node ' + n.type;})
.attr('r', 5)
// don't show until next tick
.style('display', 'none')
.on('mousedown', function() {
d3.event.stopPropagation();
})
.on('mouseover', function(n) {
$('#description').text(n.label);
})
.on('mouseout', function(d, i) {
$('#description').html('');
})
.call(force.drag);
newG.append('path')
.attr('transform', 'scale(0.08)')
.attr('stroke', 'black')
.attr('stroke-width', 4)
.attr('fill', 'white')
.attr('d', pathDataForNode);
newG.append('text')
.attr('x', 3)
.attr('y', 0)
.attr('font-size', 3.25)
.attr('transform', function(n) {
if (n.type !== 'observer')
return 'translate(1.5, 0)';
else
return null;
})
node.exit().remove();
node
.classed('invalidated', function(n) { return n.invalidated; })
.classed('running', function(n) { return n.running; })
.classed('changed', function(n) { return n.changed; })
.attr('fill', function(n) {
if (n.invalidated)
return "url(#diagonalHatch)";
else
return null;
});
var tspan = node.selectAll('text').filter(function(n) {
// This filter is used to disregard all nodes whose labels have
// not changed since the last time we updated them.
var changed = n.label !== this.label;
this.label = n.label;
return changed;
}).selectAll('tspan')
.data(function(n) {
var lines = n.label.replace(/ /g, '\xA0').split('\n');
if (lines.length > MAX_LINES) {
lines.splice(MAX_LINES);
}
return lines;
});
tspan.enter().append('tspan');
tspan.exit().remove();
tspan
.attr('x', 8)
.attr('dy', function(line, i) { return i > 0 ? '1em' : 0})
.attr('opacity', function(line, i) {
return Math.min(1, (MAX_LINES - i) * 0.25 - 0.15);
})
.text(function(line) { return line; });
link = d3.select('#links').selectAll('.link').data(links);
layoutDirty = layoutDirty || !link.enter().empty() || !link.exit().empty();
link.enter().append('path')
.attr('class', 'link')
.attr('marker-mid', 'url(#triangle)');
link.exit().remove();
if (layoutDirty) {
force
.nodes(nodeList.filter(function(n) {return !n.hide;}))
.start();
layoutDirty = false;
}
}
function onTick() {
node
.style('display', null)
.attr('transform', function(n) {
return 'translate(' + n.x + ' ' + n.y + ')';
});
link
.attr('d', function(link) {
var source = getSourceCoords(link.source);
var target = getTargetCoords(link.target)
var mid = {
x: (source.x + target.x) / 2,
y: (source.y + target.y) / 2
}
return 'M' + source.x + ',' + source.y +
' L' + mid.x + ',' + mid.y +
' L' + target.x + ',' + target.y;
});
}
function createNodeWithUndo(data) {
var node;
if (!data.prevId) {
node = {
label: data.label,
type: data.type,
hide: data.hide
};
nodes[data.id] = node;
pushUndo(function() {
delete nodes[data.id];
});
if (!node.hide) {
nodeList.push(node);
pushUndo(function() {
nodeList.pop();
});
}
} else {
node = nodes[data.prevId];
var oldLabel = node.label;
var oldInvalidated = node.invalidated;
delete nodes[data.prevId];
nodes[data.id] = node;
node.label = data.label;
node.invalidated = false;
pushUndo(function() {
node.label = oldLabel;
node.invalidated = oldInvalidated;
delete nodes[data.id];
nodes[data.prevId] = node;
});
}
}
Array.prototype.pushWithUndo = function(value) {
var self = this;
this.push(value);
pushUndo(function() {
self.pop();
});
}
Array.prototype.shiftWithUndo = function(value) {
var self = this;
var value = this.shift();
pushUndo(function() {
self.unshift(value);
});
return value;
}
var undoStack = [];
var currentUndos = null;
function startUndoScope() {
if (currentUndos !== null)
throw new Error('Illegal state');
currentUndos = [];
}
function pushUndo(func) {
currentUndos.push(func);
}
function endUndoScope() {
var localUndos = currentUndos;
undoStack.push(function() {
while (localUndos.length) {
localUndos.pop()();
}
});
currentUndos = null;
}
function undo() {
if (undoStack.length) {
undoStack.pop()();
update();
return true;
}
return false;
}
function undoAll() {
while (undo()) {}
}
// Here we monkeypatch Math.random to take part in the undo mechanism.
// This allows "random" d3 force-layout decisions to be reproducible.
// If we don't do this, then doing/undoing/redoing a node creation step
// looks very confusing, as the node comes flying in from a different
// direction each time.
var trueRandom = Math.random;
Math.random = (function() {
var randomStack = [];
return function() {
if (!currentUndos)
return trueRandom();
var value;
if (randomStack.length > 0) {
value = randomStack.pop();
}
else {
value = trueRandom();
}
pushUndo(function() {
randomStack.push(value);
});
return value;
};
})();
var callbacks = {
ctx: function(data) {
createNodeWithUndo(data);
return true;
},
dep: function(data) {
var dependsOn = nodes[data.dependsOn];
if (!dependsOn) {
createNodeWithUndo({id: data.dependsOn, label: data.dependsOn, type: 'value'});
dependsOn = nodes[data.dependsOn];
}
if (dependsOn.hide) {
dependsOn.hide = false;
nodeList.push(dependsOn);
pushUndo(function() {
dependsOn.hide = true;
nodeList.pop();
});
}
links.push({
source: nodes[data.id],
target: nodes[data.dependsOn]
});
pushUndo(function() {
links.pop();
});
},
depId: function(data) {
links.push({
source: nodes[data.id],
target: nodes[data.dependsOn]
});
pushUndo(function() {
links.pop();
});
},
invalidate: function(data) {
var node = nodes[data.id];
if (node.invalidated)
throw new Error('Illegal sequence');
node.invalidated = true;
pushUndo(function() {
node.invalidated = false;
});
var origLinks = links;
links = links.filter(function(link) {
return link.source !== node;
});
pushUndo(function() {
links = origLinks;
});
},
valueChange: function(data) {
var existed = !!nodes[data.id];
createNodeWithUndo({
id: data.id,
label: data.id + ' = ' + data.value,
type: 'value',
prevId: nodes[data.id] ? data.id : null,
hide: existed ? nodes[data.id].hide : true
});
if (!existed || nodes[data.id].hide)
return true;
nodes[data.id].changed = true;
pushUndo(function() {
nodes[data.id].changed = false;
});
executeBeforeNextCommand.pushWithUndo(function() {
nodes[data.id].changed = false;
pushUndo(function() {
nodes[data.id].changed = true;
});
});
},
enter: function(data) {
var node = nodes[data.id];
node.running = true;
pushUndo(function() {
node.running = false;
});
},
exit: function(data) {
var node = nodes[data.id];
node.running = false;
pushUndo(function() {
node.running = true;
});
}
};
function processMessage(data, suppressUpdate) {
console.log(JSON.stringify(data));
if (!callbacks.hasOwnProperty(data.action))
throw new Error('Unknown action ' + data.action);
var result = callbacks[data.action].call(callbacks, data);
if (!suppressUpdate)
update();
return result;
}
var executeBeforeNextCommand = [];
function doNext(suppressUpdate) {
if (!log.length)
return;
startUndoScope();
while (executeBeforeNextCommand.length) {
executeBeforeNextCommand.shiftWithUndo()();
}
while (log.length) {
var result = (function() {
var message = log.shift();
pushUndo(function() {
log.unshift(message);
})
return processMessage(message, suppressUpdate);
})();
if (!result)
break;
}
if (!log.length) {
$('#ended').fadeIn(1500);
pushUndo(function() {
$('#ended').hide();
});
}
step++;
updateTimeline();
pushUndo(function() {
step--;
updateTimeline();
});
endUndoScope();
}
function countSteps() {
if (undoStack.length !== 0) {
throw new Error(
'Illegal state; must call countSteps before execution begins');
}
var steps = 0;
while (log.length) {
doNext();
steps++;
}
while (undoStack.length)
undoStack.pop()();
return steps;
}
function updateTimeline() {
$('#timeline-fill').width((step/totalSteps*100) + '%');
}
function zoom() {
var scale = d3.event.scale;
var x = d3.event.translate[0];
var y = d3.event.translate[1];
d3.select('#viz').attr('transform', 'scale(' + scale + ') translate(' + x/scale + ' ' + y/scale + ')');
}
// The total number of steps, as far as the user is concerned, in the log.
// This may/will be different than the number of log entries, since each
// step may include more than one log entry.
var totalSteps;
// The current step we're on.
var step;
$(function() {
d3.select('svg').call(d3.behavior.zoom().scale(4).on('zoom', zoom));
$(document.body).on('keydown', function(e) {
if (e.which === 39 || e.which === 32) { // space, right
// Move one step ahead
doNext();
}
if (e.which === 37) { // left
// Move one step back
undo();
}
if (e.which === 35) { // end
// Seek to end
while (log.length) {
doNext();
}
}
if (e.which === 36) { // home
// Seek to beginning
undoAll();
}
});
// Timeline click and scrub
$('#timeline').on('click mousemove', function(e) {
// Make sure left mouse button is down.
// Firefox is stupid; e.which is always 1 on mousemove events,
// even when button is not down!! So read e.originalEvent.buttons.
if (typeof(e.originalEvent.buttons) !== 'undefined') {
if (e.originalEvent.buttons !== 1)
return;
} else if (e.which !== 1) {
return;
}
var timeline = e.currentTarget;
var pos = e.offsetX || e.originalEvent.layerX;
var width = timeline.offsetWidth;
var targetStep = Math.round((pos/width) * totalSteps);
while (step < targetStep) {
doNext();
}
while (step > targetStep && step != 1) {
undo();
}
});
totalSteps = countSteps();
step = 0;
doNext();
// don't allow undoing past initial state
while (undoStack.length)
undoStack.pop();
executeBeforeNextCommand.push(function() {
$('#instructions').fadeOut(1000);
// It's weird for the instructions to fade back in, so no pushUndo here
});
});
</script>
<body>
<svg>
<defs>
<marker id="triangle"
viewBox="0 0 10 10"
refX="5" refY="5"
markerWidth="6"
markerHeight="6"
orient="auto">
<path d="M 10 0 L 0 5 L 10 10 z" />
</marker>
<pattern id="diagonalHatch" patternUnits="userSpaceOnUse" width="1" height="1">
<path stroke="black" stroke-width="0.25" fill="none"
d="M-1,1 l2,-2
M0,4 l4,-4
M3,5 l2,-2" />
</pattern>
</defs>
<g id="viz" transform="scale(4)">
<g id="links"></g>
<g id="nodes"></g>
</g>
</svg>
<div id="instructions">
Press right-arrow to advance
</div>
<div id="ended" style="display: none;">
<strong>You&rsquo;ve reached the end</strong><br/>Press the Home key to start over
</div>
<div id="legend">
<div class="color normal"></div> Normal<br/>
<div class="color invalidated"></div> Invalidated<br/>
<div class="color running"></div> Running<br/>
</div>
<br/>
<pre id="description"><br/></pre>
<div id="timeline">
<div id="timeline-bg">
<div id="timeline-fill"></div>
</div>
</div>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,5 +1,5 @@
/* ===================================================
* bootstrap-transition.js v2.0.4
* bootstrap-transition.js v2.1.0
* http://twitter.github.com/bootstrap/javascript.html#transitions
* ===================================================
* Copyright 2012 Twitter, Inc.
@@ -36,8 +36,7 @@
, transEndEventNames = {
'WebkitTransition' : 'webkitTransitionEnd'
, 'MozTransition' : 'transitionend'
, 'OTransition' : 'oTransitionEnd'
, 'msTransition' : 'MSTransitionEnd'
, 'OTransition' : 'oTransitionEnd otransitionend'
, 'transition' : 'transitionend'
}
, name
@@ -59,7 +58,7 @@
})
}(window.jQuery);/* ==========================================================
* bootstrap-alert.js v2.0.4
* bootstrap-alert.js v2.1.0
* http://twitter.github.com/bootstrap/javascript.html#alerts
* ==========================================================
* Copyright 2012 Twitter, Inc.
@@ -148,7 +147,7 @@
})
}(window.jQuery);/* ============================================================
* bootstrap-button.js v2.0.4
* bootstrap-button.js v2.1.0
* http://twitter.github.com/bootstrap/javascript.html#buttons
* ============================================================
* Copyright 2012 Twitter, Inc.
@@ -243,7 +242,7 @@
})
}(window.jQuery);/* ==========================================================
* bootstrap-carousel.js v2.0.4
* bootstrap-carousel.js v2.1.0
* http://twitter.github.com/bootstrap/javascript.html#carousel
* ==========================================================
* Copyright 2012 Twitter, Inc.
@@ -290,7 +289,7 @@
}
, to: function (pos) {
var $active = this.$element.find('.active')
var $active = this.$element.find('.item.active')
, children = $active.parent().children()
, activePos = children.index($active)
, that = this
@@ -312,6 +311,10 @@
, pause: function (e) {
if (!e) this.paused = true
if (this.$element.find('.next, .prev').length && $.support.transition.end) {
this.$element.trigger($.support.transition.end)
this.cycle()
}
clearInterval(this.interval)
this.interval = null
return this
@@ -328,13 +331,15 @@
}
, slide: function (type, next) {
var $active = this.$element.find('.active')
var $active = this.$element.find('.item.active')
, $next = next || $active[type]()
, isCycling = this.interval
, direction = type == 'next' ? 'left' : 'right'
, fallback = type == 'next' ? 'first' : 'last'
, that = this
, e = $.Event('slide')
, e = $.Event('slide', {
relatedTarget: $next[0]
})
this.sliding = true
@@ -382,9 +387,10 @@
var $this = $(this)
, data = $this.data('carousel')
, options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option)
, action = typeof option == 'string' ? option : options.slide
if (!data) $this.data('carousel', (data = new Carousel(this, options)))
if (typeof option == 'number') data.to(option)
else if (typeof option == 'string' || (option = options.slide)) data[option]()
else if (action) data[action]()
else if (options.interval) data.cycle()
})
}
@@ -411,7 +417,7 @@
})
}(window.jQuery);/* =============================================================
* bootstrap-collapse.js v2.0.4
* bootstrap-collapse.js v2.1.0
* http://twitter.github.com/bootstrap/javascript.html#collapse
* =============================================================
* Copyright 2012 Twitter, Inc.
@@ -479,7 +485,7 @@
this.$element[dimension](0)
this.transition('addClass', $.Event('show'), 'shown')
this.$element[dimension](this.$element[0][scroll])
$.support.transition && this.$element[dimension](this.$element[0][scroll])
}
, hide: function () {
@@ -556,18 +562,19 @@
* ==================== */
$(function () {
$('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) {
$('body').on('click.collapse.data-api', '[data-toggle=collapse]', function (e) {
var $this = $(this), href
, target = $this.attr('data-target')
|| e.preventDefault()
|| (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
, option = $(target).data('collapse') ? 'toggle' : $this.data()
$this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
$(target).collapse(option)
})
})
}(window.jQuery);/* ============================================================
* bootstrap-dropdown.js v2.0.4
* bootstrap-dropdown.js v2.1.0
* http://twitter.github.com/bootstrap/javascript.html#dropdowns
* ============================================================
* Copyright 2012 Twitter, Inc.
@@ -594,7 +601,7 @@
/* DROPDOWN CLASS DEFINITION
* ========================= */
var toggle = '[data-toggle="dropdown"]'
var toggle = '[data-toggle=dropdown]'
, Dropdown = function (element) {
var $el = $(element).on('click.dropdown.data-api', this.toggle)
$('html').on('click.dropdown.data-api', function () {
@@ -609,34 +616,82 @@
, toggle: function (e) {
var $this = $(this)
, $parent
, selector
, isActive
if ($this.is('.disabled, :disabled')) return
selector = $this.attr('data-target')
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
$parent = $(selector)
$parent.length || ($parent = $this.parent())
$parent = getParent($this)
isActive = $parent.hasClass('open')
clearMenus()
if (!isActive) $parent.toggleClass('open')
if (!isActive) {
$parent.toggleClass('open')
$this.focus()
}
return false
}
, keydown: function (e) {
var $this
, $items
, $active
, $parent
, isActive
, index
if (!/(38|40|27)/.test(e.keyCode)) return
$this = $(this)
e.preventDefault()
e.stopPropagation()
if ($this.is('.disabled, :disabled')) return
$parent = getParent($this)
isActive = $parent.hasClass('open')
if (!isActive || (isActive && e.keyCode == 27)) return $this.click()
$items = $('[role=menu] li:not(.divider) a', $parent)
if (!$items.length) return
index = $items.index($items.filter(':focus'))
if (e.keyCode == 38 && index > 0) index-- // up
if (e.keyCode == 40 && index < $items.length - 1) index++ // down
if (!~index) index = 0
$items
.eq(index)
.focus()
}
}
function clearMenus() {
$(toggle).parent().removeClass('open')
getParent($(toggle))
.removeClass('open')
}
function getParent($this) {
var selector = $this.attr('data-target')
, $parent
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
$parent = $(selector)
$parent.length || ($parent = $this.parent())
return $parent
}
@@ -659,14 +714,16 @@
* =================================== */
$(function () {
$('html').on('click.dropdown.data-api', clearMenus)
$('html')
.on('click.dropdown.data-api touchstart.dropdown.data-api', clearMenus)
$('body')
.on('click.dropdown', '.dropdown form', function (e) { e.stopPropagation() })
.on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle)
.on('click.dropdown touchstart.dropdown.data-api', '.dropdown', function (e) { e.stopPropagation() })
.on('click.dropdown.data-api touchstart.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
.on('keydown.dropdown.data-api touchstart.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
})
}(window.jQuery);/* =========================================================
* bootstrap-modal.js v2.0.4
* bootstrap-modal.js v2.1.0
* http://twitter.github.com/bootstrap/javascript.html#modals
* =========================================================
* Copyright 2012 Twitter, Inc.
@@ -693,10 +750,11 @@
/* MODAL CLASS DEFINITION
* ====================== */
var Modal = function (content, options) {
var Modal = function (element, options) {
this.options = options
this.$element = $(content)
this.$element = $(element)
.delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
this.options.remote && this.$element.find('.modal-body').load(this.options.remote)
}
Modal.prototype = {
@@ -719,8 +777,9 @@
this.isShown = true
escape.call(this)
backdrop.call(this, function () {
this.escape()
this.backdrop(function () {
var transition = $.support.transition && that.$element.hasClass('fade')
if (!that.$element.parent().length) {
@@ -734,7 +793,12 @@
that.$element[0].offsetWidth // force reflow
}
that.$element.addClass('in')
that.$element
.addClass('in')
.attr('aria-hidden', false)
.focus()
that.enforceFocus()
transition ?
that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) :
@@ -758,90 +822,98 @@
$('body').removeClass('modal-open')
escape.call(this)
this.escape()
this.$element.removeClass('in')
$(document).off('focusin.modal')
this.$element
.removeClass('in')
.attr('aria-hidden', true)
$.support.transition && this.$element.hasClass('fade') ?
hideWithTransition.call(this) :
hideModal.call(this)
this.hideWithTransition() :
this.hideModal()
}
}
/* MODAL PRIVATE METHODS
* ===================== */
function hideWithTransition() {
var that = this
, timeout = setTimeout(function () {
that.$element.off($.support.transition.end)
hideModal.call(that)
}, 500)
this.$element.one($.support.transition.end, function () {
clearTimeout(timeout)
hideModal.call(that)
})
}
function hideModal(that) {
this.$element
.hide()
.trigger('hidden')
backdrop.call(this)
}
function backdrop(callback) {
var that = this
, animate = this.$element.hasClass('fade') ? 'fade' : ''
if (this.isShown && this.options.backdrop) {
var doAnimate = $.support.transition && animate
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
.appendTo(document.body)
if (this.options.backdrop != 'static') {
this.$backdrop.click($.proxy(this.hide, this))
, enforceFocus: function () {
var that = this
$(document).on('focusin.modal', function (e) {
if (that.$element[0] !== e.target && !that.$element.has(e.target).length) {
that.$element.focus()
}
})
}
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
, escape: function () {
var that = this
if (this.isShown && this.options.keyboard) {
this.$element.on('keyup.dismiss.modal', function ( e ) {
e.which == 27 && that.hide()
})
} else if (!this.isShown) {
this.$element.off('keyup.dismiss.modal')
}
}
this.$backdrop.addClass('in')
, hideWithTransition: function () {
var that = this
, timeout = setTimeout(function () {
that.$element.off($.support.transition.end)
that.hideModal()
}, 500)
doAnimate ?
this.$backdrop.one($.support.transition.end, callback) :
callback()
this.$element.one($.support.transition.end, function () {
clearTimeout(timeout)
that.hideModal()
})
}
} else if (!this.isShown && this.$backdrop) {
this.$backdrop.removeClass('in')
, hideModal: function (that) {
this.$element
.hide()
.trigger('hidden')
$.support.transition && this.$element.hasClass('fade')?
this.$backdrop.one($.support.transition.end, $.proxy(removeBackdrop, this)) :
removeBackdrop.call(this)
this.backdrop()
}
} else if (callback) {
callback()
}
}
, removeBackdrop: function () {
this.$backdrop.remove()
this.$backdrop = null
}
function removeBackdrop() {
this.$backdrop.remove()
this.$backdrop = null
}
, backdrop: function (callback) {
var that = this
, animate = this.$element.hasClass('fade') ? 'fade' : ''
function escape() {
var that = this
if (this.isShown && this.options.keyboard) {
$(document).on('keyup.dismiss.modal', function ( e ) {
e.which == 27 && that.hide()
})
} else if (!this.isShown) {
$(document).off('keyup.dismiss.modal')
}
if (this.isShown && this.options.backdrop) {
var doAnimate = $.support.transition && animate
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
.appendTo(document.body)
if (this.options.backdrop != 'static') {
this.$backdrop.click($.proxy(this.hide, this))
}
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
this.$backdrop.addClass('in')
doAnimate ?
this.$backdrop.one($.support.transition.end, callback) :
callback()
} else if (!this.isShown && this.$backdrop) {
this.$backdrop.removeClass('in')
$.support.transition && this.$element.hasClass('fade')?
this.$backdrop.one($.support.transition.end, $.proxy(this.removeBackdrop, this)) :
this.removeBackdrop()
} else if (callback) {
callback()
}
}
}
@@ -873,17 +945,23 @@
$(function () {
$('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
var $this = $(this), href
, $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
, option = $target.data('modal') ? 'toggle' : $.extend({}, $target.data(), $this.data())
var $this = $(this)
, href = $this.attr('href')
, $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
, option = $target.data('modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
e.preventDefault()
$target.modal(option)
$target
.modal(option)
.one('hide', function () {
$this.focus()
})
})
})
}(window.jQuery);/* ===========================================================
* bootstrap-tooltip.js v2.0.4
* bootstrap-tooltip.js v2.1.0
* http://twitter.github.com/bootstrap/javascript.html#tooltips
* Inspired by the original jQuery.tipsy by Jason Frame
* ===========================================================
@@ -928,11 +1006,13 @@
this.options = this.getOptions(options)
this.enabled = true
if (this.options.trigger != 'manual') {
eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
if (this.options.trigger == 'click') {
this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
} else if (this.options.trigger != 'manual') {
eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
this.$element.on(eventIn, this.options.selector, $.proxy(this.enter, this))
this.$element.on(eventOut, this.options.selector, $.proxy(this.leave, this))
this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
}
this.options.selector ?
@@ -1032,20 +1112,11 @@
}
}
, isHTML: function(text) {
// html string detection logic adapted from jQuery
return typeof text != 'string'
|| ( text.charAt(0) === "<"
&& text.charAt( text.length - 1 ) === ">"
&& text.length >= 3
) || /^(?:[^<]*<[\w\W]+>[^>]*$)/.exec(text)
}
, setContent: function () {
var $tip = this.tip()
, title = this.getTitle()
$tip.find('.tooltip-inner')[this.isHTML(title) ? 'html' : 'text'](title)
$tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
$tip.removeClass('fade in top bottom left right')
}
@@ -1069,6 +1140,8 @@
$.support.transition && this.$tip.hasClass('fade') ?
removeWithAnimation() :
$tip.remove()
return this
}
, fixTitle: function () {
@@ -1128,6 +1201,10 @@
this[this.tip().hasClass('in') ? 'hide' : 'show']()
}
, destroy: function () {
this.hide().$element.off('.' + this.type).removeData(this.type)
}
}
@@ -1154,11 +1231,12 @@
, trigger: 'hover'
, title: ''
, delay: 0
, html: true
}
}(window.jQuery);
/* ===========================================================
* bootstrap-popover.js v2.0.4
* bootstrap-popover.js v2.1.0
* http://twitter.github.com/bootstrap/javascript.html#popovers
* ===========================================================
* Copyright 2012 Twitter, Inc.
@@ -1185,7 +1263,7 @@
/* POPOVER PUBLIC CLASS DEFINITION
* =============================== */
var Popover = function ( element, options ) {
var Popover = function (element, options) {
this.init('popover', element, options)
}
@@ -1202,8 +1280,8 @@
, title = this.getTitle()
, content = this.getContent()
$tip.find('.popover-title')[this.isHTML(title) ? 'html' : 'text'](title)
$tip.find('.popover-content > *')[this.isHTML(content) ? 'html' : 'text'](content)
$tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
$tip.find('.popover-content > *')[this.options.html ? 'html' : 'text'](content)
$tip.removeClass('fade top bottom left right in')
}
@@ -1230,6 +1308,10 @@
return this.$tip
}
, destroy: function () {
this.hide().$element.off('.' + this.type).removeData(this.type)
}
})
@@ -1250,12 +1332,13 @@
$.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
placement: 'right'
, trigger: 'click'
, content: ''
, template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'
})
}(window.jQuery);/* =============================================================
* bootstrap-scrollspy.js v2.0.4
* bootstrap-scrollspy.js v2.1.0
* http://twitter.github.com/bootstrap/javascript.html#scrollspy
* =============================================================
* Copyright 2012 Twitter, Inc.
@@ -1279,15 +1362,15 @@
"use strict"; // jshint ;_;
/* SCROLLSPY CLASS DEFINITION
* ========================== */
/* SCROLLSPY CLASS DEFINITION
* ========================== */
function ScrollSpy( element, options) {
function ScrollSpy(element, options) {
var process = $.proxy(this.process, this)
, $element = $(element).is('body') ? $(window) : $(element)
, href
this.options = $.extend({}, $.fn.scrollspy.defaults, options)
this.$scrollElement = $element.on('scroll.scroll.data-api', process)
this.$scrollElement = $element.on('scroll.scroll-spy.data-api', process)
this.selector = (this.options.target
|| ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
|| '') + ' .nav li > a'
@@ -1314,7 +1397,7 @@
, href = $el.data('target') || $el.attr('href')
, $href = /^#\w/.test(href) && $(href)
return ( $href
&& href.length
&& $href.length
&& [[ $href.position().top, href ]] ) || null
})
.sort(function (a, b) { return a[0] - b[0] })
@@ -1364,7 +1447,7 @@
.parent('li')
.addClass('active')
if (active.parent('.dropdown-menu')) {
if (active.parent('.dropdown-menu').length) {
active = active.closest('li.dropdown').addClass('active')
}
@@ -1377,7 +1460,7 @@
/* SCROLLSPY PLUGIN DEFINITION
* =========================== */
$.fn.scrollspy = function ( option ) {
$.fn.scrollspy = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('scrollspy')
@@ -1397,7 +1480,7 @@
/* SCROLLSPY DATA-API
* ================== */
$(function () {
$(window).on('load', function () {
$('[data-spy="scroll"]').each(function () {
var $spy = $(this)
$spy.scrollspy($spy.data())
@@ -1405,7 +1488,7 @@
})
}(window.jQuery);/* ========================================================
* bootstrap-tab.js v2.0.4
* bootstrap-tab.js v2.1.0
* http://twitter.github.com/bootstrap/javascript.html#tabs
* ========================================================
* Copyright 2012 Twitter, Inc.
@@ -1432,7 +1515,7 @@
/* TAB CLASS DEFINITION
* ==================== */
var Tab = function ( element ) {
var Tab = function (element) {
this.element = $(element)
}
@@ -1539,7 +1622,7 @@
})
}(window.jQuery);/* =============================================================
* bootstrap-typeahead.js v2.0.4
* bootstrap-typeahead.js v2.1.0
* http://twitter.github.com/bootstrap/javascript.html#typeahead
* =============================================================
* Copyright 2012 Twitter, Inc.
@@ -1617,17 +1700,23 @@
}
, lookup: function (event) {
var that = this
, items
, q
var items
this.query = this.$element.val()
if (!this.query) {
if (!this.query || this.query.length < this.options.minLength) {
return this.shown ? this.hide() : this
}
items = $.grep(this.source, function (item) {
items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source
return items ? this.process(items) : this
}
, process: function (items) {
var that = this
items = $.grep(items, function (item) {
return that.matcher(item)
})
@@ -1709,7 +1798,7 @@
.on('keyup', $.proxy(this.keyup, this))
if ($.browser.webkit || $.browser.msie) {
this.$element.on('keydown', $.proxy(this.keypress, this))
this.$element.on('keydown', $.proxy(this.keydown, this))
}
this.$menu
@@ -1717,6 +1806,40 @@
.on('mouseenter', 'li', $.proxy(this.mouseenter, this))
}
, move: function (e) {
if (!this.shown) return
switch(e.keyCode) {
case 9: // tab
case 13: // enter
case 27: // escape
e.preventDefault()
break
case 38: // up arrow
e.preventDefault()
this.prev()
break
case 40: // down arrow
e.preventDefault()
this.next()
break
}
e.stopPropagation()
}
, keydown: function (e) {
this.suppressKeyPressRepeat = !~$.inArray(e.keyCode, [40,38,9,13,27])
this.move(e)
}
, keypress: function (e) {
if (this.suppressKeyPressRepeat) return
this.move(e)
}
, keyup: function (e) {
switch(e.keyCode) {
case 40: // down arrow
@@ -1742,32 +1865,6 @@
e.preventDefault()
}
, keypress: function (e) {
if (!this.shown) return
switch(e.keyCode) {
case 9: // tab
case 13: // enter
case 27: // escape
e.preventDefault()
break
case 38: // up arrow
if (e.type != 'keydown') break
e.preventDefault()
this.prev()
break
case 40: // down arrow
if (e.type != 'keydown') break
e.preventDefault()
this.next()
break
}
e.stopPropagation()
}
, blur: function (e) {
var that = this
setTimeout(function () { that.hide() }, 150)
@@ -1805,12 +1902,13 @@
, items: 8
, menu: '<ul class="typeahead dropdown-menu"></ul>'
, item: '<li><a href="#"></a></li>'
, minLength: 1
}
$.fn.typeahead.Constructor = Typeahead
/* TYPEAHEAD DATA-API
/* TYPEAHEAD DATA-API
* ================== */
$(function () {
@@ -1822,4 +1920,108 @@
})
})
}(window.jQuery);
/* ==========================================================
* bootstrap-affix.js v2.1.0
* http://twitter.github.com/bootstrap/javascript.html#affix
* ==========================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* AFFIX CLASS DEFINITION
* ====================== */
var Affix = function (element, options) {
this.options = $.extend({}, $.fn.affix.defaults, options)
this.$window = $(window).on('scroll.affix.data-api', $.proxy(this.checkPosition, this))
this.$element = $(element)
this.checkPosition()
}
Affix.prototype.checkPosition = function () {
if (!this.$element.is(':visible')) return
var scrollHeight = $(document).height()
, scrollTop = this.$window.scrollTop()
, position = this.$element.offset()
, offset = this.options.offset
, offsetBottom = offset.bottom
, offsetTop = offset.top
, reset = 'affix affix-top affix-bottom'
, affix
if (typeof offset != 'object') offsetBottom = offsetTop = offset
if (typeof offsetTop == 'function') offsetTop = offset.top()
if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()
affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ?
false : offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ?
'bottom' : offsetTop != null && scrollTop <= offsetTop ?
'top' : false
if (this.affixed === affix) return
this.affixed = affix
this.unpin = affix == 'bottom' ? position.top - scrollTop : null
this.$element.removeClass(reset).addClass('affix' + (affix ? '-' + affix : ''))
}
/* AFFIX PLUGIN DEFINITION
* ======================= */
$.fn.affix = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('affix')
, options = typeof option == 'object' && option
if (!data) $this.data('affix', (data = new Affix(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.affix.Constructor = Affix
$.fn.affix.defaults = {
offset: 0
}
/* AFFIX DATA-API
* ============== */
$(window).on('load', function () {
$('[data-spy="affix"]').each(function () {
var $spy = $(this)
, data = $spy.data()
data.offset = data.offset || {}
data.offsetBottom && (data.offset.bottom = data.offsetBottom)
data.offsetTop && (data.offset.top = data.offsetTop)
$spy.affix(data)
})
})
}(window.jQuery);

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,123 @@
div.dataTables_length label {
float: left;
text-align: left;
}
div.dataTables_length select {
width: 75px;
}
div.dataTables_filter label {
float: right;
}
div.dataTables_info {
padding-top: 8px;
}
div.dataTables_paginate {
float: right;
margin: 0;
}
table.table {
clear: both;
margin-bottom: 6px !important;
max-width: none !important;
}
table.table thead .sorting,
table.table thead .sorting_asc,
table.table thead .sorting_desc,
table.table thead .sorting_asc_disabled,
table.table thead .sorting_desc_disabled {
cursor: pointer;
*cursor: hand;
}
table.table thead .sorting { background: url('images/sort_both.png') no-repeat center right; }
table.table thead .sorting_asc { background: url('images/sort_asc.png') no-repeat center right; }
table.table thead .sorting_desc { background: url('images/sort_desc.png') no-repeat center right; }
table.table thead .sorting_asc_disabled { background: url('images/sort_asc_disabled.png') no-repeat center right; }
table.table thead .sorting_desc_disabled { background: url('images/sort_desc_disabled.png') no-repeat center right; }
table.dataTable th:active {
outline: none;
}
table.dataTable tr.odd td.sorting_1 { background-color: #D3D6FF; }
table.dataTable tr.odd td.sorting_2 { background-color: #DADCFF; }
table.dataTable tr.odd td.sorting_3 { background-color: #E0E2FF; }
table.dataTable tr.even td.sorting_1 { background-color: #EAEBFF; }
table.dataTable tr.even td.sorting_2 { background-color: #F2F3FF; }
table.dataTable tr.even td.sorting_3 { background-color: #F9F9FF; }
/* Scrolling */
div.dataTables_scrollHead table {
margin-bottom: 0 !important;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
div.dataTables_scrollHead table thead tr:last-child th:first-child,
div.dataTables_scrollHead table thead tr:last-child td:first-child {
border-bottom-left-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
div.dataTables_scrollBody table {
border-top: none;
margin-bottom: 0 !important;
}
div.dataTables_scrollBody tbody tr:first-child th,
div.dataTables_scrollBody tbody tr:first-child td {
border-top: none;
}
div.dataTables_scrollFoot table {
border-top: none;
}
/* Active rows */
.table tbody tr.active td,
.table tbody tr.active th {
background-color: #08C;
color: white;
}
.table tbody tr.active:hover td,
.table tbody tr.active:hover th {
background-color: #0075b0 !important;
}
.table-striped tbody tr.active:nth-child(odd) td,
.table-striped tbody tr.active:nth-child(odd) th {
background-color: #017ebc;
}
/* Processing indicator */
.dataTables_processing {
position: absolute;
top: 50%;
left: 50%;
width: 250px;
height: 30px;
margin-left: -125px;
margin-top: -15px;
padding: 14px 0 2px 0;
border: 1px solid #ddd;
text-align: center;
color: #999;
font-size: 14px;
background-color: white;
}
/* Search boxes in the footer */
table tfoot input {
width: 100%;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,109 @@
/* Set the defaults for DataTables initialisation */
$.extend( true, $.fn.dataTable.defaults, {
"sDom": "<'row-fluid'<'span6'l><'span6'f>r>t<'row-fluid'<'span6'i><'span6'p>>",
"sPaginationType": "bootstrap",
"oLanguage": {
"sLengthMenu": "_MENU_ records per page"
}
} );
/* Default class modification */
$.extend( $.fn.dataTableExt.oStdClasses, {
"sWrapper": "dataTables_wrapper form-inline"
} );
/* API method to get paging information */
$.fn.dataTableExt.oApi.fnPagingInfo = function ( oSettings )
{
return {
"iStart": oSettings._iDisplayStart,
"iEnd": oSettings.fnDisplayEnd(),
"iLength": oSettings._iDisplayLength,
"iTotal": oSettings.fnRecordsTotal(),
"iFilteredTotal": oSettings.fnRecordsDisplay(),
"iPage": oSettings._iDisplayLength === -1 ?
0 : Math.ceil( oSettings._iDisplayStart / oSettings._iDisplayLength ),
"iTotalPages": oSettings._iDisplayLength === -1 ?
0 : Math.ceil( oSettings.fnRecordsDisplay() / oSettings._iDisplayLength )
};
};
/* Bootstrap style pagination control */
$.extend( $.fn.dataTableExt.oPagination, {
"bootstrap": {
"fnInit": function( oSettings, nPaging, fnDraw ) {
var oLang = oSettings.oLanguage.oPaginate;
var fnClickHandler = function ( e ) {
e.preventDefault();
if ( oSettings.oApi._fnPageChange(oSettings, e.data.action) ) {
fnDraw( oSettings );
}
};
$(nPaging).addClass('pagination').append(
'<ul>'+
'<li class="prev disabled"><a href="#">&larr; '+oLang.sPrevious+'</a></li>'+
'<li class="next disabled"><a href="#">'+oLang.sNext+' &rarr; </a></li>'+
'</ul>'
);
var els = $('a', nPaging);
$(els[0]).bind( 'click.DT', { action: "previous" }, fnClickHandler );
$(els[1]).bind( 'click.DT', { action: "next" }, fnClickHandler );
},
"fnUpdate": function ( oSettings, fnDraw ) {
var iListLength = 5;
var oPaging = oSettings.oInstance.fnPagingInfo();
var an = oSettings.aanFeatures.p;
var i, ien, j, sClass, iStart, iEnd, iHalf=Math.floor(iListLength/2);
if ( oPaging.iTotalPages < iListLength) {
iStart = 1;
iEnd = oPaging.iTotalPages;
}
else if ( oPaging.iPage <= iHalf ) {
iStart = 1;
iEnd = iListLength;
} else if ( oPaging.iPage >= (oPaging.iTotalPages-iHalf) ) {
iStart = oPaging.iTotalPages - iListLength + 1;
iEnd = oPaging.iTotalPages;
} else {
iStart = oPaging.iPage - iHalf + 1;
iEnd = iStart + iListLength - 1;
}
for ( i=0, ien=an.length ; i<ien ; i++ ) {
// Remove the middle elements
$('li:gt(0)', an[i]).filter(':not(:last)').remove();
// Add the new list items and their event handlers
for ( j=iStart ; j<=iEnd ; j++ ) {
sClass = (j==oPaging.iPage+1) ? 'class="active"' : '';
$('<li '+sClass+'><a href="#">'+j+'</a></li>')
.insertBefore( $('li:last', an[i])[0] )
.bind('click', function (e) {
e.preventDefault();
oSettings._iDisplayStart = (parseInt($('a', this).text(),10)-1) * oPaging.iLength;
fnDraw( oSettings );
} );
}
// Add / remove disabled classes from the static elements
if ( oPaging.iPage === 0 ) {
$('li:first', an[i]).addClass('disabled');
} else {
$('li:first', an[i]).removeClass('disabled');
}
if ( oPaging.iPage === oPaging.iTotalPages-1 || oPaging.iTotalPages === 0 ) {
$('li:last', an[i]).addClass('disabled');
} else {
$('li:last', an[i]).removeClass('disabled');
}
}
}
}
} );

View File

@@ -0,0 +1,155 @@
/*
* File: jquery.dataTables.min.js
* Version: 1.9.4
* Author: Allan Jardine (www.sprymedia.co.uk)
* Info: www.datatables.net
*
* Copyright 2008-2012 Allan Jardine, all rights reserved.
*
* This source file is free software, under either the GPL v2 license or a
* BSD style license, available at:
* http://datatables.net/license_gpl2
* http://datatables.net/license_bsd
*
* This source file 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 license files for details.
*/
(function(X,l,n){var L=function(h){var j=function(e){function o(a,b){var c=j.defaults.columns,d=a.aoColumns.length,c=h.extend({},j.models.oColumn,c,{sSortingClass:a.oClasses.sSortable,sSortingClassJUI:a.oClasses.sSortJUI,nTh:b?b:l.createElement("th"),sTitle:c.sTitle?c.sTitle:b?b.innerHTML:"",aDataSort:c.aDataSort?c.aDataSort:[d],mData:c.mData?c.oDefaults:d});a.aoColumns.push(c);if(a.aoPreSearchCols[d]===n||null===a.aoPreSearchCols[d])a.aoPreSearchCols[d]=h.extend({},j.models.oSearch);else if(c=a.aoPreSearchCols[d],
c.bRegex===n&&(c.bRegex=!0),c.bSmart===n&&(c.bSmart=!0),c.bCaseInsensitive===n)c.bCaseInsensitive=!0;m(a,d,null)}function m(a,b,c){var d=a.aoColumns[b];c!==n&&null!==c&&(c.mDataProp&&!c.mData&&(c.mData=c.mDataProp),c.sType!==n&&(d.sType=c.sType,d._bAutoType=!1),h.extend(d,c),p(d,c,"sWidth","sWidthOrig"),c.iDataSort!==n&&(d.aDataSort=[c.iDataSort]),p(d,c,"aDataSort"));var i=d.mRender?Q(d.mRender):null,f=Q(d.mData);d.fnGetData=function(a,b){var c=f(a,b);return d.mRender&&b&&""!==b?i(c,b,a):c};d.fnSetData=
L(d.mData);a.oFeatures.bSort||(d.bSortable=!1);!d.bSortable||-1==h.inArray("asc",d.asSorting)&&-1==h.inArray("desc",d.asSorting)?(d.sSortingClass=a.oClasses.sSortableNone,d.sSortingClassJUI=""):-1==h.inArray("asc",d.asSorting)&&-1==h.inArray("desc",d.asSorting)?(d.sSortingClass=a.oClasses.sSortable,d.sSortingClassJUI=a.oClasses.sSortJUI):-1!=h.inArray("asc",d.asSorting)&&-1==h.inArray("desc",d.asSorting)?(d.sSortingClass=a.oClasses.sSortableAsc,d.sSortingClassJUI=a.oClasses.sSortJUIAscAllowed):-1==
h.inArray("asc",d.asSorting)&&-1!=h.inArray("desc",d.asSorting)&&(d.sSortingClass=a.oClasses.sSortableDesc,d.sSortingClassJUI=a.oClasses.sSortJUIDescAllowed)}function k(a){if(!1===a.oFeatures.bAutoWidth)return!1;da(a);for(var b=0,c=a.aoColumns.length;b<c;b++)a.aoColumns[b].nTh.style.width=a.aoColumns[b].sWidth}function G(a,b){var c=r(a,"bVisible");return"number"===typeof c[b]?c[b]:null}function R(a,b){var c=r(a,"bVisible"),c=h.inArray(b,c);return-1!==c?c:null}function t(a){return r(a,"bVisible").length}
function r(a,b){var c=[];h.map(a.aoColumns,function(a,i){a[b]&&c.push(i)});return c}function B(a){for(var b=j.ext.aTypes,c=b.length,d=0;d<c;d++){var i=b[d](a);if(null!==i)return i}return"string"}function u(a,b){for(var c=b.split(","),d=[],i=0,f=a.aoColumns.length;i<f;i++)for(var g=0;g<f;g++)if(a.aoColumns[i].sName==c[g]){d.push(g);break}return d}function M(a){for(var b="",c=0,d=a.aoColumns.length;c<d;c++)b+=a.aoColumns[c].sName+",";return b.length==d?"":b.slice(0,-1)}function ta(a,b,c,d){var i,f,
g,e,w;if(b)for(i=b.length-1;0<=i;i--){var j=b[i].aTargets;h.isArray(j)||D(a,1,"aTargets must be an array of targets, not a "+typeof j);f=0;for(g=j.length;f<g;f++)if("number"===typeof j[f]&&0<=j[f]){for(;a.aoColumns.length<=j[f];)o(a);d(j[f],b[i])}else if("number"===typeof j[f]&&0>j[f])d(a.aoColumns.length+j[f],b[i]);else if("string"===typeof j[f]){e=0;for(w=a.aoColumns.length;e<w;e++)("_all"==j[f]||h(a.aoColumns[e].nTh).hasClass(j[f]))&&d(e,b[i])}}if(c){i=0;for(a=c.length;i<a;i++)d(i,c[i])}}function H(a,
b){var c;c=h.isArray(b)?b.slice():h.extend(!0,{},b);var d=a.aoData.length,i=h.extend(!0,{},j.models.oRow);i._aData=c;a.aoData.push(i);for(var f,i=0,g=a.aoColumns.length;i<g;i++)c=a.aoColumns[i],"function"===typeof c.fnRender&&c.bUseRendered&&null!==c.mData?F(a,d,i,S(a,d,i)):F(a,d,i,v(a,d,i)),c._bAutoType&&"string"!=c.sType&&(f=v(a,d,i,"type"),null!==f&&""!==f&&(f=B(f),null===c.sType?c.sType=f:c.sType!=f&&"html"!=c.sType&&(c.sType="string")));a.aiDisplayMaster.push(d);a.oFeatures.bDeferRender||ea(a,
d);return d}function ua(a){var b,c,d,i,f,g,e;if(a.bDeferLoading||null===a.sAjaxSource)for(b=a.nTBody.firstChild;b;){if("TR"==b.nodeName.toUpperCase()){c=a.aoData.length;b._DT_RowIndex=c;a.aoData.push(h.extend(!0,{},j.models.oRow,{nTr:b}));a.aiDisplayMaster.push(c);f=b.firstChild;for(d=0;f;){g=f.nodeName.toUpperCase();if("TD"==g||"TH"==g)F(a,c,d,h.trim(f.innerHTML)),d++;f=f.nextSibling}}b=b.nextSibling}i=T(a);d=[];b=0;for(c=i.length;b<c;b++)for(f=i[b].firstChild;f;)g=f.nodeName.toUpperCase(),("TD"==
g||"TH"==g)&&d.push(f),f=f.nextSibling;c=0;for(i=a.aoColumns.length;c<i;c++){e=a.aoColumns[c];null===e.sTitle&&(e.sTitle=e.nTh.innerHTML);var w=e._bAutoType,o="function"===typeof e.fnRender,k=null!==e.sClass,n=e.bVisible,m,p;if(w||o||k||!n){g=0;for(b=a.aoData.length;g<b;g++)f=a.aoData[g],m=d[g*i+c],w&&"string"!=e.sType&&(p=v(a,g,c,"type"),""!==p&&(p=B(p),null===e.sType?e.sType=p:e.sType!=p&&"html"!=e.sType&&(e.sType="string"))),e.mRender?m.innerHTML=v(a,g,c,"display"):e.mData!==c&&(m.innerHTML=v(a,
g,c,"display")),o&&(p=S(a,g,c),m.innerHTML=p,e.bUseRendered&&F(a,g,c,p)),k&&(m.className+=" "+e.sClass),n?f._anHidden[c]=null:(f._anHidden[c]=m,m.parentNode.removeChild(m)),e.fnCreatedCell&&e.fnCreatedCell.call(a.oInstance,m,v(a,g,c,"display"),f._aData,g,c)}}if(0!==a.aoRowCreatedCallback.length){b=0;for(c=a.aoData.length;b<c;b++)f=a.aoData[b],A(a,"aoRowCreatedCallback",null,[f.nTr,f._aData,b])}}function I(a,b){return b._DT_RowIndex!==n?b._DT_RowIndex:null}function fa(a,b,c){for(var b=J(a,b),d=0,a=
a.aoColumns.length;d<a;d++)if(b[d]===c)return d;return-1}function Y(a,b,c,d){for(var i=[],f=0,g=d.length;f<g;f++)i.push(v(a,b,d[f],c));return i}function v(a,b,c,d){var i=a.aoColumns[c];if((c=i.fnGetData(a.aoData[b]._aData,d))===n)return a.iDrawError!=a.iDraw&&null===i.sDefaultContent&&(D(a,0,"Requested unknown parameter "+("function"==typeof i.mData?"{mData function}":"'"+i.mData+"'")+" from the data source for row "+b),a.iDrawError=a.iDraw),i.sDefaultContent;if(null===c&&null!==i.sDefaultContent)c=
i.sDefaultContent;else if("function"===typeof c)return c();return"display"==d&&null===c?"":c}function F(a,b,c,d){a.aoColumns[c].fnSetData(a.aoData[b]._aData,d)}function Q(a){if(null===a)return function(){return null};if("function"===typeof a)return function(b,d,i){return a(b,d,i)};if("string"===typeof a&&(-1!==a.indexOf(".")||-1!==a.indexOf("["))){var b=function(a,d,i){var f=i.split("."),g;if(""!==i){var e=0;for(g=f.length;e<g;e++){if(i=f[e].match(U)){f[e]=f[e].replace(U,"");""!==f[e]&&(a=a[f[e]]);
g=[];f.splice(0,e+1);for(var f=f.join("."),e=0,h=a.length;e<h;e++)g.push(b(a[e],d,f));a=i[0].substring(1,i[0].length-1);a=""===a?g:g.join(a);break}if(null===a||a[f[e]]===n)return n;a=a[f[e]]}}return a};return function(c,d){return b(c,d,a)}}return function(b){return b[a]}}function L(a){if(null===a)return function(){};if("function"===typeof a)return function(b,d){a(b,"set",d)};if("string"===typeof a&&(-1!==a.indexOf(".")||-1!==a.indexOf("["))){var b=function(a,d,i){var i=i.split("."),f,g,e=0;for(g=
i.length-1;e<g;e++){if(f=i[e].match(U)){i[e]=i[e].replace(U,"");a[i[e]]=[];f=i.slice();f.splice(0,e+1);g=f.join(".");for(var h=0,j=d.length;h<j;h++)f={},b(f,d[h],g),a[i[e]].push(f);return}if(null===a[i[e]]||a[i[e]]===n)a[i[e]]={};a=a[i[e]]}a[i[i.length-1].replace(U,"")]=d};return function(c,d){return b(c,d,a)}}return function(b,d){b[a]=d}}function Z(a){for(var b=[],c=a.aoData.length,d=0;d<c;d++)b.push(a.aoData[d]._aData);return b}function ga(a){a.aoData.splice(0,a.aoData.length);a.aiDisplayMaster.splice(0,
a.aiDisplayMaster.length);a.aiDisplay.splice(0,a.aiDisplay.length);y(a)}function ha(a,b){for(var c=-1,d=0,i=a.length;d<i;d++)a[d]==b?c=d:a[d]>b&&a[d]--; -1!=c&&a.splice(c,1)}function S(a,b,c){var d=a.aoColumns[c];return d.fnRender({iDataRow:b,iDataColumn:c,oSettings:a,aData:a.aoData[b]._aData,mDataProp:d.mData},v(a,b,c,"display"))}function ea(a,b){var c=a.aoData[b],d;if(null===c.nTr){c.nTr=l.createElement("tr");c.nTr._DT_RowIndex=b;c._aData.DT_RowId&&(c.nTr.id=c._aData.DT_RowId);c._aData.DT_RowClass&&
(c.nTr.className=c._aData.DT_RowClass);for(var i=0,f=a.aoColumns.length;i<f;i++){var g=a.aoColumns[i];d=l.createElement(g.sCellType);d.innerHTML="function"===typeof g.fnRender&&(!g.bUseRendered||null===g.mData)?S(a,b,i):v(a,b,i,"display");null!==g.sClass&&(d.className=g.sClass);g.bVisible?(c.nTr.appendChild(d),c._anHidden[i]=null):c._anHidden[i]=d;g.fnCreatedCell&&g.fnCreatedCell.call(a.oInstance,d,v(a,b,i,"display"),c._aData,b,i)}A(a,"aoRowCreatedCallback",null,[c.nTr,c._aData,b])}}function va(a){var b,
c,d;if(0!==h("th, td",a.nTHead).length){b=0;for(d=a.aoColumns.length;b<d;b++)if(c=a.aoColumns[b].nTh,c.setAttribute("role","columnheader"),a.aoColumns[b].bSortable&&(c.setAttribute("tabindex",a.iTabIndex),c.setAttribute("aria-controls",a.sTableId)),null!==a.aoColumns[b].sClass&&h(c).addClass(a.aoColumns[b].sClass),a.aoColumns[b].sTitle!=c.innerHTML)c.innerHTML=a.aoColumns[b].sTitle}else{var i=l.createElement("tr");b=0;for(d=a.aoColumns.length;b<d;b++)c=a.aoColumns[b].nTh,c.innerHTML=a.aoColumns[b].sTitle,
c.setAttribute("tabindex","0"),null!==a.aoColumns[b].sClass&&h(c).addClass(a.aoColumns[b].sClass),i.appendChild(c);h(a.nTHead).html("")[0].appendChild(i);V(a.aoHeader,a.nTHead)}h(a.nTHead).children("tr").attr("role","row");if(a.bJUI){b=0;for(d=a.aoColumns.length;b<d;b++){c=a.aoColumns[b].nTh;i=l.createElement("div");i.className=a.oClasses.sSortJUIWrapper;h(c).contents().appendTo(i);var f=l.createElement("span");f.className=a.oClasses.sSortIcon;i.appendChild(f);c.appendChild(i)}}if(a.oFeatures.bSort)for(b=
0;b<a.aoColumns.length;b++)!1!==a.aoColumns[b].bSortable?ia(a,a.aoColumns[b].nTh,b):h(a.aoColumns[b].nTh).addClass(a.oClasses.sSortableNone);""!==a.oClasses.sFooterTH&&h(a.nTFoot).children("tr").children("th").addClass(a.oClasses.sFooterTH);if(null!==a.nTFoot){c=N(a,null,a.aoFooter);b=0;for(d=a.aoColumns.length;b<d;b++)c[b]&&(a.aoColumns[b].nTf=c[b],a.aoColumns[b].sClass&&h(c[b]).addClass(a.aoColumns[b].sClass))}}function W(a,b,c){var d,i,f,g=[],e=[],h=a.aoColumns.length,j;c===n&&(c=!1);d=0;for(i=
b.length;d<i;d++){g[d]=b[d].slice();g[d].nTr=b[d].nTr;for(f=h-1;0<=f;f--)!a.aoColumns[f].bVisible&&!c&&g[d].splice(f,1);e.push([])}d=0;for(i=g.length;d<i;d++){if(a=g[d].nTr)for(;f=a.firstChild;)a.removeChild(f);f=0;for(b=g[d].length;f<b;f++)if(j=h=1,e[d][f]===n){a.appendChild(g[d][f].cell);for(e[d][f]=1;g[d+h]!==n&&g[d][f].cell==g[d+h][f].cell;)e[d+h][f]=1,h++;for(;g[d][f+j]!==n&&g[d][f].cell==g[d][f+j].cell;){for(c=0;c<h;c++)e[d+c][f+j]=1;j++}g[d][f].cell.rowSpan=h;g[d][f].cell.colSpan=j}}}function x(a){var b=
A(a,"aoPreDrawCallback","preDraw",[a]);if(-1!==h.inArray(!1,b))E(a,!1);else{var c,d,b=[],i=0,f=a.asStripeClasses.length;c=a.aoOpenRows.length;a.bDrawing=!0;a.iInitDisplayStart!==n&&-1!=a.iInitDisplayStart&&(a._iDisplayStart=a.oFeatures.bServerSide?a.iInitDisplayStart:a.iInitDisplayStart>=a.fnRecordsDisplay()?0:a.iInitDisplayStart,a.iInitDisplayStart=-1,y(a));if(a.bDeferLoading)a.bDeferLoading=!1,a.iDraw++;else if(a.oFeatures.bServerSide){if(!a.bDestroying&&!wa(a))return}else a.iDraw++;if(0!==a.aiDisplay.length){var g=
a._iDisplayStart;d=a._iDisplayEnd;a.oFeatures.bServerSide&&(g=0,d=a.aoData.length);for(;g<d;g++){var e=a.aoData[a.aiDisplay[g]];null===e.nTr&&ea(a,a.aiDisplay[g]);var j=e.nTr;if(0!==f){var o=a.asStripeClasses[i%f];e._sRowStripe!=o&&(h(j).removeClass(e._sRowStripe).addClass(o),e._sRowStripe=o)}A(a,"aoRowCallback",null,[j,a.aoData[a.aiDisplay[g]]._aData,i,g]);b.push(j);i++;if(0!==c)for(e=0;e<c;e++)if(j==a.aoOpenRows[e].nParent){b.push(a.aoOpenRows[e].nTr);break}}}else b[0]=l.createElement("tr"),a.asStripeClasses[0]&&
(b[0].className=a.asStripeClasses[0]),c=a.oLanguage,f=c.sZeroRecords,1==a.iDraw&&null!==a.sAjaxSource&&!a.oFeatures.bServerSide?f=c.sLoadingRecords:c.sEmptyTable&&0===a.fnRecordsTotal()&&(f=c.sEmptyTable),c=l.createElement("td"),c.setAttribute("valign","top"),c.colSpan=t(a),c.className=a.oClasses.sRowEmpty,c.innerHTML=ja(a,f),b[i].appendChild(c);A(a,"aoHeaderCallback","header",[h(a.nTHead).children("tr")[0],Z(a),a._iDisplayStart,a.fnDisplayEnd(),a.aiDisplay]);A(a,"aoFooterCallback","footer",[h(a.nTFoot).children("tr")[0],
Z(a),a._iDisplayStart,a.fnDisplayEnd(),a.aiDisplay]);i=l.createDocumentFragment();c=l.createDocumentFragment();if(a.nTBody){f=a.nTBody.parentNode;c.appendChild(a.nTBody);if(!a.oScroll.bInfinite||!a._bInitComplete||a.bSorted||a.bFiltered)for(;c=a.nTBody.firstChild;)a.nTBody.removeChild(c);c=0;for(d=b.length;c<d;c++)i.appendChild(b[c]);a.nTBody.appendChild(i);null!==f&&f.appendChild(a.nTBody)}A(a,"aoDrawCallback","draw",[a]);a.bSorted=!1;a.bFiltered=!1;a.bDrawing=!1;a.oFeatures.bServerSide&&(E(a,!1),
a._bInitComplete||$(a))}}function aa(a){a.oFeatures.bSort?O(a,a.oPreviousSearch):a.oFeatures.bFilter?K(a,a.oPreviousSearch):(y(a),x(a))}function xa(a){var b=h("<div></div>")[0];a.nTable.parentNode.insertBefore(b,a.nTable);a.nTableWrapper=h('<div id="'+a.sTableId+'_wrapper" class="'+a.oClasses.sWrapper+'" role="grid"></div>')[0];a.nTableReinsertBefore=a.nTable.nextSibling;for(var c=a.nTableWrapper,d=a.sDom.split(""),i,f,g,e,w,o,k,m=0;m<d.length;m++){f=0;g=d[m];if("<"==g){e=h("<div></div>")[0];w=d[m+
1];if("'"==w||'"'==w){o="";for(k=2;d[m+k]!=w;)o+=d[m+k],k++;"H"==o?o=a.oClasses.sJUIHeader:"F"==o&&(o=a.oClasses.sJUIFooter);-1!=o.indexOf(".")?(w=o.split("."),e.id=w[0].substr(1,w[0].length-1),e.className=w[1]):"#"==o.charAt(0)?e.id=o.substr(1,o.length-1):e.className=o;m+=k}c.appendChild(e);c=e}else if(">"==g)c=c.parentNode;else if("l"==g&&a.oFeatures.bPaginate&&a.oFeatures.bLengthChange)i=ya(a),f=1;else if("f"==g&&a.oFeatures.bFilter)i=za(a),f=1;else if("r"==g&&a.oFeatures.bProcessing)i=Aa(a),f=
1;else if("t"==g)i=Ba(a),f=1;else if("i"==g&&a.oFeatures.bInfo)i=Ca(a),f=1;else if("p"==g&&a.oFeatures.bPaginate)i=Da(a),f=1;else if(0!==j.ext.aoFeatures.length){e=j.ext.aoFeatures;k=0;for(w=e.length;k<w;k++)if(g==e[k].cFeature){(i=e[k].fnInit(a))&&(f=1);break}}1==f&&null!==i&&("object"!==typeof a.aanFeatures[g]&&(a.aanFeatures[g]=[]),a.aanFeatures[g].push(i),c.appendChild(i))}b.parentNode.replaceChild(a.nTableWrapper,b)}function V(a,b){var c=h(b).children("tr"),d,i,f,g,e,j,o,k,m,p;a.splice(0,a.length);
f=0;for(j=c.length;f<j;f++)a.push([]);f=0;for(j=c.length;f<j;f++){d=c[f];for(i=d.firstChild;i;){if("TD"==i.nodeName.toUpperCase()||"TH"==i.nodeName.toUpperCase()){k=1*i.getAttribute("colspan");m=1*i.getAttribute("rowspan");k=!k||0===k||1===k?1:k;m=!m||0===m||1===m?1:m;g=0;for(e=a[f];e[g];)g++;o=g;p=1===k?!0:!1;for(e=0;e<k;e++)for(g=0;g<m;g++)a[f+g][o+e]={cell:i,unique:p},a[f+g].nTr=d}i=i.nextSibling}}}function N(a,b,c){var d=[];c||(c=a.aoHeader,b&&(c=[],V(c,b)));for(var b=0,i=c.length;b<i;b++)for(var f=
0,g=c[b].length;f<g;f++)if(c[b][f].unique&&(!d[f]||!a.bSortCellsTop))d[f]=c[b][f].cell;return d}function wa(a){if(a.bAjaxDataGet){a.iDraw++;E(a,!0);var b=Ea(a);ka(a,b);a.fnServerData.call(a.oInstance,a.sAjaxSource,b,function(b){Fa(a,b)},a);return!1}return!0}function Ea(a){var b=a.aoColumns.length,c=[],d,i,f,g;c.push({name:"sEcho",value:a.iDraw});c.push({name:"iColumns",value:b});c.push({name:"sColumns",value:M(a)});c.push({name:"iDisplayStart",value:a._iDisplayStart});c.push({name:"iDisplayLength",
value:!1!==a.oFeatures.bPaginate?a._iDisplayLength:-1});for(f=0;f<b;f++)d=a.aoColumns[f].mData,c.push({name:"mDataProp_"+f,value:"function"===typeof d?"function":d});if(!1!==a.oFeatures.bFilter){c.push({name:"sSearch",value:a.oPreviousSearch.sSearch});c.push({name:"bRegex",value:a.oPreviousSearch.bRegex});for(f=0;f<b;f++)c.push({name:"sSearch_"+f,value:a.aoPreSearchCols[f].sSearch}),c.push({name:"bRegex_"+f,value:a.aoPreSearchCols[f].bRegex}),c.push({name:"bSearchable_"+f,value:a.aoColumns[f].bSearchable})}if(!1!==
a.oFeatures.bSort){var e=0;d=null!==a.aaSortingFixed?a.aaSortingFixed.concat(a.aaSorting):a.aaSorting.slice();for(f=0;f<d.length;f++){i=a.aoColumns[d[f][0]].aDataSort;for(g=0;g<i.length;g++)c.push({name:"iSortCol_"+e,value:i[g]}),c.push({name:"sSortDir_"+e,value:d[f][1]}),e++}c.push({name:"iSortingCols",value:e});for(f=0;f<b;f++)c.push({name:"bSortable_"+f,value:a.aoColumns[f].bSortable})}return c}function ka(a,b){A(a,"aoServerParams","serverParams",[b])}function Fa(a,b){if(b.sEcho!==n){if(1*b.sEcho<
a.iDraw)return;a.iDraw=1*b.sEcho}(!a.oScroll.bInfinite||a.oScroll.bInfinite&&(a.bSorted||a.bFiltered))&&ga(a);a._iRecordsTotal=parseInt(b.iTotalRecords,10);a._iRecordsDisplay=parseInt(b.iTotalDisplayRecords,10);var c=M(a),c=b.sColumns!==n&&""!==c&&b.sColumns!=c,d;c&&(d=u(a,b.sColumns));for(var i=Q(a.sAjaxDataProp)(b),f=0,g=i.length;f<g;f++)if(c){for(var e=[],h=0,j=a.aoColumns.length;h<j;h++)e.push(i[f][d[h]]);H(a,e)}else H(a,i[f]);a.aiDisplay=a.aiDisplayMaster.slice();a.bAjaxDataGet=!1;x(a);a.bAjaxDataGet=
!0;E(a,!1)}function za(a){var b=a.oPreviousSearch,c=a.oLanguage.sSearch,c=-1!==c.indexOf("_INPUT_")?c.replace("_INPUT_",'<input type="text" />'):""===c?'<input type="text" />':c+' <input type="text" />',d=l.createElement("div");d.className=a.oClasses.sFilter;d.innerHTML="<label>"+c+"</label>";a.aanFeatures.f||(d.id=a.sTableId+"_filter");c=h('input[type="text"]',d);d._DT_Input=c[0];c.val(b.sSearch.replace('"',"&quot;"));c.bind("keyup.DT",function(){for(var c=a.aanFeatures.f,d=this.value===""?"":this.value,
g=0,e=c.length;g<e;g++)c[g]!=h(this).parents("div.dataTables_filter")[0]&&h(c[g]._DT_Input).val(d);d!=b.sSearch&&K(a,{sSearch:d,bRegex:b.bRegex,bSmart:b.bSmart,bCaseInsensitive:b.bCaseInsensitive})});c.attr("aria-controls",a.sTableId).bind("keypress.DT",function(a){if(a.keyCode==13)return false});return d}function K(a,b,c){var d=a.oPreviousSearch,i=a.aoPreSearchCols,f=function(a){d.sSearch=a.sSearch;d.bRegex=a.bRegex;d.bSmart=a.bSmart;d.bCaseInsensitive=a.bCaseInsensitive};if(a.oFeatures.bServerSide)f(b);
else{Ga(a,b.sSearch,c,b.bRegex,b.bSmart,b.bCaseInsensitive);f(b);for(b=0;b<a.aoPreSearchCols.length;b++)Ha(a,i[b].sSearch,b,i[b].bRegex,i[b].bSmart,i[b].bCaseInsensitive);Ia(a)}a.bFiltered=!0;h(a.oInstance).trigger("filter",a);a._iDisplayStart=0;y(a);x(a);la(a,0)}function Ia(a){for(var b=j.ext.afnFiltering,c=r(a,"bSearchable"),d=0,i=b.length;d<i;d++)for(var f=0,g=0,e=a.aiDisplay.length;g<e;g++){var h=a.aiDisplay[g-f];b[d](a,Y(a,h,"filter",c),h)||(a.aiDisplay.splice(g-f,1),f++)}}function Ha(a,b,c,
d,i,f){if(""!==b)for(var g=0,b=ma(b,d,i,f),d=a.aiDisplay.length-1;0<=d;d--)i=Ja(v(a,a.aiDisplay[d],c,"filter"),a.aoColumns[c].sType),b.test(i)||(a.aiDisplay.splice(d,1),g++)}function Ga(a,b,c,d,i,f){d=ma(b,d,i,f);i=a.oPreviousSearch;c||(c=0);0!==j.ext.afnFiltering.length&&(c=1);if(0>=b.length)a.aiDisplay.splice(0,a.aiDisplay.length),a.aiDisplay=a.aiDisplayMaster.slice();else if(a.aiDisplay.length==a.aiDisplayMaster.length||i.sSearch.length>b.length||1==c||0!==b.indexOf(i.sSearch)){a.aiDisplay.splice(0,
a.aiDisplay.length);la(a,1);for(b=0;b<a.aiDisplayMaster.length;b++)d.test(a.asDataSearch[b])&&a.aiDisplay.push(a.aiDisplayMaster[b])}else for(b=c=0;b<a.asDataSearch.length;b++)d.test(a.asDataSearch[b])||(a.aiDisplay.splice(b-c,1),c++)}function la(a,b){if(!a.oFeatures.bServerSide){a.asDataSearch=[];for(var c=r(a,"bSearchable"),d=1===b?a.aiDisplayMaster:a.aiDisplay,i=0,f=d.length;i<f;i++)a.asDataSearch[i]=na(a,Y(a,d[i],"filter",c))}}function na(a,b){var c=b.join(" ");-1!==c.indexOf("&")&&(c=h("<div>").html(c).text());
return c.replace(/[\n\r]/g," ")}function ma(a,b,c,d){if(c)return a=b?a.split(" "):oa(a).split(" "),a="^(?=.*?"+a.join(")(?=.*?")+").*$",RegExp(a,d?"i":"");a=b?a:oa(a);return RegExp(a,d?"i":"")}function Ja(a,b){return"function"===typeof j.ext.ofnSearch[b]?j.ext.ofnSearch[b](a):null===a?"":"html"==b?a.replace(/[\r\n]/g," ").replace(/<.*?>/g,""):"string"===typeof a?a.replace(/[\r\n]/g," "):a}function oa(a){return a.replace(RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\|\\$|\\^|\\-)","g"),
"\\$1")}function Ca(a){var b=l.createElement("div");b.className=a.oClasses.sInfo;a.aanFeatures.i||(a.aoDrawCallback.push({fn:Ka,sName:"information"}),b.id=a.sTableId+"_info");a.nTable.setAttribute("aria-describedby",a.sTableId+"_info");return b}function Ka(a){if(a.oFeatures.bInfo&&0!==a.aanFeatures.i.length){var b=a.oLanguage,c=a._iDisplayStart+1,d=a.fnDisplayEnd(),i=a.fnRecordsTotal(),f=a.fnRecordsDisplay(),g;g=0===f?b.sInfoEmpty:b.sInfo;f!=i&&(g+=" "+b.sInfoFiltered);g+=b.sInfoPostFix;g=ja(a,g);
null!==b.fnInfoCallback&&(g=b.fnInfoCallback.call(a.oInstance,a,c,d,i,f,g));a=a.aanFeatures.i;b=0;for(c=a.length;b<c;b++)h(a[b]).html(g)}}function ja(a,b){var c=a.fnFormatNumber(a._iDisplayStart+1),d=a.fnDisplayEnd(),d=a.fnFormatNumber(d),i=a.fnRecordsDisplay(),i=a.fnFormatNumber(i),f=a.fnRecordsTotal(),f=a.fnFormatNumber(f);a.oScroll.bInfinite&&(c=a.fnFormatNumber(1));return b.replace(/_START_/g,c).replace(/_END_/g,d).replace(/_TOTAL_/g,i).replace(/_MAX_/g,f)}function ba(a){var b,c,d=a.iInitDisplayStart;
if(!1===a.bInitialised)setTimeout(function(){ba(a)},200);else{xa(a);va(a);W(a,a.aoHeader);a.nTFoot&&W(a,a.aoFooter);E(a,!0);a.oFeatures.bAutoWidth&&da(a);b=0;for(c=a.aoColumns.length;b<c;b++)null!==a.aoColumns[b].sWidth&&(a.aoColumns[b].nTh.style.width=q(a.aoColumns[b].sWidth));a.oFeatures.bSort?O(a):a.oFeatures.bFilter?K(a,a.oPreviousSearch):(a.aiDisplay=a.aiDisplayMaster.slice(),y(a),x(a));null!==a.sAjaxSource&&!a.oFeatures.bServerSide?(c=[],ka(a,c),a.fnServerData.call(a.oInstance,a.sAjaxSource,
c,function(c){var f=a.sAjaxDataProp!==""?Q(a.sAjaxDataProp)(c):c;for(b=0;b<f.length;b++)H(a,f[b]);a.iInitDisplayStart=d;if(a.oFeatures.bSort)O(a);else{a.aiDisplay=a.aiDisplayMaster.slice();y(a);x(a)}E(a,false);$(a,c)},a)):a.oFeatures.bServerSide||(E(a,!1),$(a))}}function $(a,b){a._bInitComplete=!0;A(a,"aoInitComplete","init",[a,b])}function pa(a){var b=j.defaults.oLanguage;!a.sEmptyTable&&(a.sZeroRecords&&"No data available in table"===b.sEmptyTable)&&p(a,a,"sZeroRecords","sEmptyTable");!a.sLoadingRecords&&
(a.sZeroRecords&&"Loading..."===b.sLoadingRecords)&&p(a,a,"sZeroRecords","sLoadingRecords")}function ya(a){if(a.oScroll.bInfinite)return null;var b='<select size="1" '+('name="'+a.sTableId+'_length"')+">",c,d,i=a.aLengthMenu;if(2==i.length&&"object"===typeof i[0]&&"object"===typeof i[1]){c=0;for(d=i[0].length;c<d;c++)b+='<option value="'+i[0][c]+'">'+i[1][c]+"</option>"}else{c=0;for(d=i.length;c<d;c++)b+='<option value="'+i[c]+'">'+i[c]+"</option>"}b+="</select>";i=l.createElement("div");a.aanFeatures.l||
(i.id=a.sTableId+"_length");i.className=a.oClasses.sLength;i.innerHTML="<label>"+a.oLanguage.sLengthMenu.replace("_MENU_",b)+"</label>";h('select option[value="'+a._iDisplayLength+'"]',i).attr("selected",!0);h("select",i).bind("change.DT",function(){var b=h(this).val(),i=a.aanFeatures.l;c=0;for(d=i.length;c<d;c++)i[c]!=this.parentNode&&h("select",i[c]).val(b);a._iDisplayLength=parseInt(b,10);y(a);if(a.fnDisplayEnd()==a.fnRecordsDisplay()){a._iDisplayStart=a.fnDisplayEnd()-a._iDisplayLength;if(a._iDisplayStart<
0)a._iDisplayStart=0}if(a._iDisplayLength==-1)a._iDisplayStart=0;x(a)});h("select",i).attr("aria-controls",a.sTableId);return i}function y(a){a._iDisplayEnd=!1===a.oFeatures.bPaginate?a.aiDisplay.length:a._iDisplayStart+a._iDisplayLength>a.aiDisplay.length||-1==a._iDisplayLength?a.aiDisplay.length:a._iDisplayStart+a._iDisplayLength}function Da(a){if(a.oScroll.bInfinite)return null;var b=l.createElement("div");b.className=a.oClasses.sPaging+a.sPaginationType;j.ext.oPagination[a.sPaginationType].fnInit(a,
b,function(a){y(a);x(a)});a.aanFeatures.p||a.aoDrawCallback.push({fn:function(a){j.ext.oPagination[a.sPaginationType].fnUpdate(a,function(a){y(a);x(a)})},sName:"pagination"});return b}function qa(a,b){var c=a._iDisplayStart;if("number"===typeof b)a._iDisplayStart=b*a._iDisplayLength,a._iDisplayStart>a.fnRecordsDisplay()&&(a._iDisplayStart=0);else if("first"==b)a._iDisplayStart=0;else if("previous"==b)a._iDisplayStart=0<=a._iDisplayLength?a._iDisplayStart-a._iDisplayLength:0,0>a._iDisplayStart&&(a._iDisplayStart=
0);else if("next"==b)0<=a._iDisplayLength?a._iDisplayStart+a._iDisplayLength<a.fnRecordsDisplay()&&(a._iDisplayStart+=a._iDisplayLength):a._iDisplayStart=0;else if("last"==b)if(0<=a._iDisplayLength){var d=parseInt((a.fnRecordsDisplay()-1)/a._iDisplayLength,10)+1;a._iDisplayStart=(d-1)*a._iDisplayLength}else a._iDisplayStart=0;else D(a,0,"Unknown paging action: "+b);h(a.oInstance).trigger("page",a);return c!=a._iDisplayStart}function Aa(a){var b=l.createElement("div");a.aanFeatures.r||(b.id=a.sTableId+
"_processing");b.innerHTML=a.oLanguage.sProcessing;b.className=a.oClasses.sProcessing;a.nTable.parentNode.insertBefore(b,a.nTable);return b}function E(a,b){if(a.oFeatures.bProcessing)for(var c=a.aanFeatures.r,d=0,i=c.length;d<i;d++)c[d].style.visibility=b?"visible":"hidden";h(a.oInstance).trigger("processing",[a,b])}function Ba(a){if(""===a.oScroll.sX&&""===a.oScroll.sY)return a.nTable;var b=l.createElement("div"),c=l.createElement("div"),d=l.createElement("div"),i=l.createElement("div"),f=l.createElement("div"),
g=l.createElement("div"),e=a.nTable.cloneNode(!1),j=a.nTable.cloneNode(!1),o=a.nTable.getElementsByTagName("thead")[0],k=0===a.nTable.getElementsByTagName("tfoot").length?null:a.nTable.getElementsByTagName("tfoot")[0],m=a.oClasses;c.appendChild(d);f.appendChild(g);i.appendChild(a.nTable);b.appendChild(c);b.appendChild(i);d.appendChild(e);e.appendChild(o);null!==k&&(b.appendChild(f),g.appendChild(j),j.appendChild(k));b.className=m.sScrollWrapper;c.className=m.sScrollHead;d.className=m.sScrollHeadInner;
i.className=m.sScrollBody;f.className=m.sScrollFoot;g.className=m.sScrollFootInner;a.oScroll.bAutoCss&&(c.style.overflow="hidden",c.style.position="relative",f.style.overflow="hidden",i.style.overflow="auto");c.style.border="0";c.style.width="100%";f.style.border="0";d.style.width=""!==a.oScroll.sXInner?a.oScroll.sXInner:"100%";e.removeAttribute("id");e.style.marginLeft="0";a.nTable.style.marginLeft="0";null!==k&&(j.removeAttribute("id"),j.style.marginLeft="0");d=h(a.nTable).children("caption");0<
d.length&&(d=d[0],"top"===d._captionSide?e.appendChild(d):"bottom"===d._captionSide&&k&&j.appendChild(d));""!==a.oScroll.sX&&(c.style.width=q(a.oScroll.sX),i.style.width=q(a.oScroll.sX),null!==k&&(f.style.width=q(a.oScroll.sX)),h(i).scroll(function(){c.scrollLeft=this.scrollLeft;if(k!==null)f.scrollLeft=this.scrollLeft}));""!==a.oScroll.sY&&(i.style.height=q(a.oScroll.sY));a.aoDrawCallback.push({fn:La,sName:"scrolling"});a.oScroll.bInfinite&&h(i).scroll(function(){if(!a.bDrawing&&h(this).scrollTop()!==
0&&h(this).scrollTop()+h(this).height()>h(a.nTable).height()-a.oScroll.iLoadGap&&a.fnDisplayEnd()<a.fnRecordsDisplay()){qa(a,"next");y(a);x(a)}});a.nScrollHead=c;a.nScrollFoot=f;return b}function La(a){var b=a.nScrollHead.getElementsByTagName("div")[0],c=b.getElementsByTagName("table")[0],d=a.nTable.parentNode,i,f,g,e,j,o,k,m,p=[],n=[],l=null!==a.nTFoot?a.nScrollFoot.getElementsByTagName("div")[0]:null,R=null!==a.nTFoot?l.getElementsByTagName("table")[0]:null,r=a.oBrowser.bScrollOversize,s=function(a){k=
a.style;k.paddingTop="0";k.paddingBottom="0";k.borderTopWidth="0";k.borderBottomWidth="0";k.height=0};h(a.nTable).children("thead, tfoot").remove();i=h(a.nTHead).clone()[0];a.nTable.insertBefore(i,a.nTable.childNodes[0]);g=a.nTHead.getElementsByTagName("tr");e=i.getElementsByTagName("tr");null!==a.nTFoot&&(j=h(a.nTFoot).clone()[0],a.nTable.insertBefore(j,a.nTable.childNodes[1]),o=a.nTFoot.getElementsByTagName("tr"),j=j.getElementsByTagName("tr"));""===a.oScroll.sX&&(d.style.width="100%",b.parentNode.style.width=
"100%");var t=N(a,i);i=0;for(f=t.length;i<f;i++)m=G(a,i),t[i].style.width=a.aoColumns[m].sWidth;null!==a.nTFoot&&C(function(a){a.style.width=""},j);a.oScroll.bCollapse&&""!==a.oScroll.sY&&(d.style.height=d.offsetHeight+a.nTHead.offsetHeight+"px");i=h(a.nTable).outerWidth();if(""===a.oScroll.sX){if(a.nTable.style.width="100%",r&&(h("tbody",d).height()>d.offsetHeight||"scroll"==h(d).css("overflow-y")))a.nTable.style.width=q(h(a.nTable).outerWidth()-a.oScroll.iBarWidth)}else""!==a.oScroll.sXInner?a.nTable.style.width=
q(a.oScroll.sXInner):i==h(d).width()&&h(d).height()<h(a.nTable).height()?(a.nTable.style.width=q(i-a.oScroll.iBarWidth),h(a.nTable).outerWidth()>i-a.oScroll.iBarWidth&&(a.nTable.style.width=q(i))):a.nTable.style.width=q(i);i=h(a.nTable).outerWidth();C(s,e);C(function(a){p.push(q(h(a).width()))},e);C(function(a,b){a.style.width=p[b]},g);h(e).height(0);null!==a.nTFoot&&(C(s,j),C(function(a){n.push(q(h(a).width()))},j),C(function(a,b){a.style.width=n[b]},o),h(j).height(0));C(function(a,b){a.innerHTML=
"";a.style.width=p[b]},e);null!==a.nTFoot&&C(function(a,b){a.innerHTML="";a.style.width=n[b]},j);if(h(a.nTable).outerWidth()<i){g=d.scrollHeight>d.offsetHeight||"scroll"==h(d).css("overflow-y")?i+a.oScroll.iBarWidth:i;if(r&&(d.scrollHeight>d.offsetHeight||"scroll"==h(d).css("overflow-y")))a.nTable.style.width=q(g-a.oScroll.iBarWidth);d.style.width=q(g);a.nScrollHead.style.width=q(g);null!==a.nTFoot&&(a.nScrollFoot.style.width=q(g));""===a.oScroll.sX?D(a,1,"The table cannot fit into the current element which will cause column misalignment. The table has been drawn at its minimum possible width."):
""!==a.oScroll.sXInner&&D(a,1,"The table cannot fit into the current element which will cause column misalignment. Increase the sScrollXInner value or remove it to allow automatic calculation")}else d.style.width=q("100%"),a.nScrollHead.style.width=q("100%"),null!==a.nTFoot&&(a.nScrollFoot.style.width=q("100%"));""===a.oScroll.sY&&r&&(d.style.height=q(a.nTable.offsetHeight+a.oScroll.iBarWidth));""!==a.oScroll.sY&&a.oScroll.bCollapse&&(d.style.height=q(a.oScroll.sY),r=""!==a.oScroll.sX&&a.nTable.offsetWidth>
d.offsetWidth?a.oScroll.iBarWidth:0,a.nTable.offsetHeight<d.offsetHeight&&(d.style.height=q(a.nTable.offsetHeight+r)));r=h(a.nTable).outerWidth();c.style.width=q(r);b.style.width=q(r);c=h(a.nTable).height()>d.clientHeight||"scroll"==h(d).css("overflow-y");b.style.paddingRight=c?a.oScroll.iBarWidth+"px":"0px";null!==a.nTFoot&&(R.style.width=q(r),l.style.width=q(r),l.style.paddingRight=c?a.oScroll.iBarWidth+"px":"0px");h(d).scroll();if(a.bSorted||a.bFiltered)d.scrollTop=0}function C(a,b,c){for(var d=
0,i=0,f=b.length,g,e;i<f;){g=b[i].firstChild;for(e=c?c[i].firstChild:null;g;)1===g.nodeType&&(c?a(g,e,d):a(g,d),d++),g=g.nextSibling,e=c?e.nextSibling:null;i++}}function Ma(a,b){if(!a||null===a||""===a)return 0;b||(b=l.body);var c,d=l.createElement("div");d.style.width=q(a);b.appendChild(d);c=d.offsetWidth;b.removeChild(d);return c}function da(a){var b=0,c,d=0,i=a.aoColumns.length,f,e,j=h("th",a.nTHead),o=a.nTable.getAttribute("width");e=a.nTable.parentNode;for(f=0;f<i;f++)a.aoColumns[f].bVisible&&
(d++,null!==a.aoColumns[f].sWidth&&(c=Ma(a.aoColumns[f].sWidthOrig,e),null!==c&&(a.aoColumns[f].sWidth=q(c)),b++));if(i==j.length&&0===b&&d==i&&""===a.oScroll.sX&&""===a.oScroll.sY)for(f=0;f<a.aoColumns.length;f++)c=h(j[f]).width(),null!==c&&(a.aoColumns[f].sWidth=q(c));else{b=a.nTable.cloneNode(!1);f=a.nTHead.cloneNode(!0);d=l.createElement("tbody");c=l.createElement("tr");b.removeAttribute("id");b.appendChild(f);null!==a.nTFoot&&(b.appendChild(a.nTFoot.cloneNode(!0)),C(function(a){a.style.width=
""},b.getElementsByTagName("tr")));b.appendChild(d);d.appendChild(c);d=h("thead th",b);0===d.length&&(d=h("tbody tr:eq(0)>td",b));j=N(a,f);for(f=d=0;f<i;f++){var k=a.aoColumns[f];k.bVisible&&null!==k.sWidthOrig&&""!==k.sWidthOrig?j[f-d].style.width=q(k.sWidthOrig):k.bVisible?j[f-d].style.width="":d++}for(f=0;f<i;f++)a.aoColumns[f].bVisible&&(d=Na(a,f),null!==d&&(d=d.cloneNode(!0),""!==a.aoColumns[f].sContentPadding&&(d.innerHTML+=a.aoColumns[f].sContentPadding),c.appendChild(d)));e.appendChild(b);
""!==a.oScroll.sX&&""!==a.oScroll.sXInner?b.style.width=q(a.oScroll.sXInner):""!==a.oScroll.sX?(b.style.width="",h(b).width()<e.offsetWidth&&(b.style.width=q(e.offsetWidth))):""!==a.oScroll.sY?b.style.width=q(e.offsetWidth):o&&(b.style.width=q(o));b.style.visibility="hidden";Oa(a,b);i=h("tbody tr:eq(0)",b).children();0===i.length&&(i=N(a,h("thead",b)[0]));if(""!==a.oScroll.sX){for(f=d=e=0;f<a.aoColumns.length;f++)a.aoColumns[f].bVisible&&(e=null===a.aoColumns[f].sWidthOrig?e+h(i[d]).outerWidth():
e+(parseInt(a.aoColumns[f].sWidth.replace("px",""),10)+(h(i[d]).outerWidth()-h(i[d]).width())),d++);b.style.width=q(e);a.nTable.style.width=q(e)}for(f=d=0;f<a.aoColumns.length;f++)a.aoColumns[f].bVisible&&(e=h(i[d]).width(),null!==e&&0<e&&(a.aoColumns[f].sWidth=q(e)),d++);i=h(b).css("width");a.nTable.style.width=-1!==i.indexOf("%")?i:q(h(b).outerWidth());b.parentNode.removeChild(b)}o&&(a.nTable.style.width=q(o))}function Oa(a,b){""===a.oScroll.sX&&""!==a.oScroll.sY?(h(b).width(),b.style.width=q(h(b).outerWidth()-
a.oScroll.iBarWidth)):""!==a.oScroll.sX&&(b.style.width=q(h(b).outerWidth()))}function Na(a,b){var c=Pa(a,b);if(0>c)return null;if(null===a.aoData[c].nTr){var d=l.createElement("td");d.innerHTML=v(a,c,b,"");return d}return J(a,c)[b]}function Pa(a,b){for(var c=-1,d=-1,i=0;i<a.aoData.length;i++){var e=v(a,i,b,"display")+"",e=e.replace(/<.*?>/g,"");e.length>c&&(c=e.length,d=i)}return d}function q(a){if(null===a)return"0px";if("number"==typeof a)return 0>a?"0px":a+"px";var b=a.charCodeAt(a.length-1);
return 48>b||57<b?a:a+"px"}function Qa(){var a=l.createElement("p"),b=a.style;b.width="100%";b.height="200px";b.padding="0px";var c=l.createElement("div"),b=c.style;b.position="absolute";b.top="0px";b.left="0px";b.visibility="hidden";b.width="200px";b.height="150px";b.padding="0px";b.overflow="hidden";c.appendChild(a);l.body.appendChild(c);b=a.offsetWidth;c.style.overflow="scroll";a=a.offsetWidth;b==a&&(a=c.clientWidth);l.body.removeChild(c);return b-a}function O(a,b){var c,d,i,e,g,k,o=[],m=[],p=
j.ext.oSort,l=a.aoData,q=a.aoColumns,G=a.oLanguage.oAria;if(!a.oFeatures.bServerSide&&(0!==a.aaSorting.length||null!==a.aaSortingFixed)){o=null!==a.aaSortingFixed?a.aaSortingFixed.concat(a.aaSorting):a.aaSorting.slice();for(c=0;c<o.length;c++)if(d=o[c][0],i=R(a,d),e=a.aoColumns[d].sSortDataType,j.ext.afnSortData[e])if(g=j.ext.afnSortData[e].call(a.oInstance,a,d,i),g.length===l.length){i=0;for(e=l.length;i<e;i++)F(a,i,d,g[i])}else D(a,0,"Returned data sort array (col "+d+") is the wrong length");c=
0;for(d=a.aiDisplayMaster.length;c<d;c++)m[a.aiDisplayMaster[c]]=c;var r=o.length,s;c=0;for(d=l.length;c<d;c++)for(i=0;i<r;i++){s=q[o[i][0]].aDataSort;g=0;for(k=s.length;g<k;g++)e=q[s[g]].sType,e=p[(e?e:"string")+"-pre"],l[c]._aSortData[s[g]]=e?e(v(a,c,s[g],"sort")):v(a,c,s[g],"sort")}a.aiDisplayMaster.sort(function(a,b){var c,d,e,i,f;for(c=0;c<r;c++){f=q[o[c][0]].aDataSort;d=0;for(e=f.length;d<e;d++)if(i=q[f[d]].sType,i=p[(i?i:"string")+"-"+o[c][1]](l[a]._aSortData[f[d]],l[b]._aSortData[f[d]]),0!==
i)return i}return p["numeric-asc"](m[a],m[b])})}(b===n||b)&&!a.oFeatures.bDeferRender&&P(a);c=0;for(d=a.aoColumns.length;c<d;c++)e=q[c].sTitle.replace(/<.*?>/g,""),i=q[c].nTh,i.removeAttribute("aria-sort"),i.removeAttribute("aria-label"),q[c].bSortable?0<o.length&&o[0][0]==c?(i.setAttribute("aria-sort","asc"==o[0][1]?"ascending":"descending"),i.setAttribute("aria-label",e+("asc"==(q[c].asSorting[o[0][2]+1]?q[c].asSorting[o[0][2]+1]:q[c].asSorting[0])?G.sSortAscending:G.sSortDescending))):i.setAttribute("aria-label",
e+("asc"==q[c].asSorting[0]?G.sSortAscending:G.sSortDescending)):i.setAttribute("aria-label",e);a.bSorted=!0;h(a.oInstance).trigger("sort",a);a.oFeatures.bFilter?K(a,a.oPreviousSearch,1):(a.aiDisplay=a.aiDisplayMaster.slice(),a._iDisplayStart=0,y(a),x(a))}function ia(a,b,c,d){Ra(b,{},function(b){if(!1!==a.aoColumns[c].bSortable){var e=function(){var d,e;if(b.shiftKey){for(var f=!1,h=0;h<a.aaSorting.length;h++)if(a.aaSorting[h][0]==c){f=!0;d=a.aaSorting[h][0];e=a.aaSorting[h][2]+1;a.aoColumns[d].asSorting[e]?
(a.aaSorting[h][1]=a.aoColumns[d].asSorting[e],a.aaSorting[h][2]=e):a.aaSorting.splice(h,1);break}!1===f&&a.aaSorting.push([c,a.aoColumns[c].asSorting[0],0])}else 1==a.aaSorting.length&&a.aaSorting[0][0]==c?(d=a.aaSorting[0][0],e=a.aaSorting[0][2]+1,a.aoColumns[d].asSorting[e]||(e=0),a.aaSorting[0][1]=a.aoColumns[d].asSorting[e],a.aaSorting[0][2]=e):(a.aaSorting.splice(0,a.aaSorting.length),a.aaSorting.push([c,a.aoColumns[c].asSorting[0],0]));O(a)};a.oFeatures.bProcessing?(E(a,!0),setTimeout(function(){e();
a.oFeatures.bServerSide||E(a,!1)},0)):e();"function"==typeof d&&d(a)}})}function P(a){var b,c,d,e,f,g=a.aoColumns.length,j=a.oClasses;for(b=0;b<g;b++)a.aoColumns[b].bSortable&&h(a.aoColumns[b].nTh).removeClass(j.sSortAsc+" "+j.sSortDesc+" "+a.aoColumns[b].sSortingClass);c=null!==a.aaSortingFixed?a.aaSortingFixed.concat(a.aaSorting):a.aaSorting.slice();for(b=0;b<a.aoColumns.length;b++)if(a.aoColumns[b].bSortable){f=a.aoColumns[b].sSortingClass;e=-1;for(d=0;d<c.length;d++)if(c[d][0]==b){f="asc"==c[d][1]?
j.sSortAsc:j.sSortDesc;e=d;break}h(a.aoColumns[b].nTh).addClass(f);a.bJUI&&(f=h("span."+j.sSortIcon,a.aoColumns[b].nTh),f.removeClass(j.sSortJUIAsc+" "+j.sSortJUIDesc+" "+j.sSortJUI+" "+j.sSortJUIAscAllowed+" "+j.sSortJUIDescAllowed),f.addClass(-1==e?a.aoColumns[b].sSortingClassJUI:"asc"==c[e][1]?j.sSortJUIAsc:j.sSortJUIDesc))}else h(a.aoColumns[b].nTh).addClass(a.aoColumns[b].sSortingClass);f=j.sSortColumn;if(a.oFeatures.bSort&&a.oFeatures.bSortClasses){a=J(a);e=[];for(b=0;b<g;b++)e.push("");b=0;
for(d=1;b<c.length;b++)j=parseInt(c[b][0],10),e[j]=f+d,3>d&&d++;f=RegExp(f+"[123]");var o;b=0;for(c=a.length;b<c;b++)j=b%g,d=a[b].className,o=e[j],j=d.replace(f,o),j!=d?a[b].className=h.trim(j):0<o.length&&-1==d.indexOf(o)&&(a[b].className=d+" "+o)}}function ra(a){if(a.oFeatures.bStateSave&&!a.bDestroying){var b,c;b=a.oScroll.bInfinite;var d={iCreate:(new Date).getTime(),iStart:b?0:a._iDisplayStart,iEnd:b?a._iDisplayLength:a._iDisplayEnd,iLength:a._iDisplayLength,aaSorting:h.extend(!0,[],a.aaSorting),
oSearch:h.extend(!0,{},a.oPreviousSearch),aoSearchCols:h.extend(!0,[],a.aoPreSearchCols),abVisCols:[]};b=0;for(c=a.aoColumns.length;b<c;b++)d.abVisCols.push(a.aoColumns[b].bVisible);A(a,"aoStateSaveParams","stateSaveParams",[a,d]);a.fnStateSave.call(a.oInstance,a,d)}}function Sa(a,b){if(a.oFeatures.bStateSave){var c=a.fnStateLoad.call(a.oInstance,a);if(c){var d=A(a,"aoStateLoadParams","stateLoadParams",[a,c]);if(-1===h.inArray(!1,d)){a.oLoadedState=h.extend(!0,{},c);a._iDisplayStart=c.iStart;a.iInitDisplayStart=
c.iStart;a._iDisplayEnd=c.iEnd;a._iDisplayLength=c.iLength;a.aaSorting=c.aaSorting.slice();a.saved_aaSorting=c.aaSorting.slice();h.extend(a.oPreviousSearch,c.oSearch);h.extend(!0,a.aoPreSearchCols,c.aoSearchCols);b.saved_aoColumns=[];for(d=0;d<c.abVisCols.length;d++)b.saved_aoColumns[d]={},b.saved_aoColumns[d].bVisible=c.abVisCols[d];A(a,"aoStateLoaded","stateLoaded",[a,c])}}}}function s(a){for(var b=0;b<j.settings.length;b++)if(j.settings[b].nTable===a)return j.settings[b];return null}function T(a){for(var b=
[],a=a.aoData,c=0,d=a.length;c<d;c++)null!==a[c].nTr&&b.push(a[c].nTr);return b}function J(a,b){var c=[],d,e,f,g,h,j;e=0;var o=a.aoData.length;b!==n&&(e=b,o=b+1);for(f=e;f<o;f++)if(j=a.aoData[f],null!==j.nTr){e=[];for(d=j.nTr.firstChild;d;)g=d.nodeName.toLowerCase(),("td"==g||"th"==g)&&e.push(d),d=d.nextSibling;g=d=0;for(h=a.aoColumns.length;g<h;g++)a.aoColumns[g].bVisible?c.push(e[g-d]):(c.push(j._anHidden[g]),d++)}return c}function D(a,b,c){a=null===a?"DataTables warning: "+c:"DataTables warning (table id = '"+
a.sTableId+"'): "+c;if(0===b)if("alert"==j.ext.sErrMode)alert(a);else throw Error(a);else X.console&&console.log&&console.log(a)}function p(a,b,c,d){d===n&&(d=c);b[c]!==n&&(a[d]=b[c])}function Ta(a,b){var c,d;for(d in b)b.hasOwnProperty(d)&&(c=b[d],"object"===typeof e[d]&&null!==c&&!1===h.isArray(c)?h.extend(!0,a[d],c):a[d]=c);return a}function Ra(a,b,c){h(a).bind("click.DT",b,function(b){a.blur();c(b)}).bind("keypress.DT",b,function(a){13===a.which&&c(a)}).bind("selectstart.DT",function(){return!1})}
function z(a,b,c,d){c&&a[b].push({fn:c,sName:d})}function A(a,b,c,d){for(var b=a[b],e=[],f=b.length-1;0<=f;f--)e.push(b[f].fn.apply(a.oInstance,d));null!==c&&h(a.oInstance).trigger(c,d);return e}function Ua(a){var b=h('<div style="position:absolute; top:0; left:0; height:1px; width:1px; overflow:hidden"><div style="position:absolute; top:1px; left:1px; width:100px; overflow:scroll;"><div id="DT_BrowserTest" style="width:100%; height:10px;"></div></div></div>')[0];l.body.appendChild(b);a.oBrowser.bScrollOversize=
100===h("#DT_BrowserTest",b)[0].offsetWidth?!0:!1;l.body.removeChild(b)}function Va(a){return function(){var b=[s(this[j.ext.iApiIndex])].concat(Array.prototype.slice.call(arguments));return j.ext.oApi[a].apply(this,b)}}var U=/\[.*?\]$/,Wa=X.JSON?JSON.stringify:function(a){var b=typeof a;if("object"!==b||null===a)return"string"===b&&(a='"'+a+'"'),a+"";var c,d,e=[],f=h.isArray(a);for(c in a)d=a[c],b=typeof d,"string"===b?d='"'+d+'"':"object"===b&&null!==d&&(d=Wa(d)),e.push((f?"":'"'+c+'":')+d);return(f?
"[":"{")+e+(f?"]":"}")};this.$=function(a,b){var c,d,e=[],f;d=s(this[j.ext.iApiIndex]);var g=d.aoData,o=d.aiDisplay,k=d.aiDisplayMaster;b||(b={});b=h.extend({},{filter:"none",order:"current",page:"all"},b);if("current"==b.page){c=d._iDisplayStart;for(d=d.fnDisplayEnd();c<d;c++)(f=g[o[c]].nTr)&&e.push(f)}else if("current"==b.order&&"none"==b.filter){c=0;for(d=k.length;c<d;c++)(f=g[k[c]].nTr)&&e.push(f)}else if("current"==b.order&&"applied"==b.filter){c=0;for(d=o.length;c<d;c++)(f=g[o[c]].nTr)&&e.push(f)}else if("original"==
b.order&&"none"==b.filter){c=0;for(d=g.length;c<d;c++)(f=g[c].nTr)&&e.push(f)}else if("original"==b.order&&"applied"==b.filter){c=0;for(d=g.length;c<d;c++)f=g[c].nTr,-1!==h.inArray(c,o)&&f&&e.push(f)}else D(d,1,"Unknown selection options");e=h(e);c=e.filter(a);e=e.find(a);return h([].concat(h.makeArray(c),h.makeArray(e)))};this._=function(a,b){var c=[],d,e,f=this.$(a,b);d=0;for(e=f.length;d<e;d++)c.push(this.fnGetData(f[d]));return c};this.fnAddData=function(a,b){if(0===a.length)return[];var c=[],
d,e=s(this[j.ext.iApiIndex]);if("object"===typeof a[0]&&null!==a[0])for(var f=0;f<a.length;f++){d=H(e,a[f]);if(-1==d)return c;c.push(d)}else{d=H(e,a);if(-1==d)return c;c.push(d)}e.aiDisplay=e.aiDisplayMaster.slice();(b===n||b)&&aa(e);return c};this.fnAdjustColumnSizing=function(a){var b=s(this[j.ext.iApiIndex]);k(b);a===n||a?this.fnDraw(!1):(""!==b.oScroll.sX||""!==b.oScroll.sY)&&this.oApi._fnScrollDraw(b)};this.fnClearTable=function(a){var b=s(this[j.ext.iApiIndex]);ga(b);(a===n||a)&&x(b)};this.fnClose=
function(a){for(var b=s(this[j.ext.iApiIndex]),c=0;c<b.aoOpenRows.length;c++)if(b.aoOpenRows[c].nParent==a)return(a=b.aoOpenRows[c].nTr.parentNode)&&a.removeChild(b.aoOpenRows[c].nTr),b.aoOpenRows.splice(c,1),0;return 1};this.fnDeleteRow=function(a,b,c){var d=s(this[j.ext.iApiIndex]),e,f,a="object"===typeof a?I(d,a):a,g=d.aoData.splice(a,1);e=0;for(f=d.aoData.length;e<f;e++)null!==d.aoData[e].nTr&&(d.aoData[e].nTr._DT_RowIndex=e);e=h.inArray(a,d.aiDisplay);d.asDataSearch.splice(e,1);ha(d.aiDisplayMaster,
a);ha(d.aiDisplay,a);"function"===typeof b&&b.call(this,d,g);d._iDisplayStart>=d.fnRecordsDisplay()&&(d._iDisplayStart-=d._iDisplayLength,0>d._iDisplayStart&&(d._iDisplayStart=0));if(c===n||c)y(d),x(d);return g};this.fnDestroy=function(a){var b=s(this[j.ext.iApiIndex]),c=b.nTableWrapper.parentNode,d=b.nTBody,i,f,a=a===n?!1:a;b.bDestroying=!0;A(b,"aoDestroyCallback","destroy",[b]);if(!a){i=0;for(f=b.aoColumns.length;i<f;i++)!1===b.aoColumns[i].bVisible&&this.fnSetColumnVis(i,!0)}h(b.nTableWrapper).find("*").andSelf().unbind(".DT");
h("tbody>tr>td."+b.oClasses.sRowEmpty,b.nTable).parent().remove();b.nTable!=b.nTHead.parentNode&&(h(b.nTable).children("thead").remove(),b.nTable.appendChild(b.nTHead));b.nTFoot&&b.nTable!=b.nTFoot.parentNode&&(h(b.nTable).children("tfoot").remove(),b.nTable.appendChild(b.nTFoot));b.nTable.parentNode.removeChild(b.nTable);h(b.nTableWrapper).remove();b.aaSorting=[];b.aaSortingFixed=[];P(b);h(T(b)).removeClass(b.asStripeClasses.join(" "));h("th, td",b.nTHead).removeClass([b.oClasses.sSortable,b.oClasses.sSortableAsc,
b.oClasses.sSortableDesc,b.oClasses.sSortableNone].join(" "));b.bJUI&&(h("th span."+b.oClasses.sSortIcon+", td span."+b.oClasses.sSortIcon,b.nTHead).remove(),h("th, td",b.nTHead).each(function(){var a=h("div."+b.oClasses.sSortJUIWrapper,this),c=a.contents();h(this).append(c);a.remove()}));!a&&b.nTableReinsertBefore?c.insertBefore(b.nTable,b.nTableReinsertBefore):a||c.appendChild(b.nTable);i=0;for(f=b.aoData.length;i<f;i++)null!==b.aoData[i].nTr&&d.appendChild(b.aoData[i].nTr);!0===b.oFeatures.bAutoWidth&&
(b.nTable.style.width=q(b.sDestroyWidth));if(f=b.asDestroyStripes.length){a=h(d).children("tr");for(i=0;i<f;i++)a.filter(":nth-child("+f+"n + "+i+")").addClass(b.asDestroyStripes[i])}i=0;for(f=j.settings.length;i<f;i++)j.settings[i]==b&&j.settings.splice(i,1);e=b=null};this.fnDraw=function(a){var b=s(this[j.ext.iApiIndex]);!1===a?(y(b),x(b)):aa(b)};this.fnFilter=function(a,b,c,d,e,f){var g=s(this[j.ext.iApiIndex]);if(g.oFeatures.bFilter){if(c===n||null===c)c=!1;if(d===n||null===d)d=!0;if(e===n||null===
e)e=!0;if(f===n||null===f)f=!0;if(b===n||null===b){if(K(g,{sSearch:a+"",bRegex:c,bSmart:d,bCaseInsensitive:f},1),e&&g.aanFeatures.f){b=g.aanFeatures.f;c=0;for(d=b.length;c<d;c++)try{b[c]._DT_Input!=l.activeElement&&h(b[c]._DT_Input).val(a)}catch(o){h(b[c]._DT_Input).val(a)}}}else h.extend(g.aoPreSearchCols[b],{sSearch:a+"",bRegex:c,bSmart:d,bCaseInsensitive:f}),K(g,g.oPreviousSearch,1)}};this.fnGetData=function(a,b){var c=s(this[j.ext.iApiIndex]);if(a!==n){var d=a;if("object"===typeof a){var e=a.nodeName.toLowerCase();
"tr"===e?d=I(c,a):"td"===e&&(d=I(c,a.parentNode),b=fa(c,d,a))}return b!==n?v(c,d,b,""):c.aoData[d]!==n?c.aoData[d]._aData:null}return Z(c)};this.fnGetNodes=function(a){var b=s(this[j.ext.iApiIndex]);return a!==n?b.aoData[a]!==n?b.aoData[a].nTr:null:T(b)};this.fnGetPosition=function(a){var b=s(this[j.ext.iApiIndex]),c=a.nodeName.toUpperCase();return"TR"==c?I(b,a):"TD"==c||"TH"==c?(c=I(b,a.parentNode),a=fa(b,c,a),[c,R(b,a),a]):null};this.fnIsOpen=function(a){for(var b=s(this[j.ext.iApiIndex]),c=0;c<
b.aoOpenRows.length;c++)if(b.aoOpenRows[c].nParent==a)return!0;return!1};this.fnOpen=function(a,b,c){var d=s(this[j.ext.iApiIndex]),e=T(d);if(-1!==h.inArray(a,e)){this.fnClose(a);var e=l.createElement("tr"),f=l.createElement("td");e.appendChild(f);f.className=c;f.colSpan=t(d);"string"===typeof b?f.innerHTML=b:h(f).html(b);b=h("tr",d.nTBody);-1!=h.inArray(a,b)&&h(e).insertAfter(a);d.aoOpenRows.push({nTr:e,nParent:a});return e}};this.fnPageChange=function(a,b){var c=s(this[j.ext.iApiIndex]);qa(c,a);
y(c);(b===n||b)&&x(c)};this.fnSetColumnVis=function(a,b,c){var d=s(this[j.ext.iApiIndex]),e,f,g=d.aoColumns,h=d.aoData,o,m;if(g[a].bVisible!=b){if(b){for(e=f=0;e<a;e++)g[e].bVisible&&f++;m=f>=t(d);if(!m)for(e=a;e<g.length;e++)if(g[e].bVisible){o=e;break}e=0;for(f=h.length;e<f;e++)null!==h[e].nTr&&(m?h[e].nTr.appendChild(h[e]._anHidden[a]):h[e].nTr.insertBefore(h[e]._anHidden[a],J(d,e)[o]))}else{e=0;for(f=h.length;e<f;e++)null!==h[e].nTr&&(o=J(d,e)[a],h[e]._anHidden[a]=o,o.parentNode.removeChild(o))}g[a].bVisible=
b;W(d,d.aoHeader);d.nTFoot&&W(d,d.aoFooter);e=0;for(f=d.aoOpenRows.length;e<f;e++)d.aoOpenRows[e].nTr.colSpan=t(d);if(c===n||c)k(d),x(d);ra(d)}};this.fnSettings=function(){return s(this[j.ext.iApiIndex])};this.fnSort=function(a){var b=s(this[j.ext.iApiIndex]);b.aaSorting=a;O(b)};this.fnSortListener=function(a,b,c){ia(s(this[j.ext.iApiIndex]),a,b,c)};this.fnUpdate=function(a,b,c,d,e){var f=s(this[j.ext.iApiIndex]),b="object"===typeof b?I(f,b):b;if(h.isArray(a)&&c===n){f.aoData[b]._aData=a.slice();
for(c=0;c<f.aoColumns.length;c++)this.fnUpdate(v(f,b,c),b,c,!1,!1)}else if(h.isPlainObject(a)&&c===n){f.aoData[b]._aData=h.extend(!0,{},a);for(c=0;c<f.aoColumns.length;c++)this.fnUpdate(v(f,b,c),b,c,!1,!1)}else{F(f,b,c,a);var a=v(f,b,c,"display"),g=f.aoColumns[c];null!==g.fnRender&&(a=S(f,b,c),g.bUseRendered&&F(f,b,c,a));null!==f.aoData[b].nTr&&(J(f,b)[c].innerHTML=a)}c=h.inArray(b,f.aiDisplay);f.asDataSearch[c]=na(f,Y(f,b,"filter",r(f,"bSearchable")));(e===n||e)&&k(f);(d===n||d)&&aa(f);return 0};
this.fnVersionCheck=j.ext.fnVersionCheck;this.oApi={_fnExternApiFunc:Va,_fnInitialise:ba,_fnInitComplete:$,_fnLanguageCompat:pa,_fnAddColumn:o,_fnColumnOptions:m,_fnAddData:H,_fnCreateTr:ea,_fnGatherData:ua,_fnBuildHead:va,_fnDrawHead:W,_fnDraw:x,_fnReDraw:aa,_fnAjaxUpdate:wa,_fnAjaxParameters:Ea,_fnAjaxUpdateDraw:Fa,_fnServerParams:ka,_fnAddOptionsHtml:xa,_fnFeatureHtmlTable:Ba,_fnScrollDraw:La,_fnAdjustColumnSizing:k,_fnFeatureHtmlFilter:za,_fnFilterComplete:K,_fnFilterCustom:Ia,_fnFilterColumn:Ha,
_fnFilter:Ga,_fnBuildSearchArray:la,_fnBuildSearchRow:na,_fnFilterCreateSearch:ma,_fnDataToSearch:Ja,_fnSort:O,_fnSortAttachListener:ia,_fnSortingClasses:P,_fnFeatureHtmlPaginate:Da,_fnPageChange:qa,_fnFeatureHtmlInfo:Ca,_fnUpdateInfo:Ka,_fnFeatureHtmlLength:ya,_fnFeatureHtmlProcessing:Aa,_fnProcessingDisplay:E,_fnVisibleToColumnIndex:G,_fnColumnIndexToVisible:R,_fnNodeToDataIndex:I,_fnVisbleColumns:t,_fnCalculateEnd:y,_fnConvertToWidth:Ma,_fnCalculateColumnWidths:da,_fnScrollingWidthAdjust:Oa,_fnGetWidestNode:Na,
_fnGetMaxLenString:Pa,_fnStringToCss:q,_fnDetectType:B,_fnSettingsFromNode:s,_fnGetDataMaster:Z,_fnGetTrNodes:T,_fnGetTdNodes:J,_fnEscapeRegex:oa,_fnDeleteIndex:ha,_fnReOrderIndex:u,_fnColumnOrdering:M,_fnLog:D,_fnClearTable:ga,_fnSaveState:ra,_fnLoadState:Sa,_fnCreateCookie:function(a,b,c,d,e){var f=new Date;f.setTime(f.getTime()+1E3*c);var c=X.location.pathname.split("/"),a=a+"_"+c.pop().replace(/[\/:]/g,"").toLowerCase(),g;null!==e?(g="function"===typeof h.parseJSON?h.parseJSON(b):eval("("+b+")"),
b=e(a,g,f.toGMTString(),c.join("/")+"/")):b=a+"="+encodeURIComponent(b)+"; expires="+f.toGMTString()+"; path="+c.join("/")+"/";a=l.cookie.split(";");e=b.split(";")[0].length;f=[];if(4096<e+l.cookie.length+10){for(var j=0,o=a.length;j<o;j++)if(-1!=a[j].indexOf(d)){var k=a[j].split("=");try{(g=eval("("+decodeURIComponent(k[1])+")"))&&g.iCreate&&f.push({name:k[0],time:g.iCreate})}catch(m){}}for(f.sort(function(a,b){return b.time-a.time});4096<e+l.cookie.length+10;){if(0===f.length)return;d=f.pop();l.cookie=
d.name+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path="+c.join("/")+"/"}}l.cookie=b},_fnReadCookie:function(a){for(var b=X.location.pathname.split("/"),a=a+"_"+b[b.length-1].replace(/[\/:]/g,"").toLowerCase()+"=",b=l.cookie.split(";"),c=0;c<b.length;c++){for(var d=b[c];" "==d.charAt(0);)d=d.substring(1,d.length);if(0===d.indexOf(a))return decodeURIComponent(d.substring(a.length,d.length))}return null},_fnDetectHeader:V,_fnGetUniqueThs:N,_fnScrollBarWidth:Qa,_fnApplyToChildren:C,_fnMap:p,_fnGetRowData:Y,
_fnGetCellData:v,_fnSetCellData:F,_fnGetObjectDataFn:Q,_fnSetObjectDataFn:L,_fnApplyColumnDefs:ta,_fnBindAction:Ra,_fnExtend:Ta,_fnCallbackReg:z,_fnCallbackFire:A,_fnJsonString:Wa,_fnRender:S,_fnNodeToColumnIndex:fa,_fnInfoMacros:ja,_fnBrowserDetect:Ua,_fnGetColumns:r};h.extend(j.ext.oApi,this.oApi);for(var sa in j.ext.oApi)sa&&(this[sa]=Va(sa));var ca=this;this.each(function(){var a=0,b,c,d;c=this.getAttribute("id");var i=!1,f=!1;if("table"!=this.nodeName.toLowerCase())D(null,0,"Attempted to initialise DataTables on a node which is not a table: "+
this.nodeName);else{a=0;for(b=j.settings.length;a<b;a++){if(j.settings[a].nTable==this){if(e===n||e.bRetrieve)return j.settings[a].oInstance;if(e.bDestroy){j.settings[a].oInstance.fnDestroy();break}else{D(j.settings[a],0,"Cannot reinitialise DataTable.\n\nTo retrieve the DataTables object for this table, pass no arguments or see the docs for bRetrieve and bDestroy");return}}if(j.settings[a].sTableId==this.id){j.settings.splice(a,1);break}}if(null===c||""===c)this.id=c="DataTables_Table_"+j.ext._oExternConfig.iNextUnique++;
var g=h.extend(!0,{},j.models.oSettings,{nTable:this,oApi:ca.oApi,oInit:e,sDestroyWidth:h(this).width(),sInstance:c,sTableId:c});j.settings.push(g);g.oInstance=1===ca.length?ca:h(this).dataTable();e||(e={});e.oLanguage&&pa(e.oLanguage);e=Ta(h.extend(!0,{},j.defaults),e);p(g.oFeatures,e,"bPaginate");p(g.oFeatures,e,"bLengthChange");p(g.oFeatures,e,"bFilter");p(g.oFeatures,e,"bSort");p(g.oFeatures,e,"bInfo");p(g.oFeatures,e,"bProcessing");p(g.oFeatures,e,"bAutoWidth");p(g.oFeatures,e,"bSortClasses");
p(g.oFeatures,e,"bServerSide");p(g.oFeatures,e,"bDeferRender");p(g.oScroll,e,"sScrollX","sX");p(g.oScroll,e,"sScrollXInner","sXInner");p(g.oScroll,e,"sScrollY","sY");p(g.oScroll,e,"bScrollCollapse","bCollapse");p(g.oScroll,e,"bScrollInfinite","bInfinite");p(g.oScroll,e,"iScrollLoadGap","iLoadGap");p(g.oScroll,e,"bScrollAutoCss","bAutoCss");p(g,e,"asStripeClasses");p(g,e,"asStripClasses","asStripeClasses");p(g,e,"fnServerData");p(g,e,"fnFormatNumber");p(g,e,"sServerMethod");p(g,e,"aaSorting");p(g,
e,"aaSortingFixed");p(g,e,"aLengthMenu");p(g,e,"sPaginationType");p(g,e,"sAjaxSource");p(g,e,"sAjaxDataProp");p(g,e,"iCookieDuration");p(g,e,"sCookiePrefix");p(g,e,"sDom");p(g,e,"bSortCellsTop");p(g,e,"iTabIndex");p(g,e,"oSearch","oPreviousSearch");p(g,e,"aoSearchCols","aoPreSearchCols");p(g,e,"iDisplayLength","_iDisplayLength");p(g,e,"bJQueryUI","bJUI");p(g,e,"fnCookieCallback");p(g,e,"fnStateLoad");p(g,e,"fnStateSave");p(g.oLanguage,e,"fnInfoCallback");z(g,"aoDrawCallback",e.fnDrawCallback,"user");
z(g,"aoServerParams",e.fnServerParams,"user");z(g,"aoStateSaveParams",e.fnStateSaveParams,"user");z(g,"aoStateLoadParams",e.fnStateLoadParams,"user");z(g,"aoStateLoaded",e.fnStateLoaded,"user");z(g,"aoRowCallback",e.fnRowCallback,"user");z(g,"aoRowCreatedCallback",e.fnCreatedRow,"user");z(g,"aoHeaderCallback",e.fnHeaderCallback,"user");z(g,"aoFooterCallback",e.fnFooterCallback,"user");z(g,"aoInitComplete",e.fnInitComplete,"user");z(g,"aoPreDrawCallback",e.fnPreDrawCallback,"user");g.oFeatures.bServerSide&&
g.oFeatures.bSort&&g.oFeatures.bSortClasses?z(g,"aoDrawCallback",P,"server_side_sort_classes"):g.oFeatures.bDeferRender&&z(g,"aoDrawCallback",P,"defer_sort_classes");e.bJQueryUI?(h.extend(g.oClasses,j.ext.oJUIClasses),e.sDom===j.defaults.sDom&&"lfrtip"===j.defaults.sDom&&(g.sDom='<"H"lfr>t<"F"ip>')):h.extend(g.oClasses,j.ext.oStdClasses);h(this).addClass(g.oClasses.sTable);if(""!==g.oScroll.sX||""!==g.oScroll.sY)g.oScroll.iBarWidth=Qa();g.iInitDisplayStart===n&&(g.iInitDisplayStart=e.iDisplayStart,
g._iDisplayStart=e.iDisplayStart);e.bStateSave&&(g.oFeatures.bStateSave=!0,Sa(g,e),z(g,"aoDrawCallback",ra,"state_save"));null!==e.iDeferLoading&&(g.bDeferLoading=!0,a=h.isArray(e.iDeferLoading),g._iRecordsDisplay=a?e.iDeferLoading[0]:e.iDeferLoading,g._iRecordsTotal=a?e.iDeferLoading[1]:e.iDeferLoading);null!==e.aaData&&(f=!0);""!==e.oLanguage.sUrl?(g.oLanguage.sUrl=e.oLanguage.sUrl,h.getJSON(g.oLanguage.sUrl,null,function(a){pa(a);h.extend(true,g.oLanguage,e.oLanguage,a);ba(g)}),i=!0):h.extend(!0,
g.oLanguage,e.oLanguage);null===e.asStripeClasses&&(g.asStripeClasses=[g.oClasses.sStripeOdd,g.oClasses.sStripeEven]);b=g.asStripeClasses.length;g.asDestroyStripes=[];if(b){c=!1;d=h(this).children("tbody").children("tr:lt("+b+")");for(a=0;a<b;a++)d.hasClass(g.asStripeClasses[a])&&(c=!0,g.asDestroyStripes.push(g.asStripeClasses[a]));c&&d.removeClass(g.asStripeClasses.join(" "))}c=[];a=this.getElementsByTagName("thead");0!==a.length&&(V(g.aoHeader,a[0]),c=N(g));if(null===e.aoColumns){d=[];a=0;for(b=
c.length;a<b;a++)d.push(null)}else d=e.aoColumns;a=0;for(b=d.length;a<b;a++)e.saved_aoColumns!==n&&e.saved_aoColumns.length==b&&(null===d[a]&&(d[a]={}),d[a].bVisible=e.saved_aoColumns[a].bVisible),o(g,c?c[a]:null);ta(g,e.aoColumnDefs,d,function(a,b){m(g,a,b)});a=0;for(b=g.aaSorting.length;a<b;a++){g.aaSorting[a][0]>=g.aoColumns.length&&(g.aaSorting[a][0]=0);var k=g.aoColumns[g.aaSorting[a][0]];g.aaSorting[a][2]===n&&(g.aaSorting[a][2]=0);e.aaSorting===n&&g.saved_aaSorting===n&&(g.aaSorting[a][1]=
k.asSorting[0]);c=0;for(d=k.asSorting.length;c<d;c++)if(g.aaSorting[a][1]==k.asSorting[c]){g.aaSorting[a][2]=c;break}}P(g);Ua(g);a=h(this).children("caption").each(function(){this._captionSide=h(this).css("caption-side")});b=h(this).children("thead");0===b.length&&(b=[l.createElement("thead")],this.appendChild(b[0]));g.nTHead=b[0];b=h(this).children("tbody");0===b.length&&(b=[l.createElement("tbody")],this.appendChild(b[0]));g.nTBody=b[0];g.nTBody.setAttribute("role","alert");g.nTBody.setAttribute("aria-live",
"polite");g.nTBody.setAttribute("aria-relevant","all");b=h(this).children("tfoot");if(0===b.length&&0<a.length&&(""!==g.oScroll.sX||""!==g.oScroll.sY))b=[l.createElement("tfoot")],this.appendChild(b[0]);0<b.length&&(g.nTFoot=b[0],V(g.aoFooter,g.nTFoot));if(f)for(a=0;a<e.aaData.length;a++)H(g,e.aaData[a]);else ua(g);g.aiDisplay=g.aiDisplayMaster.slice();g.bInitialised=!0;!1===i&&ba(g)}});ca=null;return this};j.fnVersionCheck=function(e){for(var h=function(e,h){for(;e.length<h;)e+="0";return e},m=j.ext.sVersion.split("."),
e=e.split("."),k="",n="",l=0,t=e.length;l<t;l++)k+=h(m[l],3),n+=h(e[l],3);return parseInt(k,10)>=parseInt(n,10)};j.fnIsDataTable=function(e){for(var h=j.settings,m=0;m<h.length;m++)if(h[m].nTable===e||h[m].nScrollHead===e||h[m].nScrollFoot===e)return!0;return!1};j.fnTables=function(e){var o=[];jQuery.each(j.settings,function(j,k){(!e||!0===e&&h(k.nTable).is(":visible"))&&o.push(k.nTable)});return o};j.version="1.9.4";j.settings=[];j.models={};j.models.ext={afnFiltering:[],afnSortData:[],aoFeatures:[],
aTypes:[],fnVersionCheck:j.fnVersionCheck,iApiIndex:0,ofnSearch:{},oApi:{},oStdClasses:{},oJUIClasses:{},oPagination:{},oSort:{},sVersion:j.version,sErrMode:"alert",_oExternConfig:{iNextUnique:0}};j.models.oSearch={bCaseInsensitive:!0,sSearch:"",bRegex:!1,bSmart:!0};j.models.oRow={nTr:null,_aData:[],_aSortData:[],_anHidden:[],_sRowStripe:""};j.models.oColumn={aDataSort:null,asSorting:null,bSearchable:null,bSortable:null,bUseRendered:null,bVisible:null,_bAutoType:!0,fnCreatedCell:null,fnGetData:null,
fnRender:null,fnSetData:null,mData:null,mRender:null,nTh:null,nTf:null,sClass:null,sContentPadding:null,sDefaultContent:null,sName:null,sSortDataType:"std",sSortingClass:null,sSortingClassJUI:null,sTitle:null,sType:null,sWidth:null,sWidthOrig:null};j.defaults={aaData:null,aaSorting:[[0,"asc"]],aaSortingFixed:null,aLengthMenu:[10,25,50,100],aoColumns:null,aoColumnDefs:null,aoSearchCols:[],asStripeClasses:null,bAutoWidth:!0,bDeferRender:!1,bDestroy:!1,bFilter:!0,bInfo:!0,bJQueryUI:!1,bLengthChange:!0,
bPaginate:!0,bProcessing:!1,bRetrieve:!1,bScrollAutoCss:!0,bScrollCollapse:!1,bScrollInfinite:!1,bServerSide:!1,bSort:!0,bSortCellsTop:!1,bSortClasses:!0,bStateSave:!1,fnCookieCallback:null,fnCreatedRow:null,fnDrawCallback:null,fnFooterCallback:null,fnFormatNumber:function(e){if(1E3>e)return e;for(var h=e+"",e=h.split(""),j="",h=h.length,k=0;k<h;k++)0===k%3&&0!==k&&(j=this.oLanguage.sInfoThousands+j),j=e[h-k-1]+j;return j},fnHeaderCallback:null,fnInfoCallback:null,fnInitComplete:null,fnPreDrawCallback:null,
fnRowCallback:null,fnServerData:function(e,j,m,k){k.jqXHR=h.ajax({url:e,data:j,success:function(e){e.sError&&k.oApi._fnLog(k,0,e.sError);h(k.oInstance).trigger("xhr",[k,e]);m(e)},dataType:"json",cache:!1,type:k.sServerMethod,error:function(e,h){"parsererror"==h&&k.oApi._fnLog(k,0,"DataTables warning: JSON data from server could not be parsed. This is caused by a JSON formatting error.")}})},fnServerParams:null,fnStateLoad:function(e){var e=this.oApi._fnReadCookie(e.sCookiePrefix+e.sInstance),j;try{j=
"function"===typeof h.parseJSON?h.parseJSON(e):eval("("+e+")")}catch(m){j=null}return j},fnStateLoadParams:null,fnStateLoaded:null,fnStateSave:function(e,h){this.oApi._fnCreateCookie(e.sCookiePrefix+e.sInstance,this.oApi._fnJsonString(h),e.iCookieDuration,e.sCookiePrefix,e.fnCookieCallback)},fnStateSaveParams:null,iCookieDuration:7200,iDeferLoading:null,iDisplayLength:10,iDisplayStart:0,iScrollLoadGap:100,iTabIndex:0,oLanguage:{oAria:{sSortAscending:": activate to sort column ascending",sSortDescending:": activate to sort column descending"},
oPaginate:{sFirst:"First",sLast:"Last",sNext:"Next",sPrevious:"Previous"},sEmptyTable:"No data available in table",sInfo:"Showing _START_ to _END_ of _TOTAL_ entries",sInfoEmpty:"Showing 0 to 0 of 0 entries",sInfoFiltered:"(filtered from _MAX_ total entries)",sInfoPostFix:"",sInfoThousands:",",sLengthMenu:"Show _MENU_ entries",sLoadingRecords:"Loading...",sProcessing:"Processing...",sSearch:"Search:",sUrl:"",sZeroRecords:"No matching records found"},oSearch:h.extend({},j.models.oSearch),sAjaxDataProp:"aaData",
sAjaxSource:null,sCookiePrefix:"SpryMedia_DataTables_",sDom:"lfrtip",sPaginationType:"two_button",sScrollX:"",sScrollXInner:"",sScrollY:"",sServerMethod:"GET"};j.defaults.columns={aDataSort:null,asSorting:["asc","desc"],bSearchable:!0,bSortable:!0,bUseRendered:!0,bVisible:!0,fnCreatedCell:null,fnRender:null,iDataSort:-1,mData:null,mRender:null,sCellType:"td",sClass:"",sContentPadding:"",sDefaultContent:null,sName:"",sSortDataType:"std",sTitle:null,sType:null,sWidth:null};j.models.oSettings={oFeatures:{bAutoWidth:null,
bDeferRender:null,bFilter:null,bInfo:null,bLengthChange:null,bPaginate:null,bProcessing:null,bServerSide:null,bSort:null,bSortClasses:null,bStateSave:null},oScroll:{bAutoCss:null,bCollapse:null,bInfinite:null,iBarWidth:0,iLoadGap:null,sX:null,sXInner:null,sY:null},oLanguage:{fnInfoCallback:null},oBrowser:{bScrollOversize:!1},aanFeatures:[],aoData:[],aiDisplay:[],aiDisplayMaster:[],aoColumns:[],aoHeader:[],aoFooter:[],asDataSearch:[],oPreviousSearch:{},aoPreSearchCols:[],aaSorting:null,aaSortingFixed:null,
asStripeClasses:null,asDestroyStripes:[],sDestroyWidth:0,aoRowCallback:[],aoHeaderCallback:[],aoFooterCallback:[],aoDrawCallback:[],aoRowCreatedCallback:[],aoPreDrawCallback:[],aoInitComplete:[],aoStateSaveParams:[],aoStateLoadParams:[],aoStateLoaded:[],sTableId:"",nTable:null,nTHead:null,nTFoot:null,nTBody:null,nTableWrapper:null,bDeferLoading:!1,bInitialised:!1,aoOpenRows:[],sDom:null,sPaginationType:"two_button",iCookieDuration:0,sCookiePrefix:"",fnCookieCallback:null,aoStateSave:[],aoStateLoad:[],
oLoadedState:null,sAjaxSource:null,sAjaxDataProp:null,bAjaxDataGet:!0,jqXHR:null,fnServerData:null,aoServerParams:[],sServerMethod:null,fnFormatNumber:null,aLengthMenu:null,iDraw:0,bDrawing:!1,iDrawError:-1,_iDisplayLength:10,_iDisplayStart:0,_iDisplayEnd:10,_iRecordsTotal:0,_iRecordsDisplay:0,bJUI:null,oClasses:{},bFiltered:!1,bSorted:!1,bSortCellsTop:null,oInit:null,aoDestroyCallback:[],fnRecordsTotal:function(){return this.oFeatures.bServerSide?parseInt(this._iRecordsTotal,10):this.aiDisplayMaster.length},
fnRecordsDisplay:function(){return this.oFeatures.bServerSide?parseInt(this._iRecordsDisplay,10):this.aiDisplay.length},fnDisplayEnd:function(){return this.oFeatures.bServerSide?!1===this.oFeatures.bPaginate||-1==this._iDisplayLength?this._iDisplayStart+this.aiDisplay.length:Math.min(this._iDisplayStart+this._iDisplayLength,this._iRecordsDisplay):this._iDisplayEnd},oInstance:null,sInstance:null,iTabIndex:0,nScrollHead:null,nScrollFoot:null};j.ext=h.extend(!0,{},j.models.ext);h.extend(j.ext.oStdClasses,
{sTable:"dataTable",sPagePrevEnabled:"paginate_enabled_previous",sPagePrevDisabled:"paginate_disabled_previous",sPageNextEnabled:"paginate_enabled_next",sPageNextDisabled:"paginate_disabled_next",sPageJUINext:"",sPageJUIPrev:"",sPageButton:"paginate_button",sPageButtonActive:"paginate_active",sPageButtonStaticDisabled:"paginate_button paginate_button_disabled",sPageFirst:"first",sPagePrevious:"previous",sPageNext:"next",sPageLast:"last",sStripeOdd:"odd",sStripeEven:"even",sRowEmpty:"dataTables_empty",
sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",sInfo:"dataTables_info",sPaging:"dataTables_paginate paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"sorting_asc",sSortDesc:"sorting_desc",sSortable:"sorting",sSortableAsc:"sorting_asc_disabled",sSortableDesc:"sorting_desc_disabled",sSortableNone:"sorting_disabled",sSortColumn:"sorting_",sSortJUIAsc:"",sSortJUIDesc:"",sSortJUI:"",sSortJUIAscAllowed:"",sSortJUIDescAllowed:"",sSortJUIWrapper:"",sSortIcon:"",
sScrollWrapper:"dataTables_scroll",sScrollHead:"dataTables_scrollHead",sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody",sScrollFoot:"dataTables_scrollFoot",sScrollFootInner:"dataTables_scrollFootInner",sFooterTH:"",sJUIHeader:"",sJUIFooter:""});h.extend(j.ext.oJUIClasses,j.ext.oStdClasses,{sPagePrevEnabled:"fg-button ui-button ui-state-default ui-corner-left",sPagePrevDisabled:"fg-button ui-button ui-state-default ui-corner-left ui-state-disabled",sPageNextEnabled:"fg-button ui-button ui-state-default ui-corner-right",
sPageNextDisabled:"fg-button ui-button ui-state-default ui-corner-right ui-state-disabled",sPageJUINext:"ui-icon ui-icon-circle-arrow-e",sPageJUIPrev:"ui-icon ui-icon-circle-arrow-w",sPageButton:"fg-button ui-button ui-state-default",sPageButtonActive:"fg-button ui-button ui-state-default ui-state-disabled",sPageButtonStaticDisabled:"fg-button ui-button ui-state-default ui-state-disabled",sPageFirst:"first ui-corner-tl ui-corner-bl",sPageLast:"last ui-corner-tr ui-corner-br",sPaging:"dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi ui-buttonset-multi paging_",
sSortAsc:"ui-state-default",sSortDesc:"ui-state-default",sSortable:"ui-state-default",sSortableAsc:"ui-state-default",sSortableDesc:"ui-state-default",sSortableNone:"ui-state-default",sSortJUIAsc:"css_right ui-icon ui-icon-triangle-1-n",sSortJUIDesc:"css_right ui-icon ui-icon-triangle-1-s",sSortJUI:"css_right ui-icon ui-icon-carat-2-n-s",sSortJUIAscAllowed:"css_right ui-icon ui-icon-carat-1-n",sSortJUIDescAllowed:"css_right ui-icon ui-icon-carat-1-s",sSortJUIWrapper:"DataTables_sort_wrapper",sSortIcon:"DataTables_sort_icon",
sScrollHead:"dataTables_scrollHead ui-state-default",sScrollFoot:"dataTables_scrollFoot ui-state-default",sFooterTH:"ui-state-default",sJUIHeader:"fg-toolbar ui-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix",sJUIFooter:"fg-toolbar ui-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix"});h.extend(j.ext.oPagination,{two_button:{fnInit:function(e,j,m){var k=e.oLanguage.oPaginate,n=function(h){e.oApi._fnPageChange(e,h.data.action)&&m(e)},k=!e.bJUI?'<a class="'+
e.oClasses.sPagePrevDisabled+'" tabindex="'+e.iTabIndex+'" role="button">'+k.sPrevious+'</a><a class="'+e.oClasses.sPageNextDisabled+'" tabindex="'+e.iTabIndex+'" role="button">'+k.sNext+"</a>":'<a class="'+e.oClasses.sPagePrevDisabled+'" tabindex="'+e.iTabIndex+'" role="button"><span class="'+e.oClasses.sPageJUIPrev+'"></span></a><a class="'+e.oClasses.sPageNextDisabled+'" tabindex="'+e.iTabIndex+'" role="button"><span class="'+e.oClasses.sPageJUINext+'"></span></a>';h(j).append(k);var l=h("a",j),
k=l[0],l=l[1];e.oApi._fnBindAction(k,{action:"previous"},n);e.oApi._fnBindAction(l,{action:"next"},n);e.aanFeatures.p||(j.id=e.sTableId+"_paginate",k.id=e.sTableId+"_previous",l.id=e.sTableId+"_next",k.setAttribute("aria-controls",e.sTableId),l.setAttribute("aria-controls",e.sTableId))},fnUpdate:function(e){if(e.aanFeatures.p)for(var h=e.oClasses,j=e.aanFeatures.p,k,l=0,n=j.length;l<n;l++)if(k=j[l].firstChild)k.className=0===e._iDisplayStart?h.sPagePrevDisabled:h.sPagePrevEnabled,k=k.nextSibling,
k.className=e.fnDisplayEnd()==e.fnRecordsDisplay()?h.sPageNextDisabled:h.sPageNextEnabled}},iFullNumbersShowPages:5,full_numbers:{fnInit:function(e,j,m){var k=e.oLanguage.oPaginate,l=e.oClasses,n=function(h){e.oApi._fnPageChange(e,h.data.action)&&m(e)};h(j).append('<a tabindex="'+e.iTabIndex+'" class="'+l.sPageButton+" "+l.sPageFirst+'">'+k.sFirst+'</a><a tabindex="'+e.iTabIndex+'" class="'+l.sPageButton+" "+l.sPagePrevious+'">'+k.sPrevious+'</a><span></span><a tabindex="'+e.iTabIndex+'" class="'+
l.sPageButton+" "+l.sPageNext+'">'+k.sNext+'</a><a tabindex="'+e.iTabIndex+'" class="'+l.sPageButton+" "+l.sPageLast+'">'+k.sLast+"</a>");var t=h("a",j),k=t[0],l=t[1],r=t[2],t=t[3];e.oApi._fnBindAction(k,{action:"first"},n);e.oApi._fnBindAction(l,{action:"previous"},n);e.oApi._fnBindAction(r,{action:"next"},n);e.oApi._fnBindAction(t,{action:"last"},n);e.aanFeatures.p||(j.id=e.sTableId+"_paginate",k.id=e.sTableId+"_first",l.id=e.sTableId+"_previous",r.id=e.sTableId+"_next",t.id=e.sTableId+"_last")},
fnUpdate:function(e,o){if(e.aanFeatures.p){var m=j.ext.oPagination.iFullNumbersShowPages,k=Math.floor(m/2),l=Math.ceil(e.fnRecordsDisplay()/e._iDisplayLength),n=Math.ceil(e._iDisplayStart/e._iDisplayLength)+1,t="",r,B=e.oClasses,u,M=e.aanFeatures.p,L=function(h){e.oApi._fnBindAction(this,{page:h+r-1},function(h){e.oApi._fnPageChange(e,h.data.page);o(e);h.preventDefault()})};-1===e._iDisplayLength?n=k=r=1:l<m?(r=1,k=l):n<=k?(r=1,k=m):n>=l-k?(r=l-m+1,k=l):(r=n-Math.ceil(m/2)+1,k=r+m-1);for(m=r;m<=k;m++)t+=
n!==m?'<a tabindex="'+e.iTabIndex+'" class="'+B.sPageButton+'">'+e.fnFormatNumber(m)+"</a>":'<a tabindex="'+e.iTabIndex+'" class="'+B.sPageButtonActive+'">'+e.fnFormatNumber(m)+"</a>";m=0;for(k=M.length;m<k;m++)u=M[m],u.hasChildNodes()&&(h("span:eq(0)",u).html(t).children("a").each(L),u=u.getElementsByTagName("a"),u=[u[0],u[1],u[u.length-2],u[u.length-1]],h(u).removeClass(B.sPageButton+" "+B.sPageButtonActive+" "+B.sPageButtonStaticDisabled),h([u[0],u[1]]).addClass(1==n?B.sPageButtonStaticDisabled:
B.sPageButton),h([u[2],u[3]]).addClass(0===l||n===l||-1===e._iDisplayLength?B.sPageButtonStaticDisabled:B.sPageButton))}}}});h.extend(j.ext.oSort,{"string-pre":function(e){"string"!=typeof e&&(e=null!==e&&e.toString?e.toString():"");return e.toLowerCase()},"string-asc":function(e,h){return e<h?-1:e>h?1:0},"string-desc":function(e,h){return e<h?1:e>h?-1:0},"html-pre":function(e){return e.replace(/<.*?>/g,"").toLowerCase()},"html-asc":function(e,h){return e<h?-1:e>h?1:0},"html-desc":function(e,h){return e<
h?1:e>h?-1:0},"date-pre":function(e){e=Date.parse(e);if(isNaN(e)||""===e)e=Date.parse("01/01/1970 00:00:00");return e},"date-asc":function(e,h){return e-h},"date-desc":function(e,h){return h-e},"numeric-pre":function(e){return"-"==e||""===e?0:1*e},"numeric-asc":function(e,h){return e-h},"numeric-desc":function(e,h){return h-e}});h.extend(j.ext.aTypes,[function(e){if("number"===typeof e)return"numeric";if("string"!==typeof e)return null;var h,j=!1;h=e.charAt(0);if(-1=="0123456789-".indexOf(h))return null;
for(var k=1;k<e.length;k++){h=e.charAt(k);if(-1=="0123456789.".indexOf(h))return null;if("."==h){if(j)return null;j=!0}}return"numeric"},function(e){var h=Date.parse(e);return null!==h&&!isNaN(h)||"string"===typeof e&&0===e.length?"date":null},function(e){return"string"===typeof e&&-1!=e.indexOf("<")&&-1!=e.indexOf(">")?"html":null}]);h.fn.DataTable=j;h.fn.dataTable=j;h.fn.dataTableSettings=j.settings;h.fn.dataTableExt=j.ext};"function"===typeof define&&define.amd?define(["jquery"],L):jQuery&&!jQuery.fn.dataTable&&
L(jQuery)})(window,document);

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,15 @@
/**
* Hebrew translation for bootstrap-datepicker
* Sagie Maoz <sagie@maoz.info>
*/
;(function($){
$.fn.datepicker.dates['he'] = {
days: ["ראשון", "שני", "שלישי", "רביעי", "חמישי", "שישי", "שבת", "ראשון"],
daysShort: ["א", "ב", "ג", "ד", "ה", "ו", "ש", "א"],
daysMin: ["א", "ב", "ג", "ד", "ה", "ו", "ש", "א"],
months: ["ינואר", "פברואר", "מרץ", "אפריל", "מאי", "יוני", "יולי", "אוגוסט", "ספטמבר", "אוקטובר", "נובמבר", "דצמבר"],
monthsShort: ["ינו", "פבר", "מרץ", "אפר", "מאי", "יונ", "יול", "אוג", "ספט", "אוק", "נוב", "דצמ"],
today: "היום",
rtl: true
};
}(jQuery));

View File

@@ -0,0 +1,13 @@
/**
* Croatian localisation
*/
;(function($){
$.fn.datepicker.dates['hr'] = {
days: ["Nedjelja", "Ponedjelja", "Utorak", "Srijeda", "Četrtak", "Petak", "Subota", "Nedjelja"],
daysShort: ["Ned", "Pon", "Uto", "Srr", "Čet", "Pet", "Sub", "Ned"],
daysMin: ["Ne", "Po", "Ut", "Sr", "Če", "Pe", "Su", "Ne"],
months: ["Siječanj", "Veljača", "Ožujak", "Travanj", "Svibanj", "Lipanj", "Srpanj", "Kolovoz", "Rujan", "Listopad", "Studeni", "Prosinac"],
monthsShort: ["Sije", "Velj", "Ožu", "Tra", "Svi", "Lip", "Jul", "Kol", "Ruj", "Lis", "Stu", "Pro"],
today: "Danas"
};
}(jQuery));

View File

@@ -0,0 +1,16 @@
/**
* Hungarian translation for bootstrap-datepicker
* Sotus László <lacisan@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['hu'] = {
days: ["Vasárnap", "Hétfő", "Kedd", "Szerda", "Csütörtök", "Péntek", "Szombat", "Vasárnap"],
daysShort: ["Vas", "Hét", "Ked", "Sze", "Csü", "Pén", "Szo", "Vas"],
daysMin: ["Va", "Hé", "Ke", "Sz", "Cs", "Pé", "Sz", "Va"],
months: ["Január", "Február", "Március", "Április", "Május", "Június", "Július", "Augusztus", "Szeptember", "Október", "November", "December"],
monthsShort: ["Jan", "Feb", "Már", "Ápr", "Máj", "Jún", "Júl", "Aug", "Sze", "Okt", "Nov", "Dec"],
today: "Ma",
weekStart: 1,
format: "yyyy.mm.dd"
};
}(jQuery));

View File

@@ -0,0 +1,13 @@
/**
* Bahasa translation for bootstrap-datepicker
* Azwar Akbar <azwar.akbar@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['id'] = {
days: ["Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu", "Minggu"],
daysShort: ["Mgu", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab", "Mgu"],
daysMin: ["Mg", "Sn", "Sl", "Ra", "Ka", "Ju", "Sa", "Mg"],
months: ["Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember"],
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Ags", "Sep", "Okt", "Nov", "Des"]
};
}(jQuery));

View File

@@ -0,0 +1,14 @@
/**
* Icelandic translation for bootstrap-datepicker
* Hinrik Örn Sigurðsson <hinrik.sig@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['is'] = {
days: ["Sunnudagur", "Mánudagur", "Þriðjudagur", "Miðvikudagur", "Fimmtudagur", "Föstudagur", "Laugardagur", "Sunnudagur"],
daysShort: ["Sun", "Mán", "Þri", "Mið", "Fim", "Fös", "Lau", "Sun"],
daysMin: ["Su", "Má", "Þr", "Mi", "Fi", "Fö", "La", "Su"],
months: ["Janúar", "Febrúar", "Mars", "Apríl", "Maí", "Júní", "Júlí", "Ágúst", "September", "Október", "Nóvember", "Desember"],
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maí", "Jún", "Júl", "Ágú", "Sep", "Okt", "Nóv", "Des"],
today: "Í Dag"
};
}(jQuery));

View File

@@ -0,0 +1,16 @@
/**
* Italian translation for bootstrap-datepicker
* Enrico Rubboli <rubboli@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['it'] = {
days: ["Domenica", "Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì", "Sabato", "Domenica"],
daysShort: ["Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab", "Dom"],
daysMin: ["Do", "Lu", "Ma", "Me", "Gi", "Ve", "Sa", "Do"],
months: ["Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"],
monthsShort: ["Gen", "Feb", "Mar", "Apr", "Mag", "Giu", "Lug", "Ago", "Set", "Ott", "Nov", "Dic"],
today: "Oggi",
weekStart: 1,
format: "dd/mm/yyyy"
};
}(jQuery));

View File

@@ -0,0 +1,15 @@
/**
* Japanese translation for bootstrap-datepicker
* Norio Suzuki <https://github.com/suzuki/>
*/
;(function($){
$.fn.datepicker.dates['ja'] = {
days: ["日曜", "月曜", "火曜", "水曜", "木曜", "金曜", "土曜", "日曜"],
daysShort: ["日", "月", "火", "水", "木", "金", "土", "日"],
daysMin: ["日", "月", "火", "水", "木", "金", "土", "日"],
months: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
monthsShort: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
today: "今日",
format: "yyyy/mm/dd"
};
}(jQuery));

View File

@@ -0,0 +1,13 @@
/**
* Korean translation for bootstrap-datepicker
* Gu Youn <http://github.com/guyoun>
*/
;(function($){
$.fn.datepicker.dates['kr'] = {
days: ["일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일", "일요일"],
daysShort: ["일", "월", "화", "수", "목", "금", "토", "일"],
daysMin: ["일", "월", "화", "수", "목", "금", "토", "일"],
months: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"],
monthsShort: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"]
};
}(jQuery));

View File

@@ -0,0 +1,16 @@
/**
* Lithuanian translation for bootstrap-datepicker
* Šarūnas Gliebus <ssharunas@yahoo.co.uk>
*/
;(function($){
$.fn.datepicker.dates['lt'] = {
days: ["Sekmadienis", "Pirmadienis", "Antradienis", "Trečiadienis", "Ketvirtadienis", "Penktadienis", "Šeštadienis", "Sekmadienis"],
daysShort: ["S", "Pr", "A", "T", "K", "Pn", "Š", "S"],
daysMin: ["Sk", "Pr", "An", "Tr", "Ke", "Pn", "Št", "Sk"],
months: ["Sausis", "Vasaris", "Kovas", "Balandis", "Gegužė", "Birželis", "Liepa", "Rugpjūtis", "Rugsėjis", "Spalis", "Lapkritis", "Gruodis"],
monthsShort: ["Sau", "Vas", "Kov", "Bal", "Geg", "Bir", "Lie", "Rugp", "Rugs", "Spa", "Lap", "Gru"],
today: "Šiandien",
weekStart: 1
};
}(jQuery));

View File

@@ -0,0 +1,16 @@
/**
* Latvian translation for bootstrap-datepicker
* Artis Avotins <artis@apit.lv>
*/
;(function($){
$.fn.datepicker.dates['lv'] = {
days: ["Svētdiena", "Pirmdiena", "Otrdiena", "Trešdiena", "Ceturtdiena", "Piektdiena", "Sestdiena", "Svētdiena"],
daysShort: ["Sv", "P", "O", "T", "C", "Pk", "S", "Sv"],
daysMin: ["Sv", "Pr", "Ot", "Tr", "Ce", "Pk", "St", "Sv"],
months: ["Janvāris", "Februāris", "Marts", "Aprīlis", "Maijs", "Jūnijs", "Jūlijs", "Augusts", "Septembris", "Oktobris", "Novembris", "Decembris"],
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mai", "Jūn", "Jūl", "Aug", "Sep", "Okt", "Nov", "Dec."],
today: "Šodien",
weekStart: 1
};
}(jQuery));

View File

@@ -0,0 +1,14 @@
/**
* Malay translation for bootstrap-datepicker
* Ateman Faiz <noorulfaiz@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['ms'] = {
days: ["Ahad", "Isnin", "Selasa", "Rabu", "Khamis", "Jumaat", "Sabtu", "Ahad"],
daysShort: ["Aha", "Isn", "Sel", "Rab", "Kha", "Jum", "Sab", "Aha"],
daysMin: ["Ah", "Is", "Se", "Ra", "Kh", "Ju", "Sa", "Ah"],
months: ["Januari", "Februari", "Mac", "April", "Mei", "Jun", "Julai", "Ogos", "September", "Oktober", "November", "Disember"],
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Ogo", "Sep", "Okt", "Nov", "Dis"],
today: "Hari Ini"
};
}(jQuery));

View File

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

View File

@@ -0,0 +1,14 @@
/**
* Dutch translation for bootstrap-datepicker
* Reinier Goltstein <mrgoltstein@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['nl'] = {
days: ["Zondag", "Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrijdag", "Zaterdag", "Zondag"],
daysShort: ["Zo", "Ma", "Di", "Wo", "Do", "Vr", "Za", "Zo"],
daysMin: ["Zo", "Ma", "Di", "Wo", "Do", "Vr", "Za", "Zo"],
months: ["Januari", "Februari", "Maart", "April", "Mei", "Juni", "Juli", "Augustus", "September", "Oktober", "November", "December"],
monthsShort: ["Jan", "Feb", "Mrt", "Apr", "Mei", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"],
today: "Vandaag"
};
}(jQuery));

View File

@@ -0,0 +1,15 @@
/**
* Polish translation for bootstrap-datepicker
* Robert <rtpm@gazeta.pl>
*/
;(function($){
$.fn.datepicker.dates['pl'] = {
days: ["Niedziela", "Poniedziałek", "Wtorek", "Środa", "Czwartek", "Piątek", "Sobota", "Niedziela"],
daysShort: ["Nie", "Pn", "Wt", "Śr", "Czw", "Pt", "So", "Nie"],
daysMin: ["N", "Pn", "Wt", "Śr", "Cz", "Pt", "So", "N"],
months: ["Styczeń", "Luty", "Marzec", "Kwiecień", "Maj", "Czerwiec", "Lipiec", "Sierpień", "Wrzesień", "Październik", "Listopad", "Grudzień"],
monthsShort: ["Sty", "Lu", "Mar", "Kw", "Maj", "Cze", "Lip", "Sie", "Wrz", "Pa", "Lis", "Gru"],
today: "Dzisiaj",
weekStart: 1
};
}(jQuery));

View File

@@ -0,0 +1,14 @@
/**
* Brazilian translation for bootstrap-datepicker
* Cauan Cabral <cauan@radig.com.br>
*/
;(function($){
$.fn.datepicker.dates['pt-BR'] = {
days: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado", "Domingo"],
daysShort: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb", "Dom"],
daysMin: ["Do", "Se", "Te", "Qu", "Qu", "Se", "Sa", "Do"],
months: ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"],
monthsShort: ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"],
today: "Hoje"
};
}(jQuery));

View File

@@ -0,0 +1,14 @@
/**
* Portuguese translation for bootstrap-datepicker
* Original code: Cauan Cabral <cauan@radig.com.br>
* Tiago Melo <tiago.blackcode@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['pt'] = {
days: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado", "Domingo"],
daysShort: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb", "Dom"],
daysMin: ["Do", "Se", "Te", "Qu", "Qu", "Se", "Sa", "Do"],
months: ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"],
monthsShort: ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"]
};
}(jQuery));

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