Skip to content

Commit b1b767a

Browse files
author
Sanders Kleinfeld
committed
Merge pull request #121 from oreillymedia/xspec
Updated XREF handling
2 parents 75ca0e1 + 8607092 commit b1b767a

File tree

7 files changed

+1717
-50
lines changed

7 files changed

+1717
-50
lines changed

htmlbook-xsl/chunk.xsl

+80-18
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
<!-- Chunk template used to split content among multiple .html files -->
1212

13-
<!-- ToDo: For XREF hyperlinks to ids that are in the same chunk, no need to prepend filename to anchor (although it probably doesn't hurt) -->
13+
<!-- ToDo: Refactor to eliminate duplicate code around href generation for XREF vs. non-XREF <a> elems -->
14+
1415
<!-- ToDo: Add "previous" and "next" links as in the docbook-xsl stylesheets? -->
1516

1617
<!-- Imports htmlbook.xsl -->
@@ -20,7 +21,7 @@
2021
encoding="UTF-8"/>
2122
<xsl:preserve-space elements="*"/>
2223

23-
<xsl:key name="chunks" match="h:section|h:div[@data-type='part']|h:nav[@data-type='toc']" use="htmlbook:is-chunk(., $chunk.level)"/>
24+
<xsl:key name="chunks" match="h:section|h:div[@data-type='part']|h:nav[@data-type='toc']" use="htmlbook:is-chunk(.)"/>
2425

2526
<!-- Nodeset of all chunks in this document -->
2627
<xsl:variable name="chunks" select="key('chunks', '1')"/> <!-- All chunks have an is-chunk() value of 1 -->
@@ -321,23 +322,23 @@ sect5:s
321322

322323
<!-- Custom XREF template in chunk.xsl, because we need to take chunk filename into account, and update hrefs. -->
323324
<!-- All XREFs must be tagged with a @data-type containing XREF -->
324-
<xsl:template match="h:a[contains(@data-type, 'xref')]">
325-
<xsl:variable name="href-anchor">
326-
<xsl:choose>
327-
<!-- If href contains an # (as it should), we're going to assume the subsequent text is the referent id -->
328-
<xsl:when test="contains(@href, '#')">
329-
<xsl:value-of select="substring-after(@href, '#')"/>
330-
</xsl:when>
331-
<!-- Otherwise, we'll just assume the entire href is the referent id -->
332-
<xsl:otherwise>
333-
<xsl:value-of select="@href"/>
334-
</xsl:otherwise>
335-
</xsl:choose>
325+
<xsl:template match="h:a[contains(@data-type, 'xref')]" name="process-as-xref">
326+
<xsl:param name="autogenerate-xrefs" select="$autogenerate-xrefs"/>
327+
<xsl:variable name="calculated-output-href">
328+
<xsl:call-template name="calculate-output-href">
329+
<xsl:with-param name="source-href-value" select="@href"/>
330+
</xsl:call-template>
331+
</xsl:variable>
332+
<xsl:variable name="href-anchor" select="substring-after($calculated-output-href, '#')"/>
333+
<xsl:variable name="is-xref">
334+
<xsl:call-template name="href-is-xref">
335+
<xsl:with-param name="href-value" select="@href"/>
336+
</xsl:call-template>
336337
</xsl:variable>
337338
<xsl:copy>
338-
<xsl:apply-templates select="@*[not(local-name() = 'href')]"/>
339+
<xsl:apply-templates select="@*[not(name() = 'href')]"/>
339340
<xsl:choose>
340-
<xsl:when test="count(key('id', $href-anchor)) > 0">
341+
<xsl:when test="(count(key('id', $href-anchor)) &gt; 0) and ($is-xref = 1)">
341342
<xsl:variable name="target" select="key('id', $href-anchor)[1]"/>
342343
<!-- Regenerate the href here, to ensure it accurately points to correct location, including chunk filename) -->
343344
<xsl:attribute name="href">
@@ -360,8 +361,8 @@ sect5:s
360361
</xsl:otherwise>
361362
</xsl:choose>
362363
<xsl:choose>
363-
<!-- Generate XREF text node if <a> is either empty or $xref-placeholder-overwrite-contents = 1 -->
364-
<xsl:when test="$autogenerate-xrefs = 1 and (. = '' or $xref-placeholder-overwrite-contents = 1)">
364+
<!-- Generate XREF text node if $autogenerate-xrefs is enabled -->
365+
<xsl:when test="($autogenerate-xrefs = 1) and ($is-xref = 1)">
365366
<xsl:choose>
366367
<!-- If we can locate the target, process gentext with "xref-to" -->
367368
<xsl:when test="count(key('id', $href-anchor)) > 0">
@@ -385,6 +386,67 @@ sect5:s
385386
</xsl:copy>
386387
</xsl:template>
387388

389+
<!-- href and content handling for a elements that are not indexterms, xrefs, or footnoterefs -->
390+
<xsl:template match="h:a[not((contains(@data-type, 'xref')) or
391+
(contains(@data-type, 'footnoteref')) or
392+
(contains(@data-type, 'indexterm')))][@href]">
393+
<!-- If the element is empty, does not have data-type="link", and is a valid XREF, go ahead and treat it like an <a> element with data-type="xref" -->
394+
<xsl:variable name="is-xref">
395+
<xsl:call-template name="href-is-xref">
396+
<xsl:with-param name="href-value" select="@href"/>
397+
</xsl:call-template>
398+
</xsl:variable>
399+
<xsl:variable name="calculated-output-href">
400+
<xsl:call-template name="calculate-output-href">
401+
<xsl:with-param name="source-href-value" select="@href"/>
402+
</xsl:call-template>
403+
</xsl:variable>
404+
<xsl:variable name="href-anchor" select="substring-after($calculated-output-href, '#')"/>
405+
<xsl:choose>
406+
<xsl:when test="(not(node())) and
407+
($is-xref = 1) and
408+
not(@data-type='link')">
409+
<xsl:call-template name="process-as-xref"/>
410+
</xsl:when>
411+
<!-- If href is an xref then process href -->
412+
<xsl:when test="$is-xref = 1">
413+
<xsl:copy>
414+
<xsl:apply-templates select="@*[not(name(.) = 'href')]"/>
415+
<xsl:attribute name="href">
416+
<xsl:choose>
417+
<xsl:when test="(count(key('id', $href-anchor)) &gt; 0) and ($is-xref = 1)">
418+
<xsl:variable name="target" select="key('id', $href-anchor)[1]"/>
419+
<!-- Regenerate the href here, to ensure it accurately points to correct location, including chunk filename) -->
420+
<xsl:call-template name="href.target">
421+
<xsl:with-param name="object" select="$target"/>
422+
<xsl:with-param name="source-link-node" select="."/>
423+
</xsl:call-template>
424+
</xsl:when>
425+
<xsl:otherwise>
426+
<xsl:call-template name="log-message">
427+
<xsl:with-param name="type" select="'WARNING'"/>
428+
<xsl:with-param name="message">
429+
<xsl:text>Unable to locate target for a element with @href value:</xsl:text>
430+
<xsl:value-of select="@href"/>
431+
</xsl:with-param>
432+
</xsl:call-template>
433+
<!-- Oh well, just copy any existing href to output -->
434+
<xsl:apply-templates select="@href"/>
435+
</xsl:otherwise>
436+
</xsl:choose>
437+
</xsl:attribute>
438+
<xsl:apply-templates/>
439+
</xsl:copy>
440+
</xsl:when>
441+
<!-- Otherwise, no special processing -->
442+
<xsl:otherwise>
443+
<xsl:copy>
444+
<xsl:apply-templates select="@*|node()"/>
445+
</xsl:copy>
446+
</xsl:otherwise>
447+
</xsl:choose>
448+
</xsl:template>
449+
388450
<!-- Generate target @href value pointing to given node, in the appropriate chunk -->
389451
<!-- Borrowed and adapted from xhtml/html.xsl and xhtml/chunk-common.xsl in docbook-xsl stylesheets -->
390452
<xsl:template name="href.target">

htmlbook-xsl/functions-exsl.xsl

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
<func:function name="htmlbook:is-chunk">
1313
<xsl:param name="node"/>
14-
<xsl:param name="chunk.level"/>
14+
<xsl:param name="chunk.level" select="$chunk.level"/>
1515
<xsl:choose>
1616
<xsl:when test="$node[self::h:div[contains(@data-type, 'part')]]">
1717
<func:result>1</func:result>

htmlbook-xsl/htmlbook.xsl

+1-8
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,7 @@
7676
<xsl:call-template name="log-message">
7777
<xsl:with-param name="type" select="'WARNING'"/>
7878
<xsl:with-param name="message">
79-
<xsl:choose>
80-
<xsl:when test="$xref-placeholder-overwrite-contents != 1">
81-
<xsl:text>Warning: the following XREFs already have content in their text nodes, which will not be overwritten (rerun stylesheets with $xref-placeholder-overwrite-contents = 1 if you want to overwrite):</xsl:text>
82-
</xsl:when>
83-
<xsl:otherwise>
84-
<xsl:text>Warning: the following XREFs already have content in their text nodes, which will be overwritten (rerun stylesheets with $xref-placeholder-overwrite-contents = 0 if you don't want to overwrite):</xsl:text>
85-
</xsl:otherwise>
86-
</xsl:choose>
79+
<xsl:text>Warning: the following XREFs already have content in their text nodes, which will be overwritten (rerun stylesheets with $autogenerate-xrefs = 0 if you don't want to autogenerate XREFs):</xsl:text>
8780
<xsl:for-each select="//h:a[@data-type='xref'][. != '']">
8881
XREF text: <xsl:value-of select="normalize-space(.)"/>; XREF target: <xsl:value-of select="@href"/>
8982
</xsl:for-each>

htmlbook-xsl/param.xsl

-3
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,6 @@ sect5:none
121121

122122
<!-- Process footnotes into separate marker/hyperlink and footnote content -->
123123
<xsl:param name="process.footnotes" select="0"/>
124-
125-
<!-- Specify whether or not to overwrite any content in XREF <a> elements when doing XREF gentext -->
126-
<xsl:param name="xref-placeholder-overwrite-contents" select="0"/>
127124

128125
<!-- Specifies type of XREF to use for different kinds of sections -->
129126
<!-- Choices are:

htmlbook-xsl/xrefgen.xsl

+111-20
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,28 @@
1313

1414
<!-- Default rule for TOC generation -->
1515

16-
<!-- ToDo: Add support for separate handling for data-type="link", where it's an internal cross-reference, but you definitely
17-
do not want to override text node -->
18-
1916
<!-- All XREFs must be tagged with a @data-type containing XREF -->
20-
<xsl:template match="h:a[contains(@data-type, 'xref')]">
21-
<xsl:variable name="href-anchor">
22-
<xsl:choose>
23-
<!-- If href contains an # (as it should), we're going to assume the subsequent text is the referent id -->
24-
<xsl:when test="contains(@href, '#')">
25-
<xsl:value-of select="substring-after(@href, '#')"/>
26-
</xsl:when>
27-
<!-- Otherwise, we'll just assume the entire href is the referent id -->
28-
<xsl:otherwise>
29-
<xsl:value-of select="@href"/>
30-
</xsl:otherwise>
31-
</xsl:choose>
17+
<xsl:template match="h:a[contains(@data-type, 'xref')]" name="process-as-xref">
18+
<xsl:param name="autogenerate-xrefs" select="$autogenerate-xrefs"/>
19+
<xsl:variable name="calculated-output-href">
20+
<xsl:call-template name="calculate-output-href">
21+
<xsl:with-param name="source-href-value" select="@href"/>
22+
</xsl:call-template>
23+
</xsl:variable>
24+
<xsl:variable name="href-anchor" select="substring-after($calculated-output-href, '#')"/>
25+
<xsl:variable name="is-xref">
26+
<xsl:call-template name="href-is-xref">
27+
<xsl:with-param name="href-value" select="@href"/>
28+
</xsl:call-template>
3229
</xsl:variable>
3330
<xsl:copy>
34-
<xsl:apply-templates select="@*"/>
35-
<xsl:choose>
36-
<!-- Generate XREF text node if <a> is either empty or $xref-placeholder-overwrite-contents = 1 -->
37-
<xsl:when test="$autogenerate-xrefs = 1 and (. = '' or $xref-placeholder-overwrite-contents = 1)">
31+
<xsl:apply-templates select="@*[not(name(.) = 'href')]"/>
32+
<xsl:attribute name="href">
33+
<xsl:value-of select="$calculated-output-href"/>
34+
</xsl:attribute>
35+
<xsl:choose>
36+
<!-- Generate XREF text node if $autogenerate-xrefs is enabled -->
37+
<xsl:when test="($autogenerate-xrefs = 1) and ($is-xref = 1)">
3838
<xsl:choose>
3939
<!-- If we can locate the target, process gentext with "xref-to" -->
4040
<xsl:when test="count(key('id', $href-anchor)) > 0">
@@ -65,6 +65,37 @@
6565
</xsl:copy>
6666
</xsl:template>
6767

68+
<!-- href and content handling for a elements that are not indexterms, xrefs, or footnoterefs -->
69+
<xsl:template match="h:a[not((contains(@data-type, 'xref')) or
70+
(contains(@data-type, 'footnoteref')) or
71+
(contains(@data-type, 'indexterm')))][@href]">
72+
<!-- If the element is empty, does not have data-type="link", and is a valid XREF, go ahead and treat it like an <a> element with data-type="xref" -->
73+
<xsl:variable name="is-xref">
74+
<xsl:call-template name="href-is-xref">
75+
<xsl:with-param name="href-value" select="@href"/>
76+
</xsl:call-template>
77+
</xsl:variable>
78+
<xsl:choose>
79+
<xsl:when test="(not(node())) and
80+
($is-xref = 1) and
81+
not(@data-type='link')">
82+
<xsl:call-template name="process-as-xref"/>
83+
</xsl:when>
84+
<!-- Otherwise just process href and apply-templates for everything else -->
85+
<xsl:otherwise>
86+
<xsl:copy>
87+
<xsl:apply-templates select="@*[not(name(.) = 'href')]"/>
88+
<xsl:attribute name="href">
89+
<xsl:call-template name="calculate-output-href">
90+
<xsl:with-param name="source-href-value" select="@href"/>
91+
</xsl:call-template>
92+
</xsl:attribute>
93+
<xsl:apply-templates/>
94+
</xsl:copy>
95+
</xsl:otherwise>
96+
</xsl:choose>
97+
</xsl:template>
98+
6899
<!-- Adapted from docbook-xsl templates in xhtml/xref.xsl -->
69100
<xsl:template match="*" mode="xref-to">
70101
<xsl:param name="referrer"/>
@@ -225,7 +256,7 @@
225256
<!-- ============================================================ -->
226257

227258
<!-- Adapted from docbook-xsl templates in common/gentext.xsl -->
228-
<!-- For reasons of simplicity and relevancy, only supporting %n, %t, and %d substitutions -->
259+
<!-- For reasons of simplicity and relevance, only supporting %n, %t, and %d substitutions -->
229260
<xsl:template name="substitute-markup">
230261
<xsl:param name="template" select="''"/>
231262
<xsl:param name="allow-anchors" select="'0'"/>
@@ -484,4 +515,64 @@
484515
</xsl:choose>
485516
</xsl:template>
486517

518+
<!-- Utility template for determining whether an @href is a valid XREF -->
519+
<!-- Returns 1 if href is an XREF, and 0 if not -->
520+
<xsl:template name="href-is-xref">
521+
<xsl:param name="href-value" select="@href"/>
522+
<xsl:choose>
523+
<xsl:when test="starts-with($href-value, '#')">1</xsl:when>
524+
<xsl:when test="starts-with($href-value, 'mailto:')">0</xsl:when>
525+
<!-- If we weren't worried about XSLT 1.0 compatibility, might be better to use a regex here -->
526+
<xsl:when test="contains($href-value, '://')">0</xsl:when>
527+
<xsl:otherwise>1</xsl:otherwise>
528+
</xsl:choose>
529+
</xsl:template>
530+
531+
<!-- Utility template for processing @href attributes on <a> elements -->
532+
<!-- For XREFs, grab either the text content after the last # sign, or all the content if there is no # sign -->
533+
<!-- For non-XREFs, don't touch at all -->
534+
<xsl:template name="calculate-output-href">
535+
<xsl:param name="source-href-value" select="@href"/>
536+
<xsl:param name="href-is-xref"/>
537+
538+
<xsl:variable name="is-xref">
539+
<xsl:choose>
540+
<xsl:when test="($href-is-xref = 0) or ($href-is-xref = 1)">
541+
<xsl:value-of select="$href-is-xref"/>
542+
</xsl:when>
543+
<xsl:otherwise>
544+
<xsl:call-template name="href-is-xref">
545+
<xsl:with-param name="href-value" select="$source-href-value"/>
546+
</xsl:call-template>
547+
</xsl:otherwise>
548+
</xsl:choose>
549+
</xsl:variable>
550+
551+
<xsl:choose>
552+
<xsl:when test="$is-xref = 1">
553+
<xsl:choose>
554+
<!-- If there is more than one # sign in content, recursively call template to get content after first # -->
555+
<xsl:when test="contains(substring-after($source-href-value, '#'), '#')">
556+
<xsl:call-template name="calculate-output-href">
557+
<xsl:with-param name="source-href-value" select="substring-after($source-href-value, '#')"/>
558+
<xsl:with-param name="href-is-xref" select="1"/>
559+
</xsl:call-template>
560+
</xsl:when>
561+
<!-- If there is a # sign in content, grab the # and all content thereafter -->
562+
<xsl:when test="contains($source-href-value, '#')">
563+
<xsl:value-of select="concat('#', substring-after($source-href-value, '#'))"/>
564+
</xsl:when>
565+
<!-- Otherwise, just use all the text as is, with a # sign prepended-->
566+
<xsl:otherwise>
567+
<xsl:value-of select="concat('#', $source-href-value)"/>
568+
</xsl:otherwise>
569+
</xsl:choose>
570+
</xsl:when>
571+
<xsl:otherwise>
572+
<!-- Just use the text as is -->
573+
<xsl:value-of select="$source-href-value"/>
574+
</xsl:otherwise>
575+
</xsl:choose>
576+
</xsl:template>
577+
487578
</xsl:stylesheet>

0 commit comments

Comments
 (0)