Using TyXML

Load tyxml and the tyxml syntax extension.


In [1]:
#require "tyxml,iocaml-kernel.notebook"


	Camlp4 Parsing version 4.02.0+rc1

/home/andyman/.opam/4.02.0+rc1/lib/ocaml/str.cma: loaded
/home/andyman/.opam/4.02.0+rc1/lib/tyxml: added to search path
/home/andyman/.opam/4.02.0+rc1/lib/tyxml/tyxml.cma: loaded
/home/andyman/.opam/4.02.0+rc1/lib/iocaml-kernel: added to search path

In [2]:
#require "tyxml.syntax"


/home/andyman/.opam/4.02.0+rc1/lib/tyxml/pa_tyxml.cma: loaded

HTML

Define a function which takes a list of expanded tyxml html quotations and displays them.


In [3]:
(* setup pa_tyxml so we can write html quotations *)
let html q = 
    Html5.P.print_list ~output:(output_string Iocaml.mime) q;
    Iocaml.send_mime "text/html"

module Html5 = Html5.M


Out[3]:
val html : 'a Html5.M.elt list -> unit = <fun>
Out[3]:
module Html5 = Html5.M

Simple HTML example.


In [4]:
html [
    << <h1> hello </h1> >>;
    << <p> world </p> >>
]


hello

world

Out[4]:
- : unit = ()

Javascript

Javascript example. Load Chart.js and display a line chart with randomly generated data. Note that the javascript is embedded into the notebook and will be run when it is (re)loaded. If this causes an exception during loading the notebook will fail to start the kernel.


In [5]:
let random_data () = 
    let a = Array.init 7 (fun _ -> Random.int 100) in
    List.fold_left (fun s x ->
        let x = string_of_int x in
        if s="" then x else s ^ "," ^ x) "" (Array.to_list a)
;;
    
let run () = html [
    << <canvas id="myChart" width="400" height="400"/> >>;
    << <script> 
    
    $$.getScript("https://rawgit.com/nnnick/Chart.js/master/Chart.js", function () {
        var ctx = document.getElementById("myChart").getContext("2d");
        var data = {
            labels : ["January","February","March","April","May","June","July"],
            datasets : [
                {
                    fillColor : "rgba(220,220,220,0.5)",
                    strokeColor : "rgba(220,220,220,1)",
                    pointColor : "rgba(220,220,220,1)",
                    pointStrokeColor : "#fff",
                    data : [$str:random_data()$]
                },
                {
                    fillColor : "rgba(151,187,205,0.5)",
                    strokeColor : "rgba(151,187,205,1)",
                    pointColor : "rgba(151,187,205,1)",
                    pointStrokeColor : "#fff",
                    data : [$str:random_data()$]
                }
            ]
        }
        new Chart(ctx).Line(data);
    })
    
    </script> >>;   
]


Out[5]:
val random_data : unit -> string = <fun>
Out[5]:
val run : unit -> unit = <fun>

In [6]:
run ()


Out[6]:
- : unit = ()

SVG

Here's a programatic example of SVG generation (because I cannot get the syntax extension to work).


In [7]:
let svg q = 
    Svg.P.print_list ~output:(output_string Iocaml.mime) q;
    Iocaml.send_mime "text/html"


Out[7]:
val svg : 'a Svg.M.elt list -> unit = <fun>

In [8]:
let d = (Svg.M.(
    svg ~a:[a_width (100.,None); a_height (100.,None)]
        [circle ~a:[a_r (40.,None); a_cx (50.,None); a_cy (50.,None) ] 
             []
        ]))


Out[8]:
val d : [> Svg_types.svg ] Svg.M.elt = <abstr>

In [9]:
Svg.P.print_list ~output:print_string [d]


<svg width="100" height="100"><circle r="40" cx="50" cy="50" /></svg>
Out[9]:
- : unit = ()

In [11]:
svg [d]


Out[11]:
- : unit = ()
Out[11]:
- : unit = ()

_I believe there is a bug with patyxml (2.2.0) for svg output. This is a test that works with a fix provided upstream.


In [12]:
svg (let module Svg = Svg.M in [ <:svg< <svg width="100" height="100"><circle r="40" cx="50" cy="50" /></svg> >> ])


Out[12]:
- : unit = ()

#install_printer

IPython has the ability to print a rich representation of the output value returned by a cell. We can do something similar by subverting the OCaml #install_printer directive.


In [13]:
let html_printer_list fmt t = (html t; Format.fprintf fmt "<html>"; ())
let html_printer fmt x = html_printer_list fmt [x]


Out[13]:
val html_printer_list : Format.formatter -> 'a Html5.M.elt list -> unit =
  <fun>
Out[13]:
val html_printer : Format.formatter -> 'a Html5.M.elt -> unit = <fun>

In [14]:
#install_printer html_printer;;
#install_printer html_printer_list;;

In [15]:
let _ = << <b> automatically printed </b> >>


automatically printed
Out[15]:
- : [> Html5_types.b ] Html5.elt = <html>

The TyXML syntax extension does not allow quotations at the expression level so we need to wrap it with the let _.

The next example is a html table printer for some simple types with a bit of control over formatting.


In [16]:
type html_table = 
   | String_list of int * string list
   | String_array of int * string array
   | Int_list of int * int list
   | Int_array of int * int array
   
let rec html_table data = 
    let rec f width n row str = 
        match str with
        | [] -> if row=[] then [] else [List.rev row]
        | h::t ->
            if n=width then List.rev row :: f width 0 [] str
            else f width (n+1) (h::row) t
    in
    let print l = 
        let row = List.map (fun x -> << <td>$str:x$</td> >>) in
        let rows = List.map (fun x -> << <tr>$list:row x$</tr> >>) in
        << <table> $list:rows l$ </table> >>
    in
    match data with
    | String_list(w,l) -> print (f w 0 [] l)
    | String_array(w,l) -> html_table (String_list(w, Array.to_list l))
    | Int_list(w,l) -> html_table (String_list(w, List.map string_of_int l))
    | Int_array(w,l) -> html_table (Int_list(w, Array.to_list l))

let html_table_printer fmt x = (html [(html_table x)]; Format.fprintf fmt "<table>"; ());;

#install_printer html_table_printer;;


Out[16]:
type html_table =
    String_list of int * string list
  | String_array of int * string array
  | Int_list of int * int list
  | Int_array of int * int array
Out[16]:
val html_table : html_table -> [> Html5_types.table ] Html5.elt = <fun>
Out[16]:
val html_table_printer : Format.formatter -> html_table -> unit = <fun>

In [17]:
String_list(2, [ "john"; "smith"; "joe"; "blogs" ])


johnsmith
joeblogs
Out[17]:
- : html_table = <table>

In [18]:
let data = Array.init 21 (fun i -> i) in
Array.init 3(fun i -> Int_array((i+1)*3,data))


012
345
678
91011
121314
151617
181920
012345
67891011
121314151617
181920
012345678
91011121314151617
181920
Out[18]:
- : html_table array = [|<table>; <table>; <table>|]