Mercurial > urweb
changeset 1833:be0c4e2e488a
Allow any FFI module to declare new HTML tags
author | Adam Chlipala <adam@chlipala.net> |
---|---|
date | Wed, 28 Nov 2012 16:56:45 -0500 (2012-11-28) |
parents | 373e2c3f03b2 |
children | 690638bd9fef |
files | demo/more/grid.ur demo/more/grid.urs demo/more/out/grid.css demo/subforms.ur doc/manual.tex src/monoize.sml src/urweb.grm tests/ffitag.ur tests/ffitag.urp tests/tagffi.urs |
diffstat | 10 files changed, 41 insertions(+), 27 deletions(-) [+] |
line wrap: on
line diff
--- a/demo/more/grid.ur Wed Nov 28 11:45:46 2012 -0500 +++ b/demo/more/grid.ur Wed Nov 28 16:56:45 2012 -0500 @@ -40,10 +40,10 @@ val pageLength : option int end) = struct - style tabl - style tr - style th - style td + style tab + style row + style header + style data style agg fun make (row : M.row) [input] [filter] (m : colMeta' M.row input filter) : transaction input = m.Project row @@ -122,13 +122,13 @@ M.folder M.cols grid.Cols grid.Filters row fun render (grid : grid) = <xml> - <table class={tabl}> - <tr class={tr}> + <table class={tab}> + <tr class={row}> <th/> <th/> <th><button value="No sort" onclick={fn _ => set grid.Sort None}/></th> {@mapX2 [fst3] [colMeta M.row] [tr] (fn [nm :: Name] [p :: (Type * Type * Type)] [rest :: {(Type * Type * Type)}] [[nm] ~ rest] data (meta : colMeta M.row p) => - <xml><th class={th}> + <xml><th class={header}> {case (meta.Handlers data).Sort of None => txt (meta.Handlers data).Header | sort => <xml><button value={(meta.Handlers data).Header} @@ -185,7 +185,7 @@ cols <- makeAll grid.Cols row'; set colsS cols in - <xml><tr class={tr}> + <xml><tr class={row}> <td> <dyn signal={b <- signal grid.Selection; return (if b then @@ -213,18 +213,18 @@ return (@mapX3 [fst3] [colMeta M.row] [snd3] [_] (fn [nm :: Name] [t :: (Type * Type * Type)] [rest :: {(Type * Type * Type)}] - [[nm] ~ rest] data meta v => - <xml><td class={td}> + [[nm] ~ rest] dat meta v => + <xml><td class={data}> <dyn signal={b <- signal ud; return (if b then - (meta.Handlers data).Edit v + (meta.Handlers dat).Edit v else - (meta.Handlers data).Display + (meta.Handlers dat).Display v)}/> <dyn signal={b <- signal ud; if b then valid <- - (meta.Handlers data).Validate v; + (meta.Handlers dat).Validate v; return (if valid then <xml/> else
--- a/demo/more/grid.urs Wed Nov 28 11:45:46 2012 -0500 +++ b/demo/more/grid.urs Wed Nov 28 16:56:45 2012 -0500 @@ -49,9 +49,9 @@ val showSelection : grid -> source bool val selection : grid -> signal (list M.row) - style tabl - style tr - style th - style td + style tab + style row + style header + style data style agg end
--- a/demo/more/out/grid.css Wed Nov 28 11:45:46 2012 -0500 +++ b/demo/more/out/grid.css Wed Nov 28 16:56:45 2012 -0500 @@ -1,16 +1,16 @@ -.Grid1_tabl { +.Grid1_tab { border-style: solid } -.Grid1_th { +.Grid1_header { border-style: solid } -.Grid1_tr { +.Grid1_row { border-style: solid } -.Grid1_td { +.Grid1_data { border-style: solid }
--- a/demo/subforms.ur Wed Nov 28 11:45:46 2012 -0500 +++ b/demo/subforms.ur Wed Nov 28 16:56:45 2012 -0500 @@ -13,7 +13,7 @@ </body></xml> end -fun subforms n = +fun subfrms n = if n <= 0 then <xml/> else @@ -22,13 +22,13 @@ <hidden{#Num} value={show n}/> <li>{[n]}: <textbox{#Text}/></li> </entry> - {subforms (n - 1)} + {subfrms (n - 1)} </xml> fun form n = return <xml><body> <form> <subforms{#Lines}> - {subforms n} + {subfrms n} </subforms> <submit action={sub}/> </form>
--- a/doc/manual.tex Wed Nov 28 11:45:46 2012 -0500 +++ b/doc/manual.tex Wed Nov 28 16:56:45 2012 -0500 @@ -2482,6 +2482,12 @@ \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} +\subsection{Introducing New HTML Tags} + +FFI modules may introduce new tags as values with $\mt{Basis.tag}$ types. See \texttt{basis.urs} for examples of how tags are declared. The identifier of a tag value is used as its rendering in HTML. The Ur/Web syntax sugar for XML literals desugars each use of a tag into a reference to an identifier with the same name. There is no need to provide implementations (i.e., in C or JavaScript code) for such identifiers. + +The onus is on the coder of a new tag's interface to think about consequences for code injection attacks, messing with the DOM in ways that may break Ur/Web reactive programming, etc. + \section{Compiler Phases}
--- a/src/monoize.sml Wed Nov 28 11:45:46 2012 -0500 +++ b/src/monoize.sml Wed Nov 28 16:56:45 2012 -0500 @@ -3203,7 +3203,7 @@ let fun getTag' (e, _) = case e of - L.EFfi ("Basis", tag) => (tag, []) + L.EFfi (_, tag) => (tag, []) | L.ECApp (e, t) => let val (tag, ts) = getTag' e in @@ -3215,7 +3215,7 @@ fun getTag (e, _) = case e of - L.EFfiApp ("Basis", tag, [((L.ERecord [], _), _)]) => (tag, []) + L.EFfiApp (_, tag, [((L.ERecord [], _), _)]) => (tag, []) | L.EApp (e, (L.ERecord [], _)) => getTag' e | _ => (E.errorAt loc "Non-constant XML tag"; Print.eprefaces' [("Expression", CorePrint.p_exp env tag)];
--- a/src/urweb.grm Wed Nov 28 11:45:46 2012 -0500 +++ b/src/urweb.grm Wed Nov 28 16:56:45 2012 -0500 @@ -1608,7 +1608,7 @@ val pos = s (BEGIN_TAGleft, BEGIN_TAGright) in (bt, - (EVar (["Basis"], bt, Infer), pos)) + (EVar ([], bt, Infer), pos)) end) | tagHead LBRACE cexp RBRACE (#1 tagHead, (ECApp (#2 tagHead, cexp), s (tagHeadleft, RBRACEright)))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/ffitag.ur Wed Nov 28 16:56:45 2012 -0500 @@ -0,0 +1,3 @@ +open Tagffi + +fun main () : transaction page = return <xml><body><funky>test</funky></body></xml>