ssxml.ss 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. (define (list->html-text lst)
  2. (let loop ((lst lst)
  3. (res #f))
  4. (cond
  5. ((null? lst) res)
  6. (else
  7. (let ((e (car lst)))
  8. (loop (cdr lst)
  9. (string-append
  10. (if res res "")
  11. (cond
  12. ((list? e) (list->html-text e))
  13. ((and (string? e) (not res)) e)
  14. ((string? e)
  15. (string-append
  16. (if (char=? (string-ref e 0) #\<) "" " ")
  17. e))
  18. (else (format (if res " ~a" "~a") e))))))))))
  19. (define (escape str)
  20. (let loop ((result "")
  21. (last-start 0)
  22. (cur 0))
  23. (if (>= cur (string-length str))
  24. (string-append result (substring str last-start cur))
  25. (let* ((ch (string-ref str cur))
  26. (entity (case ch
  27. ((#\&) "&amp;")
  28. ((#\<) "&lt;")
  29. ((#\>) "&gt;")
  30. ((#\") "&quot;")
  31. ((#\') "&#x27;")
  32. (else #f))))
  33. (if entity
  34. (loop (string-append
  35. result
  36. (substring str last-start cur)
  37. entity)
  38. (+ cur 1)
  39. (+ cur 1))
  40. (loop result last-start (+ cur 1)))))))
  41. (define (tag name is-void escape-needed)
  42. (lambda args
  43. (let loop ((attrs "")
  44. (content "")
  45. (attr #f)
  46. (args args))
  47. (cond
  48. ((null? args)
  49. (string-append
  50. "<"name attrs
  51. (if attr
  52. (string-append "=\""(symbol->string attr)"\"")
  53. "")
  54. (if is-void
  55. " />"
  56. (string-append
  57. ">"
  58. (if (zero? (string-length content))
  59. ""
  60. (if escape-needed (escape content) content))
  61. "</"name">"))))
  62. ((symbol? (car args))
  63. (loop (string-append
  64. attrs
  65. (if attr
  66. (string-append "=\""(symbol->string attr)"\" ")
  67. " ")
  68. (symbol->string (car args)))
  69. content
  70. (car args)
  71. (cdr args)))
  72. (attr
  73. (loop (string-append
  74. attrs"=\""
  75. (escape
  76. (let ((cattr (car args)))
  77. (if (list? cattr)
  78. (apply string-append cattr)
  79. cattr)))
  80. "\"")
  81. content
  82. #f
  83. (cdr args)))
  84. (is-void
  85. (error "tag" (string-append "<"name attrs" />: That is void-tag, it can't have any children")))
  86. (else
  87. (loop attrs
  88. (string-append
  89. content
  90. (if (and (not (string=? content ""))
  91. (not (char=? #\> (string-ref content (- (string-length content) 1)))))
  92. " "
  93. "")
  94. (list->html-text (list (car args))))
  95. #f
  96. (cdr args)))))))
  97. (define-syntax html-tag
  98. (lambda (stx)
  99. (syntax-case stx ()
  100. ((_ name)
  101. (let ((name* (datum->syntax #'name
  102. (string->symbol
  103. (string-append (symbol->string (syntax->datum #'name)) "*")))))
  104. #`(begin
  105. (define name (tag (symbol->string 'name) #f #f))
  106. (define #,name* (tag (symbol->string 'name) #f #t))))))))
  107. (define-syntax html-tags
  108. (syntax-rules ()
  109. ((_ name ...)
  110. (begin (html-tag name) ...))))
  111. (define-syntax html-tags-void
  112. (syntax-rules ()
  113. ((_ name ...)
  114. (begin (define name (tag (symbol->string 'name) #t #f)) ...))))
  115. (html-tags
  116. html body head title style details summary marquee textarea
  117. p a i b h1 h2 h3 h4 h5 h6 sup sub div span section ul li ol
  118. main footer header nav canvas button form script noscript
  119. table tr td th tbody caption q s pre code label center font)
  120. (html-tags-void
  121. link meta img input br hr wbr)
  122. (define (comment . content)
  123. (string-append
  124. "<!--"
  125. (list->html-text (map escape content))
  126. " -->"))
  127. ;; those tags are mostly used without any attributes
  128. (define br/ "<br />")
  129. (define hr/ "<hr />")
  130. (define wbr/ "<wbr />")
  131. (define (!html . content)
  132. (display
  133. (string-append "<!DOCTYPE html>"
  134. (apply html `(,(comment "this HTML document was generated by SSXML library") ,@content)))))
  135. (define (!xhtml . content)
  136. (display
  137. (string-append
  138. "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
  139. "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
  140. " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"
  141. (apply html `(xmlns "http://www.w3.org/1999/xhtml"
  142. ,(comment "this XHTML document was generated by SSXML library")
  143. ,@content)))))
  144. (define (list->html-list lst)
  145. (ul
  146. (map (lambda (e)
  147. (li
  148. (if (list? e)
  149. (list->html-list e)
  150. e)))
  151. lst)))
  152. (define (html-list . content)
  153. (list->html-list content))
  154. (define (map-tag tag content)
  155. (map (lambda (c) (apply tag c)) content))
  156. (define (html-list* . content)
  157. (apply html-list (map escape content)))