# Copyright 2010-2014 Meik Michalke <meik.michalke@hhu.de>
#
# This file is part of the R package rkwarddev.
#
# rkwarddev is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# rkwarddev is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with rkwarddev. If not, see <http://www.gnu.org/licenses/>.
#' Create JavaScript outline from RKWard plugin XML
#'
#' You don't need to define a \code{preview()} function, as this can be added automatically by rkwarddev's code scanners.
#'
#' For previews, use \code{js(if("!is_preview") {...})} style JavaScript code to toggle between modes (applies to \code{preprocess}, \code{calculate} and
#' \code{printout}).
#'
#' @param require A character vector with names of R packages that the dialog depends on.
#' @param variables Either a character string to be included to read in all needed variables from the dialog (see \code{\link{rk.JS.scan}}),
#' or a (list of) objects of class \code{rk.JS.var} which will be coerced into character. These variables will be defined in
#' the \code{calculate()} and/or \code{printout()} functions.
#' @param globals Like \code{variables}, but these variables will be defined globally. If \code{variables} is set as well,
#' the function tries to remove duplicate definitions.
#' @param results.header A character string to headline the printed results. Include escapes quotes (\\") if needed.
#' Set to \code{FALSE} or \code{""} if you need more control and want to define the header section in \code{printout}.
#' @param header.add A named list of additional info to add to the header. Each entry must be named \code{add}
#' or \code{addFromUI} -- see \code{\link[rkwarddev:rk.JS.header]{rk.JS.header}} for details.
#' @param preprocess A character string to be included in the \code{preprocess()} function. This string will be
#' pasted as-is, after \code{require} has been evaluated.
#' @param calculate A character string to be included in the \code{calculate()} function. This string will be
#' pasted as-is, after \code{variables} has been evaluated.
#' @param printout A character string to be included in the \code{printout()} function. This string will be
#' pasted as-is, after \code{results.header} has been evaluated. Appended after \code{doPrintout} if set (which is deprecated).
#' @param doPrintout Deprecated: A character string to be included in the \code{doPrintout()} function. This string will be
#' pasted as-is.
#' @param preview Either a logical value, if \code{TRUE}, a \code{preview()} function will be added in any case.
#' Can also be a character string to be used as-is inside the \code{preview()} function.
#' @param load.silencer Either a character string (ID of probably a checkbox), or an object of class \code{XiMpLe.node}.
#' This defines a switch you can add to your plugin, to set the \code{require()} call inside \code{suppressMessages()},
#' hence suppressing all load messages (except for warnings and errors) of required packages in the output.
#' @param gen.info Logical, if \code{TRUE} a comment note will be written into the document,
#' that it was generated by \code{rkwarddev} and changes should be done to the script.
#' You can also provide a character string naming the very rkwarddev script file that generates this JS file,
#' which will then also be added to the comment.
#' @param indent.by A character string defining how indentation should be done.
#' @param level Integer, which indentation level to use, minimum is 1.
#' @param guess.getter Locigal, if \code{TRUE} try to get a good default getter function for JavaScript
#' variable values. Only used if \code{header.add} contains XiMpLe nodes.
#' @return A character string.
#' @seealso \code{\link[rkwarddev:rk.paste.JS]{rk.paste.JS}},
#' \code{\link[rkwarddev:rk.JS.vars]{rk.JS.vars}},
#' \code{\link[rkwarddev:rk.JS.array]{rk.JS.array}},
#' \code{\link[rkwarddev:js]{js}},
#' \code{\link[rkwarddev:echo]{echo}},
#' \code{\link[rkwarddev:id]{id}},
#' \code{\link[rkwarddev:rk.JS.header]{rk.JS.header}},
#' and the \href{help:rkwardplugins}{Introduction to Writing Plugins for RKWard}
#' @export
rk.JS.doc <- function(require=c(), variables=NULL, globals=NULL, results.header=NULL, header.add=list(),
preprocess=NULL, calculate=NULL, printout=NULL, doPrintout=NULL, preview=FALSE, load.silencer=NULL, gen.info=TRUE,
indent.by=rk.get.indent(), level=2, guess.getter=FALSE){
# variable to determine whether to add setGlobalVars() to preprocess() later
addSetGlobalVars <- FALSE
if(!is.null(doPrintout) | isTRUE(preview) | is.character(preview)){
needPreview <- TRUE
} else {
needPreview <- FALSE
}
allVarsJSvar <- all(!is.null(variables), sapply(variables, inherits, "rk.JS.var"))
allGlobJSvar <- all(!is.null(globals), sapply(globals, inherits, "rk.JS.var"))
if(isTRUE(allGlobJSvar)){
# get all JS var names from globals
JSVarNamesGlobals <- unlist(lapply(globals, paste.JS.var, names.only=TRUE))
# remove variables if there's duplicates in globals
if(isTRUE(allVarsJSvar)){
# first case: variables are rk.JS.var objects
# check variables for duplicates
variables <- lapply(variables,
function(thisVar){
thisVarVars <- slot(thisVar, "vars")
thisVarVarsNames <- sapply(thisVarVars, paste.JS.var, names.only=TRUE)
slot(thisVar, "vars") <- thisVarVars[!thisVarVarsNames %in% JSVarNamesGlobals]
return(thisVar)
}
)
} else if(is.character(variables)){
# second case: variables is a character vector
rawVarSoup <- unlist(strsplit(variables, ";"))
varsInGlobals <- grepl(
paste0("^(var)*[[:space:]]*(", paste0(JSVarNamesGlobals, collapse="|"),")[[:space:]=;]+"),
trim(rawVarSoup)
)
variables <- paste0(paste0(rawVarSoup[!varsInGlobals], collapse=";"), ";")
}
} else {}
# some data transformation
if(allVarsJSvar){
variables <- rk.paste.JS(
paste0(unlist(sapply(variables, function(x){rk.paste.JS(x, var=FALSE)})))
)
} else {}
if(allGlobJSvar){
globalNames <- paste0("var ", unlist(lapply(globals, paste.JS.var, names.only=TRUE)), ";", collapse="\n")
globalFunction <- paste0(
"function setGlobalVars(){\n",
paste0(
unlist(lapply(globals, rk.paste.JS, var=FALSE, level=level)),
collapse="\n"
),
"\n}",
collapse="\n"
)
globals <- paste0(globalNames, "\n\n", globalFunction)
addSetGlobalVars <- TRUE
} else {}
if(isTRUE(gen.info)){
js.gen.info <- rk.paste.JS(generator.info(), level=1)
} else if(is.character(gen.info)){
js.gen.info <- rk.paste.JS(generator.info(script=gen.info), level=1)
} else {
js.gen.info <- ""
}
if(!is.null(globals)){
js.globals <- paste0(
"// define variables globally\n",
trim.n(paste0(globals, collapse="")))
} else {
js.globals <- NULL
}
js.require <- unlist(sapply(require, function(this.req){
if(is.null(load.silencer)){
req.result <- writeRequire(requirement=this.req, needPreview=needPreview, suppress=FALSE, level=level, indent.by=indent.by)
} else {
# get the ID, if it's a XiMpLe.node
req.result <- rk.paste.JS(
jsChkSuppress <- rk.JS.vars(load.silencer),
# somehow "quietly=TRUE" doens't always do the trick
ite(jsChkSuppress,
writeRequire(requirement=this.req, needPreview=needPreview, suppress=TRUE, level=level + 1, indent.by=indent.by),
writeRequire(requirement=this.req, needPreview=needPreview, suppress=FALSE, level=level + 1, indent.by=indent.by)
)
)
}
return(req.result)
}))
js.preprocess <- paste0("function preprocess(is_preview){\n",
ifelse(isTRUE(addSetGlobalVars), paste0(indent(level=level, by=indent.by), "setGlobalVars();\n"), ""),
indent(level=level, by=indent.by), "// add requirements etc. here\n",
trim.n(paste(js.require, collapse="")),
"\n",
ifelse(is.null(preprocess), "", paste0("\n", preprocess, "\n")),
"}")
js.calculate <- paste0("function calculate(is_preview){\n",
# for plots we only need something here if calculate is not empty
if(!isTRUE(needPreview) | !is.null(calculate)){paste0(
ifelse(is.null(variables), "", paste0(
indent(level=level, by=indent.by), "// read in variables from dialog\n",
trim.n(paste(variables, collapse="")), "\n\n")),
ifelse(is.null(calculate),
paste0(indent(level=level, by=indent.by), "// generate the R code to be evaluated here\n"),
paste0(indent(level=level, by=indent.by), "// the R code to be evaluated\n",calculate, "\n")))
} else {},
"}")
js.printout <- paste0("function printout(is_preview){\n",
if(is.null(doPrintout)){
paste0(
if(isTRUE(needPreview)){
paste0(
ifelse(is.null(variables), "", paste0(
indent(level=level, by=indent.by), "// read in variables from dialog\n",
trim.n(paste(variables, collapse="")), "\n\n")),
indent(level=level, by=indent.by), "// printout the results\n",
if(is.character(results.header) & !identical(results.header, "")){
rk.paste.JS(
js(
if("!is_preview"){
rk.JS.header(results.header, guess.getter=guess.getter, .add=header.add)
} else {}
)
)
} else {}
)
} else {
paste0(
indent(2, by=indent.by), "// printout the results\n",
if(is.character(results.header) & !identical(results.header, "")){
paste0(indent(level=level, by=indent.by), rk.JS.header(results.header, guess.getter=guess.getter, .add=header.add), "\n")
} else {}
)
},
if(!is.null(printout)){
paste0(printout, "\n")
} else {}
)
} else {
rk.paste.JS(
"// all the real work is moved to a custom defined function doPrintout() below",
"// true in this case means: We want all the headers that should be printed in the output:",
"doPrintout(!is_preview);",
level=level, indent.by=indent.by)
},
"\n}"
)
# this part will create preview() and doPrintout(full), if needed
if(is.null(doPrintout)){
js.doPrintout <- ""
} else {
warning(
paste0(
"rk.JS.doc: using 'doPrintout' for previews is a deprecated feature and might be removed in future releases!\n",
" you should move your printout code to 'printout' and replace 'if(full)...' conditions with 'if(!is_preview))...'."
),
call.=FALSE
)
js.doPrintout <- paste0(
"function doPrintout(full){\n",
ifelse(is.null(variables), "", paste0(
indent(level=level, by=indent.by), "// read in variables from dialog\n",
trim.n(paste(variables, collapse="")), "\n\n")),
indent(level=level, by=indent.by), "// create the plot\n",
if(is.character(results.header) && !identical(results.header, "")){
rk.paste.JS(
js(
if("full"){
rk.JS.header(results.header, guess.getter=guess.getter, .add=header.add)
} else {}
)
)
} else {},
"\n\n",
doPrintout,
if(!is.null(printout)){
paste0("\n\n", indent(level=level, by=indent.by), "// left over from the printout function\n", printout, "\n\n")
} else {},
"\n}")
if(!is.character(preview)) {
preview <- TRUE
} else {}
}
# this part will create preview() if needed
if(isTRUE(preview)){
js.preview <- paste0("function preview(){\n",
rk.paste.JS(
"preprocess(true);",
"calculate(true);",
"printout(true);",
level=level, indent.by=indent.by
),
"\n}"
)
} else if(is.character(preview)) {
js.preview <- paste0("function preview(){\n",
rk.paste.JS(
preview,
level=level, indent.by=indent.by
),
"\n}"
)
} else {
js.preview <- ""
}
# clean up empty strings
all.js <- c(js.gen.info, js.globals, js.preview, js.preprocess, js.calculate, js.printout, js.doPrintout)
all.js <- all.js[!identical(all.js, "")]
JS.doc <- paste0(all.js, collapse="\n\n")
return(JS.doc)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.