changeset 2220:794017f378de

Merge.
author Ziv Scully <ziv@mit.edu>
date Mon, 24 Nov 2014 20:47:38 -0500
parents ff38b3e0cdfd f05fcb206571
children 278e10629ba1
files include/urweb/urweb_cpp.h src/c/urweb.c src/monoize.sml
diffstat 13 files changed, 300 insertions(+), 93 deletions(-) [+]
line wrap: on
line diff
--- a/doc/manual.tex	Mon Nov 24 20:41:24 2014 -0500
+++ b/doc/manual.tex	Mon Nov 24 20:47:38 2014 -0500
@@ -2348,13 +2348,15 @@
   &&& \texttt{<}g\texttt{>}l^*\texttt{</}x\texttt{>} & \textrm{tag with children} \\
   &&& \{e\} & \textrm{computed XML fragment} \\
   &&& \{[e]\} & \textrm{injection of an Ur expression, via the $\mt{Top}.\mt{txt}$ function} \\
-  \textrm{Tag} & g &::=& h \; (x = v)^* \\
+  \textrm{Tag} & g &::=& h \; (x [= v])^* \\
   \textrm{Tag head} & h &::=& x & \textrm{tag name} \\
   &&& h\{c\} & \textrm{constructor parameter} \\
   \textrm{Attribute value} & v &::=& \ell & \textrm{literal value} \\
   &&& \{e\} & \textrm{computed value} \\
 \end{array}$$
 
+When the optional $= v$ is omitted in an XML attribute, the attribute is assigned value $\mt{True}$ in Ur/Web, and it is rendered to HTML merely as including the attribute name without a value.  If such a Boolean attribute is manually set to value $\mt{False}$, then it is omitted altogether in generating HTML.
+
 Further, there is a special convenience and compatibility form for setting CSS classes of tags.  If a \cd{class} attribute has a value that is a string literal, the literal is parsed in the usual HTML way and replaced with calls to appropriate Ur/Web combinators.  Any dashes in the text are replaced with underscores to determine Ur identifiers.  The same desugaring can be accessed in a normal expression context by calling the pseudo-function \cd{CLASS} on a string literal.
 
 Similar support is provided for \cd{style} attributes.  Normal CSS syntax may be used in string literals that are \cd{style} attribute values, and the desugaring may be accessed elsewhere with the pseudo-function \cd{STYLE}.
--- a/include/urweb/urweb_cpp.h	Mon Nov 24 20:41:24 2014 -0500
+++ b/include/urweb/urweb_cpp.h	Mon Nov 24 20:47:38 2014 -0500
@@ -397,4 +397,7 @@
 
 extern const char uw_begin_xhtml[], uw_begin_html5[];
 
+int uw_remoteSock(struct uw_context *);
+void uw_set_remoteSock(struct uw_context *, int sock);
+
 #endif
--- a/lib/js/urweb.js	Mon Nov 24 20:41:24 2014 -0500
+++ b/lib/js/urweb.js	Mon Nov 24 20:47:38 2014 -0500
@@ -1038,30 +1038,79 @@
     return x;
 }
 
-function inp(s, name) {
+function inpt(type, s, name) {
     if (suspendScripts)
         return;
 
     var x = input(document.createElement("input"), s,
-                  function(x) { return function(v) { if (x.value != v) x.value = v; }; }, "text", name);
+                  function(x) { return function(v) { if (x.value != v) x.value = v; }; }, type, name);
     x.value = s.data;
     x.onkeyup = x.oninput = x.onchange = x.onpropertychange = function() { sv(s, x.value) };
 
     return x;
 }
 
+function inp(s, name) {
+    return inpt("text", s, name);
+}
+
 function password(s, name) {
-    if (suspendScripts)
-        return;
+    return inpt("password", s, name);
+}
 
-    var x = input(document.createElement("input"), s,
-                  function(x) { return function(v) { if (x.value != v) x.value = v; }; }, "password", name);
-    x.value = s.data;
-    x.onkeyup = x.oninput = x.onchange = x.onpropertychange = function() { sv(s, x.value) };
+function email(s, name) {
+    return inpt("email", s, name);
+}
 
-    return x;
+function search(s, name) {
+    return inpt("search", s, name);
 }
 
+function url(s, name) {
+    return inpt("url", s, name);
+}
+
+function tel(s, name) {
+    return inpt("tel", s, name);
+}
+
+function color(s, name) {
+    return inpt("color", s, name);
+}
+
+function number(s, name) {
+    return inpt("number", s, name);
+}
+
+function range(s, name) {
+    return inpt("range", s, name);
+}
+
+function date(s, name) {
+    return inpt("date", s, name);
+}
+
+function datetime(s, name) {
+    return inpt("datetime", s, name);
+}
+
+function datetime_local(s, name) {
+    return inpt("datetime-local", s, name);
+}
+
+function month(s, name) {
+    return inpt("month", s, name);
+}
+
+function week(s, name) {
+    return inpt("week", s, name);
+}
+
+function time(s, name) {
+    return inpt("time", s, name);
+}
+
+
 function selectValue(x) {
     if (x.options.length == 0)
         return "";
--- a/lib/ur/basis.urs	Mon Nov 24 20:41:24 2014 -0500
+++ b/lib/ur/basis.urs	Mon Nov 24 20:47:38 2014 -0500
@@ -948,15 +948,40 @@
                   -> [[Form] ~ ctx] =>
                         nm :: Name -> unit
                         -> tag attrs ([Form] ++ ctx) inner [] [nm = ty]
+
+con inputAttrs = [Required = bool, Autofocus = bool]
+
+
 val hidden : formTag string [] [Data = data_attr, Id = string, Value = string]
 val textbox : formTag string [] ([Value = string, Size = int, Placeholder = string, Source = source string, Onchange = transaction unit,
-                                  Ontext = transaction unit] ++ boxAttrs)
-val password : formTag string [] ([Value = string, Size = int, Placeholder = string, Onchange = transaction unit] ++ boxAttrs)
+                                  Ontext = transaction unit] ++ boxAttrs ++ inputAttrs)
+val password : formTag string [] ([Value = string, Size = int, Placeholder = string, Onchange = transaction unit] ++ boxAttrs ++ inputAttrs)
 val textarea : formTag string [] ([Rows = int, Cols = int, Onchange = transaction unit,
-                                   Ontext = transaction unit] ++ boxAttrs)
+                                   Ontext = transaction unit] ++ boxAttrs ++ inputAttrs)
 
 val checkbox : formTag bool [] ([Checked = bool, Onchange = transaction unit] ++ boxAttrs)
 
+(* HTML5 widgets galore! *)
+
+type textWidget = formTag string [] ([Value = string, Size = int, Placeholder = string, Onchange = transaction unit] ++ boxAttrs ++ inputAttrs)
+
+val email : textWidget
+val search : textWidget
+val url_ : textWidget
+val tel : textWidget
+val color : textWidget
+
+val number : formTag float [] ([Value = float, Min = float, Max = float, Step = float, Size = int, Onchange = transaction unit] ++ boxAttrs ++ inputAttrs)
+val range : formTag float [] ([Value = float, Min = float, Max = float, Size = int, Onchange = transaction unit] ++ boxAttrs ++ inputAttrs)
+val date : formTag string [] ([Value = string, Min = string, Max = string, Size = int, Onchange = transaction unit] ++ boxAttrs ++ inputAttrs)
+val datetime : formTag string [] ([Value = string, Min = string, Max = string, Size = int, Onchange = transaction unit] ++ boxAttrs ++ inputAttrs)
+val datetime_local : formTag string [] ([Value = string, Min = string, Max = string, Size = int, Onchange = transaction unit] ++ boxAttrs ++ inputAttrs)
+val month : formTag string [] ([Value = string, Min = string, Max = string, Size = int, Onchange = transaction unit] ++ boxAttrs ++ inputAttrs)
+val week : formTag string [] ([Value = string, Min = string, Max = string, Size = int, Onchange = transaction unit] ++ boxAttrs ++ inputAttrs)
+val timeInput : formTag string [] ([Value = string, Min = string, Max = string, Size = int, Onchange = transaction unit] ++ boxAttrs ++ inputAttrs)
+
+
+
 type file
 val fileName : file -> option string
 val fileMimeType : file -> string
@@ -1011,19 +1036,35 @@
                   -> [[Body] ~ ctx] => [[Body] ~ inner] =>
                         unit -> tag attrs ([Body] ++ ctx) ([Body] ++ inner) [] []
 
-val ctextbox : cformTag ([Value = string, Size = int, Source = source string, Placeholder = string, Onchange = transaction unit,
-                          Ontext = transaction unit] ++ boxAttrs) []
-val cpassword : cformTag ([Value = string, Size = int, Source = source string, Placeholder = string, Onchange = transaction unit,
-                          Ontext = transaction unit] ++ boxAttrs) []
+type ctext = cformTag ([Value = string, Size = int, Source = source string, Placeholder = string,
+                        Onchange = transaction unit, Ontext = transaction unit] ++ boxAttrs ++ inputAttrs) []
+
+val ctextbox : ctext
+val cpassword : ctext
+val cemail : ctext
+val csearch : ctext
+val curl : ctext
+val ctel : ctext
+val ccolor : ctext
+
+val cnumber : cformTag ([Source = source float, Value = float, Min = float, Max = float, Step = float, Size = int, Onchange = transaction unit] ++ boxAttrs ++ inputAttrs) []
+val crange : cformTag ([Source = source float, Value = float, Min = float, Max = float, Size = int, Onchange = transaction unit] ++ boxAttrs ++ inputAttrs) []
+val cdate : cformTag ([Source = source string, Value = string, Min = string, Max = string, Size = int, Onchange = transaction unit] ++ boxAttrs ++ inputAttrs) []
+val cdatetime : cformTag ([Source = source string, Value = string, Min = string, Max = string, Size = int, Onchange = transaction unit] ++ boxAttrs ++ inputAttrs) []
+val cdatetime_local : cformTag ([Source = source string, Value = string, Min = string, Max = string, Size = int, Onchange = transaction unit] ++ boxAttrs ++ inputAttrs) []
+val cmonth : cformTag ([Source = source string, Value = string, Min = string, Max = string, Size = int, Onchange = transaction unit] ++ boxAttrs ++ inputAttrs) []
+val cweek : cformTag ([Source = source string, Value = string, Min = string, Max = string, Size = int, Onchange = transaction unit] ++ boxAttrs ++ inputAttrs) []
+val ctime : cformTag ([Source = source string, Value = string, Min = string, Max = string, Size = int, Onchange = transaction unit] ++ boxAttrs ++ inputAttrs) []
+
 val button : cformTag ([Value = string] ++ boxAttrs) []
 
-val ccheckbox : cformTag ([Value = bool, Size = int, Source = source bool, Onchange = transaction unit] ++ boxAttrs) []
+val ccheckbox : cformTag ([Value = bool, Size = int, Source = source bool, Onchange = transaction unit] ++ boxAttrs ++ inputAttrs) []
 
 val cselect : cformTag ([Source = source string, Onchange = transaction unit] ++ boxAttrs) [Cselect]
 val coption : unit -> tag [Value = string, Selected = bool] [Cselect, Body] [] [] []
 
 val ctextarea : cformTag ([Value = string, Rows = int, Cols = int, Source = source string, Onchange = transaction unit,
-                           Ontext = transaction unit] ++ boxAttrs) []
+                           Ontext = transaction unit] ++ boxAttrs ++ inputAttrs) []
 
 (*** Tables *)
 
--- a/src/c/http.c	Mon Nov 24 20:41:24 2014 -0500
+++ b/src/c/http.c	Mon Nov 24 20:47:38 2014 -0500
@@ -89,6 +89,8 @@
       sock = uw_dequeue();
     }
 
+    uw_set_remoteSock(ctx, sock);
+
     qprintf("Handling connection with thread #%d.\n", me);
 
     while (1) {
--- a/src/c/urweb.c	Mon Nov 24 20:41:24 2014 -0500
+++ b/src/c/urweb.c	Mon Nov 24 20:47:38 2014 -0500
@@ -479,6 +479,8 @@
 
   // For caching.
   char *recording;
+
+  int remoteSock;
 };
 
 size_t uw_headers_max = SIZE_MAX;
@@ -564,6 +566,8 @@
 
   ctx->recording = 0;
 
+  ctx->remoteSock = -1;
+
   return ctx;
 }
 
@@ -651,6 +655,7 @@
   ctx->amInitializing = 0;
   ctx->usedSig = 0;
   ctx->needsResig = 0;
+  ctx->remoteSock = -1;
 }
 
 void uw_reset_keep_request(uw_context ctx) {
@@ -4471,3 +4476,11 @@
 
   return s;
 }
+
+int uw_remoteSock(uw_context ctx) {
+  return ctx->remoteSock;
+}
+
+void uw_set_remoteSock(uw_context ctx, int sock) {
+  ctx->remoteSock = sock;
+}
--- a/src/monoize.sml	Mon Nov 24 20:41:24 2014 -0500
+++ b/src/monoize.sml	Mon Nov 24 20:47:38 2014 -0500
@@ -3289,7 +3289,7 @@
                 val (style, fm) = monoExp (env, st, fm) style
                 val (dynStyle, fm) = monoExp (env, st, fm) dynStyle
 
-                val dynamics = ["dyn", "ctextbox", "cpassword", "ccheckbox", "cselect", "coption", "ctextarea", "active", "script"]
+                val dynamics = ["dyn", "ctextbox", "cpassword", "ccheckbox", "cselect", "coption", "ctextarea", "active", "script", "cemail", "csearch", "curl", "ctel", "ccolor"]
 
                 fun isSome (e, _) =
                     case e of
@@ -3589,6 +3589,29 @@
                                else
 			           "span"
 
+                fun cinput (fallback, dynamic) =
+		    case List.find (fn ("Source", _, _) => true | _ => false) attrs of
+                        NONE =>
+                        let
+			    val (ts, fm) = tagStart "input"
+                        in
+			    ((L'.EStrcat (ts,
+                                          strH (" type=\"" ^ fallback ^ "\" />")),
+                              loc), fm)
+                        end
+                      | SOME (_, src, _) =>
+                        let
+			    val sc = strcat [str (dynamic ^ "(exec("),
+					     (L'.EJavaScript (L'.Script, src), loc),
+					     str "))"]
+			    val sc = setAttrs sc
+                        in
+			    (strcat [str "<script type=\"text/javascript\">",
+				     sc,
+				     str "</script>"],
+			     fm)
+                        end
+
 		val baseAll as (base, fm) =
                     case tag of
 			"body" => let
@@ -3669,6 +3692,19 @@
                            | _ => (Print.prefaces "Targs" (map (fn t => ("T", CorePrint.p_con env t)) targs);
                                    raise Fail "No name passed to textbox tag"))
                       | "password" => input "password"
+                      | "email" => input "email"
+                      | "search" => input "search"
+                      | "url_" => input "url"
+                      | "tel" => input "tel"
+                      | "color" => input "color"
+                      | "number" => input "number"
+                      | "range" => input "range"
+                      | "date" => input "date"
+                      | "datetime" => input "datetime"
+                      | "datetime_local" => input "datetime-local"
+                      | "month" => input "month"
+                      | "week" => input "week"
+                      | "timeInput" => input "time"
                       | "textarea" =>
 			(case targs of
                              [_, (L.CName name, _)] =>
@@ -3719,75 +3755,24 @@
                            | _ => (Print.prefaces "Targs" (map (fn t => ("T", CorePrint.p_con env t)) targs);
                                    raise Fail "No name passed to lselect tag"))
 
-                      | "ctextbox" =>
-			(case List.find (fn ("Source", _, _) => true | _ => false) attrs of
-                             NONE =>
-                             let
-				 val (ts, fm) = tagStart "input"
-                             in
-				 ((L'.EStrcat (ts,
-                                               strH " type=\"text\" />"),
-                                   loc), fm)
-                             end
-                           | SOME (_, src, _) =>
-                             let
-				 val sc = strcat [str "inp(exec(",
-						  (L'.EJavaScript (L'.Script, src), loc),
-						  str "))"]
-				 val sc = setAttrs sc
-                             in
-				 (strcat [str "<script type=\"text/javascript\">",
-					  sc,
-					  str "</script>"],
-				  fm)
-                             end)
-
-                      | "cpassword" =>
-			(case List.find (fn ("Source", _, _) => true | _ => false) attrs of
-                             NONE =>
-                             let
-				 val (ts, fm) = tagStart "input"
-                             in
-				 ((L'.EStrcat (ts,
-                                               strH " type=\"password\" />"),
-                                   loc), fm)
-                             end
-                           | SOME (_, src, _) =>
-                             let
-				 val sc = strcat [str "password(exec(",
-						  (L'.EJavaScript (L'.Script, src), loc),
-						  str "))"]
-				 val sc = setAttrs sc
-                             in
-				 (strcat [str "<script type=\"text/javascript\">",
-					  sc,
-					  str "</script>"],
-				  fm)
-                             end)
-
-                      | "ccheckbox" =>
-			(case List.find (fn ("Source", _, _) => true | _ => false) attrs of
-                             NONE =>
-                             let
-				 val (ts, fm) = tagStart "input type=\"checkbox\""
-                             in
-				 ((L'.EStrcat (ts,
-                                               strH " />"),
-                                   loc), fm)
-                             end
-                           | SOME (_, src, _) =>
-                             let
-				 val sc = strcat [str "chk(exec(",
-						  (L'.EJavaScript (L'.Script, src), loc),
-						  str "))"]
-				 val sc = setAttrs sc
-                             in
-				 (strcat [str "<script type=\"text/javascript\">",
-					  sc,
-					  str "</script>"],
-				  fm)
-                             end)
-
+                      | "ctextbox" => cinput ("text", "inp")
+                      | "cpassword" => cinput ("password", "password")
+                      | "cemail" => cinput ("email", "email")
+                      | "csearch" => cinput ("search", "search")
+                      | "curl" => cinput ("url", "url")
+                      | "ctel" => cinput ("tel", "tel")
+                      | "ccolor" => cinput ("color", "color")
+
+                      | "cnumber" => cinput ("number", "number")
+                      | "crange" => cinput ("range", "range")
+                      | "cdate" => cinput ("date", "date")
+                      | "cdatetime" => cinput ("datetime", "datetime")
+                      | "cdatetime_local" => cinput ("datetime-local", "datetime_local")
+                      | "cmonth" => cinput ("month", "month")
+                      | "cweek" => cinput ("week", "week")
+                      | "ctime" => cinput ("time", "time")
+
+                      | "ccheckbox" => cinput ("checkbox", "chk")
                       | "cselect" =>
 			(case List.find (fn ("Source", _, _) => true | _ => false) attrs of
                              NONE =>
--- a/src/urweb.grm	Mon Nov 24 20:41:24 2014 -0500
+++ b/src/urweb.grm	Mon Nov 24 20:47:38 2014 -0500
@@ -221,6 +221,9 @@
 fun tagIn bt =
     case bt of
         "table" => "tabl"
+      | "url" => "url_"
+      | "datetime-local" => "datetime_local"
+      | "cdatetime-local" => "cdatetime_local"
       | _ => bt
 
 datatype prop_kind = Delete | Update
@@ -1747,6 +1750,12 @@
                                                              else
                                                                  attrv)
                                                  end)
+       | SYMBOL                         (let
+                                             val loc = s (SYMBOLleft, SYMBOLright)
+                                         in
+                                             Normal ((CName (makeAttr SYMBOL), loc),
+                                                     (EVar (["Basis"], "True", Infer), loc))
+                                         end)
                 
 attrv  : INT                            (EPrim (Prim.Int INT), s (INTleft, INTright))
        | FLOAT                          (EPrim (Prim.Float FLOAT), s (FLOATleft, FLOATright))
--- a/src/urweb.lex	Mon Nov 24 20:41:24 2014 -0500
+++ b/src/urweb.lex	Mon Nov 24 20:47:38 2014 -0500
@@ -277,19 +277,19 @@
                                    continue ())
                           end);
 
-<INITIAL> "<" {id} "/>"=>(let
+<INITIAL> "<" {xmlid} "/>"=>(let
 			      val tag = String.substring (yytext, 1, size yytext - 3)
 			  in
 			      Tokens.XML_BEGIN_END (tag, yypos, yypos + size yytext)
 			  end);
-<INITIAL> "<" {id} ">"=> (let
+<INITIAL> "<" {xmlid} ">"=> (let
 			      val tag = String.substring (yytext, 1, size yytext - 2)
 			  in
 			      YYBEGIN XML;
 			      xmlTag := tag :: (!xmlTag);
 			      Tokens.XML_BEGIN (tag, yypos, yypos + size yytext)
 			  end);
-<XML> "</" {id} ">"   => (let
+<XML> "</" {xmlid} ">" => (let
 			      val id = String.substring (yytext, 2, size yytext - 3)
 			  in
 			      case !xmlTag of
@@ -304,7 +304,7 @@
 			          Tokens.END_TAG (id, yypos, yypos + size yytext)
 			  end);
 
-<XML> "<" {id}        => (YYBEGIN XMLTAG;
+<XML> "<" {xmlid}     => (YYBEGIN XMLTAG;
 			  Tokens.BEGIN_TAG (String.extract (yytext, 1, NONE),
 					    yypos, yypos + size yytext));
 
--- a/tests/ctextbox.urp	Mon Nov 24 20:41:24 2014 -0500
+++ b/tests/ctextbox.urp	Mon Nov 24 20:47:38 2014 -0500
@@ -1,4 +1,5 @@
 debug
 allow url http://localhost/*
+rewrite url Ctextbox/*
 
 ctextbox
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/html5_cforms.ur	Mon Nov 24 20:47:38 2014 -0500
@@ -0,0 +1,56 @@
+fun dn [a] (_ : show a) (x : source a) : xbody = <xml>
+  <dyn signal={v <- signal x; return (txt v)}/>
+</xml>
+
+fun main () : transaction page =
+    a <- source "";
+    b <- source True;
+    c <- source "a@b";
+    d <- source "";
+    e <- source "";
+    f <- source "";
+    g <- source 1.0;
+    h <- source 1.0;
+    i <- source "#CCCCCC";
+    j <- source "2014/11/16";
+    k <- source "2014/11/16 12:30:45";
+    l <- source "2014/11/16 12:30:45";
+    m <- source "2014/11";
+    n <- source "2014-W7";
+    o <- source "12:30:45";
+
+    return <xml><body>
+      <ctextbox source={a}/>
+      <ccheckbox source={b}/>
+      <cemail source={c}/>
+      <curl source={d}/>
+      <ctel source={e}/>
+      <csearch source={f}/>
+      <cnumber source={g} min={-10.0} max={10.0} step={0.5}/>
+      <crange source={h} min={-10.0} max={10.0}/>
+      <ccolor source={i}/>
+      <cdate source={j}/>
+      <cdatetime source={k}/>
+      <cdatetime-local source={l}/>
+      <cmonth source={m}/>
+      <cweek source={n}/>
+      <ctime source={o}/>
+
+      <hr/>
+        
+      {dn a};
+      {dn b};
+      {dn c};
+      {dn d};
+      {dn e};
+      {dn f};
+      {dn g};
+      {dn h};
+      {dn i};
+      {dn j};
+      {dn k};
+      {dn l};
+      {dn m};
+      {dn n};
+      {dn o}
+    </body></xml>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/html5_forms.ur	Mon Nov 24 20:47:38 2014 -0500
@@ -0,0 +1,45 @@
+fun handler r = return <xml><body>
+  A: {[r.A]}<br/>
+  B: {[r.B]}<br/>
+  C: {[r.C]}<br/>
+  D: {[r.D]}<br/>
+  E: {[r.E]}<br/>
+  F: {[r.F]}<br/>
+  G: {[r.G]}<br/>
+  H: {[r.H]}<br/>
+  I: {[r.I]}<br/>
+  J: {[r.J]}<br/>
+  K: {[r.K]}<br/>
+  L: {[r.L]}<br/>
+  M: {[r.M]}<br/>
+  N: {[r.N]}<br/>
+  O: {[r.O]}<br/>
+  P: {[r.P]}<br/>
+</body></xml>
+
+fun main () =
+    return <xml><body>
+      <form>
+        <textbox{#A} required placeholder="bobby"/>
+        <textbox{#B} placeholder="soggy" autofocus/>
+        <checkbox{#C}/>
+        <email{#D}/>
+        <url{#E}/>
+        <tel{#F}/>
+        <search{#G}/>
+
+        <hr/>
+
+        <color{#H}/>
+        <number{#I} min={17.0} max={32.8} value={20.6} step={2.5}/>
+        <range{#J} min={17.0} max={32.8} value={20.6}/>
+        <date{#K}/>
+        <datetime{#L}/>
+        <datetime-local{#M}/>
+        <month{#N}/>
+        <week{#O}/>
+        <timeInput{#P}/>
+
+        <submit action={handler}/>
+      </form>
+    </body></xml>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/html5_forms.urs	Mon Nov 24 20:47:38 2014 -0500
@@ -0,0 +1,1 @@
+val main : unit -> transaction page