summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/manual.tex6
-rw-r--r--lib/js/urweb.js6
-rw-r--r--lib/ur/basis.urs3
-rw-r--r--src/monoize.sml14
-rw-r--r--tests/active.ur14
5 files changed, 40 insertions, 3 deletions
diff --git a/doc/manual.tex b/doc/manual.tex
index 00d2bc55..9e73e5ae 100644
--- a/doc/manual.tex
+++ b/doc/manual.tex
@@ -2136,7 +2136,11 @@ The semantics of \cd{<dyn>} tags is somewhat subtle. When the signal associated
Currently, the only way to avoid undesired resets is to avoid regeneration of containing subtrees. There are two main strategies for achieving that goal. First, when changes to a subtree can be confined to CSS classes of tags, the \texttt{dynClass} pseudo-attribute may be used instead (see Section \ref{xml}), as it does not regenerate subtrees. Second, a single \cd{<dyn>} tag may be broken into multiple tags, in a way that makes finer-grained dependency structure explicit. This latter strategy can avoid ``spurious'' regenerations that are not actually required to achieve the intended semantics.
-Transactions can be run on the client by including them in attributes like the $\mt{Onclick}$ attribute of $\mt{button}$, and GUI widgets like $\mt{ctextbox}$ have $\mt{Source}$ attributes that can be used to connect them to sources, so that their values can be read by code running because of, e.g., an $\mt{Onclick}$ event.
+Transactions can be run on the client by including them in attributes like the $\mt{Onclick}$ attribute of $\mt{button}$, and GUI widgets like $\mt{ctextbox}$ have $\mt{Source}$ attributes that can be used to connect them to sources, so that their values can be read by code running because of, e.g., an $\mt{Onclick}$ event. It is also possible to create an ``active'' HTML fragment that runs a $\mt{transaction}$ to determine its content, possibly allocating some sources in the process:
+
+$$\begin{array}{l}
+ \mt{val} \; \mt{active} : \mt{unit} \to \mt{tag} \; [\mt{Code} = \mt{transaction} \; \mt{xbody}] \; \mt{body} \; [] \; [] \; []
+\end{array}$$
\subsubsection{Remote Procedure Calls}
diff --git a/lib/js/urweb.js b/lib/js/urweb.js
index 35023924..87adc0a3 100644
--- a/lib/js/urweb.js
+++ b/lib/js/urweb.js
@@ -901,6 +901,12 @@ function setInnerHTML(node, html) {
runScripts(node);
}
+function active(s) {
+ var span = document.createElement("span");
+ addNode(span);
+ setInnerHTML(span, execF(s));
+}
+
function input(x, s, recreate, type, name) {
if (name) x.name = name;
if (type) x.type = type;
diff --git a/lib/ur/basis.urs b/lib/ur/basis.urs
index 7f254a2f..8ac94668 100644
--- a/lib/ur/basis.urs
+++ b/lib/ur/basis.urs
@@ -767,6 +767,9 @@ val giveFocus : id -> transaction unit
val dyn : ctx ::: {Unit} -> use ::: {Type} -> bind ::: {Type} -> [ctx ~ [Dyn]] => unit
-> tag [Signal = signal (xml ([Dyn] ++ ctx) use bind)] ([Dyn] ++ ctx) [] use bind
+val active : unit
+ -> tag [Code = transaction xbody] body [] [] []
+
val head : unit -> tag [] html head [] []
val title : unit -> tag [] head [] [] []
val link : unit -> tag [Id = id, Rel = string, Typ = string, Href = url, Media = string] head [] [] []
diff --git a/src/monoize.sml b/src/monoize.sml
index 86d8389f..403e0b84 100644
--- a/src/monoize.sml
+++ b/src/monoize.sml
@@ -3234,7 +3234,7 @@ fun monoExp (env, st, fm) (all as (e, loc)) =
val (style, fm) = monoExp (env, st, fm) style
val (dynStyle, fm) = monoExp (env, st, fm) dynStyle
- val dynamics = ["dyn", "ctextbox", "ccheckbox", "cselect", "coption", "ctextarea"]
+ val dynamics = ["dyn", "ctextbox", "ccheckbox", "cselect", "coption", "ctextarea", "active"]
fun isSome (e, _) =
case e of
@@ -3541,9 +3541,19 @@ fun monoExp (env, st, fm) (all as (e, loc)) =
(L'.EStrcat ((L'.EJavaScript (L'.Script, e), loc),
(L'.EPrim (Prim.String ("))</script>")), loc)), loc)), loc),
fm)
- | _ => raise Fail "Monoize: Bad dyn attributes"
+ | _ => raise Fail "Monoize: Bad <dyn> attributes"
end
+ | "active" =>
+ (case attrs of
+ [("Code", e, _)] =>
+ ((L'.EStrcat
+ ((L'.EPrim (Prim.String ("<script type=\"text/javascript\">active(execD(")), loc),
+ (L'.EStrcat ((L'.EJavaScript (L'.Script, e), loc),
+ (L'.EPrim (Prim.String ("))</script>")), loc)), loc)), loc),
+ fm)
+ | _ => raise Fail "Monoize: Bad <active> attributes")
+
| "submit" => normal ("input type=\"submit\"", NONE)
| "image" => normal ("input type=\"image\"", NONE)
| "button" => normal ("input type=\"submit\"", NONE)
diff --git a/tests/active.ur b/tests/active.ur
new file mode 100644
index 00000000..e5fa68db
--- /dev/null
+++ b/tests/active.ur
@@ -0,0 +1,14 @@
+fun counter' () =
+ s <- source 0;
+ return <xml>
+ <dyn signal={n <- signal s; return (txt n)}/>
+ <button onclick={fn _ => n <- get s; set s (n + 1)}/>
+ </xml>
+
+fun counter () = <xml><active code={counter' ()}/></xml>
+
+fun main () : transaction page = return <xml><body>
+ {counter ()}
+ <hr/>
+ {counter ()}
+</body></xml>