; ;======================================================================== ; HTML generation template (require-extension srfi-1) (require-extension srfi-13) (require-extension sxml-transforms) (require-extension doctype) (require-extension html-form) (define nl (list->string (list #\newline))) (define lookup-def (lambda (k lst . rest) (let-optionals rest ((default #f)) (alist-ref k lst eq? default)))) (define lookup-field (lambda (k lst . rest) (let-optionals rest ((default #f)) (let loop ((lst lst)) (if (null? lst) default (let ((elm (car lst))) (match elm ((s . _) (if (eq? s k) (cdr elm) (loop (cdr lst)))) (else (loop (cdr lst)))))))))) (define (s+ . rest) (apply string-append (map ->string rest))) (define (make-schedule def) (let* ((evs (filter-map (lambda (x) (and (eq? 'event (car x)) (cdr x))) def)) (brief (lookup-def 'brief def))) `(div (@ (class ,(if brief 'brief-schedule 'schedule))) ,@(map (lambda (ev) (let ((date (lookup-def 'date ev)) (time (lookup-def 'time ev)) (desc (or (lookup-def 'desc ev) (lookup-def 'description ev)))) `(dl (@ (class event)) ,(if date `(dt (@ (class date)) ,date) "") ,(if time `(dt (@ (class time)) ,time) "") ,(if desc `(dd (@ (class desc)) ,desc) "")))) evs)))) (define (pid parent id) (let ((parent (and parent (if (string? parent) parent (symbol->string parent)))) (id (if (string? id) id (symbol->string id)))) (if parent (string-concatenate (list parent "_" id)) id))) (define (make-navbar head-parms) (let ((links (lookup-def 'Links head-parms '()))) (and (pair? links) `(div (@ (id "navbar")) (ul . ,(map (lambda (x) (match x ((name val . rest) (let ((label (or (lookup-def 'label rest) name))) `(li (a (@ (href ,val)) ,label)))))) links)) (hr))))) (define (make-header head-parms) `(head ,nl (title ,(lookup-def 'title head-parms)) ,nl (meta (@ (http-equiv "Content-Type") (content "text/html; charset=UTF-8"))) ,nl (meta (@ (http-equiv "Content-Style-Type") (content "text/css"))) ,nl ,(let ((style (lookup-def 'style head-parms)) (print-style (lookup-def 'print-style head-parms))) (list (if style `(link (@ (rel "stylesheet") (type "text/css") (href ,style))) '()) (if print-style `(link (@ (rel "stylesheet") (type "text/css") (media "print") (href ,print-style))) '()))) ,nl ,(zip (map (lambda (key) (let ((val (lookup-def key head-parms ))) (and val `(meta (@ (name ,(symbol->string key)) (content ,val)))))) '(description AuthorAddress keywords Date-Revision-yyyymmdd Date-Creation-yyyymmdd)) (circular-list nl)) ,nl ,(let ((links (lookup-def 'Links head-parms '()))) (and (pair? links) (map (lambda (link-key) (let ((val (lookup-def link-key links))) (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)))))) (define (make-footer head-parms) `((br) (div (@ (id "footer")) (hr) (h3 "Last updated " ,(let* ((date-revised (car (lookup-def 'Date-Revision-yyyymmdd head-parms))) (year (string->number (string-copy date-revised 0 4))) (month (string->number (string-copy date-revised 4 6))) (day (string->number (string-copy date-revised 6 8))) (month-name (vector-ref '#("January" "February" "March" "April" "May" "June" "July" "August" "September" "October" "November" "December") (- month 1)))) (list month-name " " day ", " year)) ,(let ((revision (car (lookup-def 'Revision head-parms)))) (if revision (string-concatenate (list " (Revision " revision ") ")) ""))) ,(let ((links (lookup-def 'Links head-parms '()))) (and (pair? links) (let ((home (lookup-def 'home links))) (and home `(p "This site's top page is " (a (@ (href ,home)) (strong ,home))))))) (div (address ,(lookup-def 'AuthorAddress head-parms)) (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))) (and rcs-id `(h4 ,rcs-id)))))) (define (generate-HTML Content) (let* ;; Universal transformation rules. Work for every HTML, ;; present and future ((universal-conversion-rules `((@ ((*default* ;; local override for attributes . ,(lambda (attr-key . value) (enattr attr-key value)))) . ,(lambda (trigger . value) (cons '@ value))) (*default* . ,(let ((with-nl ;; Block-level HTML elements: ;; We insert a NL before them. ;; No NL is inserted before or after an ;; inline element. '(br ;; BR is technically inline, but we ;; treat it as block p div hr iframe h1 h2 h3 h3 h5 h6 dl ul ol li dt dd pre table tr th td center blockquote form address body thead tfoot tbody col colgroup))) (lambda (tag . elems) (let ((nl? (and (memq tag with-nl) #\newline))) (if (and (pair? elems) (pair? (car elems)) (eq? '@ (caar elems))) (list nl? #\< tag (cdar elems) #\> (and (pair? (cdr elems)) (list (cdr elems) "" tag #\> nl?))) (list nl? #\< tag #\> (and (pair? elems) (list elems "" tag #\> nl?)) )))))) (*text* . ,(lambda (trigger str) (if (string? str) (string->goodHTML str) str))) (n_ ;; a non-breaking space . ,(lambda (tag . elems) (list " " elems))))) ;; Transformation rules to drop out everything but the ;; 'Header' node (search-Header-rules `((Header *preorder* . ,(lambda (tag . elems) (cons tag elems))) (*default* . ,(lambda (attr-key . elems) (let loop ((elems elems)) (cond ((null? elems) '()) ((not (pair? (car elems))) (loop (cdr elems))) ((eq? 'Header (caar elems)) (car elems)) (else (loop (cdr elems))))))) (*text* . ,(lambda (trigger str) '())))) ) (let ((header-parms (lookup-def 'Header (list (post-order Content search-Header-rules))))) (SRV:send-reply (pre-post-order Content `( ,@universal-conversion-rules (html:begin . ,(lambda (tag . elems) (let ((embedded? (lookup-def 'Embedded header-parms))) (if embedded? elems (list "" nl "" nl elems "" nl))))) (Header *macro* . ,(lambda (tag . headers) (let ((embedded? (lookup-def 'Embedded header-parms))) (if embedded? (list) (make-header headers))))) (navbar ; Find the Header in the Content . ,(lambda (tag) ; and create the navigation bar (let ((header-parms (lookup-def 'Header (list (post-order Content search-Header-rules)) ))) (post-order (make-navbar header-parms) universal-conversion-rules)))) (body . ,(lambda (tag . elems) (list "
" nl elems ""))) (page-content *macro* . ,(lambda (tag . elems) `(div (@ (id "content")) ,nl ,elems))) (footer ;; Find the Header in the Content . ,(lambda (tag) ;; and create the footer of the page (post-order (make-footer header-parms) universal-conversion-rules))) (page-title *macro* ;; Find the Header in the Content . ,(lambda (tag) ;; and create the page title rule `(div (@ (id "header")) (h1 ,(lookup-def 'title header-parms)) ,nl (h2 ,(lookup-def 'subtitle header-parms))))) (abstract ;; The abstract of the document ((Revision . ,(lambda (tag) ;; Find the Header in the Content ;; and create the revision record (list "Revision " (lookup-def 'Revision header-parms) ". "))) (keywords . ,(lambda (tag) '())) ) . ,(lambda (tag . abstract-body) (list "