├── LICENSE ├── inst ├── www │ ├── RCloud-flexo.png │ ├── js │ │ ├── require-flexdashboard.js │ │ └── rcloud-flexdashboard.js │ ├── css │ │ └── dashboard.css │ ├── flexdashboard.html │ └── RCloud-flexo.svg └── javascript │ └── rcloud.flexdashboard.js ├── NAMESPACE ├── R ├── render.R └── onload.R ├── DESCRIPTION └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2016 2 | COPYRIGHT HOLDER: AT&T Intellectual Property 3 | -------------------------------------------------------------------------------- /inst/www/RCloud-flexo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MangoTheCat/rcloud.flexdashboard/master/inst/www/RCloud-flexo.png -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | 2 | importFrom(flexdashboard, flex_dashboard) 3 | importFrom(rcloud.rmd, exportRmd) 4 | importFrom(rcloud.support, rcloud.get.notebook) 5 | importFrom(rcloud.support, rcloud.install.js.module) 6 | importFrom(rcloud.support, rcloud.session.notebook) 7 | importFrom(rmarkdown, render) 8 | -------------------------------------------------------------------------------- /inst/www/js/require-flexdashboard.js: -------------------------------------------------------------------------------- 1 | requirejs.config(requirejs_config_obj); // jshint ignore:line 2 | 3 | var deps = common_deps; // jshint ignore:line 4 | 5 | deps.push( 6 | // rcloud's mini.js and bundle 7 | '../../shared.R/rcloud.flexdashboard/js/rcloud-flexdashboard', 'rcloud_bundle'); 8 | 9 | start_require(deps); // jshint ignore:line 10 | -------------------------------------------------------------------------------- /inst/www/css/dashboard.css: -------------------------------------------------------------------------------- 1 | body, html { 2 | margin: 0; padding: 0; height: 100%; overflow: hidden; 3 | } 4 | 5 | #rcloud-flexdashboard { 6 | position:absolute; left: 0; right: 0; bottom: 0; top: 0px; 7 | } 8 | 9 | .centered { 10 | position: fixed; 11 | top: 50%; 12 | left: 50%; 13 | /* bring your own prefixes */ 14 | transform: translate(-50%, -50%); 15 | } 16 | -------------------------------------------------------------------------------- /R/render.R: -------------------------------------------------------------------------------- 1 | 2 | renderFlexDashboard <- function(id, version = NULL) { 3 | 4 | tmp <- tempfile(fileext = ".Rmd") 5 | on.exit(unlink(tmp), add = TRUE) 6 | exportRmd(id, version, file = tmp) 7 | 8 | tmp2 <- tempfile(fileext = ".html") 9 | on.exit(unlink(tmp2), add = TRUE) 10 | render( 11 | input = tmp, 12 | output_file = tmp2 13 | ) 14 | 15 | contents <- paste(readLines(tmp2), collapse = "\n") 16 | 17 | caps$render( 18 | "#rcloud-flexdashboard", 19 | gsub("\"", """, contents) 20 | ) 21 | 22 | invisible() 23 | } 24 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: rcloud.flexdashboard 2 | Type: Package 3 | Title: 'flexdashboard' Documents in RCloud Notebooks 4 | Version: 1.0.0 5 | Date: 2016-10-12 6 | Authors@R: person("Gábor", "Csárdi", email = "gcsardi@mango-solutions.com", 7 | role = c("aut", "cre")) 8 | Description: Create, edit and show a 'flexdashboard' dashboard in an 9 | RCloud notebook. 10 | License: MIT + file LICENSE 11 | RCloud-Extension: gui 12 | Encoding: UTF-8 13 | RoxygenNote: 5.0.1.9000 14 | Imports: 15 | flexdashboard, 16 | rmarkdown, 17 | rcloud.rmd, 18 | rcloud.support (>= 1.7-0) 19 | -------------------------------------------------------------------------------- /R/onload.R: -------------------------------------------------------------------------------- 1 | 2 | caps <- NULL 3 | 4 | .onLoad <- function(libname, pkgname) { 5 | 6 | path <- system.file( 7 | package = "rcloud.flexdashboard", 8 | "javascript", 9 | "rcloud.flexdashboard.js" 10 | ) 11 | 12 | caps <<- rcloud.install.js.module( 13 | "rcloud.flexdashboard", 14 | paste(readLines(path), collapse = '\n') 15 | ) 16 | 17 | ocaps <- list(renderFlexDashboard = make_oc(renderFlexDashboard)) 18 | 19 | if (!is.null(caps)) caps$init(ocaps) 20 | } 21 | 22 | .rcloud.export.ocaps <- function() { list() } 23 | 24 | make_oc <- function(x) { 25 | do.call(base::`:::`, list("rcloud.support", "make.oc"))(x) 26 | } 27 | -------------------------------------------------------------------------------- /inst/www/flexdashboard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RCloud flexdashboard 5 | 6 | 7 | 8 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 17 |
18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /inst/javascript/rcloud.flexdashboard.js: -------------------------------------------------------------------------------- 1 | 2 | ((function() { 3 | 4 | return { 5 | init: function(ocaps, k) { 6 | 7 | // Are we in the notebook? 8 | if (RCloud.UI.advanced_menu.add) { 9 | 10 | RCloud.UI.share_button.add({ 11 | 'flexdashboard.html': { 12 | sort: 1000, 13 | page: 'shared.R/rcloud.flexdashboard/flexdashboard.html' 14 | } 15 | }); 16 | 17 | } else { 18 | onError = function(x) { 19 | $('#rcloud-flexdashboard-loading').remove(); 20 | RCloud.UI.fatal_dialog(x.message, "Close") 21 | } 22 | 23 | oc = RCloud.promisify_paths(ocaps, [ 24 | ['renderFlexDashboard'] 25 | ], true); 26 | 27 | window.RCloudFlexDashboard = window.RCloudFlexDashboard || {}; 28 | window.RCloudFlexDashboard.renderFlexDashboard = function(x, y) { 29 | oc.renderFlexDashboard(x, y).catch(onError).then(function() {}); 30 | } 31 | } 32 | 33 | k() 34 | }, 35 | 36 | render: function(target, html, k) { 37 | $('#rcloud-flexdashboard-loading').remove(); 38 | var content = ""; 39 | var parsed = $(html) 40 | var title = parsed.filter('title').text(); 41 | if(title!=="") { 42 | document.title = title 43 | } else { 44 | document.title = "RCloud flexdashboard" 45 | } 46 | $(target).html(content); 47 | k(null, target); 48 | } 49 | } 50 | 51 | })()); 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 |
4 | 5 |
6 |

7 | 8 | ## Install 9 | 10 | 1. Install the `rcloud.rmd` package and this package on RCloud, using 11 | `devtools::install_github()` or `install-github.me`: 12 | ```R 13 | source("https://install-github.me/att/rcloud.rmd") 14 | source("https://install-github.me/att/rcloud.flexdashboard") 15 | ``` 16 | 2. In the RCloud *Settings* menu, in the *Enable Extensions* line, add 17 | `rcloud.flexdashboard`, so that the package is loaded automatically. 18 | 3. Reload RCloud in the browser. This loads the package, and you should 19 | have the `flexdashboard.html` item in the special pages menu, right 20 | beside the RCloud logo in the top left corner 21 | 22 | ## Usage 23 | 24 | R Markdown code chunks correspond to `R` notebook cells, and R Markdown 25 | text corresponds to `markdown` notebook cells. 26 | 27 | To show the dashboard, select the `flexdashboard.html` special page, and 28 | then open it. `rcloud.flexdashboard` writes out an R Markdown file, calls 29 | `rmarkdown::render` on it, and then sends the outputted standalone HTML, 30 | embedded into an `iframe` to the browser. 31 | 32 | ## Developer notes 33 | 34 | The package uses `rcloud.rmd` to export the notebook to R Markdown. 35 | 36 | The only tricky part of the implementation is loading the `rcloud.flexdashboard` 37 | package when in the dashboard. The `call_notebook` OCAP could be used to 38 | evaluate the notebook, but this would require that the user loads the 39 | `rcloud.flexdashboard` package manually in the notebook, and also that the user 40 | calls a special function in the last cell to transfer the formatted notebook 41 | to the browser. This is a solution that is used in the `rcloud.rcap` package. 42 | 43 | Instead of this, we use a trick to trigger the loading of the package from JS. 44 | For this we call `rcloud._ocaps.load_module_package()` which loads the package 45 | as a side effect. We also need to define 46 | ```r 47 | .rcloud.export.ocaps <- function() { list() } 48 | ``` 49 | in the R package to avoid an error, because `load_module_package()` evaluates 50 | this. 51 | 52 | When the package loads, we set up an OCAP in `.onLoad()`, and also assign it to 53 | `window.RCloudFlexDashboard`, so that we can call it from JS: 54 | ```js 55 | rcloud._ocaps.load_module_package( 56 | "rcloud.flexdashboard", 57 | function(x) { 58 | window.RCloudFlexDashboard.renderFlexDashboard( 59 | notebook, 60 | version, 61 | function(x) { console.log(x); } 62 | ); 63 | } 64 | ); 65 | ``` 66 | 67 | The OCAP that we call simply saves the notebook to an R Markdown file (without 68 | actually evaluating it), and then calls `rmarkdown::render()` on it to create 69 | a standalone HTML file, which is then transmitted to the browser. 70 | 71 | While this implementation works for authenticated users, it does not currently 72 | work for anonymous users, and it might be rewritten completely, see e.g. 73 | https://github.com/att/rcloud.flexdashboard/issues/13 74 | -------------------------------------------------------------------------------- /inst/www/js/rcloud-flexdashboard.js: -------------------------------------------------------------------------------- 1 | // jshint ignore: start 2 | function main() { 3 | function getURLParameter(name) { 4 | return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search)||[,""])[1].replace(/\+/g, '%20'))||null; 5 | } 6 | 7 | function getQueryArgs() { 8 | var r, res = {}, s = location.search; 9 | while ((r = (new RegExp('[?|&]([^=&]+?)=([^&;#]+)(.*)').exec(s))) !== null) { 10 | res[decodeURIComponent(r[1])] = decodeURIComponent(r[2]); 11 | s = r[3]; 12 | } 13 | return res; 14 | } 15 | 16 | function onError(e) { 17 | $('#rcloud-flexdashboard-loading').remove(); 18 | RCloud.UI.fatal_dialog(e.message, "Close"); 19 | } 20 | 21 | rclient = RClient.create({ 22 | debug: false, 23 | mode: "client", // "IDE" = edit (separated), "call" = API (one process), "client" = JS (currently one process but may change) 24 | host: location.href.replace(/^http/,"ws").replace(/#.*$/,""), 25 | on_connect: function(ocaps) { 26 | rcloud = RCloud.create(ocaps.rcloud); 27 | var promise; 28 | if (rcloud.authenticated) { 29 | promise = rcloud.session_init(rcloud.username(), rcloud.github_token()); 30 | } else { 31 | promise = rcloud.anonymous_session_init(); 32 | } 33 | promise = promise.then(function(hello) { 34 | rclient.post_response(hello); 35 | }); 36 | 37 | // resolve(rcloud.init_client_side_data()); // what was this for?!? 38 | 39 | var notebook = getURLParameter("notebook"), 40 | version = getURLParameter("version"); 41 | var tag = getURLParameter("tag"); 42 | if(!version && tag) { 43 | promise = promise.then(function() { 44 | return rcloud.get_version_by_tag(notebook, tag) 45 | .then(function(v) { 46 | version = v; 47 | }); 48 | }); 49 | }; 50 | promise = promise.then(function() { 51 | // Just tell R to load the rcloud.flexdashboard package, 52 | // and the packag will do the rest 53 | rcloud._ocaps.load_module_package( 54 | "rcloud.flexdashboard", 55 | function(x) { 56 | window.RCloudFlexDashboard.renderFlexDashboard( 57 | notebook, 58 | version, 59 | function(x) { console.log(x); } 60 | ); 61 | } 62 | ); 63 | }); 64 | promise = promise.catch(onError); 65 | return true; 66 | }, 67 | on_data: RCloud.session.on_data, 68 | on_oob_message: RCloud.session.on_oob_message, 69 | on_error: function(msg, status_code) { 70 | // debugger; 71 | if (msg == 'Login failed. Shutting down!') { 72 | window.location = 73 | (window.location.protocol + 74 | '//' + window.location.host + 75 | '/login.R?redirect=' + 76 | encodeURIComponent(window.location.pathname + window.location.search)); 77 | return true; 78 | } else 79 | return false; 80 | } 81 | }); 82 | } 83 | -------------------------------------------------------------------------------- /inst/www/RCloud-flexo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xmlflexdashboard 113 | --------------------------------------------------------------------------------