bookclub-advr

DSLC Advanced R Book Club
git clone https://git.eamoncaddigan.net/bookclub-advr.git
Log | Files | Refs | README | LICENSE

DiagrammeR.js (6644B)


      1 HTMLWidgets.widget({
      2 
      3   name: 'DiagrammeR',
      4 
      5   type: 'output',
      6 
      7   initialize: function(el, width, height) {
      8 
      9     /* wait to initialize until renderValue
     10         since x not provided until then
     11         and mermaid will try to build the diagram
     12         as soon as class of the div is set to "mermaid"
     13     */
     14 
     15     /* to prevent auto init() by mermaid
     16         not documented but
     17         see lines https://github.com/knsv/mermaid/blob/master/src/main.js#L100-L109
     18           mermaid_config in global with mermaid_config.startOnLoad = false
     19         appears to turn off the auto init behavior
     20         allowing us to callback after manually init and then callback
     21         after complete
     22     */
     23     window.mermaid.startOnLoad = false;
     24 
     25     // set config options for Gantt
     26     //   undocumented but these can be provided
     27     //   so from R
     28     //   m1 <- mermaid(spec)
     29     //   m1$x$config = list(ganttConfig = list( barHeight = 100 ) )
     30     mermaid.ganttConfig = {
     31         titleTopMargin:25,
     32         barHeight:20,
     33         barGap:4,
     34         topPadding:50,
     35         sidePadding:100,
     36         gridLineStartPadding:35,
     37         fontSize:11,
     38         numberSectionStyles:4,
     39         axisFormatter: [
     40             // Within a day
     41             ["%I:%M", function (d) {
     42                 return d.getHours();
     43             }],
     44             // Monday a week
     45             ["w. %U", function (d) {
     46                 return d.getDay() == 1;
     47             }],
     48             // Day within a week (not monday)
     49             ["%a %d", function (d) {
     50                 return d.getDay() && d.getDate() != 1;
     51             }],
     52             // within a month
     53             ["%b %d", function (d) {
     54                 return d.getDate() != 1;
     55             }],
     56             // Month
     57             ["%m-%y", function (d) {
     58                 return d.getMonth();
     59             }]
     60         ]
     61     };
     62 
     63     return {
     64       // TODO: add instance fields as required
     65     }
     66 
     67   },
     68 
     69   renderValue: function(el, x, instance) {
     70 
     71     // if no diagram provided then assume
     72     // that the diagrams are provided through htmltools tags
     73     // and DiagrammeR was just used for dependencies
     74     if ( x.diagram != "" ) {
     75       el.innerHTML = x.diagram;
     76       //if dynamic such as shiny remove data-processed
     77       // so mermaid will reprocess and redraw
     78       el.removeAttribute("data-processed");
     79       el.classList.add('mermaid');
     80       //make sure if shiny that we turn display back on
     81       el.style.display = "";
     82       //again if dynamic such as shiny
     83       //  explicitly run mermaid.init()
     84     } else {
     85       // set display to none
     86       // should we remove instead??
     87       el.style.display = "none";
     88     }
     89 
     90     // check for undocumented ganttConfig
     91     //   to override the defaults manually entered
     92     //   in initialize above
     93     //   note this is really sloppy and will not
     94     //   work well if multiple gantt charts
     95     //   with custom configs here
     96     if( typeof x.config !== "undefined" &&
     97          typeof x.config.ganttConfig !== "undefined" ){
     98       Object.keys(x.config.ganttConfig).map(function(k){
     99         window.mermaid.ganttConfig[k] = x.config.ganttConfig[k];
    100       })
    101     }
    102 
    103 
    104     // use this to sort of make our diagram responsive
    105     //  or at a minimum fit within the bounds set by htmlwidgets
    106     //  for the parent container
    107     function makeResponsive(el){
    108        var svg = el.getElementsByTagName("svg")[0];
    109        if(svg){
    110         if(svg.width) {svg.removeAttribute("width")};
    111         if(svg.height) {svg.removeAttribute("height")};
    112         svg.style.width = "100%";
    113         svg.style.height = "100%";
    114        }
    115     };
    116 
    117 
    118     // get all DiagrammeR mermaids widgets
    119     dg = document.getElementsByClassName("DiagrammeR");
    120     // run mermaid.init
    121     //  but use try catch block
    122     //  to send error to the htmlwidget for display
    123     try{
    124       mermaid.init( el );
    125 
    126       // sort of make our diagram responsive
    127       //   should we make this an option?
    128       //   if so, then could easily add to list of post process tasks
    129       makeResponsive( el );
    130 
    131       if (HTMLWidgets.shinyMode) {
    132         // Get widget id
    133         var id = el.id;
    134 
    135         $("#" + id + " .node").click(function(e) {
    136           // Build return object *obj* with node-id and node textContent
    137           var obj = {
    138             id: e.currentTarget.id,
    139             nodeValues: e.currentTarget.textContent
    140           };
    141           // Send *obj* to Shiny's inputs (input$[id]+_click  e.g.: input$vtree_click))
    142           Shiny.setInputValue(id + "_click", obj, {priority: "event"});
    143         });
    144       }
    145 
    146       /*
    147       // change the id of our SVG assigned by mermaid to prevent conflict
    148       //   mermaid.init has a counter that will reset to 0
    149       //   and cause duplication of SVG id if multiple
    150       d3.select(el).select("svg")
    151         .attr("id", "mermaidChart-" + el.id);
    152       // now we have to change the styling assigned by mermaid
    153       //   to point to our new id that we have assigned
    154       //   will add if since sequence diagrams do not have stylesheet
    155       if(d3.select(el).select("svg").select("style")[0][0]){
    156         d3.select(el).select("svg").select("style")[0][0].innerHTML = d3.select(el).select("svg")
    157           .select("style")[0][0].innerHTML
    158       */
    159       /// sep comment for / in regex    .replace(/mermaidChart[0-9]*/gi, "mermaidChart-" + el.id);
    160       /*}
    161       */
    162 
    163       // set up a container for tasks to perform after completion
    164       //  one example would be add callbacks for event handling
    165       //  styling
    166       if (!(typeof x.tasks === "undefined") ){
    167         if ( (typeof x.tasks.length === "undefined") ||
    168          (typeof x.tasks === "function" ) ) {
    169            // handle a function not enclosed in array
    170            // should be able to remove once using jsonlite
    171            x.tasks = [x.tasks];
    172         }
    173         x.tasks.map(function(t){
    174           // for each tasks add it to the mermaid.tasks with el
    175           t.call(el);
    176         })
    177       }
    178 
    179     } catch(e) {
    180       // if error look for last processed DiagrammeR
    181       //  and send error to the container div
    182       //  with pre containing the errors
    183       var processedDg = d3.selectAll(".DiagrammeR[data-processed=true]");
    184       // select the last
    185       processedDg = d3.select(processedDg[0][processedDg[0].length - 1])
    186       // remove the svg
    187       processedDg.select("svg").remove();
    188 
    189       //if dynamic such as shiny remove data-processed
    190       // so mermaid will reprocess and redraw
    191       if (HTMLWidgets.shinyMode) {
    192         el.removeAttribute("data-processed")
    193       }
    194 
    195       processedDg.append("pre").html( ["parse error with " + x.diagram, e.message].join("\n") )
    196     }
    197 
    198   },
    199 
    200   resize: function(el, width, height, instance) {
    201 
    202   }
    203 
    204 
    205 });