PageRenderTime 285ms CodeModel.GetById 232ms app.highlight 46ms RepoModel.GetById 2ms app.codeStats 0ms

/lib/erl_cgi/src/esp_html.erl

https://github.com/gebi/jungerl
Erlang | 673 lines | 376 code | 114 blank | 183 comment | 33 complexity | 83bd1e7e63a80b553318a1ef8d3715a5 MD5 | raw file
  1%%% File    : esp_html.erl
  2%%% Author  : root <tony@localhost.localdomain>
  3%%% Description : HTML generator from esp format
  4%%% Created : 27 Mar 2002 by root <tony@localhost.localdomain>
  5
  6-module(esp_html).
  7
  8-export([format/1, format/2]).
  9-export([to_value/1]).
 10-export([pcdata/2, pcdata/1]).
 11-import(lists, [map/2, reverse/1,member/2]).
 12
 13%% -define(debug,true).
 14
 15-ifdef(debug).
 16-define(dbg(Fmt,Args), io:format(Fmt, Args)).
 17-else.
 18-define(dbg(Fmt,Args), ok).
 19-endif.
 20
 21-record(state,
 22	{
 23	  %% default formatting function is just a dummy
 24	  fmt = fun(Tag,As,Body,Ps) -> "" end, 
 25	  %% default environment is empty [{Var,Value}]
 26	  env = []                        
 27	 }).
 28	  
 29			
 30
 31-define(HTML_4_0, 
 32	"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">").
 33-define(HTML_4_0_Frameset,
 34	"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Frameset//EN\">").
 35
 36format({document,Cs}) ->
 37    format({document, Cs}, #state { fmt = fun format_fun/4 });
 38format({flow,Cs}) ->
 39    format({flow, Cs}, #state { fmt = fun format_fun/4 }).
 40
 41format({document, Cs}, St) ->
 42    case Cs of
 43	[{html,As1,Cs1}] ->
 44	    elem(html,As1,Cs1,[html],St);
 45	[{head,As1,Cs1},{body,As2,Cs2}] ->
 46	    elem(html,[],[{head,As1,Cs1},{body,As2,Cs2}],[html],St);
 47	Cs ->
 48	    [?HTML_4_0 ++ "$\n",
 49	     repeat(fun(T) -> html:is_flow(T) end, Cs, [body,html],St)]
 50    end;
 51format({flow, Cs}, St) ->
 52    repeat(fun(T) -> html:is_flow(T) end, Cs, [body,html],St).
 53    
 54
 55
 56
 57fmt(St,Tag,As,Body,Ps) ->
 58    case St#state.fmt of
 59	undefined -> "";
 60	Fun -> Fun(Tag,As,Body,Ps)
 61    end.
 62    
 63
 64format_fun(Tag,As,Body,Ps) ->
 65    TAG = upper(atom_to_list(Tag)),
 66    %% Indent = indent(Ps),
 67    case tag_type(Tag) of
 68	break_both ->
 69	    [$<,TAG,attrlist(Tag,As),$>,$\n,
 70	     Body,
 71	     $<,$/,TAG,$>,$\n];
 72	break_begin ->
 73	    [$<,TAG,attrlist(Tag,As),$>,$\n,Body,$<,$/,TAG,$>];
 74	break_end ->
 75	    [$<,TAG,attrlist(Tag,As),$>, Body,$<,$/,TAG,$>,$\n];	    
 76	break_none ->
 77	    [$<,TAG,attrlist(Tag,As),$>, Body,$<,$/,TAG,$>];
 78	no_end_tag ->
 79	    [$<,TAG,attrlist(Tag,As),$>]
 80    end.
 81
 82indent(Ps) ->
 83    lists:duplicate(length(Ps)*2,$\s).
 84
 85tag_type(meta) -> no_end_tag;
 86tag_type(base) -> no_end_tag;
 87tag_type(link) -> no_end_tag;
 88tag_type(param) -> no_end_tag;
 89tag_type(img) ->   no_end_tag;
 90tag_type(col) -> no_end_tag;
 91tag_type(frame) ->  no_end_tag;
 92tag_type(br) -> no_end_tag;
 93tag_type(base_font) -> no_end_tag;
 94tag_type(hr) -> no_end_tag;
 95tag_type(area) -> no_end_tag;
 96tag_type(input) -> no_end_tag;
 97tag_type(tt) -> break_none;
 98tag_type(i) ->  break_none;
 99tag_type(b) ->  break_none;
100tag_type(u) ->  break_none;
101tag_type(s) ->  break_none;
102tag_type(strike) ->  break_none;
103tag_type(big) ->  break_none;
104tag_type(small) ->  break_none;
105tag_type(p) -> break_end;
106tag_type(option) -> break_end;
107tag_type(title) -> break_end;
108tag_type(_) -> break_both.
109
110
111upper([C|Cs]) when C >= $a, C =< $z ->
112    [(C-$a)+$A | upper(Cs)];
113upper([C|Cs]) ->
114    [C|upper(Cs)];
115upper([]) -> [].
116    
117
118%%
119%% EXTENSION <erl>#PCDATA</erl>
120%%
121elem(erl,As,Exprs,Ps,St) ->
122    %% el(erl,As,Ts,fun(T) -> T == pcdata end,Ps,St);
123    %% Exprs erlang code
124    {value,V,Bs} = erl_eval:exprs(Exprs,[]),
125    %% V must be a erlang tagged html format
126    ?dbg("erlang value = ~p\n", [V]),
127    V;
128
129%%
130%% <!ENTITY % html.content "HEAD, BODY">
131%% <!ELEMENT HTML O O (%html.content;)    -- document root element -->
132%%
133elem(html,As,Cs,Ps,St) ->
134    {E1,Cs1} = require(head,Cs,Ps,St),
135    {E0,E2} = case Cs1 of 
136		  [{body,As2,Cs2}] -> 
137		      {?HTML_4_0 ++ "\n",
138		       elem(body,As2,Cs2,[body,html],St)};
139		  [{frameset,As2,Cs2}] ->
140		      {?HTML_4_0_Frameset ++ "\n",
141		       elem(frameset,As2,Cs2,[frameset,html],St)}
142	      end,
143    [E0,fmt(St,html,As,[E1,E2],Ps)];
144
145%%
146%% <!ELEMENT HEAD O O (%head.content;) +(%head.misc;) -- document head -->
147%%
148%% FIXME: head.content = TITLE & BASE?
149%% i.e one TITLE and optionally one BASE in any order
150%% 
151elem(head,As,Cs,Ps,St) ->
152    el(head,As,Cs,fun(T) -> html:is_head_misc(T) or 
153				html:is_head_content(T) end,Ps,St);
154
155%%
156%% <!ELEMENT BODY O O (%flow)+ +(INS|DEL) -- document body -->
157%%
158%% FIXME: INS and DEL is not handled
159%%
160elem(body,As,Cs,Ps,St) -> el(body,As,Cs, html:flow(),Ps,St);
161
162elem(title, As,Cs,Ps,St) ->
163    el(title,As,Cs,fun(T) -> T == pcdata end,Ps,St);
164%%    ["<TITLE", attrlist(title,As), ">", pcdata(Title,false), "</TITLE>\n"];
165%%
166%% <!ELEMENT META - O EMPTY               -- generic metainformation -->
167%%
168elem(meta, As, Cs,Ps,St) -> el0(meta,As,Cs,Ps,St);
169
170%%
171%% <!ELEMENT BASE - O EMPTY               -- document base URI -->
172%%
173elem(base,As,Cs,Ps,St) -> el0(base,As,Cs,Ps,St);
174
175%%
176%% <!ELEMENT LINK - O EMPTY               -- a media-independent link -->
177%%
178elem(link,As,Cs,Ps,St)  -> el0(link,As,Cs,Ps,St);
179
180elem(noscript, As, Cs,Ps,St) -> el(noscript,As,Cs,html:block(),Ps,St);
181
182elem(object, As, Cs,Ps,St) -> el(object,As,Cs,
183			   fun(T) -> html:is_flow(T) or (T == param) end,Ps,St);
184
185elem(param, As, Cs,Ps,St) -> el0(param,As,Cs,Ps,St);
186
187elem(img, As, Cs,Ps,St) -> el0(img,As,Cs,Ps,St);
188
189
190elem(style,As,Cs,Ps,St) ->
191    {Begin,End} = case lists:keysearch(hide, 1, As) of
192		      {value,{_,true}} -> 
193			  {"<!--\n", " -->\n"};
194		      _ -> { "", "" }
195		  end,
196    ["<STYLE", attrlist(style,As), ">\n", 
197     Begin,
198     map(fun({Name,Style}) -> [Name, " { ", Style, " }\n"] end, Cs),
199     End,
200     "</STYLE>\n"];
201
202elem(script,As,Script,Ps,St) ->
203    {Begin,End} = case lists:keysearch(hide, 1, As) of
204		      {value,{_,true}} -> 
205			  case lists:keysearch(type, 1, As) of
206			      "text/tcl"        -> 
207				  {"<!--\n", "# -->\n"};
208			      "text/vbscript"   -> 
209				  {"<!--\n", "' -->\n"};
210			      "text/javascript" -> 
211				  {"<!--\n", "// -->\n"};
212			      "text/erlscript"  -> 
213				  {"<!--\n", "%  -->\n"};
214			      _ ->
215				  {"<!--\n", " -->\n"}
216			  end;
217		      _ -> { "", "" }
218		  end,
219    [ "<SCRIPT", attrlist(script,As), ">\n",    
220      Begin,
221      Script, "\n",
222      End,
223      "</SCRIPT>\n"];
224
225elem(form, As, Cs,Ps,St) -> el(form,As,Cs,
226			    fun(T) -> html:is_flow(T) and (T =/= form) end,Ps,St);
227%%
228%% <!ELEMENT TABLE - -
229%%     (CAPTION?, (COL*|COLGROUP*), THEAD?, TFOOT?, TBODY+)>
230%%
231elem(table, As, Cs0,Ps,St) ->
232    {E1,Cs1} = optional(caption,Cs0,Ps,St),
233    {E2,Cs2} = repeat0(fun(Tag) -> (Tag == col) or (Tag == colgroup) end,
234		      Cs1,Ps,St),
235    {E3,Cs3} = optional(thead,Cs2,Ps,St),
236    {E4,Cs4} = optional(tfoot,Cs3,Ps,St),    
237    {E5,Cs5} = repeat0(fun(Tag) -> Tag==tbody end,Cs4,Ps,St),
238    E6 = repeat(fun(T) -> T == tr end,Cs5,Ps,St),
239    fmt(St,table,As,[E1,E2,E3,E4,E5,E6],Ps);
240
241%%
242%% <!ELEMENT TBODY    O O (TR)+           -- table body -->    
243%%
244elem(tbody, As, Cs,Ps,St) -> el(tbody,As,Cs,fun(T) -> T == tr end,Ps,St);
245
246%%
247%% <!ELEMENT THEAD    - O (TR)+           -- table header -->
248%%
249elem(thead, As, Cs,Ps,St) -> el(thead,As,Cs,fun(T) -> T == tr end,Ps,St);
250
251%%
252%% <!ELEMENT TFOOT    - O (TR)+           -- table footer -->
253%%
254elem(tfoot, As, Cs,Ps,St) -> el(tfoot,As,Cs,fun(T) -> T == tr end,Ps,St);
255
256%%
257%% <!ELEMENT TR       - O (TH|TD)+        -- table row -->
258%%
259elem(tr,As,Cs,Ps,St) -> el(tr,As,Cs,fun(T) -> (T == th) or (T == td) end,Ps,St);
260
261%%
262%% <!ELEMENT (TH|TD)  - O (%flow;)* -- table header cell, table data cell-->
263%%
264elem(th,As,Cs,Ps,St)  -> el(th,As,Cs,html:flow(),Ps,St);
265elem(td,As,Cs,Ps,St) ->  el(td,As,Cs,html:flow(),Ps,St);
266
267%%
268%% <!ELEMENT CAPTION  - - (%inline;)*     -- table caption -->
269%%
270elem(caption,As,Cs,Ps,St)  -> el(caption,As,Cs,html:inline(),Ps,St);
271elem(col,As,Cs,Ps,St)      -> el0(col,As,Cs,Ps,St);
272elem(colgroup,As,Cs,Ps,St) -> el(colgroup,As,Cs,fun(T) -> T == col end,Ps,St);
273
274%% Frames
275
276%%
277%% <!ELEMENT FRAMESET - - ((FRAMESET|FRAME)+ & NOFRAMES?) -- window subdivision-->
278%%
279elem(frameset,As,Cs,Ps,St) ->
280    el(frameset,As,Cs, 
281       fun(T) ->
282	       (T == frameset) or (T == frame) or (T == noframes)
283       end,Ps,St);
284
285elem(frame, As, Cs,Ps,St) -> el0(frame,As,Cs,Ps,St);
286
287elem(iframe, As, Cs,Ps,St) -> el(iframe,As,Cs, html:flow(),Ps,St);
288
289elem(noframes,As,Cs,Ps,St) -> el(noframes,As,Cs, html:flow(),Ps,St);
290
291%%
292%% LAYER, ILAYER and NOLAYER
293%%
294elem(layer,As,Cs,Ps,St) ->   el(layer, As, Cs, html:flow(),Ps,St);
295
296elem(ilayer, As,Cs,Ps,St) -> el(ilayer, As, Cs, html:flow(),Ps,St);
297
298elem(nolayer,As,Cs,Ps,St) -> el(nolayer, As, Cs, html:flow(),Ps,St);
299
300%% Lists
301elem(ul, As, Cs,Ps,St) -> el(ul,As,Cs,fun(T) -> T == li end,Ps,St);
302elem(ol, As, Cs,Ps,St) -> el(ol,As,Cs,fun(T) -> T == li end,Ps,St);
303elem(li, As, Cs,Ps,St) -> el(li,As,Cs,html:flow(),Ps,St);
304elem(dl, As, Cs,Ps,St) -> el(dl,As,Cs,fun(T) -> (T == dt) or (T == dd) end,Ps,St);
305elem(dt, As, Cs,Ps,St) -> el(dt,As,Cs,html:inline(),Ps,St);
306elem(dd, As, Cs,Ps,St) -> el(dd,As,Cs,html:flow(),Ps,St);
307
308%%
309%% %fontstyle
310%% <!ELEMENT (%fontstyle;|%phrase;) - - (%inline;)*>
311%%
312
313elem(tt,As, Cs,Ps,St)     -> el(tt,As,Cs,html:inline(),Ps,St);
314elem(i,As, Cs,Ps,St)      -> el(i,As,Cs,html:inline(),Ps,St);
315elem(b,As, Cs,Ps,St)      -> el(b,As,Cs,html:inline(),Ps,St);
316elem(u,As, Cs,Ps,St)      -> el(u,As,Cs,html:inline(),Ps,St);
317elem(s,As, Cs,Ps,St)      -> el(s,As,Cs,html:inline(),Ps,St);
318elem(strike,As, Cs,Ps,St) -> el(strike,As,Cs,html:inline(),Ps,St);
319elem(big,As, Cs,Ps,St)    -> el(big,As,Cs,html:inline(),Ps,St);
320elem(small,As, Cs,Ps,St)  -> el(small,As,Cs,html:inline(),Ps,St);
321
322%%
323%% <!ELEMENT (%heading;)  - - (%inline;)* -- heading -->
324%% 
325elem(h1,As,Cs,Ps,St)      -> el(h1,As,Cs,html:inline(),Ps,St);
326elem(h2,As,Cs,Ps,St)      -> el(h2,As,Cs,html:inline(),Ps,St);
327elem(h3,As,Cs,Ps,St)      -> el(h3,As,Cs,html:inline(),Ps,St);
328elem(h4,As,Cs,Ps,St)      -> el(h4,As,Cs,html:inline(),Ps,St);
329elem(h5,As,Cs,Ps,St)      -> el(h5,As,Cs,html:inline(),Ps,St);
330elem(h6,As,Cs,Ps,St)      -> el(h6,As,Cs,html:inline(),Ps,St);
331
332%%
333%% <!ELEMENT ADDRESS - - (%inline;)* -- information on author -->
334%%
335elem(address,As,Cs,Ps,St) -> el(address,As,Cs,html:inline(),Ps,St);
336
337%%
338%% %phrase
339%% <!ELEMENT (%fontstyle;|%phrase;) - - (%inline;)*>
340%%
341
342elem(em,As,Cs,Ps,St)      -> el(em,As,Cs,html:inline(),Ps,St);
343elem(strong,As,Cs,Ps,St)  -> el(strong,As,Cs,html:inline(),Ps,St);
344elem(dfn,As,Cs,Ps,St)     -> el(dfn,As,Cs,html:inline(),Ps,St);
345elem(code,As,Cs,Ps,St)    -> el(code,As,Cs,html:inline(),Ps,St);
346elem(samp,As,Cs,Ps,St)    -> el(samp,As,Cs,html:inline(),Ps,St);
347elem(kbd,As,Cs,Ps,St)     -> el(kbd,As,Cs,html:inline(),Ps,St);
348elem(var,As,Cs,Ps,St)     -> el(var,As,Cs,html:inline(),Ps,St);
349elem(cite,As,Cs,Ps,St)    -> el(cite,As,Cs,html:inline(),Ps,St);
350elem(abbr,As,Cs,Ps,St)    -> el(abbr,As,Cs,html:inline(),Ps,St);
351elem(acronym,As,Cs,Ps,St) -> el(acronum,As,Cs,html:inline(),Ps,St);
352
353%%
354%% <!ELEMENT BLOCKQUOTE - - (%block;|SCRIPT)+ -- long quotation -->
355%%
356elem(blockquote,As,Cs,Ps,St) -> 
357    el(blockquote,As,Cs, fun(T) -> html:is_block(T) or (T == script) end,Ps,St);
358%%
359%% <!ELEMENT Q - - (%inline;)*            -- short inline quotation -->
360%%
361elem(q,As,Cs,Ps,St) ->  el(q,As,Cs,html:inline(),Ps,St);
362
363%%
364%% <!ELEMENT (SUB|SUP) - - (%inline;)*    -- subscript, superscript -->
365%%
366elem(sub,As,Cs,Ps,St) -> el(sub,As,Cs,html:inline(),Ps,St);
367elem(sup,As,Cs,Ps,St) -> el(sup,As,Cs,html:inline(),Ps,St);
368
369%%
370%% <!ELEMENT P - O (%inline;)*            -- paragraph -->
371%%
372elem(p,As,Cs,Ps,St) -> el(p,As,Cs,html:inline(),Ps,St);
373
374%%
375%% <!ELEMENT BR - O EMPTY                 -- forced line break -->
376%%
377elem(br,As,Cs,Ps,St) -> el0(br,As,Cs,Ps,St);
378
379%%
380%% <!ELEMENT PRE - - (%inline;)* -(%pre.exclusion;) -- preformatted text -->
381%%
382elem(pre,As,Cs,Ps,St) ->
383    el(pre,As,Cs,fun(T) -> html:is_inline(T) and not 
384			       html:is_pre_exclusion(T) end,Ps,St);
385
386%%
387%% <!ELEMENT DIV - - (%flow;)*         -- generic language/style container -->
388%%
389elem('div',As,Cs,Ps,St) -> el('div', As, Cs, html:flow(),Ps,St);
390
391%%
392%% <!ELEMENT CENTER - - (%flow;)*       -- shorthand for DIV align=center -->
393%%
394elem(center,As,Cs,Ps,St) -> el(center, As, Cs, html:flow(),Ps,St);
395
396%%
397%% <!ELEMENT SPAN - - (%inline;)*      -- generic language/style container -->
398%%
399elem(span,As,Cs,Ps,St) -> el(span, As, Cs, html:inline(),Ps,St);
400
401%%
402%% <!ELEMENT BDO - - (%inline;)*          -- I18N BiDi over-ride -->
403%%
404elem(bdo,As,Cs,Ps,St) -> el(bdo, As, Cs, html:inline(),Ps,St);
405
406%%
407%% <!ELEMENT BASEFONT - O EMPTY           -- base font size -->
408%%
409elem(basefont,As,Cs,Ps,St) -> el0(basefont, As, Cs,Ps,St);
410
411%%
412%% <!ELEMENT FONT - - (%inline;)*         -- local change to font -->
413%%
414elem(font,As,Cs,Ps,St) -> el(font, As, Cs, html:inline(),Ps,St);
415
416%%
417%% <!ELEMENT HR - O EMPTY -- horizontal rule -->
418%%
419elem(hr, As, Cs,Ps,St) -> el0(hr, As, Cs,Ps,St);
420
421%%
422%% <!ELEMENT A - - (%inline;)* -(A)       -- anchor -->
423%%
424elem(a, As, Cs,Ps,St) -> 
425    el(a, As, Cs, fun(T) -> html:is_inline(T) and (T =/= a) end,Ps,St);
426
427%%
428%% <!ELEMENT MAP - - ((%block;)+ | AREA+) -- client-side image map -->
429%%
430elem(map, As,Cs,Ps,St) ->
431    el(map, As, Cs, fun(T) -> html:is_block(T) or (T == area) end,Ps,St);
432
433%%
434%% <!ELEMENT AREA - O EMPTY               -- client-side image map area -->
435%%
436elem(area, As,Cs,Ps,St) -> el0(area, As, Cs,Ps,St);
437
438%%
439%% <!ELEMENT TEXTAREA - - (#PCDATA)       -- multi-line text field -->
440%%
441elem(textarea,As,Cs,Ps,St) -> el(textarea,As,Cs,fun(T) -> T == pcdata end,Ps,St);
442
443%%
444%% <!ELEMENT FIELDSET - - (#PCDATA,LEGEND,(%flow;)*) -- form control group -->
445%%
446elem(fieldset,As,Cs,Ps,St) ->
447    el(fieldset,As,Cs,
448       fun(T) -> (T == pcdata) or (T == legend) or html:is_flow(T) end,Ps,St);
449%%
450%% <!ELEMENT LEGEND - - (%inline;)*       -- fieldset legend -->
451%%
452elem(legend,As,Cs,Ps,St) -> el(legend,As,Cs,html:inline(),Ps,St);
453
454%% <!ELEMENT BUTTON - -
455%%     (%flow;)* -(A|%formctrl;|FORM|ISINDEX|FIELDSET|IFRAME)
456%%     -- push button -->
457elem(button,As,Cs,Ps,St) ->
458    el(button,As,Cs,
459       fun(T) -> html:is_flow(T) and
460		     (T =/= a) and (not html:is_formctrl(T)) and
461		     (T =/= form) and (T =/= isindex) and
462		     (T =/= fieldset) and (T =/= iframe)
463       end,Ps,St);
464%%
465%% <!ELEMENT LABEL - - (%inline;)* -(LABEL) -- form field label text -->
466%%
467elem(label,As,Cs,Ps,St) ->
468    el(label,As,Cs, fun(T) -> html:is_inline(T) and (T =/= label) end,Ps,St);
469
470%%
471%% <!ELEMENT INPUT - O EMPTY              -- form control -->
472%%
473elem(input, As, Cs,Ps,St) -> el0(input, As, Cs,Ps,St);
474
475
476%%
477%% <!ELEMENT SELECT - - (OPTGROUP|OPTION)+ -- option selector -->
478%%
479elem(select,As,Cs,Ps,St) ->
480    el(select,As,Cs,fun(T) -> (T == optgroup) or (T == option) end,Ps,St);
481
482%%
483%% <!ELEMENT OPTGROUP - - (OPTION)+ -- option group -->
484%%
485elem(optgroup,As,Cs,Ps,St) ->
486    el(optgroup,As,Cs,fun(T) -> (T == option) end,Ps,St);
487
488%%
489%% <!ELEMENT OPTION - O (#PCDATA)         -- selectable choice -->
490%%
491elem(option,As,Cs,Ps,St) ->
492    el(option,As,Cs,fun(T) -> T == pcdata end,Ps,St);
493
494elem(pcdata,Data,[],Ps,St) ->
495    pcdata(Data, member(pre,Ps) or member(textarea,Ps)).
496
497%%
498%% simple element
499%%   <TAG attrlist> ... </TAG>
500%%
501el(Tag,As,Cs,IsEntity,Ps,St) ->
502    Body = repeat(IsEntity,Cs,Ps,St),
503    fmt(St,Tag,As,Body,Ps).
504
505%%
506%% simple element with no (forbidden) endtag
507%%   <TAG attrlist>
508%%
509el0(Tag,As,[],Ps,St) ->
510    fmt(St,Tag,As,"",Ps);
511el0(Tag,As,Cs,Ps,St) ->
512    exit({bad_tag,Tag,Ps}).
513
514%%
515%% esp tags have some a number of forms
516%% {Tag,Attr,Elems}
517%% {Tag,Attr}
518%% Tag
519%%
520tag(T = {Tag,Attr,Elems}) -> T;
521tag({Tag,Attr}) -> {Tag,Attr,[]};
522tag(Tag) when atom(Tag) -> {Tag,[],[]}.
523
524tag1([T|Ts]) -> {tag(T),Ts};
525tag1(T) -> {tag(T),[]}.
526
527%%
528%% repeat element 
529%% repeat(BoolFun, Entity, Parents)
530%%
531repeat(IsEntity,[],Ps,St) ->
532    [];
533repeat(IsEntity,Ts,Ps,St) ->
534    {{Tag,Attr,Elems},Ts1} = tag1(Ts),
535    if Tag == erl ->
536	    Ts2 = elem(erl,Attr,Elems,[Tag|Ps],St),
537	    repeat(IsEntity,Ts2,Ps,St) ++
538		repeat(IsEntity,Ts1,Ps,St);
539       true ->
540	    case IsEntity(Tag) of
541		true ->
542		    [elem(Tag,Attr,Elems,[Tag|Ps],St)|
543		     repeat(IsEntity,Ts1,Ps,St)];
544		false ->
545		    exit({bad_tag,Tag,Ps})
546	    end
547    end.
548
549    
550%% repeat while IsEntity return 
551repeat0(IsEntity,[],Ps,St) ->
552    {[],[]};
553repeat0(IsEntity,Ts,Ps,St) ->
554    {{Tag,Attr,Elems},Ts1} = tag1(Ts),
555    case IsEntity(Tag) of
556	true -> 
557	    Elem = elem(Tag,Attr,Elems,[Tag|Ps],St),
558	    {Es,Cs} = repeat0(IsEntity,Ts1,Ps,St),
559	    {[Elem|Es],Cs};
560	false -> {[],Ts}
561    end.
562
563    
564
565
566%% Do optional FIRST element 	    
567optional(Tag,Ts,Ps,St) ->
568    case tag1(Ts) of
569	{{Tag,Attr,Elems},Cs} ->
570	    {elem(Tag,Attr,Elems,[Tag|Ps],St), Cs};
571	_ ->
572	    {"", Ts}
573    end.
574
575%% Do required FIRST element 	    
576require(Tag,Ts,Ps,St) ->
577    {{Tag,Attr,Elems},Cs} = tag1(Ts),
578    {elem(Tag,Attr,Elems,[Tag|Ps],St),Cs}.
579
580%%
581%% Emit HTML from 8-bit  iso-8859-1
582%%
583pcdata(Cs, _) ->
584    pcdata(Cs).
585
586pcdata(Cs) ->
587    pcdata1(Cs, html:chars()).    
588
589pcdata1([C|Cs], TV) when integer(C) ->
590    if
591	C == $\n -> [C | pcdata1(Cs,TV)];
592	C == $\r -> [C | pcdata1(Cs,TV)];
593	C == $\t -> [C | pcdata1(Cs,TV)];
594	C == $\s -> [C | pcdata1(Cs,TV)];
595	C > 255  -> [amp(C) | pcdata1(Cs,TV)];
596	C < 0 -> pcdata1(Cs,TV);
597	true -> [element(C+1, TV) | pcdata1(Cs,TV)]
598    end;
599pcdata1([C|Cs], TV) when binary(C) ->
600    [ pcdata1(binary_to_list(C),TV) | pcdata1(Cs,TV)];
601pcdata1([Cs1|Cs2], TV) when list(Cs1) ->
602    [pcdata1(Cs1,TV) | pcdata1(Cs2,TV)];
603pcdata1([], _) ->
604    [].
605
606amp(N) when N =< 16#ff     ->  "&#"++hex8(N)++";";
607amp(N) when N =< 16#ffff   ->  "&#"++hex16(N)++";";
608amp(N) when N =< 16#ffffff ->  "&#"++hex24(N)++";";
609amp(N) -> "&#"++hex32(N)++";".
610
611hex16(N) ->
612    hex8((N bsr 8) band 16#ff) ++ hex8(N band 16#ff).
613
614hex24(N) ->
615    hex8((N bsr 16) band 16#ff) ++ 
616	hex8((N bsr 8) band 16#ff) ++ 
617	hex8(N band 16#ff).
618
619hex32(N) ->
620    hex8((N bsr 24) band 16#ff) ++ 
621    hex8((N bsr 16) band 16#ff) ++ 
622    hex8((N bsr 8) band 16#ff) ++ 
623    hex8(N band 16#ff).
624
625
626hex8(N) ->
627    hex4((N bsr 4) band 16#f)++hex4(N band 16#f).
628
629hex4(N) when N < 10 -> [N+$0];
630hex4(N) -> [(N-10)+$A].
631    
632
633
634extract_attr(K, Default, As) ->
635    extract_attr(K, Default, As, []).
636
637extract_attr(K, Default, [{K,V}|As], Bs) ->
638    {{K,to_value(V)}, As++Bs};
639extract_attr(K, Default, [KV|As], Bs) ->
640    extract_attr(K, Default, As, [KV|Bs]);
641extract_attr(K, Default, [], Bs) -> {Default,Bs}.
642
643
644
645attrlist(Tag,As) ->
646    map(fun ({K,{erl,Exprs}}) ->
647		%% FIXME: add (cgi?) environment
648		{value,V,Bs} = erl_eval:exprs(Exprs,[]),
649		[" ", atom_to_list(K), "=", to_value(V)];
650	    ({K,V}) ->
651		[" ", atom_to_list(K), "=", to_value(V)];
652	   (K) ->
653		[" ", atom_to_list(K)]
654	end, As).
655
656unquote([$" | Cs]) -> 
657     case reverse(Cs) of
658       [$" | Cs1] -> reverse(Cs1);
659       _ -> Cs
660     end;
661unquote(Cs) ->
662     Cs.
663
664to_value(X) when integer(X) ->
665    integer_to_list(X);
666to_value(X) when atom(X) ->
667    atom_to_list(X);
668to_value(X) when list(X) ->
669    [$", X, $"];
670to_value({var,V}) ->
671    "VAR(" ++ atom_to_list(V) ++ ")".
672
673