Mercurial > urweb
changeset 1702:06791667937e
New JavaScript FFI function: setInnerHTML
author | Adam Chlipala <adam@chlipala.net> |
---|---|
date | Wed, 14 Mar 2012 10:10:56 -0400 |
parents | 6a4461757829 |
children | 6f2f74cc4ead |
files | doc/manual.tex lib/js/urweb.js tests/ffi.urs tests/setInner.js tests/setInner.ur tests/setInner.urp |
diffstat | 6 files changed, 54 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/doc/manual.tex Wed Mar 14 09:06:23 2012 -0400 +++ b/doc/manual.tex Wed Mar 14 10:10:56 2012 -0400 @@ -2384,6 +2384,8 @@ \item The behavior of the \cd{<dyn>} pseudo-tag may be mimicked by following the right convention in a piece of HTML source code with a type like $\mt{xbody}$. Such a piece of source code may be encoded with a JavaScript string. To insert a dynamic section, include a \cd{<script>} tag whose content is just a call \cd{dyn(pnode, s)}. The argument \cd{pnode} specifies what the relevant enclosing parent tag is. Use value \cd{"tr"} when the immediate parent is \cd{<tr>}, use \cd{"table"} when the immediate parent is \cd{<table>}, and use \cd{"span"} otherwise. The argument \cd{s} is a string-valued signal giving the HTML code to be inserted at this point. As with the usual \cd{<dyn>} tag, that HTML subtree is automatically updated as the value of \cd{s} changes. +\item There is only one supported method of taking HTML values generated in Ur/Web code and adding them to the DOM in FFI JavaScript code: call \cd{setInnerHTML(node, html)} to add HTML content \cd{html} within DOM node \cd{node}. Merely running \cd{node.innerHTML = html} is not guaranteed to get the job done, though programmers familiar with JavaScript will probably find it useful to think of \cd{setInnerHTML} as having this effect. The unusual idiom is required because Ur/Web uses a nonstandard representation of HTML, to support infinite nesting of code that may generate code that may generate code that.... The \cd{node} value must already be in the DOM tree at the point when \cd{setInnerHTML} is called, because some plumbing must be set up to interact sensibly with \cd{<dyn>} tags. + \item It is possible to use the more standard ``IDs and mutation'' style of JavaScript coding, though that style is unidiomatic for Ur/Web and should be avoided wherever possible. Recall the abstract type $\mt{id}$ and its constructor $\mt{fresh}$, which can be used to generate new unique IDs in Ur/Web code. Values of this type are represented as strings in JavaScript, and a function \cd{fresh()} is available to generate new unique IDs. Application-specific ID generation schemes may cause bad interactions with Ur/Web code that also generates IDs, so the recommended approach is to produce IDs only via calls to \cd{fresh()}. FFI code shouldn't depend on the ID generation scheme (on either server side or client side), but it is safe to include these IDs in tag attributes (in either server-side or client-side code) and manipulate the associated DOM nodes in the standard way (in client-side code). Be forewarned that this kind of imperative DOM manipulation may confuse the Ur/Web runtime system and interfere with proper behavior of tags like \cd{<dyn>}! \end{itemize}
--- a/lib/js/urweb.js Wed Mar 14 09:06:23 2012 -0400 +++ b/lib/js/urweb.js Wed Mar 14 10:10:56 2012 -0400 @@ -836,6 +836,38 @@ populate(x); } +function setInnerHTML(node, html) { + var x; + + if (node.previousSibling && node.previousSibling.closures != undefined) { + x = node.previousSibling; + + for (var ls = x.closures; ls; ls = ls.next) + freeClosure(ls.data); + + if (node.getElementsByTagName) { + var arr = node.getElementsByTagName("script"); + for (var i = 0; i < arr.length; ++i) + killScript(arr[i]); + } + } else { + x = document.createElement("script"); + x.dead = false; + x.sources = null; + + if (node.parentNode) + node.parentNode.insertBefore(x, node); + else + whine("setInnerHTML: node is not already in the DOM tree"); + } + + var cls = {v : null}; + var html = flatten(cls, html); + x.closures = cls.v; + node.innerHTML = html; + runScripts(node); +} + function input(x, s, recreate, type, name) { if (name) x.name = name; if (type) x.type = type;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/ffi.urs Wed Mar 14 10:10:56 2012 -0400 @@ -0,0 +1,1 @@ +val setIt : id -> xbody -> transaction unit
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/setInner.js Wed Mar 14 10:10:56 2012 -0400 @@ -0,0 +1,3 @@ +function setIt(id, html) { + setInnerHTML(document.getElementById(id), html); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/setInner.ur Wed Mar 14 10:10:56 2012 -0400 @@ -0,0 +1,9 @@ +fun main () : transaction page = + x <- fresh; + s <- source 0; + q <- source ""; + return <xml><body> + <span id={x}/> + <button onclick={v <- get q; set q (v ^ "!"); Ffi.setIt x <xml><dyn signal={n <- signal s; return <xml>n = {[n]}</xml>}/>{[v]}</xml>}/> + <button onclick={n <- get s; set s (n + 1)}/> + </body></xml>