changeset 645:1b571a05874c

React demo
author Adam Chlipala <adamc@hcoop.net>
date Tue, 10 Mar 2009 11:18:01 -0400
parents 8e17e6b615bd
children fb2a0e76dcef
files demo/prose demo/react.ur demo/react.urp demo/react.urs include/urweb.h src/c/urweb.c src/cjr_print.sml src/scriptcheck.sml
diffstat 8 files changed, 27 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/demo/prose	Tue Mar 10 10:49:18 2009 -0400
+++ b/demo/prose	Tue Mar 10 11:18:01 2009 -0400
@@ -191,3 +191,9 @@
 <p>Ur/Web makes it easy to write code whose execution should be distributed between the web server and client web browsers.  Server-side code is compiled to efficient native code, and client-side code is compiled to JavaScript.  Ur/Web programmers don't need to worry about these details, because the language and standard library provide a uniform ML-like interface for the whole process.</p>
 
 <p>Here's an example of a button that, when clicked, opens an alert dialog on the client.</p>
+
+react.urp
+
+<p>Most client-side JavaScript programs modify page contents imperatively, but Ur/Web is based on functional-reactive programming instead.  Programs allocate data sources and then describe the page as a pure function of those data sources.  When the sources change, the page changes automatically.</p>
+
+<p>Here's an example where a button modifies a data source that affects some text on the page.  The affected portion of the page is indicated with the pseudo-HTML tag <tt>dyn</tt>, whose <tt>signal</tt> attribute specifies one of these pure functions over mutable sources.  A source containing data of type <tt>t</tt> has type <tt>source t</tt> and is created with the <tt>source</tt> operation within the <tt>transaction</tt> monad.  Functions over sources are represented in the monad <tt>signal</tt>.  Like in Haskell, we overload monad notations, so that the same return and bind operators can be used to write signals and transactions.  The <tt>signal</tt> function coerces a source to a signal.</p>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demo/react.ur	Tue Mar 10 11:18:01 2009 -0400
@@ -0,0 +1,6 @@
+fun main () =
+  s <- source "You didn't click it yet.";
+  return <xml><body>
+    <button value="Click me!" onclick={set s "Now you clicked it."}/><br/>
+    <dyn signal={v <- signal s; return <xml>{[v]}</xml>}/>
+  </body></xml>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demo/react.urp	Tue Mar 10 11:18:01 2009 -0400
@@ -0,0 +1,2 @@
+
+react
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demo/react.urs	Tue Mar 10 11:18:01 2009 -0400
@@ -0,0 +1,1 @@
+val main : unit -> transaction page
--- a/include/urweb.h	Tue Mar 10 10:49:18 2009 -0400
+++ b/include/urweb.h	Tue Mar 10 11:18:01 2009 -0400
@@ -40,7 +40,7 @@
 uw_unit uw_Basis_set_client_source(uw_context, uw_Basis_int, uw_Basis_string);
 
 void uw_set_script_header(uw_context, const char*);
-char *uw_Basis_get_script(uw_context, uw_unit);
+const char *uw_Basis_get_script(uw_context, uw_unit);
 
 char *uw_Basis_htmlifyInt(uw_context, uw_Basis_int);
 char *uw_Basis_htmlifyFloat(uw_context, uw_Basis_float);
--- a/src/c/urweb.c	Tue Mar 10 10:49:18 2009 -0400
+++ b/src/c/urweb.c	Tue Mar 10 11:18:01 2009 -0400
@@ -382,11 +382,9 @@
   ctx->script_front += len;
 }
 
-char *uw_Basis_get_script(uw_context ctx, uw_unit u) {
+const char *uw_Basis_get_script(uw_context ctx, uw_unit u) {
   if (ctx->script_front == ctx->script) {
-    char *r = uw_malloc(ctx, 1);
-    r[0] = 0;
-    return r;
+    return ctx->script_header;
   } else {
     char *r = uw_malloc(ctx, 41 + (ctx->script_front - ctx->script) + strlen(ctx->script_header));
 
--- a/src/cjr_print.sml	Tue Mar 10 10:49:18 2009 -0400
+++ b/src/cjr_print.sml	Tue Mar 10 11:18:01 2009 -0400
@@ -2349,7 +2349,10 @@
                                     newline,
                                     string "uw_set_script_header(ctx, \"",
                                     string (case side of
-                                                ServerAndClient => "<script src=\\\"/app.js\\\"></script>\\n"
+                                                ServerAndClient => "<script src=\\\""
+                                                                   ^ OS.Path.joinDirFile {dir = !Monoize.urlPrefix,
+                                                                                          file = "app.js"}
+                                                                   ^ "\\\"></script>\\n"
                                               | ServerOnly => ""),
                                     string "\");",
                                     newline]),
--- a/src/scriptcheck.sml	Tue Mar 10 10:49:18 2009 -0400
+++ b/src/scriptcheck.sml	Tue Mar 10 11:18:01 2009 -0400
@@ -38,8 +38,10 @@
 val csBasis = SS.addList (SS.empty,
                           ["new_client_source",
                            "get_client_source",
-                           "set_client_source",
-                           "alert"])
+                           "set_client_source"])
+
+val scriptWords = ["<script",
+                   " onclick="]
 
 fun classify (ds, ps) =
     let
@@ -54,7 +56,7 @@
             let
                 fun hasClient e =
                     case #1 e of
-                        EPrim (Prim.String s) => inString {needle = "<script", haystack = s}
+                        EPrim (Prim.String s) => List.exists (fn n => inString {needle = n, haystack = s}) scriptWords
                       | EPrim _ => false
                       | ERel _ => false
                       | ENamed n => IS.member (csids, n)