;; $Id: dblink.dsl,v 1.1 2001/04/02 21:40:27 nwalsh Exp $
;;
;; This file is part of the Modular DocBook Stylesheet distribution.
;; See ../README or http://www.berkshire.net/~norm/dsssl/
;;

;; ========================= LINKS AND ANCHORS ==========================

(element link
  (let* ((endterm (attribute-string (normalize "endterm")))
	 (linkend (attribute-string (normalize "linkend")))
	 (target  (element-with-id linkend))
	 (etarget (if endterm 
		      (element-with-id endterm)
		      (empty-node-list))))
    ;; It isn't necessary to catch either of these errors. The normal
    ;; ID/IDREF processing in Jade will catch them, and if -wno-idref
    ;; is used, then it's your gun, your bullet, and your foot.
;;      (if (node-list-empty? target) 
;;	  (error (string-append "Link to missing ID '" linkend "'"))
;;	  (empty-sosofo))
;;      (if (and endterm (node-list-empty? etarget))
;;	  (error (string-append "EndTerm to missing ID '" endterm "' on Link"))
;;	  (empty-sosofo))
    (if (node-list-empty? target)
	(process-children)
	(make element gi: "A"
	      attributes: (list (list "HREF" (href-to target)))
	      (if (and endterm (not (node-list-empty? etarget)))
		  (with-mode xref-endterm-mode (process-node-list etarget))
		  (process-children))))))

(element ulink 
  (make element gi: "A"
	attributes: (list
		     (list "HREF" (attribute-string (normalize "url")))
		     (list "TARGET" "_top"))
	(if (node-list-empty? (children (current-node)))
	    (literal (attribute-string (normalize "url")))
	    (process-children))))

(element anchor
  (make element gi: "A"
	attributes: (list
		     (list "NAME" (attribute-string (normalize "id"))))
	(empty-sosofo)))

(element beginpage (empty-sosofo))

;; ======================================================================

(define (olink-link)
  ;; This is an olink without a TARGETDOCENT, treat it as a link within
  ;; the same document.
  (let* ((localinfo (normalize (attribute-string (normalize "localinfo"))))
	 (target    (element-with-id localinfo))
	 (linkmode  (attribute-string (normalize "linkmode")))
	 (modespec  (if linkmode (element-with-id linkmode) (empty-node-list)))
	 (xreflabel (if (node-list-empty? modespec)
			#f
			(attribute-string (normalize "xreflabel") modespec)))
	 (href      (if (node-list-empty? target)
			(error 
			 (string-append "OLink to missing ID '" localinfo "'"))
			(href-to target)))
	 (linktext  (strip (data-of (current-node)))))
    (make element gi: "A"
	  attributes: (list (list "HREF" href)
			    (list "CLASS" "OLINK"))
	  (if (equal? linktext "")
	      (if xreflabel
		  (xref-general target xreflabel)
		  (xref-general target))
	      (process-children)))))

(define (olink-href target modespec)
  (let* ((pubid     (entity-public-id target))
	 (sysid     (system-id-filename target))
	 (localinfo (normalize (attribute-string (normalize "localinfo"))))
	 (qident    (if pubid
			(string-append %olink-pubid% (url-encode-string pubid))
			(string-append %olink-sysid% (url-encode-string sysid))))
	 (qfragid   (if localinfo
		        (string-append %olink-fragid% 
				       (url-encode-string localinfo))
		        ""))
	 (lb-href   (string-append %olink-resolution% qident qfragid))
	 (modetext  (if (node-list-empty? modespec) "" (data-of modespec)))
	 (href      (if (equal? (strip modetext) "")
			lb-href
			(if localinfo
			    (string-append 
			     modetext "#" (url-encode-string localinfo))
			    modetext))))
    href))

(define (olink-simple)
  ;; Assumptions: 
  ;; - The TARGETDOCENT is identified by a public ID
  ;; - LOLCALINFO contains the ID value (i.e. HREF fragment identifier) of
  ;;   the target resource
  ;; - If the element has no content, the title extracted by
  ;;   (olink-resource-title) should be used
  ;; - The (olink-resource-title) function can deduce the title from
  ;;   the pubid and the sysid
  ;; - %olink-resolution% is the prefix to use on URLs (to point to a 
  ;;   cgi-bin script, or whatever you can make work for you)
  ;; - %olink-pubid% identifies the pubid in the query
  ;; - %olink-fragid% identifies the fragment identifier in the query
  (let* ((target   (attribute-string (normalize "targetdocent")))
	 (pubid    (entity-public-id target))
	 (sysid    (system-id-filename target))
	 (title    (olink-resource-title pubid sysid))
	 (href     (olink-href target (empty-node-list)))
	 (linktext (strip (data-of (current-node)))))
    (make element gi: "A"
	  attributes: (list (list "HREF" href)
			    (list "CLASS" "OLINK"))
	  (if (equal? linktext "")
	      (make element gi: "I" (literal title))
	      (process-children)))))

(define (olink-outline-xref olroot target linktext)
  (let* ((name  (attribute-string (normalize "name") target))
	 (label (attribute-string (normalize "label") target))
	 (title (select-elements (children target) (normalize "ttl")))
	 (substitute (list
		      (list "%g" (if name (literal name) (literal "")))
		      (list "%n" (if label (literal label) (literal "")))
		      (list "%t" (with-mode olink-title-mode
				   (process-node-list title)))))
	 (tlist   (match-split-list linktext (assoc-objs substitute))))
    (string-list-sosofo tlist substitute)))

(define (olink-outline)
  (let* ((target    (attribute-string (normalize "targetdocent")))
	 (localinfo (normalize (attribute-string (normalize "localinfo"))))
	 (sysid     (entity-generated-system-id target))
	 (basename  (trim-string sysid '(".sgm" ".xml" ".sgml")))
	 (olinkfile (string-append basename %olink-outline-ext%))
	 (olinkdoc  (sgml-parse olinkfile))
	 (olinkroot (node-property 'document-element olinkdoc))
	 (olnode    (if localinfo
			(element-with-id localinfo olinkroot)
			olinkroot))
	 (linkmode  (attribute-string (normalize "linkmode")))
	 (modespec  (if linkmode (element-with-id linkmode) (empty-node-list)))
	 (xreflabel (if (node-list-empty? modespec)
			""
			(attribute-string (normalize "xreflabel") modespec)))
	 (href      (if (equal? (attribute-string (normalize "type")) "href")
			(attribute-string (normalize "href") olnode)
			(olink-href target modespec)))
	 (linktext  (strip (data-of (current-node)))))
    (make element gi: "A"
	  attributes: (list (list "HREF" href)
			    (list "CLASS" "OLINK"))
	  (if (equal? linktext "")
	      (olink-outline-xref olinkroot olnode xreflabel)
	      (process-children)))))

(element olink
  (if (not (attribute-string (normalize "targetdocent")))
      (olink-link)
      (if (attribute-string (normalize "linkmode"))
	  (olink-outline)
	  (olink-simple))))

(mode olink-title-mode
  (default (process-children))

  (element ttl
    (make element gi: "I"
	  (process-children)))

  (element it
    (make element gi: "I"
	  (process-children)))

  (element tt
    (make element gi: "TT"
	  (process-children)))

  (element sub 
    (make element gi: "SUB"
	  (process-children)))

  (element sup 
    (make element gi: "SUP"
	  (process-children)))
)

;; ======================================================================

(element xref
  (let* ((endterm   (attribute-string (normalize "endterm")))
	 (linkend   (attribute-string (normalize "linkend")))
	 (target    (element-with-id linkend))
	 (xreflabel (if (node-list-empty? target)
			#f
			(attribute-string (normalize "xreflabel") target))))
    (if (node-list-empty? target)
	(error (string-append "XRef LinkEnd to missing ID '" linkend "'"))
	(make element gi: "A"
	      attributes: (list
			   (list "HREF" (href-to target)))
	      (if xreflabel
		  (literal xreflabel)
		  (if endterm
		      (if (node-list-empty? (element-with-id endterm))
			  (error (string-append
				  "XRef EndTerm to missing ID '" 
				  endterm "'"))
			  (with-mode xref-endterm-mode
			    (process-node-list (element-with-id endterm))))
		      (cond
		       ((or (equal? (gi target) (normalize "biblioentry"))
			    (equal? (gi target) (normalize "bibliomixed")))
			;; xref to the bibliography is a special case
			(xref-biblioentry target))
		       ((equal? (gi target) (normalize "co"))
			;; callouts are a special case
			($callout-mark$ target #f))
		       ((equal? (gi target) (normalize "listitem"))
			;; listitems are a special case
			(if (equal? (gi (parent target)) (normalize "orderedlist"))
			    (literal (orderedlist-listitem-label-recursive target))
			    (error (string-append "XRef to LISTITEM only supported in ORDEREDLISTs"))))
		       ((equal? (gi target) (normalize "question"))
			;; questions and answers are (yet another) special case
			(make sequence
			  (literal (gentext-element-name target))
			  (literal (gentext-label-title-sep target))
			  (literal (question-answer-label target))))
		       ((equal? (gi target) (normalize "answer"))
			;; questions and answers are (yet another) special case
			(make sequence
			  (literal (gentext-element-name target))
			  (literal (gentext-label-title-sep target))
			  (literal (question-answer-label target))))
		       ((equal? (gi target) (normalize "refentry"))
			;; so are refentrys
			(xref-refentry target))
		       ((equal? (gi target) (normalize "glossentry"))
			;; as are glossentrys
			(xref-glossentry target))
		       ((equal? (gi target) (normalize "author"))
			;; and authors
			(xref-author target))
		       ((equal? (gi target) (normalize "authorgroup"))
			;; and authorgroups
			(xref-authorgroup target))
; this doesn't really work very well yet
;		       ((equal? (gi target) (normalize "substeps"))
;			;; and substeps
;			(xref-substeps target))
		       (else 
			(xref-general target)))))))))

(define (xref-refentry target)
;; refmeta/refentrytitle, refmeta/manvolnum, refnamediv/refdescriptor, 
;; refnamediv/refname
  (let* ((refmeta    (select-elements (children target)
				      (normalize "refmeta")))
	 (refnamediv (select-elements (children target)
				      (normalize "refnamediv")))
	 (rfetitle   (select-elements (children refmeta) 
				      (normalize "refentrytitle")))
	 (manvolnum  (select-elements (children refmeta)
				      (normalize "manvolnum")))
	 (refdescrip (select-elements (children refnamediv)
				      (normalize "refdescriptor")))
	 (refname    (select-elements (children refnamediv)
				      (normalize "refname")))

	 (title      (if (node-list-empty? rfetitle)
			 (if (node-list-empty? refdescrip)
			     (node-list-first refname)
			     (node-list-first refdescrip))
			 (node-list-first rfetitle)))

	 (xsosofo    (make sequence
		       (process-node-list (children title))
		       (if (and %refentry-xref-manvolnum%
				(not (node-list-empty? manvolnum)))
			   (process-node-list manvolnum)
			   (empty-sosofo)))))

    (make sequence
      (if %refentry-xref-italic%
	  (make element gi: "I"
		xsosofo)
	  xsosofo))))

(define (xref-glossentry target)
  (let ((glossterms (select-elements (children target)
				     (normalize "glossterm"))))
    (with-mode xref-glossentry-mode
      (process-node-list (node-list-first glossterms)))))

(define (xref-author target)
  (literal (author-string target)))

(define (xref-authorgroup target)
  ;; it's a quirk of author-list-string that it needs to point to
  ;; one of the authors in the authorgroup, not the authorgroup.
  ;; go figure.
  (let loop ((author (select-elements (children target) (normalize "author"))))
    (if (node-list-empty? author)
	(empty-sosofo)
	(make sequence
	  (literal (author-list-string (node-list-first author)))
	  (loop (node-list-rest author))))))

;(define (xref-substeps target)
;  (let* ((steps (select-elements (children target) (normalize "step")))
;	 (firststep (node-list-first steps))
;	 (laststep (node-list-last steps))
;	 (firstlabel (auto-xref-direct firststep))
;	 (lastlabel (auto-xref-direct laststep "%n")))
;    (make sequence
;      firstlabel
;      (literal "-")
;      lastlabel)))

(define (xref-general target #!optional (xref-string #f))
  ;; This function is used by both XREF and OLINK (when no TARGETDOCENT
  ;; is specified).  The only case where xref-string is supplied is
  ;; on OLINK.
  (let ((label (attribute-string (normalize "xreflabel") target)))
    (if xref-string
	(auto-xref target xref-string)
	(if label
	    (xreflabel-sosofo label)
	    (auto-xref target)))))

(define (xref-biblioentry target)
  (let* ((abbrev (node-list-first 
		  (node-list-filter-out-pis (children target))))
	 (label  (attribute-string (normalize "xreflabel") target)))

    (if biblio-xref-title
	(let* ((citetitles (select-elements (descendants target)
					    (normalize "citetitle")))
	       (titles     (select-elements (descendants target)
					    (normalize "title")))
	       (title      (if (node-list-empty? citetitles)
			       (node-list-first titles)
			       (node-list-first citetitles))))
	  (with-mode xref-title-mode
	    (process-node-list title)))
	(if biblio-number 
	    (make sequence
	      (literal "[" (number->string (bibentry-number target)) "]"))
	    (if label
		(make sequence
		  (literal "[" label "]"))
		(if (equal? (gi abbrev) (normalize "abbrev"))
		    (make sequence
		      (process-node-list abbrev))
		    (make sequence
		      (literal "[" (id target) "]"))))))))

(mode xref-endterm-mode
  (default
    (make element gi: "I"
	  (process-children-trim))))

(define (xreflabel-sosofo xreflabel)
  (make element gi: "I"
	(literal xreflabel)))

;; Returns the title of the element as a sosofo, italicized for xref.
;;
(define (element-title-xref-sosofo nd)
  (make element gi: "I"
	(element-title-sosofo nd)))

(mode xref-title-mode
  (element title
    (make element gi: "I"
	  (process-children-trim)))

  (element citetitle
    (make element gi: "I"
	  (process-children-trim)))

  (element refname
    (process-children-trim))

  (element refentrytitle
    (process-children-trim)))

(mode xref-glossentry-mode
  (element glossterm
    ($italic-seq$)))

;; ======================================================================

(define (element-page-number-sosofo target)
  (with-mode pageno-mode
	(process-node-list target)))

(mode pageno-mode
  (default
    (current-node-page-number-sosofo)))

;; ======================================================================

