PageRenderTime 61ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/erl_cgi/src/esp_html.erl

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