; Guile Scheme xhtml generator, part of Guile Web
; Copyright (C) 2002,2004 Clinton Ebadi
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2 of the License, or
; (at your option) any later version.
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
(define-module (web html)
#:use-module (ice-9 optargs)
#:use-module (ice-9 format)
#:use-module (srfi srfi-1)
#:use-module (oop goops)
#:use-module (ice-9 pretty-print)
#:use-syntax (ice-9 syncase)
#:export (empty-tag non-empty-tag
xhtml:output-port xhtml:common-attributes
xhtml:print xhtml:DOCUMENT xhtml:real-print xhtml:dtd
xhtml:tags))
(define-class ()
(output-port #:init-keyword #:port
#:init-form (current-output-port)
#:getter xhtml:output-port)
(attributes #:init-keyword #:attributes
#:init-value '((id "") (class "") (style "") (title ""))
#:getter xhtml:common-attributes))
(define-class ()
(tag-tree #:init-keyword #:tag-tree
#:accessor tag-tree))
(define-generic xhtml-tag?)
(define-method (xhtml-tag? (tag )) #f)
(define-method (xhtml-tag? (tag )) #t)
(define xhtml:DOCUMENT (make-fluid))
(fluid-set! xhtml:DOCUMENT (make ))
(define-syntax xhtml:print
(lambda (exp)
(syntax-case exp ()
((_ exp1 exps ...)
(syntax
(if (eq? (class-of exp1) )
(with-fluids ((xhtml:DOCUMENT
exp1))
(xhtml:real-print exps ...))
(xhtml:real-print exp1 exps ...)))))))
(define-generic xhtml:real-print)
(define-method (xhtml:real-print . elements)
(letrec ((oport (xhtml:output-port (fluid-ref xhtml:DOCUMENT))))
(for-each (lambda (x) (display x oport)) elements)))
(define-method (display (item ) (port ))
(for-each (lambda (x) (display x port)) (tag-tree item)))
(define-method (display (item ))
(display item (current-output-port)))
(define (non-empty-attributes attributes)
"returns all non-empty (i.e. not string-null?) attributes from attributes"
(let loop ((non-empty (list))
(rest attributes))
(cond ((null? rest) non-empty)
(else
(if (string-null? (cadar rest))
(loop non-empty (cdr rest))
(loop (cons (car rest) non-empty)
(cdr rest)))))))
(define (merge-atts tag-defaults tag-values)
"returns a new attlist using system for default values"
(let ((system-copy (append! (copy-tree (xhtml:common-attributes
(fluid-ref xhtml:DOCUMENT)))
tag-defaults)))
(for-each (lambda (x) (set! system-copy
;; (list (cdr x)) is used so that the alist
;; entry is a proper list so that format's
;; iteration can use it
(assq-set! system-copy (car x) (list (cdr x)))))
tag-values)
system-copy))
(define (empty-tag name attributes)
(lambda* (#:key (atts '()))
(let ((new-attlist
(non-empty-attributes
(merge-atts attributes atts))))
(format #f "<~A~{~{ ~A=~S~}~} />" name new-attlist))))
(define (non-empty-tag-start name attributes)
(lambda* (#:key (atts '()))
(let ((new-attlist (non-empty-attributes
(merge-atts attributes atts))))
(format #f "<~A~{~{ ~A=~S~}~}>" name new-attlist))))
(define (non-empty-tag-end name)
(lambda ()
(format #f "~A>" name)))
(define (non-empty-tag name attributes)
(lambda* (#:key (atts '()) #:rest elements)
(let ((new-attlist (non-empty-attributes
(merge-atts attributes atts))))
;; The following is a hack to get around the broken
;; lambda* in Guile 1.6.4. Once it is fixed this will be
;; removed (it's another N iterations over the list and
;; therefore evil).
(set! elements
(remove!
(let ((del-next #f))
(lambda (e)
(cond
((eq? e #:atts) (set! del-next #t) #t)
(del-next (set! del-next #f) #t)
(else #f))))
elements))
(make
#:tag-tree
(list
(format #f "<~A~{~{ ~A=~S~}~}>" name new-attlist)
(make #:tag-tree elements)
(format #f "~A>" name))))))
; (define-syntax define-tag!
; (lambda (exp)
; (syntax-case exp ()
; ((_ name attributes empty?)
; (syntax
; (begin
; (let* ((tag-symbol (string->symbol
; (string-append
; "xhtml:"
; (symbol->string 'name))))
; (symbol-list (list tag-symbol)))
; (module-define! (current-module)
; tag-symbol
; (if empty?
; (empty-tag 'name 'attributes)
; (non-empty-tag 'name 'attributes)))
; (cond ((not empty?)
; (module-define! (current-module)
; (symbol-append tag-symbol '-start)
; (non-empty-tag-start 'name 'attributes))
; (module-define! (current-module)
; (symbol-append tag-symbol '-end)
; (non-empty-tag-end 'name))
; (set! symbol-list (append!
; symbol-list
; (list
; (symbol-append tag-symbol '-start)
; (symbol-append tag-symbol '-end))))))
; (module-export! (current-module)
; symbol-list))))))))
(define* (xhtml:dtd #:key (atts '((encoding "iso-8859-1"))) #:rest elements)
(make
#:tag-tree
(list
(format #f "~%~%" atts)
(make #:tag-tree elements))))
(define (xhtml:tags . tags)
(make #:tag-tree tags))
(load-from-path "web/html/html-defined.scm")