(chapter-start 'html
"Functions and macros for creating HTML output by cleverly mixing nydp interpolations with HTML and textile renderers.
Also provides percent-syntax for concise html-generation in nydp, for example
(%a.highlight(href \"http://github.com/\") \"Click\" (%b \"here\") \"for more information\")“)
(def default-format (arg)
; override this to ensure values are formatted ; as you desire it. By default, this returns its ; argument unchanged arg)
(def interpolate (arg)
; override this to provide error handling, logging, ; profiling, or whatever it is you might desire. ; by default, this delegates to 'default-format (default-format arg))
(def html-resplit (parts hsh)
(accum splot
(each part parts
(splot (or (hash-get hsh part)
part)))))
(def html-split-fn (seq sep hsh escapef piece-handler)
(fn (piece)
(if (isa 'string piece)
piece
(let key (j "hsf:" (seq) ":hsf")
(hash-set hsh key (piece-handler piece))
(escapef:j sep key sep)))))
(def html-build-processor (converter)
(with (hsh (hash)
sep (random-string 12))
{ method converter.method
processor (html-split-fn (seqf 0)
sep
hsh
(or converter.esc x1)
(or converter.interpolate interpolatify))
hsh hsh
sep sep }))
(def interpolatify (arg) `(interpolate ,arg))
(def html-process-parts (parts processor)
; 'parts a mixed list containing strings or uncompiled lisp forms - the cdr of 'string-pieces after parting a string with interpolations
; 'converter is a hash with the following keys
; * method - textile-to-html, haml-to-html, or strip-nydp-tags, or anything that takes a string and returns a string
; * esc - a function that takes a string and returns another string such that when 'method processes the result, the original string is returned. For example, if 'method is 'textile-to-html, then 'esc could be (fn (txt) "==~|txt|==") because "==" is used by textile to mark text not-to-be-processed.
; * interpolator - a function that takes an uncompiled lisp form and returns another uncompiled lisp form such that when executed in the context of 'string-pieces will give the desired result
; returns a mixed list containing strings or uncompiled lisp forms such that the concatenation of the list is html output by converter.method (the textile, haml, or other processor)
(html-resplit (string-split (processor.method (j:map processor.processor parts))
processor.sep)
processor.hsh))
(def html-build-interpolator (pieces converter)
`(string-pieces ,@(html-process-parts pieces (html-build-processor converter))))
(def strip-nydp-tags (txt)
; remove <nydp> and </nydp> tags from given 'txt, some editing environments (ckeditor) may require such tags ; in order to protect active content from reformatting or other transformations (string-replace "</?nydp>" "" txt))
(mac render-as-html (arg)
; produce code to convert 'arg to html using a null interpreter.
; in other words, assume input is already html. Normally, we would
; just return the argument, but it's useful to take advantage of the fact that
; each interpolation ~(foo) is replaced with ~(interpolate (foo)), and
; you may override 'interpolate for your own nefarious purposes.
;
; for example:
;
; (render-as-html "<p>hello world</p>") ;=> "<p>hello world</p>"
; (render-as-html (get-some-text-from 'somewhere)) ;=> (x1 (get-some-text-from 'somewhere))
; (render-as-html "<p>hello ~|name|</p>") ;=> (string-pieces "<p>hello" (interpolate name) "<p>")
(if (isa 'string arg)
arg
(and (pair? arg)
(eq? 'string-pieces (car arg)))
(html-build-interpolator (cdr arg) { method strip-nydp-tags esc x1 })
arg))
(mac render-as-haml (arg)
; produce code to convert 'arg to html using a haml interpreter
; for example:
;
; (render-as-haml "%p hello world") ;=> "<p>hello world</p>"
; (render-as-haml (get-some-text-from 'somewhere)) ;=> (haml-to-html (get-some-text-from 'somewhere))
; (render-as-haml "%p hello ~name") ;=> (string-pieces "<p>hello" (interpolate name) "<p>")
(if (isa 'string arg)
(haml-to-html arg)
(and (pair? arg)
(eq? 'string-pieces (car arg)))
(html-build-interpolator (cdr arg) { method haml-to-html esc x1 })
`(haml-to-html ,arg)))
(def notextile-esc (txt) “<notextile>~|txt|</notextile>”)
(mac render-as-textile (arg)
; produces code to convert 'arg to html using a textile interpreter
; for example:
;
; (render-as-textile "hello world") ;=> "<p>hello world</p>"
; (render-as-textile (get-some-text-from 'somewhere)) ;=> (textile-to-html (get-some-text-from 'somewhere))
; (render-as-textile "hello ~name") ;=> (string-pieces "<p>hello" (interpolate name) "<p>")
(if (isa 'string arg)
(textile-to-html arg)
(and (pair? arg)
(eq? 'string-pieces (car arg)))
(html-build-interpolator (cdr arg) { method textile-to-html esc notextile-esc })
`(textile-to-html ,arg)))
(def textile-to-html-source (doc)
(if (isa 'string doc)
(textile-to-html doc)
(and (pair? arg)
(eq? 'string-pieces (car arg)))
(html-build-interpolator (cdr arg) { method textile-to-html esc notextile-esc })
(error "can't convert doc to html: not a string")))
(def to-css-rule (prop val) (joinstr “” prop “:” val “;”))
(def to-css (hsh)
; convert given 'hsh to a CSS string. Keys of 'hsh are
; css properties; the corresponding values are css values.
;
; example:
; (to-css { background "black" font-size "12px" })
;
; produces
; "background:black;font-size:12px;"
;
(and hsh
(joinstr "" (map (fn (k) (to-css-rule k hsh.,k))
(hash-keys hsh)))))
(def as-tag-attrs (attrs)
(let hsh (or attrs (hash))
(j:map (fn (k) " ~|k|='~(joinstr " " hsh.,k)'")
(hash-keys hsh))))
(def html-tag/ (name attrs) “<~|name|~(as-tag-attrs attrs)/>”) (def html-tag (name attrs . content)
(let formatted (map default-format content) "<~|name|~(as-tag-attrs attrs)>~(j formatted)</~|name|>"))
(def img (src) (html-tag/ “img” { src src })) (def link-to (txt path attrs) (html-tag “a” (hash-merge { href path } (or attrs (hash))) txt)) (def html-tag-fn (name attrs) (fn content (apply html-tag name attrs content)))
(def build-html-tag-fn (tagname attrs)
(if (string-match tagname ".+/$")
`(curry html-tag/ ,(string-replace "/$" "" tagname) ,(if attrs `(brace-list ,@attrs)))
`(curry html-tag ,tagname ,(if attrs `(brace-list ,@attrs)))))
(def html-percent-syntax (tagname attrs)
(if (caris 'dot-syntax tagname)
(let dot-params (cdr tagname)
(build-html-tag-fn (to-string:car dot-params)
(+ `(class ,(joinstr " " (cdr dot-params))) attrs)))
(build-html-tag-fn (to-string tagname) attrs)))
(mac percent-syntax (empty . names)
(let name (car names)
(if (caris 'colon-syntax name)
`(colon-syntax ,(html-percent-syntax (cadr name) nil) ,@(cddr name))
(html-percent-syntax name nil))))
(define-prefix-list-macro “^%.+” vars expr
; allows (%a(href "/fr/index") "click" name)
; as shortcut for (html-tag "a" { href "/fr/index" } "click " name)
(let tag-name (car:parse:j:cdr:string-split vars)
(html-percent-syntax tag-name expr)))
(mac link-if-txt (txt path attrs)
(w/uniq link-txt
`(let ,link-txt ,txt
(if ,link-txt (link-to ,link-txt ,path ,attrs)))))
(dox-add-doc 'textile-to-html 'def '(“assumes 'arg is a string in Textile format, converts to html and returns the result”) '(arg)) (dox-add-doc 'haml-to-html 'def '(“assumes 'arg is a string in HAML format, converts to html and returns the result” ) '(arg))
(def to-url-params (hsh)
(joinstr "&" (map λk(joinstr "=" k hsh.,k) (hash-keys hsh))))
(def to-url (base params)
(joinstr "?" (reject !present? (list base (to-url-params params)))))
(def hesc (txt)
(string-replace ">" ">"
(string-replace "<" "<"
(string-replace "&" "&" txt))))
(def/cycler rowclass (oddrow evenrow))