├── .merlin ├── _tags ├── Makefile ├── opam ├── .travis.yml ├── index.html ├── README.md └── main.ml /.merlin: -------------------------------------------------------------------------------- 1 | PKG tyxml reactiveData js_of_ocaml 2 | -------------------------------------------------------------------------------- /_tags: -------------------------------------------------------------------------------- 1 | true: strict_sequence, bin_annot, warn(A-4) 2 | true: syntax(camlp4o), package(lwt, js_of_ocaml.syntax,js_of_ocaml,js_of_ocaml.tyxml,unix) 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # make JFLAGS="--pretty --noinline" 2 | JFLAGS = 3 | 4 | # Let ocamlbuild work out the dependencies. 5 | .PHONE: build-byte 6 | 7 | _build/main.js: build-byte 8 | js_of_ocaml ${JFLAGS} +weak.js _build/main.byte 9 | 10 | build-byte: 11 | ocamlbuild -cflag -g -no-links -use-ocamlfind main.byte 12 | 13 | clean: 14 | ocamlbuild -clean 15 | -------------------------------------------------------------------------------- /opam: -------------------------------------------------------------------------------- 1 | opam-version: "1.2" 2 | name: "js-skeleton" 3 | version: "dev" 4 | maintainer: "Thomas Leonard " 5 | authors: "Thomas Leonard " 6 | homepage: "https://github.com/talex5/js-skeleton" 7 | bug-reports: "https://github.com/talex5/js-skeleton/issues" 8 | license: "BSD-2-Clause" 9 | dev-repo: "https://github.com/talex5/js-skeleton.git" 10 | build: [ 11 | [make] 12 | ] 13 | depends: [ 14 | "tyxml" 15 | "reactiveData" 16 | "js_of_ocaml" 17 | "ocamlfind" {build} 18 | ] 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | install: wget https://raw.githubusercontent.com/ocaml/ocaml-travisci-skeleton/master/.travis-opam.sh 3 | script: bash -ex .travis-opam.sh 4 | sudo: false 5 | addons: 6 | apt: 7 | sources: 8 | - avsm 9 | packages: 10 | - ocaml 11 | - ocaml-base 12 | - ocaml-native-compilers 13 | - ocaml-compiler-libs 14 | - ocaml-interp 15 | - ocaml-base-nox 16 | - ocaml-nox 17 | - camlp4 18 | - camlp4-extra 19 | - time 20 | env: 21 | - FORK_USER=talex5 FORK_BRANCH=containers OPAMYES=true OCAML_VERSION=4.01 22 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | js-skeleton 6 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | js-skeleton 2 | =========== 3 | 4 | Copyright Thomas Leonard, 2015 5 | 6 | This is a template project showing how to build a Javascript web-app using `js_of_ocaml`, `TyXML` and `React`. 7 | 8 | 9 | Instructions 10 | ------------ 11 | 12 | You'll need the [opam](http://opam.ocaml.org/) package manager. 13 | It should be available through your distribution, but you can use a [generic opam binary](http://tools.ocaml.org/opam.xml) if it's missing or too old (I use opam 1.2). 14 | 15 | Install dependencies: 16 | 17 | opam install ocamlfind tyxml reactiveData js_of_ocaml 18 | 19 | Build: 20 | 21 | make 22 | 23 | To run, open `index.html` in a browser. 24 | 25 | All the code is in `main.ml` and is well commented. 26 | 27 | 28 | Conditions 29 | ---------- 30 | 31 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 32 | 33 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 34 | 35 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 36 | 37 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38 | -------------------------------------------------------------------------------- /main.ml: -------------------------------------------------------------------------------- 1 | (* Copyright (C) 2015, Thomas Leonard 2 | * See the README file for details. *) 3 | 4 | open React (* Provides [S] for signals (and [E] for events) *) 5 | 6 | (* Each module provides a [sig] block describing its public interface and a [struct] 7 | with its implementation. The [sig] blocks are optional, but make it easier to see 8 | a module's API quickly. In a larger program, you would put each module in a separate 9 | file. 10 | e.g. the contents of [Time]'s sig block would go in `time.mli` and the contents of 11 | its struct block in `time.ml`. *) 12 | 13 | module Time : sig 14 | (** A helper module to provide the current time as a reactive signal. *) 15 | 16 | val current : float S.t (** The current time, updated each second *) 17 | end = struct 18 | open Lwt.Infix (* Provides >>=, the "bind" / "and_then" operator *) 19 | 20 | let current, set_current = S.create (Unix.gettimeofday ()) 21 | 22 | let () = 23 | (* Update [current] every second *) 24 | let rec loop () = 25 | Lwt_js.sleep 1.0 >>= fun () -> 26 | set_current (Unix.gettimeofday ()); 27 | loop () in 28 | Lwt.async loop 29 | end 30 | 31 | module Model : sig 32 | (** The core application logic. *) 33 | 34 | val display : string S.t (** The output value to display on the screen *) 35 | val start : unit -> unit 36 | val stop : unit -> unit 37 | end = struct 38 | let state, set_state = S.create `Clear 39 | 40 | let start () = 41 | set_state (`Running_since (S.value Time.current)) 42 | 43 | let stop () = 44 | set_state ( 45 | match S.value state with 46 | | `Running_since start -> `Stopped_showing (S.value Time.current -. start) 47 | | `Stopped_showing _ | `Clear -> `Clear 48 | ) 49 | 50 | (* [calc time state] returns the string to display for a given time and state. 51 | Note: it works on regular values, not signals. *) 52 | let calc time = function 53 | | `Running_since start -> Printf.sprintf "%.0f s" (time -. start) 54 | | `Stopped_showing x -> Printf.sprintf "%.0f s (stopped)" x 55 | | `Clear -> "Ready" 56 | 57 | let display = 58 | (* [S.l2 calc] lifts the 2-argument function [calc] to work on 2 signals. 59 | [calc] will be called when either input changes. *) 60 | S.l2 calc Time.current state 61 | end 62 | 63 | module Templates : sig 64 | (** Render the model using HTML elements. *) 65 | 66 | val main : Html5_types.div Tyxml_js.Html5.elt 67 | (** The
element for the app. *) 68 | end = struct 69 | module R = Tyxml_js.R.Html5 (* Reactive elements, using signals *) 70 | open Tyxml_js.Html5 (* Ordinary, non-reactive HTML elements *) 71 | 72 | (* An "onclick" attribute that calls [fn] and returns [true], 73 | * ignoring the event object. *) 74 | let onclick fn = 75 | a_onclick (fun _ev -> fn (); true) 76 | 77 | let main = div [ 78 | div ~a:[a_class ["display"]] [R.pcdata Model.display]; 79 | button ~a:[onclick Model.start] [pcdata "Start"]; 80 | button ~a:[onclick Model.stop] [pcdata "Stop"]; 81 | ] 82 | end 83 | 84 | (* Initialisation code, called at start-up. *) 85 | let () = 86 | (* Add [Templates.main] to the . *) 87 | let main_div = Tyxml_js.To_dom.of_node Templates.main in 88 | Dom_html.document##body##appendChild(main_div) |> ignore 89 | --------------------------------------------------------------------------------