Skip to content

Commit 75ca0e1

Browse files
author
Sanders Kleinfeld
committed
Merge pull request #118 from oreillymedia/xspec
Refactored function handling and added XSpec tests for tocgen module
2 parents 859486b + 8103f46 commit 75ca0e1

File tree

7 files changed

+218
-21
lines changed

7 files changed

+218
-21
lines changed

htmlbook-xsl/chunk.xsl

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
encoding="UTF-8"/>
2121
<xsl:preserve-space elements="*"/>
2222

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

2525
<!-- Nodeset of all chunks in this document -->
2626
<xsl:variable name="chunks" select="key('chunks', '1')"/> <!-- All chunks have an is-chunk() value of 1 -->
@@ -76,7 +76,7 @@ sect5:s
7676
</xsl:template>
7777

7878
<xsl:template match="h:section|h:div[contains(@data-type, 'part')]|h:nav[contains(@data-type, 'toc')]">
79-
<xsl:variable name="is.chunk" select="htmlbook:is-chunk(.)"/>
79+
<xsl:variable name="is.chunk" select="htmlbook:is-chunk(., $chunk.level)"/>
8080
<!-- <xsl:message>Element name: <xsl:value-of select="local-name()"/>, data-type name: <xsl:value-of select="@data-type"/>, Is chunk: <xsl:value-of select="$is.chunk"/></xsl:message> -->
8181
<xsl:choose>
8282
<xsl:when test="$is.chunk = 1">
@@ -138,7 +138,7 @@ sect5:s
138138
<!-- Root Chunk! Needs $outputdir in full file path-->
139139
<xsl:value-of select="concat($outputdir, $chars-to-append-to-outputdir)"/>
140140
</xsl:when>
141-
<xsl:when test="$outputdir != '' and not($generate.root.chunk = 1) and not($chunk[ancestor::*[htmlbook:is-chunk(.) = 1]])">
141+
<xsl:when test="$outputdir != '' and not($generate.root.chunk = 1) and not($chunk[ancestor::*[htmlbook:is-chunk(., $chunk.level) = 1]])">
142142
<!-- $outputdir is specified and *is not* absolute filepath,
143143
and generate.root.chunk is not specified (if it is, then previous "when" will set the outputdir properly),
144144
and chunk *is not* a nested chunk -->
@@ -286,7 +286,7 @@ sect5:s
286286

287287
<!-- Check to see if parent is also chunk, in which case, call template recursively -->
288288
<xsl:variable name="parent-node" select="parent::*"/>
289-
<xsl:variable name="parent-is-chunk" select="htmlbook:is-chunk($parent-node)"/>
289+
<xsl:variable name="parent-is-chunk" select="htmlbook:is-chunk($parent-node, $chunk.level)"/>
290290
<xsl:if test="$parent-is-chunk = '1'">
291291
<xsl:call-template name="output-filename-for-chunk">
292292
<xsl:with-param name="node" select="$parent-node"/>
@@ -445,7 +445,7 @@ sect5:s
445445
<xsl:template name="filename-for-node">
446446
<xsl:param name="node"/>
447447

448-
<xsl:variable name="chunk.node" select="htmlbook:chunk-for-node($node)"/>
448+
<xsl:variable name="chunk.node" select="htmlbook:chunk-for-node($node, $chunks)"/>
449449

450450
<!-- Now get filename for chunk -->
451451
<xsl:variable name="chunk-filename">
@@ -472,7 +472,7 @@ sect5:s
472472
<xsl:template name="generate-footnotes">
473473

474474
<!-- Only generate footnotes if the current node is a chunk -->
475-
<xsl:if test="htmlbook:is-chunk(.)">
475+
<xsl:if test="htmlbook:is-chunk(., $chunk.level)">
476476

477477
<xsl:variable name="all-footnotes" select="//h:span[@data-type='footnote']"/>
478478

@@ -535,8 +535,8 @@ sect5:s
535535
<xsl:param name="chunk.node" select="parent::*"/>
536536

537537
<xsl:choose>
538-
<xsl:when test="htmlbook:chunk-for-node($chunk.node)">
539-
<xsl:variable name="current.chunk" select="htmlbook:chunk-for-node($chunk.node)"/>
538+
<xsl:when test="htmlbook:chunk-for-node($chunk.node, $chunks)">
539+
<xsl:variable name="current.chunk" select="htmlbook:chunk-for-node($chunk.node, $chunks)"/>
540540
<xsl:variable name="previous.chunk" select="$chunks[descendant::*[generate-id(.) = generate-id($current.chunk)]|
541541
following::*[generate-id(.) = generate-id($current.chunk)]][last()]"/>
542542
<xsl:if test="$previous.chunk">
@@ -570,8 +570,8 @@ sect5:s
570570
<xsl:param name="chunk.node" select="parent::*"/>
571571

572572
<xsl:choose>
573-
<xsl:when test="htmlbook:chunk-for-node($chunk.node)">
574-
<xsl:variable name="current.chunk" select="htmlbook:chunk-for-node($chunk.node)"/>
573+
<xsl:when test="htmlbook:chunk-for-node($chunk.node, $chunks)">
574+
<xsl:variable name="current.chunk" select="htmlbook:chunk-for-node($chunk.node, $chunks)"/>
575575
<xsl:variable name="next.chunk" select="$chunks[ancestor::*[generate-id(.) = generate-id($current.chunk)]|
576576
preceding::*[generate-id(.) = generate-id($current.chunk)]][1]"/>
577577
<xsl:if test="$next.chunk">

htmlbook-xsl/functions-exsl.xsl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
<func:function name="htmlbook:is-chunk">
1313
<xsl:param name="node"/>
14+
<xsl:param name="chunk.level"/>
1415
<xsl:choose>
1516
<xsl:when test="$node[self::h:div[contains(@data-type, 'part')]]">
1617
<func:result>1</func:result>
@@ -55,6 +56,7 @@
5556
<!-- Given a node, return the root node of the chunk it's in -->
5657
<func:function name="htmlbook:chunk-for-node">
5758
<xsl:param name="node"/>
59+
<xsl:param name="chunks"/>
5860

5961
<!-- 1. Get a nodeset of current element and all its ancestors, which could potentially be chunks -->
6062
<xsl:variable name="self-and-ancestors" select="$node/ancestor-or-self::*"/>

htmlbook-xsl/functions-xslt2.xsl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
<xsl:function name="htmlbook:is-chunk">
1212
<xsl:param name="node"/>
13+
<xsl:param name="chunk.level"/>
1314
<xsl:choose>
1415
<xsl:when test="$node[self::h:div[contains(@data-type, 'part')]]">1</xsl:when>
1516
<xsl:when test="$node[self::h:section[contains(@data-type, 'acknowledgments') or
@@ -43,6 +44,7 @@
4344
<!-- Given a node, return the root node of the chunk it's in -->
4445
<xsl:function name="htmlbook:chunk-for-node">
4546
<xsl:param name="node"/>
47+
<xsl:param name="chunks"/>
4648

4749
<!-- 1. Get a nodeset of current element and all its ancestors, which could potentially be chunks -->
4850
<xsl:variable name="self-and-ancestors" select="$node/ancestor-or-self::*"/>

htmlbook-xsl/opf.xsl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@
604604
<xsl:for-each select="$element-descendants[1]">
605605
<xsl:choose>
606606
<!-- Check if the element's nearest chunk ancestor is the chunk in question... -->
607-
<xsl:when test="ancestor::*[htmlbook:is-chunk(.) = 1 and not(descendant::*[htmlbook:is-chunk(.) = 1][descendant::*[generate-id() = generate-id($element-descendants[1])]])][generate-id() = $chunk-id]">
607+
<xsl:when test="ancestor::*[htmlbook:is-chunk(., $chunk.level) = 1 and not(descendant::*[htmlbook:is-chunk(., $chunk.level) = 1][descendant::*[generate-id() = generate-id($element-descendants[1])]])][generate-id() = $chunk-id]">
608608
<!--...It is: We have $element-name in this chunk! -->
609609
<xsl:text>1</xsl:text>
610610
</xsl:when>

htmlbook-xsl/tocgen.xsl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313

1414
<!-- Default rule for TOC generation -->
1515
<xsl:template match="*" mode="tocgen">
16+
<xsl:param name="toc.section.depth" select="$toc.section.depth"/>
1617
<xsl:apply-templates select="*" mode="tocgen"/>
1718
</xsl:template>
1819

1920
<xsl:template match="h:section[not(@data-type = 'dedication' or @data-type = 'titlepage' or @data-type = 'toc' or @data-type = 'colophon' or @data-type = 'copyright-page' or @data-type = 'halftitlepage')]|h:div[@data-type='part']" mode="tocgen">
21+
<xsl:param name="toc.section.depth" select="$toc.section.depth"/>
2022
<xsl:choose>
2123
<!-- Don't output entry for section elements at a level that is greater than specified $toc.section.depth -->
2224
<xsl:when test="self::h:section[contains(@data-type, 'sect') and htmlbook:section-depth(.) != '' and htmlbook:section-depth(.) &gt; $toc.section.depth]"/>
@@ -57,6 +59,9 @@
5759
<xsl:template match="h:nav[@data-type='toc']" name="generate-toc">
5860
<xsl:param name="toc.node" select="."/>
5961
<xsl:param name="scope" select="/*"/>
62+
<xsl:param name="autogenerate-toc" select="$autogenerate-toc"/>
63+
<xsl:param name="toc-placeholder-overwrite-contents" select="$toc-placeholder-overwrite-contents"/>
64+
6065
<!-- Just switch context to $toc.node, so we don't have to reference the variable in rest of template -->
6166
<xsl:for-each select="$toc.node">
6267
<xsl:choose>

htmlbook-xsl/xspec/skeleton.html

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<meta name="publisher" content="O'Reilly Media"/>
77
<meta name="creator" content="Sanders Kleinfeld"/>
88
</head>
9-
<body>
9+
<body data-type="book">
1010
<!-- cover image -->
1111
<figure data-type="cover">
1212
<img src="cover.jpg"/>
@@ -27,7 +27,7 @@ <h1>Copyright 2013 O'Reilly Media, Inc.</h1>
2727
<h1>Preface</h1>
2828
<p>This is the Preface</p>
2929
<p>SVG element:</p>
30-
<svg xmlns="http://www.w3.org/2000/svg"/>
30+
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="20"/>
3131
<section data-type="sect1">
3232
<h1>Important Prerequisite Info</h1>
3333
<p>You must know&hellip;</p>
@@ -121,13 +121,16 @@ <h3>Sub-sub-subsection</h3>
121121
</section>
122122
</section>
123123
</section>
124-
<section data-type="glossary">
125-
<h1>Glossary</h1>
126-
<p>This would be a glossary, if it had any content!</p>
127-
</section>
128-
<section data-type="bibliography">
129-
<h1>Bibliography</h1>
130-
<p>This would be a bibliography, if it had any content!</p>
131-
</section>
124+
<div data-type="part">
125+
<h1>Backmatter!!!</h1>
126+
<section data-type="glossary">
127+
<h1>Glossary</h1>
128+
<p>This would be a glossary, if it had any content!</p>
129+
</section>
130+
<section data-type="bibliography">
131+
<h1>Bibliography</h1>
132+
<p>This would be a bibliography, if it had any content!</p>
133+
</section>
134+
</div>
132135
</body>
133136
</html>

htmlbook-xsl/xspec/tocgen.xspec

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<x:description xmlns:x="http://www.jenitennison.com/xslt/xspec"
4+
xmlns:functx="http://www.functx.com"
5+
xmlns="http://www.w3.org/1999/xhtml"
6+
xmlns:ncx="http://www.daisy.org/z3986/2005/ncx/"
7+
xmlns:h="http://www.w3.org/1999/xhtml"
8+
xmlns:e="http://github.com/oreillymedia/epubrenderer"
9+
stylesheet="../htmlbook.xsl">
10+
11+
<x:param name="autogenerate-toc" select="0"/>
12+
<x:param name="toc-placeholder-overwrite-contents" select="0"/>
13+
14+
<x:scenario label="When *empty* TOC nav element is matched">
15+
<x:context select="(//h:nav)[1]">
16+
<body>
17+
<nav data-type="toc"/>
18+
<section id="chapter" data-type="chapter">
19+
<h1>This chapter head should be in TOC</h1>
20+
<p>Go TOC!</p>
21+
</section>
22+
</body>
23+
</x:context>
24+
25+
<x:scenario label="And autogenerate-toc param is *disabled*">
26+
<x:context>
27+
<x:param name="autogenerate-toc" select="0"/>
28+
</x:context>
29+
<x:expect label="TOC content *should not* be generated (nav should stay empty)">
30+
<nav data-type="toc" id="..."/>
31+
</x:expect>
32+
</x:scenario>
33+
34+
<x:scenario label="And autogenerate-toc param is *enabled*">
35+
<x:context>
36+
<x:param name="autogenerate-toc" select="1"/>
37+
</x:context>
38+
<x:expect label="TOC content should be generated"
39+
test="count(h:nav[.//h:li[contains(., 'This chapter head should be in TOC')]]) = 1"/>
40+
</x:scenario>
41+
42+
</x:scenario>
43+
44+
<x:scenario label="When *nonempty* TOC nav element is matched">
45+
<x:context select="(//h:nav)[1]">
46+
<body>
47+
<nav data-type="toc">
48+
<h1>Manual TOC</h1>
49+
<ol>
50+
<li>Entry 1</li>
51+
<li>Entry 2</li>
52+
<li>Entry 3</li>
53+
</ol>
54+
</nav>
55+
<section data-type="chapter">
56+
<h1>This chapter head should be in TOC</h1>
57+
<p>Go TOC!</p>
58+
</section>
59+
</body>
60+
</x:context>
61+
62+
<x:scenario label="And autogenerate-toc param is *disabled*">
63+
<x:context>
64+
<x:param name="autogenerate-toc" select="0"/>
65+
</x:context>
66+
<!-- Same as in context above, plus autogenerated id -->
67+
<x:expect label="TOC content *should not* be generated (nav content left as is)">
68+
<nav id="..." data-type="toc">
69+
<h1>Manual TOC</h1>
70+
<ol>
71+
<li>Entry 1</li>
72+
<li>Entry 2</li>
73+
<li>Entry 3</li>
74+
</ol>
75+
</nav>
76+
</x:expect>
77+
</x:scenario>
78+
79+
<x:scenario label="And autogenerate-toc param is *enabled*, but toc-placeholder-overwrite-contents is *disabled*">
80+
<x:context>
81+
<x:param name="autogenerate-toc" select="1"/>
82+
<x:param name="toc-placeholder-overwrite-contents" select="0"/>
83+
</x:context>
84+
<!-- Same as in context above, plus autogenerated id -->
85+
<x:expect label="TOC content *should not* be generated (nav content left as is)">
86+
<nav id="..." data-type="toc">
87+
<h1>Manual TOC</h1>
88+
<ol>
89+
<li>Entry 1</li>
90+
<li>Entry 2</li>
91+
<li>Entry 3</li>
92+
</ol>
93+
</nav>
94+
</x:expect>
95+
</x:scenario>
96+
97+
<x:scenario label="And autogenerate-toc param is *enabled*, and toc-placeholder-overwrite-contents is *enabled*">
98+
<x:context>
99+
<x:param name="autogenerate-toc" select="1"/>
100+
<x:param name="toc-placeholder-overwrite-contents" select="1"/>
101+
</x:context>
102+
<x:expect label="TOC content *should* be generated (nav content overwritten)"
103+
test="count(h:nav[.//h:li[contains(., 'This chapter head should be in TOC')]]) = 1 and
104+
not(exists(h:nav//h:h1[contains(., 'Manual TOC')]))"/>
105+
</x:scenario>
106+
</x:scenario>
107+
108+
<x:scenario label="When there are multiple empty TOC nav elements">
109+
<x:context select="//h:nav">
110+
<body>
111+
<nav data-type="toc"/>
112+
<nav data-type="toc"/>
113+
</body>
114+
<section data-type="chapter">
115+
<h1>This chapter head should be in TOC</h1>
116+
<p>Go TOC!</p>
117+
</section>
118+
<x:param name="autogenerate-toc" select="1"/>
119+
</x:context>
120+
<x:expect label="Process them all the same way"
121+
test="deep-equal(//h:nav[@data-type='toc'][1]/node(), //h:nav[@data-type='toc'][2]/node())"/>
122+
</x:scenario>
123+
124+
<x:scenario label="When a TOC is generated with a scope specified">
125+
<x:context href="skeleton.html" select="(//h:nav[@data-type='toc'])[1]">
126+
<x:param name="autogenerate-toc" select="1"/>
127+
<x:param name="scope" select="(document('../skeleton.html')//h:section[@data-type='sect1'])[1]"/>
128+
</x:context>
129+
<x:expect label="Root of TOC list should be the scope root">
130+
<nav data-type="toc" id="...">
131+
<ol>
132+
<li data-type="sect1">...</li>
133+
</ol>
134+
</nav>
135+
</x:expect>
136+
</x:scenario>
137+
138+
<x:scenario label="When a standard book-level section (chapter) is matched in tocgen mode">
139+
<x:context href="skeleton.html" select="(//h:section[@data-type='chapter'])[1]" mode="tocgen"/>
140+
<x:expect label="An entry 'li' should be generated">
141+
<li data-type="chapter"><a href="...">...</a>...</li>
142+
</x:expect>
143+
</x:scenario>
144+
145+
<x:scenario label="When a standard book-level section (part) is matched in tocgen mode">
146+
<x:context href="skeleton.html" select="(//h:div[@data-type='part'])[1]" mode="tocgen"/>
147+
<x:expect label="An entry 'li' should be generated">
148+
<li data-type="part"><a href="...">...</a>...</li>
149+
</x:expect>
150+
</x:scenario>
151+
152+
<x:scenario label="When a subsection within the toc.section.depth is matched in tocgen mode">
153+
<x:context href="skeleton.html" select="(//h:section[@data-type='sect1'])[1]" mode="tocgen">
154+
<x:param name="toc.section.depth" select="1"/>
155+
</x:context>
156+
<x:expect label="An entry 'li' should be generated">
157+
<li data-type="sect1"><a href="...">...</a></li>
158+
</x:expect>
159+
</x:scenario>
160+
161+
<x:scenario label="When a subsection at greater depth than the toc.section.depth is matched in tocgen mode">
162+
<x:context href="skeleton.html" select="(//h:section[@data-type='sect2'])[1]" mode="tocgen">
163+
<x:param name="toc.section.depth" select="1"/>
164+
</x:context>
165+
<x:expect label="An entry 'li' *should not* be generated" select="()"/>
166+
</x:scenario>
167+
168+
<x:scenario label="When a nonsection is matched in tocgen mode">
169+
<x:context href="skeleton.html" select="(//h:p)[1]" mode="tocgen"/>
170+
<x:expect label="An entry 'li' *should not* be generated" select="()"/>
171+
</x:scenario>
172+
173+
<x:scenario label="When toc-title is called for English-language content (default)">
174+
<x:call template="toc-title"/>
175+
<x:expect label="it should return the proper TOC title">Table of Contents</x:expect>
176+
</x:scenario>
177+
178+
<x:pending>
179+
<x:scenario label="When toc-title is called for non-English-language content">
180+
<!-- For this test, would need to further parameterize get-localization-value so that you could grab a different localization for the test -->
181+
<x:call template="toc-title"/>
182+
<x:expect label="it should return the proper TOC title"/>
183+
</x:scenario>
184+
</x:pending>
185+
</x:description>

0 commit comments

Comments
 (0)