; HTML Authoring in SXML for my personal Web pages ; ; The present file defines several functions and higher-order ; SXML "tags" that are used to compose HTML pages on my web site. ; In LaTeX terms, this file is similar to article.cls. ; ; See http://pobox.com/~oleg/ftp/Scheme/xml.html#XML-authoring ; for more examples and explanation. ; ; IMPORT ; Approporiate Prelude: myenv.scm or myenv-bigloo.scm ; srfi-13-local.scm or the appropriate native implementation of SRFI-13 ; util.scm ; SXML-tree-trans.scm ; SXML-to-HTML.scm ; OS:file-length, unless it is included into the core system ; (see myenv-bigloo.scm for example) ; ; $Id: SXML-to-HTML-ext.scm,v 1.2 2004/11/09 14:11:39 sperber Exp $ ; skip the lst trough the first significant element ; return the tail of lst such that (car result) is significant ; Insignificant elems are '(), #f, and lists made of them ; If all of the list is insignificant, return #f (define (signif-tail lst) (define (signif? obj) (and (not (null? obj)) obj (if (pair? obj) (or (signif? (car obj)) (signif? (cdr obj))) obj))) (and (signif? lst) (assert (pair? lst)) (if (signif? (car lst)) lst (signif-tail (cdr lst))))) ; Procedure make-header HEAD-PARMS ; Create the 'head' SXML/HTML tag. HEAD-PARMS is an assoc list of ; (h-key h-value), where h-value is a typically string; ; h-key is a symbol: ; title, description, AuthorAddress, keywords, ; Date-Revision-yyyymmdd, Date-Creation-yyyymmdd, ; long-title ; One of the h-key can be Links. ; In that case, h-value is a list of ; (l-key l-href (attr value) ...) ; where l-key is one of the following: ; start, contents, prev, next, top, home (define (make-header head-parms) `(head (title ,(lookup-def 'title head-parms)) ,(map (lambda (key) (let ((val (lookup-def key head-parms warn: #f))) (and val `(meta (@ (name ,(symbol->string key)) (content ,val)))))) '(description AuthorAddress keywords Date-Revision-yyyymmdd Date-Creation-yyyymmdd)) ,(let ((links (lookup-def 'Links head-parms '()))) (and (pair? links) (map (lambda (link-key) (let ((val (lookup-def link-key links #f))) (and val (let ((val (if (not (pair? val)) (list val) val))) `(link (@ (rel ,(symbol->string link-key)) (href ,(car val)) ,@(cdr val))))))) '(start contents prev next))))) ) ; Create a navigational bar. The argument head-parms is the same ; as the one passed to make-header. We're only concerned with the ; h-value Links (define (make-navbar head-parms) (let ((links (lookup-def 'Links head-parms '())) (nav-labels '((prev . "previous") (next . "next") (contents . "contents") (top . "top")))) (and (pair? links) `(div (@ (align "center") (class "navbar")) ,(let loop ((nav-labels nav-labels) (first? #t)) (if (null? nav-labels) '() (let ((val (lookup-def (caar nav-labels) links warn: #f))) (if (not val) (loop (cdr nav-labels) first?) (cons (list " " (if first? #f '(n_)) " " `(a (@ (href ,val)) ,(cdar nav-labels))) (loop (cdr nav-labels) #f)))))) (hr))) )) ; Create a footer. The argument head-parms is the same ; as passed to make-header. (define (make-footer head-parms) `((br) (div (hr)) (h3 "Last updated " ,(let* ((date-revised (lookup-def 'Date-Revision-yyyymmdd head-parms)) (year (string->integer date-revised 0 4)) (month (string->integer date-revised 4 6)) (day (string->integer date-revised 6 8)) (month-name (vector-ref '#("January" "February" "March" "April" "May" "June" "July" "August" "September" "October" "November" "December") (dec month)))) (list month-name " " day ", " year))) ,(let ((links (lookup-def 'Links head-parms '()))) (and (pair? links) (let ((home (lookup-def 'home links warn: #f))) (and home `(p "This site's top page is " (a (@ (href ,home)) (strong ,home))))))) (div (address "oleg-at-pobox.com or oleg-at-acm.org or oleg-at-computer.org" (br) "Your comments, problem reports, questions are very welcome!")) (p (font (@ (size "-2")) "Converted from SXML by SXML->HTML")) ,(let ((rcs-id (lookup-def 'rcs-id head-parms #f))) (and rcs-id `(h4 ,rcs-id))) )) ; Bindings for the post-order function, which traverses the SXML tree ; and converts it to a tree of fragments ; The universal transformation from SXML to HTML. The following rules ; work for every HTML, present and future (define universal-conversion-rules `((@ ((*default* ; local override for attributes . ,(lambda (attr-key . value) (enattr attr-key value)))) . ,(lambda (trigger . value) (cons '@ value))) (*default* . ,(lambda (tag . elems) (entag tag elems))) (*text* . ,(lambda (trigger str) (if (string? str) (string->goodHTML str) str))) (n_ ; a non-breaking space . ,(lambda (tag . elems) (cons " " elems))))) ; A variation of universal-conversion-rules which keeps '<', '>', '&' ; and similar characters intact. The universal-protected-rules are ; useful when the tree of fragments has to be traversed one more time. (define universal-protected-rules `((@ ((*default* ; local override for attributes . ,(lambda (attr-key . value) (enattr attr-key value)))) . ,(lambda (trigger . value) (cons '@ value))) (*default* . ,(lambda (tag . elems) (entag tag elems))) (*text* . ,(lambda (trigger str) str)) (n_ ; a non-breaking space . ,(lambda (tag . elems) (cons " " elems))))) ; The following rules define the identity transformation (define alist-conv-rules `((*default* . ,(lambda (tag . elems) (cons tag elems))) (*text* . ,(lambda (trigger str) str)))) ; Find the 'Header' node within the 'Content' SXML expression. ; Currently this query is executed via a transformation, with ; rules that drop out everything but the 'Header' node. ; We use the _breadth-first_ traversal of the Content tree. (define (find-Header Content) (letrec ((search-rules `((*default* *preorder* . ,(lambda (tag . elems) (let loop ((elems elems) (worklist '())) (cond ((null? elems) (if (null? worklist) '() (pre-post-order worklist search-rules))) ((not (pair? (car elems))) (loop (cdr elems) worklist)) ((eq? 'Header (caar elems)) (car elems)) ; found (else (loop (cdr elems) (cons (car elems) worklist))))))) ))) (lookup-def 'Header (list (pre-post-order Content search-rules)) ))) ; Transformation rules that define a number of higher-order tags, ; which give "style" to all my pages. ; Some of these rules require a pre-post-order iterator ; See xml.scm or any other of my web page master files for an example ; of using these stylesheet rules (define (generic-web-rules Content additional-rules) (append additional-rules universal-conversion-rules `((html:begin . ,(lambda (tag . elems) (list "" nl "" nl elems "" nl))) (Header *preorder* . ,(lambda (tag . headers) (post-order (make-header headers) universal-conversion-rules) )) (body . ,(lambda (tag . elems) (list "
" nl elems ""))) (navbar ; Find the Header in the Content . ,(lambda (tag) ; and create the navigation bar (let ((header-parms (find-Header Content))) (post-order (make-navbar header-parms) universal-conversion-rules)))) (footer ; Find the Header in the Content . ,(lambda (tag) ; and create the footer of the page (let ((header-parms (find-Header Content))) (post-order (make-footer header-parms) universal-conversion-rules)))) (page-title ; Find the Header in the Content . ,(lambda (tag) ; and create the page title rule (let ((header-parms (find-Header Content))) (list "" (map (lambda (line) (list " " line nl)) lines) ""))) ; (note . text-strings) ; A note (remark), similar to a footnote (note . ,(lambda (tag . text-strings) (list " [" text-strings "]" nl))) ; A reference to a file (fileref . ,(lambda (tag pathname . descr-text) (list "" (car (reverse (string-split pathname '(#\/)))) " [" (let ((file-size (OS:file-length pathname))) (if (not (positive? file-size)) (error "File not found: " pathname)) (cond ((< file-size 1024) "<1K") (else (list (quotient (+ file-size 1023) 1024) "K")))) "]