|
| 1 | +<xsl:stylesheet version="1.0" |
| 2 | + xmlns:xsl="http://www.w3.org/1999/XSL/Transform" |
| 3 | + xmlns:exsl="http://exslt.org/common" |
| 4 | + xmlns:func="http://exslt.org/functions" |
| 5 | + xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0" |
| 6 | + xmlns:h="http://www.w3.org/1999/xhtml" |
| 7 | + xmlns="http://www.w3.org/1999/xhtml" |
| 8 | + extension-element-prefixes="exsl func" |
| 9 | + xmlns:htmlbook="https://github.com/oreillymedia/HTMLBook" |
| 10 | + exclude-result-prefixes="exsl func h"> |
| 11 | + <xsl:output method="xml" |
| 12 | + encoding="UTF-8"/> |
| 13 | + <xsl:preserve-space elements="*"/> |
| 14 | + |
| 15 | + <!-- Add title heading elements for different admonition types that do not already have headings in markup --> |
| 16 | + <xsl:param name="add.title.heading.for.admonitions" select="1"/> |
| 17 | + |
| 18 | +<!-- ***************** COOKBOOK PARAMS ***************** --> |
| 19 | +<!-- *************** Overrides param.xsl *************** --> |
| 20 | + |
| 21 | +<!-- Recipe format should be "X.1 Title, no second period" --> |
| 22 | +<xsl:param name="recipe.number.and.title.separator" select="' '"/> |
| 23 | + |
| 24 | +<!-- This book should show sect2s in TOC --> |
| 25 | +<xsl:param name="toc.section.depth" select="2"/> |
| 26 | + |
| 27 | +<!-- ***************** LABEL HANDLING ***************** --> |
| 28 | +<!-- ************* Overrides common.xsl *************** --> |
| 29 | + |
| 30 | + <!-- Logic for processing sect1 headings with labels (including section numbers) --> |
| 31 | + <xsl:template match="h:section[@data-type='chapter' and not(contains(@class, 'orm:non-recipe'))]/h:section[@data-type='sect1' and not(contains(@class, 'orm:non-recipe'))]/h:h1" mode="process-heading"> |
| 32 | + <xsl:param name="autogenerate.labels" select="$autogenerate.labels"/> |
| 33 | + <!-- Labeled element is typically the parent element of the heading (e.g., <section> or <figure>) --> |
| 34 | + <xsl:param name="labeled-element" select="(parent::h:header/parent::*|parent::*[not(self::h:header)])[1]"/> |
| 35 | + <!-- Labeled element semantic name is typically the parent element of the heading's @data-type --> |
| 36 | + <xsl:param name="labeled-element-semantic-name" select="(parent::h:header/parent::*|parent::*[not(self::h:header)])[1]/@data-type"/> |
| 37 | + <!-- Name for output heading element; same as current node name by default --> |
| 38 | + <xsl:param name="output-element-name" select="local-name(.)"/> |
| 39 | + <xsl:element name="{$output-element-name}" namespace="http://www.w3.org/1999/xhtml"> |
| 40 | + <xsl:apply-templates select="@*"/> |
| 41 | + <!-- BEGIN COOKBOOK OVERRIDE --> |
| 42 | + <!-- Recipes should have labels in format #.# --> |
| 43 | + <xsl:apply-templates select="$labeled-element" mode="label.markup"/> |
| 44 | + <xsl:value-of select="$recipe.number.and.title.separator"/> |
| 45 | + <xsl:apply-templates/> |
| 46 | + <!-- END COOKBOOK OVERRIDE --> |
| 47 | + </xsl:element> |
| 48 | + </xsl:template> |
| 49 | + |
| 50 | + <!-- Logic for processing sect2 headings with labels (including section numbers) --> |
| 51 | + <xsl:template match="h:section[@data-type='chapter' and not(contains(@class, 'orm:non-recipe'))]/h:section[@data-type='sect1' and not(contains(@class, 'orm:non-recipe'))]/h:section[@data-type='sect2' and not(contains(@class, 'orm:non-recipe'))]/h:h2" mode="process-heading"> |
| 52 | + <xsl:param name="autogenerate.labels" select="$autogenerate.labels"/> |
| 53 | + <!-- Labeled element is typically the parent element of the heading (e.g., <section> or <figure>) --> |
| 54 | + <xsl:param name="labeled-element" select="(parent::h:header/parent::*|parent::*[not(self::h:header)])[1]"/> |
| 55 | + <!-- Labeled element semantic name is typically the parent element of the heading's @data-type --> |
| 56 | + <xsl:param name="labeled-element-semantic-name" select="(parent::h:header/parent::*|parent::*[not(self::h:header)])[1]/@data-type"/> |
| 57 | + <!-- Name for output heading element; same as current node name by default --> |
| 58 | + <xsl:param name="output-element-name" select="local-name(.)"/> |
| 59 | + <xsl:element name="{$output-element-name}" namespace="http://www.w3.org/1999/xhtml"> |
| 60 | + <xsl:apply-templates select="@*"/> |
| 61 | + <!-- BEGIN COOKBOOK OVERRIDE --> |
| 62 | + <!-- Recipes should have labels in format #.# --> |
| 63 | + <xsl:apply-templates select="$labeled-element" mode="label.markup"/> |
| 64 | + <xsl:value-of select="$recipe.number.and.title.separator"/> |
| 65 | + <xsl:apply-templates/> |
| 66 | + <!-- END COOKBOOK OVERRIDE --> |
| 67 | + </xsl:element> |
| 68 | + </xsl:template> |
| 69 | + |
| 70 | + <!-- Creating the sect1 labels (read: creating the X.X section numbering) --> |
| 71 | + <xsl:template match="h:section[@data-type='chapter' and not(contains(@class, 'orm:non-recipe'))]/h:section[@data-type='sect1' and not(contains(@class, 'orm:non-recipe'))]" mode="label.markup"> |
| 72 | + <xsl:variable name="current-node" select="."/> |
| 73 | + <!-- BEGIN COOKBOOK OVERRIDE --> |
| 74 | + <!-- Recipes should always be labeled with ancestor chapter --> |
| 75 | + <xsl:for-each select="ancestor::h:section[@data-type='chapter']"> |
| 76 | + <xsl:call-template name="get-label-from-data-type"> |
| 77 | + <xsl:with-param name="data-type" select="@data-type"/> |
| 78 | + </xsl:call-template> |
| 79 | + <xsl:apply-templates select="$current-node" mode="intralabel.punctuation"/> |
| 80 | + </xsl:for-each> |
| 81 | + |
| 82 | + <!-- Custom Recipe numbering logic: |
| 83 | + * Don't number Recipes with class=orm:non-recipe |
| 84 | + * Introduction sections at the beginning of chapters have labeling start at #.0 |
| 85 | + --> |
| 86 | + <xsl:variable name="is.numbered"> |
| 87 | + <xsl:choose> |
| 88 | + <xsl:when test="@class='orm:non-recipe'">1</xsl:when> |
| 89 | + <xsl:otherwise>0</xsl:otherwise> |
| 90 | + </xsl:choose> |
| 91 | + </xsl:variable> |
| 92 | + |
| 93 | + <xsl:variable name="chap.has.intro"> |
| 94 | + <xsl:choose> |
| 95 | + <xsl:when test="$is.numbered = 0"> |
| 96 | + <xsl:call-template name="check.chap.for.intro"> |
| 97 | + <xsl:with-param name="chapter" select="parent::*"/> |
| 98 | + </xsl:call-template> |
| 99 | + </xsl:when> |
| 100 | + <xsl:otherwise>1</xsl:otherwise> |
| 101 | + </xsl:choose> |
| 102 | + </xsl:variable> |
| 103 | + |
| 104 | + <xsl:variable name="recipe.level"> |
| 105 | + <xsl:value-of select="count(preceding-sibling::h:section[@data-type='sect1'][not(@class='orm:non-recipe')]) + (1 - $chap.has.intro - $is.numbered)"/> |
| 106 | + </xsl:variable> |
| 107 | + <xsl:number format="1" value="$recipe.level"/> |
| 108 | + |
| 109 | + <!-- END COOKBOOK OVERRIDE --> |
| 110 | + </xsl:template> |
| 111 | + |
| 112 | + <!-- Creating the sect2 labels (read: creating the X.X.X section numbering) --> |
| 113 | + <xsl:template match="h:section[@data-type='chapter' and not(contains(@class, 'orm:non-recipe'))]/h:section[@data-type='sect1' and not(contains(@class, 'orm:non-recipe'))]/h:section[@data-type='sect2' and not(contains(@class, 'orm:non-recipe'))]" mode="label.markup"> |
| 114 | + <!-- END OVERRIDE --> |
| 115 | + <xsl:variable name="current-node" select="."/> |
| 116 | + <!-- BEGIN COOKBOOK OVERRIDE --> |
| 117 | + <!-- Recipes should always be labeled with ancestor chapter --> |
| 118 | + <xsl:for-each select="ancestor::h:section[@data-type='chapter']"> |
| 119 | + <xsl:call-template name="get-label-from-data-type"> |
| 120 | + <xsl:with-param name="data-type" select="@data-type"/> |
| 121 | + </xsl:call-template> |
| 122 | + <xsl:apply-templates select="$current-node" mode="intralabel.punctuation"/> |
| 123 | + </xsl:for-each> |
| 124 | + |
| 125 | + <!-- Custom Recipe numbering logic: |
| 126 | + * Don't number Recipes with class=orm:non-recipe |
| 127 | + * Introduction sections at the beginning of chapters have labeling start at #.0 |
| 128 | + --> |
| 129 | + <xsl:variable name="is.numbered"> |
| 130 | + <xsl:choose> |
| 131 | + <xsl:when test="parent::h:section/@class='orm:non-recipe'">1</xsl:when> |
| 132 | + <xsl:otherwise>0</xsl:otherwise> |
| 133 | + </xsl:choose> |
| 134 | + </xsl:variable> |
| 135 | + |
| 136 | + <xsl:variable name="chap.has.intro"> |
| 137 | + <xsl:choose> |
| 138 | + <xsl:when test="$is.numbered = 0"> |
| 139 | + <xsl:call-template name="check.chap.for.intro"> |
| 140 | + <xsl:with-param name="chapter" select="ancestor::h:section[data-type='chapter']"/> |
| 141 | + </xsl:call-template> |
| 142 | + </xsl:when> |
| 143 | + <xsl:otherwise>1</xsl:otherwise> |
| 144 | + </xsl:choose> |
| 145 | + </xsl:variable> |
| 146 | + |
| 147 | + <xsl:variable name="sect1.recipe.level"> |
| 148 | + <xsl:value-of select="count(../preceding-sibling::h:section[@data-type='sect1'][not(@class='orm:non-recipe')]) + (1 - $chap.has.intro - $is.numbered)"/> |
| 149 | + </xsl:variable> |
| 150 | + <xsl:number format="1" value="$sect1.recipe.level"/> |
| 151 | + <xsl:text>.</xsl:text> |
| 152 | + |
| 153 | + <xsl:variable name="is.sect1.numbered"> |
| 154 | + <xsl:choose> |
| 155 | + <xsl:when test="@class='orm:non-recipe'">1</xsl:when> |
| 156 | + <xsl:otherwise>0</xsl:otherwise> |
| 157 | + </xsl:choose> |
| 158 | + </xsl:variable> |
| 159 | + |
| 160 | + <xsl:variable name="sect1.has.intro"> |
| 161 | + <xsl:choose> |
| 162 | + <xsl:when test="$is.sect1.numbered = 0"> |
| 163 | + <xsl:call-template name="check.sect1.for.intro"> |
| 164 | + <xsl:with-param name="sect1" select="parent::*"/> |
| 165 | + </xsl:call-template> |
| 166 | + </xsl:when> |
| 167 | + <xsl:otherwise>1</xsl:otherwise> |
| 168 | + </xsl:choose> |
| 169 | + </xsl:variable> |
| 170 | + |
| 171 | + <xsl:variable name="recipe.level"> |
| 172 | + <xsl:value-of select="count(preceding-sibling::h:section[@data-type='sect2'][not(@class='orm:non-recipe')]) + (1 - $sect1.has.intro - $is.sect1.numbered)"/> |
| 173 | + </xsl:variable> |
| 174 | + <xsl:number format="1" value="$recipe.level"/> |
| 175 | + |
| 176 | + <!-- END COOKBOOK OVERRIDE --> |
| 177 | + </xsl:template> |
| 178 | + <!-- Utility template --> |
| 179 | + <xsl:template name="check.chap.for.intro"> |
| 180 | + <xsl:param name="chapter" select="."/> |
| 181 | + <xsl:choose> |
| 182 | + <xsl:when test="$chapter/h:section[@data-type='sect1'][1]/h:h1 = 'Introduction'">1</xsl:when> |
| 183 | + <xsl:otherwise>0</xsl:otherwise> |
| 184 | + </xsl:choose> |
| 185 | + </xsl:template> |
| 186 | + |
| 187 | + <xsl:template name="check.sect1.for.intro"> |
| 188 | + <xsl:param name="sect1" select="."/> |
| 189 | + <xsl:choose> |
| 190 | + <xsl:when test="$sect1/h:section[@data-type='sect2'][1]/h:h2 = 'Introduction'">1</xsl:when> |
| 191 | + <xsl:otherwise>0</xsl:otherwise> |
| 192 | + </xsl:choose> |
| 193 | + </xsl:template> |
| 194 | + |
| 195 | +<!-- ***************** XREF HANDLING ***************** --> |
| 196 | +<!-- ************* Overrides xrefgen.xsl ************* --> |
| 197 | + |
| 198 | + <xsl:template match="h:a[@data-type='xref']" mode="class.value"> |
| 199 | + <xsl:param name="class" select="@class"/> |
| 200 | + <xsl:param name="xref.elements.pagenum.in.class" select="$xref.elements.pagenum.in.class"/> |
| 201 | + <xsl:param name="xref.target"/> |
| 202 | + <xsl:choose> |
| 203 | + <!-- BEGIN COOKBOOK OVERRIDE --> |
| 204 | + <!-- If there's an xref target, process that to determine whether a pagenum value should be added to the class --> |
| 205 | + <!-- No pagenum class for Recipe targets --> |
| 206 | + <xsl:when test="$xref.target[not(self::h:section[@data-type='sect1' and not(contains(@class, 'orm:non-recipe'))] and ancestor::h:section[@data-type='chapter' and not(contains(@class, 'orm:non-recipe'))])]"> |
| 207 | + <!-- END COOKBOOK OVERRIDE --> |
| 208 | + <xsl:variable name="xref.target.semantic.name"> |
| 209 | + <xsl:call-template name="semantic-name"> |
| 210 | + <xsl:with-param name="node" select="$xref.target"/> |
| 211 | + </xsl:call-template> |
| 212 | + </xsl:variable> |
| 213 | + <xsl:if test="$class != ''"> |
| 214 | + <xsl:value-of select="$class"/> |
| 215 | + </xsl:if> |
| 216 | + <!-- Check if target semantic name is in list of XREF elements containing pagenum --> |
| 217 | + <!-- ToDo: Consider modularizing logic into separate function if needed for reuse elsewhere --> |
| 218 | + <xsl:variable name="space-delimited-pagenum-elements" select="concat(' ', normalize-space($xref.elements.pagenum.in.class), ' ')"/> |
| 219 | + <xsl:variable name="substring-before-target-name" select="substring-before($space-delimited-pagenum-elements, $xref.target.semantic.name)"/> |
| 220 | + <xsl:variable name="substring-after-target-name" select="substring-after($space-delimited-pagenum-elements, $xref.target.semantic.name)"/> |
| 221 | + <!-- Make sure a match is both preceded and followed by a space --> |
| 222 | + <xsl:if test="substring($substring-after-target-name, 1, 1) and |
| 223 | + substring($substring-before-target-name, string-length($substring-before-target-name), 1)"> |
| 224 | + <xsl:if test="$class != ''"><xsl:text> </xsl:text></xsl:if> |
| 225 | + <xsl:text>pagenum</xsl:text> |
| 226 | + </xsl:if> |
| 227 | + </xsl:when> |
| 228 | + <xsl:otherwise> |
| 229 | + <xsl:if test="$class != ''"> |
| 230 | + <xsl:value-of select="$class"/> |
| 231 | + </xsl:if> |
| 232 | + </xsl:otherwise> |
| 233 | + </xsl:choose> |
| 234 | + </xsl:template> |
| 235 | + |
| 236 | + <!-- Custom XREF style for XREFs to recipes --> |
| 237 | + <xsl:template match="h:section[@data-type='sect1' and not(contains(@class, 'orm:non-recipe')) and ancestor::h:section[@data-type='chapter' and not(contains(@class, 'orm:non-recipe'))]]" mode="xref-to"> |
| 238 | + <xsl:param name="referrer"/> |
| 239 | + <xsl:param name="xrefstyle"/> |
| 240 | + <xsl:param name="verbose" select="1"/> |
| 241 | + |
| 242 | + <xsl:apply-templates select="." mode="object.xref.markup"> |
| 243 | + <xsl:with-param name="purpose" select="'xref'"/> |
| 244 | + <!-- BEGIN COOKBOOK OVERRIDE --> |
| 245 | + <xsl:with-param name="xrefstyle" select="$xrefstyle"/> |
| 246 | + <!-- END COOKBOOK OVERRIDE --> |
| 247 | + <xsl:with-param name="referrer" select="$referrer"/> |
| 248 | + <xsl:with-param name="verbose" select="$verbose"/> |
| 249 | + </xsl:apply-templates> |
| 250 | + </xsl:template> |
| 251 | + |
| 252 | +<!-- ***************** TOC HANDLING ***************** --> |
| 253 | +<!-- ************* Overrides tocgen.xsl ************* --> |
| 254 | + |
| 255 | +<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"> |
| 256 | + <xsl:param name="toc.section.depth" select="$toc.section.depth"/> |
| 257 | + <xsl:choose> |
| 258 | + <!-- Don't output entry for section elements at a level that is greater than specified $toc.section.depth --> |
| 259 | + <xsl:when test="self::h:section[contains(@data-type, 'sect') and htmlbook:section-depth(.) != '' and htmlbook:section-depth(.) > $toc.section.depth]"/> |
| 260 | + <!-- Otherwise, go ahead --> |
| 261 | + <xsl:otherwise> |
| 262 | + <xsl:element name="li"> |
| 263 | + <xsl:attribute name="data-type"> |
| 264 | + <xsl:value-of select="@data-type"/> |
| 265 | + </xsl:attribute> |
| 266 | + <a> |
| 267 | + <xsl:attribute name="href"> |
| 268 | + <xsl:call-template name="href.target"> |
| 269 | + <xsl:with-param name="object" select="."/> |
| 270 | + </xsl:call-template> |
| 271 | + </xsl:attribute> |
| 272 | + <!-- BEGIN COOKBOOK OVERRIDE --> |
| 273 | + <xsl:if test="(self::h:section[@data-type='sect1'] and ancestor::h:section[@data-type='chapter'] or self::h:section[@data-type='sect2'] and ancestor::h:section[@data-type='sect1']) and not(ancestor-or-self::h:section[contains(@class, 'orm:non-recipe')])"> |
| 274 | + <xsl:variable name="toc-entry-label"> |
| 275 | + <xsl:apply-templates select="." mode="label.markup"/> |
| 276 | + </xsl:variable> |
| 277 | + <xsl:value-of select="normalize-space($toc-entry-label)"/> |
| 278 | + <xsl:value-of select="$recipe.number.and.title.separator"/> |
| 279 | + </xsl:if> |
| 280 | + <!-- END COOKBOOK OVERRIDE --> |
| 281 | + <xsl:apply-templates select="." mode="title.markup"/> |
| 282 | + </a> |
| 283 | + <!-- Make sure there are descendants that conform to $toc.section.depth restrictions before generating nested TOC <ol> --> |
| 284 | + <xsl:if test="descendant::h:section[not(contains(@data-type, 'sect')) or htmlbook:section-depth(.) <= $toc.section.depth]|descendant::h:div[@data-type='part']"> |
| 285 | + <ol> |
| 286 | + <xsl:apply-templates mode="tocgen"> |
| 287 | + <xsl:with-param name="toc.section.depth" select="$toc.section.depth"/> |
| 288 | + </xsl:apply-templates> |
| 289 | + </ol> |
| 290 | + </xsl:if> |
| 291 | + </xsl:element> |
| 292 | + </xsl:otherwise> |
| 293 | + </xsl:choose> |
| 294 | + </xsl:template> |
| 295 | + |
| 296 | + <xsl:template name="string-replace-all"> |
| 297 | + <xsl:param name="text"/> |
| 298 | + <xsl:param name="replace"/> |
| 299 | + <xsl:param name="by"/> |
| 300 | + <xsl:choose> |
| 301 | + <xsl:when test="contains($text, $replace)"> |
| 302 | + <xsl:value-of select="substring-before($text,$replace)"/> |
| 303 | + <xsl:value-of select="$by"/> |
| 304 | + <xsl:call-template name="string-replace-all"> |
| 305 | + <xsl:with-param name="text" select="substring-after($text,$replace)"/> |
| 306 | + <xsl:with-param name="replace" select="$replace"/> |
| 307 | + <xsl:with-param name="by" select="$by"/> |
| 308 | + </xsl:call-template> |
| 309 | + </xsl:when> |
| 310 | + <xsl:otherwise> |
| 311 | + <xsl:value-of select="$text"/> |
| 312 | + </xsl:otherwise> |
| 313 | + </xsl:choose> |
| 314 | + </xsl:template> |
| 315 | +</xsl:stylesheet> |
| 316 | + |
| 317 | + |
0 commit comments