htmlgen.nim 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## **Warning**: This module uses ``immediate`` macros which are known to
  10. ## cause problems. Do yourself a favor and import the module
  11. ## as ``from htmlgen import nil`` and then fully qualify the macros.
  12. ##
  13. ##
  14. ## This module implements a simple `XML`:idx: and `HTML`:idx: code
  15. ## generator. Each commonly used HTML tag has a corresponding macro
  16. ## that generates a string with its HTML representation.
  17. ##
  18. ## Example:
  19. ##
  20. ## .. code-block:: Nim
  21. ## var nim = "Nim"
  22. ## echo h1(a(href="http://nim-lang.org", nim))
  23. ##
  24. ## Writes the string::
  25. ##
  26. ## <h1><a href="http://nim-lang.org">Nim</a></h1>
  27. ##
  28. import
  29. macros, strutils
  30. const
  31. coreAttr* = " id class title style "
  32. eventAttr* = " onclick ondblclick onmousedown onmouseup " &
  33. "onmouseover onmousemove onmouseout onkeypress onkeydown onkeyup onload "
  34. commonAttr* = coreAttr & eventAttr
  35. proc getIdent(e: NimNode): string {.compileTime.} =
  36. case e.kind
  37. of nnkIdent: result = normalize($e.ident)
  38. of nnkAccQuoted:
  39. result = getIdent(e[0])
  40. for i in 1 .. e.len-1:
  41. result.add getIdent(e[i])
  42. else: error("cannot extract identifier from node: " & toStrLit(e).strVal)
  43. proc delete[T](s: var seq[T], attr: T): bool =
  44. var idx = find(s, attr)
  45. if idx >= 0:
  46. var L = s.len
  47. s[idx] = s[L-1]
  48. setLen(s, L-1)
  49. result = true
  50. proc xmlCheckedTag*(e: NimNode, tag: string, optAttr = "", reqAttr = "",
  51. isLeaf = false): NimNode {.compileTime.} =
  52. ## use this procedure to define a new XML tag
  53. # copy the attributes; when iterating over them these lists
  54. # will be modified, so that each attribute is only given one value
  55. var req = split(reqAttr)
  56. var opt = split(optAttr)
  57. result = newNimNode(nnkBracket, e)
  58. result.add(newStrLitNode("<"))
  59. result.add(newStrLitNode(tag))
  60. # first pass over attributes:
  61. for i in 1..e.len-1:
  62. if e[i].kind == nnkExprEqExpr:
  63. var name = getIdent(e[i][0])
  64. if delete(req, name) or delete(opt, name):
  65. result.add(newStrLitNode(" "))
  66. result.add(newStrLitNode(name))
  67. result.add(newStrLitNode("=\""))
  68. result.add(e[i][1])
  69. result.add(newStrLitNode("\""))
  70. else:
  71. error("invalid attribute for '" & tag & "' element: " & name)
  72. # check each required attribute exists:
  73. if req.len > 0:
  74. error(req[0] & " attribute for '" & tag & "' element expected")
  75. if isLeaf:
  76. for i in 1..e.len-1:
  77. if e[i].kind != nnkExprEqExpr:
  78. error("element " & tag & " cannot be nested")
  79. result.add(newStrLitNode(" />"))
  80. else:
  81. result.add(newStrLitNode(">"))
  82. # second pass over elements:
  83. for i in 1..e.len-1:
  84. if e[i].kind != nnkExprEqExpr: result.add(e[i])
  85. result.add(newStrLitNode("</"))
  86. result.add(newStrLitNode(tag))
  87. result.add(newStrLitNode(">"))
  88. result = nestList(!"&", result)
  89. macro a*(e: varargs[untyped]): untyped =
  90. ## generates the HTML ``a`` element.
  91. let e = callsite()
  92. result = xmlCheckedTag(e, "a", "href charset type hreflang rel rev " &
  93. "accesskey tabindex" & commonAttr)
  94. macro acronym*(e: varargs[untyped]): untyped =
  95. ## generates the HTML ``acronym`` element.
  96. let e = callsite()
  97. result = xmlCheckedTag(e, "acronym", commonAttr)
  98. macro address*(e: varargs[untyped]): untyped =
  99. ## generates the HTML ``address`` element.
  100. let e = callsite()
  101. result = xmlCheckedTag(e, "address", commonAttr)
  102. macro area*(e: varargs[untyped]): untyped =
  103. ## generates the HTML ``area`` element.
  104. let e = callsite()
  105. result = xmlCheckedTag(e, "area", "shape coords href nohref" &
  106. " accesskey tabindex" & commonAttr, "alt", true)
  107. macro b*(e: varargs[untyped]): untyped =
  108. ## generates the HTML ``b`` element.
  109. let e = callsite()
  110. result = xmlCheckedTag(e, "b", commonAttr)
  111. macro base*(e: varargs[untyped]): untyped =
  112. ## generates the HTML ``base`` element.
  113. let e = callsite()
  114. result = xmlCheckedTag(e, "base", "", "href", true)
  115. macro big*(e: varargs[untyped]): untyped =
  116. ## generates the HTML ``big`` element.
  117. let e = callsite()
  118. result = xmlCheckedTag(e, "big", commonAttr)
  119. macro blockquote*(e: varargs[untyped]): untyped =
  120. ## generates the HTML ``blockquote`` element.
  121. let e = callsite()
  122. result = xmlCheckedTag(e, "blockquote", " cite" & commonAttr)
  123. macro body*(e: varargs[untyped]): untyped =
  124. ## generates the HTML ``body`` element.
  125. let e = callsite()
  126. result = xmlCheckedTag(e, "body", commonAttr)
  127. macro br*(e: varargs[untyped]): untyped =
  128. ## generates the HTML ``br`` element.
  129. let e = callsite()
  130. result = xmlCheckedTag(e, "br", "", "", true)
  131. macro button*(e: varargs[untyped]): untyped =
  132. ## generates the HTML ``button`` element.
  133. let e = callsite()
  134. result = xmlCheckedTag(e, "button", "accesskey tabindex " &
  135. "disabled name type value" & commonAttr)
  136. macro caption*(e: varargs[untyped]): untyped =
  137. ## generates the HTML ``caption`` element.
  138. let e = callsite()
  139. result = xmlCheckedTag(e, "caption", commonAttr)
  140. macro cite*(e: varargs[untyped]): untyped =
  141. ## generates the HTML ``cite`` element.
  142. let e = callsite()
  143. result = xmlCheckedTag(e, "cite", commonAttr)
  144. macro code*(e: varargs[untyped]): untyped =
  145. ## generates the HTML ``code`` element.
  146. let e = callsite()
  147. result = xmlCheckedTag(e, "code", commonAttr)
  148. macro col*(e: varargs[untyped]): untyped =
  149. ## generates the HTML ``col`` element.
  150. let e = callsite()
  151. result = xmlCheckedTag(e, "col", "span align valign" & commonAttr, "", true)
  152. macro colgroup*(e: varargs[untyped]): untyped =
  153. ## generates the HTML ``colgroup`` element.
  154. let e = callsite()
  155. result = xmlCheckedTag(e, "colgroup", "span align valign" & commonAttr)
  156. macro dd*(e: varargs[untyped]): untyped =
  157. ## generates the HTML ``dd`` element.
  158. let e = callsite()
  159. result = xmlCheckedTag(e, "dd", commonAttr)
  160. macro del*(e: varargs[untyped]): untyped =
  161. ## generates the HTML ``del`` element.
  162. let e = callsite()
  163. result = xmlCheckedTag(e, "del", "cite datetime" & commonAttr)
  164. macro dfn*(e: varargs[untyped]): untyped =
  165. ## generates the HTML ``dfn`` element.
  166. let e = callsite()
  167. result = xmlCheckedTag(e, "dfn", commonAttr)
  168. macro `div`*(e: varargs[untyped]): untyped =
  169. ## generates the HTML ``div`` element.
  170. let e = callsite()
  171. result = xmlCheckedTag(e, "div", commonAttr)
  172. macro dl*(e: varargs[untyped]): untyped =
  173. ## generates the HTML ``dl`` element.
  174. let e = callsite()
  175. result = xmlCheckedTag(e, "dl", commonAttr)
  176. macro dt*(e: varargs[untyped]): untyped =
  177. ## generates the HTML ``dt`` element.
  178. let e = callsite()
  179. result = xmlCheckedTag(e, "dt", commonAttr)
  180. macro em*(e: varargs[untyped]): untyped =
  181. ## generates the HTML ``em`` element.
  182. let e = callsite()
  183. result = xmlCheckedTag(e, "em", commonAttr)
  184. macro fieldset*(e: varargs[untyped]): untyped =
  185. ## generates the HTML ``fieldset`` element.
  186. let e = callsite()
  187. result = xmlCheckedTag(e, "fieldset", commonAttr)
  188. macro form*(e: varargs[untyped]): untyped =
  189. ## generates the HTML ``form`` element.
  190. let e = callsite()
  191. result = xmlCheckedTag(e, "form", "method encype accept accept-charset" &
  192. commonAttr, "action")
  193. macro h1*(e: varargs[untyped]): untyped =
  194. ## generates the HTML ``h1`` element.
  195. let e = callsite()
  196. result = xmlCheckedTag(e, "h1", commonAttr)
  197. macro h2*(e: varargs[untyped]): untyped =
  198. ## generates the HTML ``h2`` element.
  199. let e = callsite()
  200. result = xmlCheckedTag(e, "h2", commonAttr)
  201. macro h3*(e: varargs[untyped]): untyped =
  202. ## generates the HTML ``h3`` element.
  203. let e = callsite()
  204. result = xmlCheckedTag(e, "h3", commonAttr)
  205. macro h4*(e: varargs[untyped]): untyped =
  206. ## generates the HTML ``h4`` element.
  207. let e = callsite()
  208. result = xmlCheckedTag(e, "h4", commonAttr)
  209. macro h5*(e: varargs[untyped]): untyped =
  210. ## generates the HTML ``h5`` element.
  211. let e = callsite()
  212. result = xmlCheckedTag(e, "h5", commonAttr)
  213. macro h6*(e: varargs[untyped]): untyped =
  214. ## generates the HTML ``h6`` element.
  215. let e = callsite()
  216. result = xmlCheckedTag(e, "h6", commonAttr)
  217. macro head*(e: varargs[untyped]): untyped =
  218. ## generates the HTML ``head`` element.
  219. let e = callsite()
  220. result = xmlCheckedTag(e, "head", "profile")
  221. macro html*(e: varargs[untyped]): untyped =
  222. ## generates the HTML ``html`` element.
  223. let e = callsite()
  224. result = xmlCheckedTag(e, "html", "xmlns", "")
  225. macro hr*(): untyped =
  226. ## generates the HTML ``hr`` element.
  227. let e = callsite()
  228. result = xmlCheckedTag(e, "hr", commonAttr, "", true)
  229. macro i*(e: varargs[untyped]): untyped =
  230. ## generates the HTML ``i`` element.
  231. let e = callsite()
  232. result = xmlCheckedTag(e, "i", commonAttr)
  233. macro img*(e: varargs[untyped]): untyped =
  234. ## generates the HTML ``img`` element.
  235. let e = callsite()
  236. result = xmlCheckedTag(e, "img", "longdesc height width", "src alt", true)
  237. macro input*(e: varargs[untyped]): untyped =
  238. ## generates the HTML ``input`` element.
  239. let e = callsite()
  240. result = xmlCheckedTag(e, "input", "name type value checked maxlength src" &
  241. " alt accept disabled readonly accesskey tabindex" & commonAttr, "", true)
  242. macro ins*(e: varargs[untyped]): untyped =
  243. ## generates the HTML ``ins`` element.
  244. let e = callsite()
  245. result = xmlCheckedTag(e, "ins", "cite datetime" & commonAttr)
  246. macro kbd*(e: varargs[untyped]): untyped =
  247. ## generates the HTML ``kbd`` element.
  248. let e = callsite()
  249. result = xmlCheckedTag(e, "kbd", commonAttr)
  250. macro label*(e: varargs[untyped]): untyped =
  251. ## generates the HTML ``label`` element.
  252. let e = callsite()
  253. result = xmlCheckedTag(e, "label", "for accesskey" & commonAttr)
  254. macro legend*(e: varargs[untyped]): untyped =
  255. ## generates the HTML ``legend`` element.
  256. let e = callsite()
  257. result = xmlCheckedTag(e, "legend", "accesskey" & commonAttr)
  258. macro li*(e: varargs[untyped]): untyped =
  259. ## generates the HTML ``li`` element.
  260. let e = callsite()
  261. result = xmlCheckedTag(e, "li", commonAttr)
  262. macro link*(e: varargs[untyped]): untyped =
  263. ## generates the HTML ``link`` element.
  264. let e = callsite()
  265. result = xmlCheckedTag(e, "link", "href charset hreflang type rel rev media" &
  266. commonAttr, "", true)
  267. macro map*(e: varargs[untyped]): untyped =
  268. ## generates the HTML ``map`` element.
  269. let e = callsite()
  270. result = xmlCheckedTag(e, "map", "class title" & eventAttr, "id", false)
  271. macro meta*(e: varargs[untyped]): untyped =
  272. ## generates the HTML ``meta`` element.
  273. let e = callsite()
  274. result = xmlCheckedTag(e, "meta", "name http-equiv scheme", "content", true)
  275. macro noscript*(e: varargs[untyped]): untyped =
  276. ## generates the HTML ``noscript`` element.
  277. let e = callsite()
  278. result = xmlCheckedTag(e, "noscript", commonAttr)
  279. macro `object`*(e: varargs[untyped]): untyped =
  280. ## generates the HTML ``object`` element.
  281. let e = callsite()
  282. result = xmlCheckedTag(e, "object", "classid data codebase declare type " &
  283. "codetype archive standby width height name tabindex" & commonAttr)
  284. macro ol*(e: varargs[untyped]): untyped =
  285. ## generates the HTML ``ol`` element.
  286. let e = callsite()
  287. result = xmlCheckedTag(e, "ol", commonAttr)
  288. macro optgroup*(e: varargs[untyped]): untyped =
  289. ## generates the HTML ``optgroup`` element.
  290. let e = callsite()
  291. result = xmlCheckedTag(e, "optgroup", "disabled" & commonAttr, "label", false)
  292. macro option*(e: varargs[untyped]): untyped =
  293. ## generates the HTML ``option`` element.
  294. let e = callsite()
  295. result = xmlCheckedTag(e, "option", "selected value" & commonAttr)
  296. macro p*(e: varargs[untyped]): untyped =
  297. ## generates the HTML ``p`` element.
  298. let e = callsite()
  299. result = xmlCheckedTag(e, "p", commonAttr)
  300. macro param*(e: varargs[untyped]): untyped =
  301. ## generates the HTML ``param`` element.
  302. let e = callsite()
  303. result = xmlCheckedTag(e, "param", "value id type valuetype", "name", true)
  304. macro pre*(e: varargs[untyped]): untyped =
  305. ## generates the HTML ``pre`` element.
  306. let e = callsite()
  307. result = xmlCheckedTag(e, "pre", commonAttr)
  308. macro q*(e: varargs[untyped]): untyped =
  309. ## generates the HTML ``q`` element.
  310. let e = callsite()
  311. result = xmlCheckedTag(e, "q", "cite" & commonAttr)
  312. macro samp*(e: varargs[untyped]): untyped =
  313. ## generates the HTML ``samp`` element.
  314. let e = callsite()
  315. result = xmlCheckedTag(e, "samp", commonAttr)
  316. macro script*(e: varargs[untyped]): untyped =
  317. ## generates the HTML ``script`` element.
  318. let e = callsite()
  319. result = xmlCheckedTag(e, "script", "src charset defer", "type", false)
  320. macro select*(e: varargs[untyped]): untyped =
  321. ## generates the HTML ``select`` element.
  322. let e = callsite()
  323. result = xmlCheckedTag(e, "select", "name size multiple disabled tabindex" &
  324. commonAttr)
  325. macro small*(e: varargs[untyped]): untyped =
  326. ## generates the HTML ``small`` element.
  327. let e = callsite()
  328. result = xmlCheckedTag(e, "small", commonAttr)
  329. macro span*(e: varargs[untyped]): untyped =
  330. ## generates the HTML ``span`` element.
  331. let e = callsite()
  332. result = xmlCheckedTag(e, "span", commonAttr)
  333. macro strong*(e: varargs[untyped]): untyped =
  334. ## generates the HTML ``strong`` element.
  335. let e = callsite()
  336. result = xmlCheckedTag(e, "strong", commonAttr)
  337. macro style*(e: varargs[untyped]): untyped =
  338. ## generates the HTML ``style`` element.
  339. let e = callsite()
  340. result = xmlCheckedTag(e, "style", "media title", "type")
  341. macro sub*(e: varargs[untyped]): untyped =
  342. ## generates the HTML ``sub`` element.
  343. let e = callsite()
  344. result = xmlCheckedTag(e, "sub", commonAttr)
  345. macro sup*(e: varargs[untyped]): untyped =
  346. ## generates the HTML ``sup`` element.
  347. let e = callsite()
  348. result = xmlCheckedTag(e, "sup", commonAttr)
  349. macro table*(e: varargs[untyped]): untyped =
  350. ## generates the HTML ``table`` element.
  351. let e = callsite()
  352. result = xmlCheckedTag(e, "table", "summary border cellpadding cellspacing" &
  353. " frame rules width" & commonAttr)
  354. macro tbody*(e: varargs[untyped]): untyped =
  355. ## generates the HTML ``tbody`` element.
  356. let e = callsite()
  357. result = xmlCheckedTag(e, "tbody", "align valign" & commonAttr)
  358. macro td*(e: varargs[untyped]): untyped =
  359. ## generates the HTML ``td`` element.
  360. let e = callsite()
  361. result = xmlCheckedTag(e, "td", "colspan rowspan abbr axis headers scope" &
  362. " align valign" & commonAttr)
  363. macro textarea*(e: varargs[untyped]): untyped =
  364. ## generates the HTML ``textarea`` element.
  365. let e = callsite()
  366. result = xmlCheckedTag(e, "textarea", " name disabled readonly accesskey" &
  367. " tabindex" & commonAttr, "rows cols", false)
  368. macro tfoot*(e: varargs[untyped]): untyped =
  369. ## generates the HTML ``tfoot`` element.
  370. let e = callsite()
  371. result = xmlCheckedTag(e, "tfoot", "align valign" & commonAttr)
  372. macro th*(e: varargs[untyped]): untyped =
  373. ## generates the HTML ``th`` element.
  374. let e = callsite()
  375. result = xmlCheckedTag(e, "th", "colspan rowspan abbr axis headers scope" &
  376. " align valign" & commonAttr)
  377. macro thead*(e: varargs[untyped]): untyped =
  378. ## generates the HTML ``thead`` element.
  379. let e = callsite()
  380. result = xmlCheckedTag(e, "thead", "align valign" & commonAttr)
  381. macro title*(e: varargs[untyped]): untyped =
  382. ## generates the HTML ``title`` element.
  383. let e = callsite()
  384. result = xmlCheckedTag(e, "title")
  385. macro tr*(e: varargs[untyped]): untyped =
  386. ## generates the HTML ``tr`` element.
  387. let e = callsite()
  388. result = xmlCheckedTag(e, "tr", "align valign" & commonAttr)
  389. macro tt*(e: varargs[untyped]): untyped =
  390. ## generates the HTML ``tt`` element.
  391. let e = callsite()
  392. result = xmlCheckedTag(e, "tt", commonAttr)
  393. macro ul*(e: varargs[untyped]): untyped =
  394. ## generates the HTML ``ul`` element.
  395. let e = callsite()
  396. result = xmlCheckedTag(e, "ul", commonAttr)
  397. macro `var`*(e: varargs[untyped]): untyped =
  398. ## generates the HTML ``var`` element.
  399. let e = callsite()
  400. result = xmlCheckedTag(e, "var", commonAttr)
  401. when isMainModule:
  402. let nim = "Nim"
  403. assert h1(a(href="http://nim-lang.org", nim)) ==
  404. """<h1><a href="http://nim-lang.org">Nim</a></h1>"""
  405. assert form(action="test", `accept-charset` = "Content-Type") ==
  406. """<form action="test" accept-charset="Content-Type"></form>"""