Skip to content

Commit

Permalink
version 0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
rich-iannone authored and cran-robot committed Feb 4, 2016
0 parents commit 59dbb88
Show file tree
Hide file tree
Showing 19 changed files with 754 additions and 0 deletions.
22 changes: 22 additions & 0 deletions DESCRIPTION
@@ -0,0 +1,22 @@
Package: DiagrammeRsvg
Type: Package
Title: Export DiagrammeR Graphviz Graphs as SVG
Version: 0.1
Date: 2016-02-02
Authors@R: person(
"Richard", "Iannone"
, role = c("aut", "cre")
, email = "riannone@me.com"
)
Maintainer: Richard Iannone <riannone@me.com>
Description: Allows for export of DiagrammeR Graphviz objects to SVG.
License: MIT + file LICENSE
Imports: V8 (>= 0.10)
URL: https://github.com/rich-iannone/DiagrammeRsvg
BugReports: https://github.com/rich-iannone/DiagrammeRsvg/issues
RoxygenNote: 5.0.1
NeedsCompilation: no
Packaged: 2016-02-03 22:57:22 UTC; riannone
Author: Richard Iannone [aut, cre]
Repository: CRAN
Date/Publication: 2016-02-04 11:27:31
2 changes: 2 additions & 0 deletions LICENSE
@@ -0,0 +1,2 @@
YEAR: 2016
COPYRIGHT HOLDER: Richard Iannone
18 changes: 18 additions & 0 deletions MD5
@@ -0,0 +1,18 @@
b57b0037679924de303e78bfeef0a5a7 *DESCRIPTION
5bc3b4b14b49ebcc410d51288b2df7bc *LICENSE
7043dc5cc00de15b212e100d1debabf1 *NAMESPACE
767abd58bb19f11af3592513b7d0f77b *R/export_svg.R
ba8971116d0fa84a1c73a7f61082a273 *README.md
fdfe1e83b79eec205a4843cabcb0a971 *inst/htmlwidgets/DiagrammeR.js
49b47a8ee6d9356eadd28546e5385573 *inst/htmlwidgets/DiagrammeR.yaml
c93c404ae98abcb660c70a31f58e25e2 *inst/htmlwidgets/grViz.js
db70d925759cdf5fbda9ecf69cc9f371 *inst/htmlwidgets/grViz.yaml
5e44e63b7491ec4b18f73d1b38334a04 *inst/htmlwidgets/lib/d3/LICENSE
c9ddc1063ecffea499c39c74f96bccc0 *inst/htmlwidgets/lib/d3/d3.min.js
04d7548521ef50aea6ddc72a7f1981f6 *inst/htmlwidgets/lib/dagre-d3/LICENSE
0fac740c944fa84c3646d8d60882c17c *inst/htmlwidgets/lib/dagre-d3/dagre-d3.min.js
4c6828671350452fdcb46bf28af7f42a *inst/htmlwidgets/lib/styles/styles.css
0597ece6806477c1382a570bf20e40a8 *inst/htmlwidgets/lib/viz/viz.js
4730f144eb0807d31fdeea3e4517d280 *inst/htmlwidgets/vivagraph.js
c38b9b43fa32fd10d0295d8441d905c2 *inst/htmlwidgets/vivagraph.yaml
d44b1090da7705eec8314b6cf8fc48bc *man/export_svg.Rd
5 changes: 5 additions & 0 deletions NAMESPACE
@@ -0,0 +1,5 @@
# Generated by roxygen2: do not edit by hand

export(export_svg)
importFrom(V8,new_context)
importFrom(utils,packageVersion)
48 changes: 48 additions & 0 deletions R/export_svg.R
@@ -0,0 +1,48 @@
#' Export grViz graph as SVG with \code{V8}
#' @description Use viz.js with \code{V8} to get the diagram rendered as SVG
#' in R instead of the browser.
#' @param gv htmlwidget to render as SVG.
#' @return \code{string} of SVG XML text.
#' @examples
#' \dontrun{
#' library(DiagrammeR)
#' svg <- export_svg(grViz('digraph{a->b; c->a; c->b; c->d;}'))
#'
#' # this can then be used with htmltools and can save significantly
#' # on size of output using svg rather than unrendered grViz
#' library(htmltools)
#' html_print(HTML(svg))
#' }
#' @importFrom V8 new_context
#' @importFrom utils packageVersion
#' @export export_svg

export_svg <- function(gv){

# Check to make sure that V8 is available
if(!requireNamespace("V8")) stop("V8 is required to export.",
call. = FALSE)

# Ensure that the minimum version of V8 is 1.0
stopifnot(packageVersion("V8") >= "0.10")

# Check to make sure gv is grViz
if(!inherits(gv, "grViz")) "gv must be a grViz htmlwidget."

# Create a new V8 context
ct <- new_context("window")

# Source the `vis.js` JS library
invisible(ct$source(system.file("htmlwidgets/lib/viz/viz.js",
package = "DiagrammeR")))

# Create the SVG file
svg <-
ct$call("Viz",
gv$x$diagram,
"svg",
gv$x$config$engine,
gv$x$config$options)

return(svg)
}
9 changes: 9 additions & 0 deletions README.md
@@ -0,0 +1,9 @@
# DiagrammeRsvg

A utility package for providing **SVG** export to **DiagrammeR** graph diagrams.

To install the package, use this statement:

```R
devtools::install_github('rich-iannone/DiagrammeRsvg')
```
190 changes: 190 additions & 0 deletions inst/htmlwidgets/DiagrammeR.js
@@ -0,0 +1,190 @@
HTMLWidgets.widget({

name: 'DiagrammeR',

type: 'output',

initialize: function(el, width, height) {

/* wait to initialize until renderValue
since x not provided until then
and mermaid will try to build the diagram
as soon as class of the div is set to "mermaid"
*/

/* to prevent auto init() by mermaid
not documented but
see lines https://github.com/knsv/mermaid/blob/master/src/main.js#L100-L109
mermaid_config in global with mermaid_config.startOnLoad = false
appears to turn off the auto init behavior
allowing us to callback after manually init and then callback
after complete
*/
window.mermaid.startOnLoad = false;

// set config options for Gantt
// undocumented but these can be provided
// so from R
// m1 <- mermaid(spec)
// m1$x$config = list(ganttConfig = list( barHeight = 100 ) )
mermaid.ganttConfig = {
titleTopMargin:25,
barHeight:20,
barGap:4,
topPadding:50,
sidePadding:100,
gridLineStartPadding:35,
fontSize:11,
numberSectionStyles:4,
axisFormatter: [
// Within a day
["%I:%M", function (d) {
return d.getHours();
}],
// Monday a week
["w. %U", function (d) {
return d.getDay() == 1;
}],
// Day within a week (not monday)
["%a %d", function (d) {
return d.getDay() && d.getDate() != 1;
}],
// within a month
["%b %d", function (d) {
return d.getDate() != 1;
}],
// Month
["%m-%y", function (d) {
return d.getMonth();
}]
]
};

return {
// TODO: add instance fields as required
}

},

renderValue: function(el, x, instance) {

// if no diagram provided then assume
// that the diagrams are provided through htmltools tags
// and DiagrammeR was just used for dependencies
if ( x.diagram != "" ) {
el.innerHTML = x.diagram;
//if dynamic such as shiny remove data-processed
// so mermaid will reprocess and redraw
el.removeAttribute("data-processed");
el.classList.add('mermaid');
//make sure if shiny that we turn display back on
el.style.display = "";
//again if dynamic such as shiny
// explicitly run mermaid.init()
} else {
// set display to none
// should we remove instead??
el.style.display = "none";
}

// check for undocumented ganttConfig
// to override the defaults manually entered
// in initialize above
// note this is really sloppy and will not
// work well if multiple gantt charts
// with custom configs here
if( typeof x.config !== "undefined" &&
typeof x.config.ganttConfig !== "undefined" ){
Object.keys(x.config.ganttConfig).map(function(k){
window.mermaid.ganttConfig[k] = x.config.ganttConfig[k];
})
}


// use this to sort of make our diagram responsive
// or at a minimum fit within the bounds set by htmlwidgets
// for the parent container
function makeResponsive(el){
var svg = el.getElementsByTagName("svg")[0];
if(svg){
if(svg.width) {svg.removeAttribute("width")};
if(svg.height) {svg.removeAttribute("height")};
svg.style.width = "100%";
svg.style.height = "100%";
}
};


// get all DiagrammeR mermaids widgets
dg = document.getElementsByClassName("DiagrammeR");
// run mermaid.init
// but use try catch block
// to send error to the htmlwidget for display
try{
mermaid.init( el );

// sort of make our diagram responsive
// should we make this an option?
// if so, then could easily add to list of post process tasks
makeResponsive( el );

/*
// change the id of our SVG assigned by mermaid to prevent conflict
// mermaid.init has a counter that will reset to 0
// and cause duplication of SVG id if multiple
d3.select(el).select("svg")
.attr("id", "mermaidChart-" + el.id);
// now we have to change the styling assigned by mermaid
// to point to our new id that we have assigned
// will add if since sequence diagrams do not have stylesheet
if(d3.select(el).select("svg").select("style")[0][0]){
d3.select(el).select("svg").select("style")[0][0].innerHTML = d3.select(el).select("svg")
.select("style")[0][0].innerHTML
*/
/// sep comment for / in regex .replace(/mermaidChart[0-9]*/gi, "mermaidChart-" + el.id);
/*}
*/

// set up a container for tasks to perform after completion
// one example would be add callbacks for event handling
// styling
if (!(typeof x.tasks === "undefined") ){
if ( (typeof x.tasks.length === "undefined") ||
(typeof x.tasks === "function" ) ) {
// handle a function not enclosed in array
// should be able to remove once using jsonlite
x.tasks = [x.tasks];
}
x.tasks.map(function(t){
// for each tasks add it to the mermaid.tasks with el
t.call(el);
})
}

} catch(e) {
// if error look for last processed DiagrammeR
// and send error to the container div
// with pre containing the errors
var processedDg = d3.selectAll(".DiagrammeR[data-processed=true]");
// select the last
processedDg = d3.select(processedDg[0][processedDg[0].length - 1])
// remove the svg
processedDg.select("svg").remove();

//if dynamic such as shiny remove data-processed
// so mermaid will reprocess and redraw
if (HTMLWidgets.shinyMode) {
el.removeAttribute("data-processed")
}

processedDg.append("pre").html( ["parse error with " + x.diagram, e.message].join("\n") )
}

},

resize: function(el, width, height, instance) {

}


});
22 changes: 22 additions & 0 deletions inst/htmlwidgets/DiagrammeR.yaml
@@ -0,0 +1,22 @@
dependencies:
- name: d3
version: 3.3.8
src: htmlwidgets/lib/d3
script: d3.min.js
- name: dagre
version: 0.4.0
src: "htmlwidgets/lib/dagre-d3"
script: "dagre-d3.min.js"
- name: mermaid
version: 0.3.0
src: htmlwidgets/lib/mermaid
script: dist/mermaid.slim.min.js
stylesheet: dist/mermaid.css
- name: DiagrammeR-styles
version: 0.2
src: htmlwidgets/lib/styles
stylesheet: styles.css
- name: chromatography
version: 0.1
src: htmlwidgets/lib/chromatography
script: chromatography.js
71 changes: 71 additions & 0 deletions inst/htmlwidgets/grViz.js
@@ -0,0 +1,71 @@
HTMLWidgets.widget({

name: 'grViz',

type: 'output',

initialize: function(el, width, height) {

return {
// TODO: add instance fields as required
}

},

renderValue: function(el, x, instance) {
// use this to sort of make our diagram responsive
// or at a minimum fit within the bounds set by htmlwidgets
// for the parent container
function makeResponsive(el){
var svg = el.getElementsByTagName("svg")[0];
if(svg){
if(svg.width) {svg.removeAttribute("width")};
if(svg.height) {svg.removeAttribute("height")};
svg.style.width = "100%";
svg.style.height = "100%";
}
};

if ( x.diagram != "" ) {

if ( typeof x.config === "undefined" ){
x.config = {};
x.config.engine = "dot";
x.config.options = {};
}

try {
el.innerHTML = Viz( x.diagram, format="svg", engine=x.config.engine, options=x.config.options );

makeResponsive(el);

// set up a container for tasks to perform after completion
// one example would be add callbacks for event handling
// styling
if (!(typeof x.tasks === "undefined") ){
if ( (typeof x.tasks.length === "undefined") ||
(typeof x.tasks === "function" ) ) {
// handle a function not enclosed in array
// should be able to remove once using jsonlite
x.tasks = [x.tasks];
}
x.tasks.map(function(t){
// for each tasks add it to the mermaid.tasks with el
t.call(el);
})
}
} catch(e){
var p = document.createElement("pre")
p.innerText = e;
el.appendChild(p);
}
}

},

resize: function(el, width, height, instance) {

}


});

0 comments on commit 59dbb88

Please sign in to comment.