| | <xsl:text>\mainmatter
</xsl:text> |
| | |
| | - <!-- |
| | - <xsl:apply-templates select="*[name()!='book']" /> |
| | - --> |
| | - <!-- |
| | - | "Muenchian Method" as described in |
| | - | XSLT Cookbook, by Sal Mangano, O'Reilly, pg195 |
| | - | for sorting tagged recipes into "chapters" |
| | - +--> |
| | - <xsl:for-each select="recipe[count(. | key('recipe-by-category',tags/tag[1])[1]) = 1]"> |
| | - <xsl:variable name="current-grouping-key" select="tags/tag[1]"/> |
| | - <xsl:if test="$current-grouping-key != ''"> |
| | - <xsl:text>
\recipecategory{</xsl:text> |
| | - <xsl:apply-templates select="$current-grouping-key" mode="escape-capitalize"/> |
| | - <xsl:text>}

</xsl:text> |
| | - <xsl:variable name="current-group" |
| | - select="key('recipe-by-category', $current-grouping-key)" /> |
| | - <xsl:for-each select="$current-group"> |
| | - <xsl:apply-templates select="." /> |
| | - </xsl:for-each> |
| | - </xsl:if> |
| | - </xsl:for-each> |
| | - |
| | - <!-- |
| | - | All untagged recipes to go into a "Recipes" chapter |
| | - +--> |
| | - <xsl:if test="recipe[tags/tag = '' or not(tags/tag)]"> |
| | - <xsl:text>
\recipecategory{Recipes}

</xsl:text> |
| | - <xsl:apply-templates select="recipe[tags/tag = '' or not(tags/tag)]" /> |
| | - </xsl:if> |
| | - |
| | - <xsl:text>\backmatter
</xsl:text> |
| | - <xsl:text>\renewcommand{\clearforchapter}{\newpage}
</xsl:text> |
| | - <xsl:if test="$ingredient-index"> |
| | - <xsl:text>\renewcommand{\indexname}{Ingredients in Alphabetic Order}
</xsl:text> |
| | - <xsl:text>\printindex[flatingred]
</xsl:text> |
| | - <xsl:text>\renewcommand{\indexname}{Ingredients by Categories}
</xsl:text> |
| | - <xsl:text>\reduceindexindent
</xsl:text> |
| | - <xsl:text>\printindex[catingred]
</xsl:text> |
| | - </xsl:if> |
| | - <xsl:text>\end{document}
</xsl:text> |
| | -</xsl:template> |
| | - |
| | -<!-- |
| | - | Meta information about the book. |
| | - +--> |
| | -<xsl:template match="book"> |
| | - <xsl:text>\title{</xsl:text> |
| | - <xsl:apply-templates select="title" mode="escape-capitalize" /> |
| | - <xsl:text>}
</xsl:text> |
| | - <xsl:text>\date{</xsl:text> |
| | - <xsl:apply-templates select="created" mode="escape-capitalize" /> |
| | - <xsl:text>}
</xsl:text> |
| | - <xsl:text>\author{</xsl:text> |
| | - <xsl:apply-templates select="author" mode="escape-capitalize" /> |
| | - <xsl:text>}
</xsl:text> |
| | - <xsl:apply-templates select="photo" mode="book" /> |
| | -</xsl:template> |
| | - |
| | -<!-- |
| | - | Transforms the recipe from XML into LaTeX. |
| | - +--> |
| | -<xsl:template match="recipe"> |
| | - <!-- |
| | - | If a facing full-page photo is used, check whether |
| | - | a filler photo (advert) is required to make up |
| | - | the page number. |
| | - +--> |
| | - <xsl:if test="description/photo[@use='one-page' or @use='two-page']"> |
| | - <xsl:text>\checkfiller
</xsl:text> |
| | - </xsl:if> |
| | - <xsl:text>\begin{recipe}{</xsl:text> |
| | - <xsl:apply-templates select="description/title" /> |
| | - <xsl:text>}
</xsl:text> |
| | - <xsl:apply-templates select="description/photo[@use='recipe-inset']" mode="recipe" /> |
| | - <xsl:apply-templates /> |
| | - <xsl:apply-templates select="description/photo[@use='one-page' or @use='two-page']" mode="recipe" /> |
| | - <xsl:text>\end{recipe}

</xsl:text> |
| | -</xsl:template> |
| | - |
| | - |
| | -<xsl:template match="photo[@use='front-cover']" mode="book"> |
| | - <xsl:apply-templates select="uri" mode="book" /> |
| | -</xsl:template> |
| | - |
| | -<xsl:template match="photo[@use='recipe-inset']" mode="recipe"> |
| | - <xsl:apply-templates select="uri" mode="recipe-inset" /> |
| | -</xsl:template> |
| | - |
| | -<xsl:template match="photo[@use='one-page']" mode="recipe"> |
| | - <xsl:apply-templates select="uri" mode="one-page" /> |
| | -</xsl:template> |
| | - |
| | -<xsl:template match="photo[@use='two-page']" mode="recipe"> |
| | - <xsl:apply-templates select="uri" mode="two-page" /> |
| | -</xsl:template> |
| | - |
| | -<xsl:template match="uri" mode="book"> |
| | - <xsl:if test="$book-front-cover"> |
| | - <xsl:text>\frontcoverphoto{</xsl:text> |
| | - <xsl:apply-templates /> |
| | - <xsl:text>}
</xsl:text> |
| | - </xsl:if> |
| | -</xsl:template> |
| | - |
| | -<xsl:template match="uri" mode="recipe-inset"> |
| | - <xsl:text>\insetphoto{</xsl:text> |
| | - <xsl:apply-templates /> |
| | - <xsl:text>}
</xsl:text> |
| | -</xsl:template> |
| | - |
| | -<xsl:template match="uri" mode="one-page"> |
| | - <xsl:text>\fullpagephoto{</xsl:text> |
| | - <xsl:apply-templates /> |
| | - <xsl:text>}
</xsl:text> |
| | -</xsl:template> |
| | - |
| | -<xsl:template match="uri" mode="two-page"> |
| | - <xsl:text>\twopagespreadphoto{</xsl:text> |
| | - <xsl:apply-templates /> |
| | - <xsl:text>}
</xsl:text> |
| | -</xsl:template> |
| | - |
| | - |
| | -<xsl:template match="description/title"> |
| | - <xsl:apply-templates mode="escape-capitalize" /> |
| | -</xsl:template> |
| | - |
| | -<!-- Pre-heated oven temperature, not to be confused with ingredient prep. --> |
| | -<xsl:template match="preparation"> |
| | - <xsl:apply-templates /> |
| | -</xsl:template> |
| | - |
| | -<xsl:template match="oven"> |
| | - <xsl:text>\oven{</xsl:text> |
| | - <xsl:value-of select="@temperature" /> |
| | - <xsl:text>\,</xsl:text> |
| | - <xsl:choose> |
| | - <xsl:when test="@unit='C' or @unit='F'"> |
| | - <xsl:text>\textdegree </xsl:text> |
| | - </xsl:when> |
| | - </xsl:choose> |
| | - <xsl:value-of select="@unit" /> |
| | - <xsl:text>}

</xsl:text> |
| | -</xsl:template> |
| | - |
| | -<xsl:template match="equipment[descendant::text()]"> |
| | - <xsl:text>\begin{equipment}
</xsl:text> |
| | - <!-- Select non-empty child nodes. --> |
| | - <xsl:for-each select="*[child::*]"> |
| | - <xsl:text> \item[</xsl:text> |
| | - <xsl:call-template name="escape-capitalize"> |
| | - <xsl:with-param name="ec" select="name()" /> |
| | - </xsl:call-template> |
| | - <xsl:text>] </xsl:text> |
| | - <xsl:call-template name="escape-capitalize"> |
| | - <xsl:with-param name="ec" select="." /> |
| | - </xsl:call-template> |
| | - <xsl:text>
</xsl:text> |
| | - </xsl:for-each> |
| | - <xsl:text>\end{equipment}

</xsl:text> |
| | -</xsl:template> |
| | - |
| | -<!-- |
| | - | Puts the value of all labels (ingredients and directions) in brackets. |
| | - +--> |
| | -<xsl:template match="@label"> |
| | - <xsl:text>[</xsl:text> |
| | - <xsl:call-template name="escape-capitalize"> |
| | - <xsl:with-param name="ec" select="." /> |
| | - </xsl:call-template> |
| | - <xsl:text>]</xsl:text> |
| | - <xsl:text>
</xsl:text> |
| | -</xsl:template> |
| | - |
| | -<xsl:template match="ingredients[parent::recipe]"> |
| | - <xsl:text>\begin{ingredients}
</xsl:text> |
| | - <xsl:apply-templates select="@label" /> |
| | - <xsl:apply-templates select="ingredient" /> |
| | - <xsl:text>\end{ingredients}

</xsl:text> |
| | - |
| | - <!-- |
| | - | If non-empty conditions exist, insert a preparation section. |
| | - +--> |
| | - <xsl:if test="ingredient[@condition != '']"> |
| | - <xsl:text>\begin{preparation}
</xsl:text> |
| | - <xsl:apply-templates select="ingredient[@condition != '']" mode="prep" /> |
| | - <xsl:text>\end{preparation}
</xsl:text> |
| | - </xsl:if> |
| | -</xsl:template> |
| | - |
| | -<xsl:template match="ingredient"> |
| | - <xsl:text> \ingred{</xsl:text> |
| | - |
| | - <xsl:call-template name="utf-fraction"> |
| | - <xsl:with-param name="quantity" select="@min-quantity" /> |
| | - </xsl:call-template> |
| | - |
| | - <xsl:if test="@max-quantity"> |
| | - <xsl:text>--</xsl:text> |
| | - |
| | - <xsl:call-template name="utf-fraction"> |
| | - <xsl:with-param name="quantity" select="@max-quantity" /> |
| | - </xsl:call-template> |
| | - </xsl:if> |
| | - <xsl:text>}{</xsl:text> |
| | - <xsl:value-of select="@unit" /> |
| | - <xsl:text>}{</xsl:text> |
| | - <xsl:apply-templates select="key('ingredient-id', @id)/@name" /> |
| | - <xsl:text>}%
</xsl:text> |
| | - |
| | - <!-- Put the ingredients in a categorized list. --> |
| | - <xsl:if test="key('ingredient-id',@id)/categories/category"> |
| | - <xsl:for-each select="key('ingredient-id',@id)/categories/category"> |
| | - <xsl:text> \index[catingred]{</xsl:text> |
| | - <xsl:call-template name="split"> |
| | - <xsl:with-param name="pText" select="@name" /> |
| | - <xsl:with-param name="pToken" select="','" /> |
| | - <xsl:with-param name="pSubst" select="'!'" /> |
| | - </xsl:call-template> |
| | - <xsl:text>!</xsl:text> |
| | - <xsl:apply-templates select="../../@name" /> |
| | - <xsl:text>}%
</xsl:text> |
| | - </xsl:for-each> |
| | - </xsl:if> |
| | - |
| | - <!-- Put the ingredients in a flat list. --> |
| | - <xsl:text> \index[flatingred]{</xsl:text> |
| | - <xsl:apply-templates select="key('ingredient-id', @id)/@name" /> |
| | - <xsl:text>!</xsl:text> |
| | - <xsl:apply-templates select="ancestor::recipe/description/title" /> |
| | - <xsl:text>}%
</xsl:text> |
| | -</xsl:template> |
| | - |
| | -<!-- |
| | - | Extracts preparation instructions from ingredients. |
| | - +--> |
| | -<xsl:template match="ingredient[@condition != '']" mode="prep"> |
| | - <xsl:for-each select="."> |
| | - <xsl:text>\item </xsl:text> |
| | - <xsl:apply-templates select="@condition" mode="prep" /> |
| | - <xsl:text> the </xsl:text> |
| | - <xsl:apply-templates select="key('ingredient-id', @id)/@name" /> |
| | - <xsl:text>.
</xsl:text> |
| | - </xsl:for-each> |
| | -</xsl:template> |
| | - |
| | -<!-- |
| | - | Splits the ingredient preconditions using the Oxford serial comma: |
| | - | http://www.mhonarc.org/archive/html/xsl-list/2008-06/msg00401.html |
| | - | by Ronnie Royston |
| | - +--> |
| | -<xsl:template match="@condition" mode="prep"> |
| | - <xsl:variable name="actions" select="str:tokenize( ., ',' )" /> |
| | - <xsl:variable name="tokens" select="count( $actions )" /> |
| | - |
| | - <xsl:variable name="condition"> |
| | - <xsl:for-each select="$actions"> |
| | - <xsl:choose> |
| | - <xsl:when test="$tokens > 2"> |
| | - <xsl:choose> |
| | - <xsl:when test="position()=1"> |
| | - <xsl:value-of select="concat(' ', ., ',')"/> |
| | - </xsl:when> |
| | - <xsl:when test="position()=last()"> |
| | - <xsl:value-of select="concat(' and ', .)"/> |
| | - </xsl:when> |
| | - <xsl:otherwise> |
| | - <xsl:value-of select="concat(' ', .,',')"/> |
| | - </xsl:otherwise> |
| | - </xsl:choose> |
| | - </xsl:when> |
| | - <xsl:when test="$tokens = 2"> |
| | - <xsl:choose> |
| | - <xsl:when test="position()=1"> |
| | - <xsl:value-of select="concat(' ', .,' and')"/> |
| | - </xsl:when> |
| | - <xsl:otherwise> |
| | - <xsl:value-of select="concat(' ', .)"/> |
| | - </xsl:otherwise> |
| | - </xsl:choose> |
| | - </xsl:when> |
| | - <xsl:otherwise> |
| | - <xsl:value-of select="concat(' ', .)"/> |
| | - </xsl:otherwise> |
| | - </xsl:choose> |
| | - </xsl:for-each> |
| | - </xsl:variable> |
| | - |
| | - <xsl:call-template name="escape"> |
| | - <xsl:with-param |
| | - name="e" select="string:capitalize(normalize-space($condition))" /> |
| | - </xsl:call-template> |
| | -</xsl:template> |
| | - |
| | -<!-- |
| | - | Splits a delimited string by substitution text. |
| | - +--> |
| | -<xsl:template name="split"> |
| | - <xsl:param name="pText"/> |
| | - <xsl:param name="pToken"/> |
| | - <xsl:param name="pSubst" /> |
| | - |
| | - <xsl:choose> |
| | - <!-- End of recursion. --> |
| | - <xsl:when test="string-length($pText) = 0" /> |
| | - |
| | - <!-- While there are more tokens... --> |
| | - <xsl:when test="contains($pText, $pToken)"> |
| | - <xsl:value-of select="substring-before($pText, $pToken)"/> |
| | - <xsl:value-of select="$pSubst"/> |
| | - |
| | - <xsl:call-template name="split"> |
| | - <xsl:with-param name="pText" select="substring-after($pText, $pToken)"/> |
| | - <xsl:with-param name="pToken" select="$pToken"/> |
| | - <xsl:with-param name="pSubst" select="$pSubst" /> |
| | - </xsl:call-template> |
| | - </xsl:when> |
| | - <xsl:otherwise> |
| | - <!-- The text had no tokens, so display it without parsing. --> |
| | - <xsl:value-of select="$pText"/> |
| | - </xsl:otherwise> |
| | - </xsl:choose> |
| | -</xsl:template> |
| | - |
| | -<!-- |
| | - | Splits a space-delimited string, wrapping values in an "id" element. |
| | - +--> |
| | -<xsl:template name="output-tokens"> |
| | - <xsl:param name="list" /> |
| | - |
| | - <xsl:variable name="newlist" select="concat(normalize-space($list), ' ')" /> |
| | - <xsl:variable name="first" select="substring-before($newlist, ' ')" /> |
| | - <xsl:variable name="remaining" select="substring-after($newlist, ' ')" /> |
| | - <id> |
| | - <xsl:value-of select="$first" /> |
| | - </id> |
| | - <xsl:if test="$remaining"> |
| | - <xsl:call-template name="output-tokens"> |
| | - <xsl:with-param name="list" select="$remaining" /> |
| | - </xsl:call-template> |
| | - </xsl:if> |
| | -</xsl:template> |
| | - |
| | -<xsl:template match="directions[1]"> |
| | - <xsl:text>\startinstructions

</xsl:text> |
| | -</xsl:template> |
| | - |
| | -<xsl:template match="directions[descendant::text()]"> |
| | - <xsl:text>\begin{instructions}
</xsl:text> |
| | - <xsl:apply-templates select="@label" /> |
| | - <xsl:apply-templates select="step" /> |
| | - <xsl:text>\end{instructions}

</xsl:text> |
| | -</xsl:template> |
| | - |
| | -<xsl:template match="step"> |
| | - <xsl:text> \item </xsl:text> |
| | - <xsl:value-of select="normalize-space(string:capitalize(@action))" /> |
| | - <xsl:text> </xsl:text> |
| | - <xsl:apply-templates mode="escape" /> |
| | - <xsl:text>.
</xsl:text> |
| | -</xsl:template> |
| | - |
| | -<!-- |
| | - | Provides output escaping around user-defined text. This ensures that |
| | - | users cannot embed malicious LaTeX code inside their recipes. |
| | - | |
| | - | All text that needs to be escaped ultimately is transformed through this |
| | - | template. |
| | - | |
| | - | @param e - The string to escape. |
| | - | @see http://stackoverflow.com/a/2627303/59087 |
| | - | @see http://tex.stackexchange.com/q/97448/2148 |
| | - +--> |
| | -<xsl:template name="escape"> |
| | - <xsl:param name="e" /> |
| | - |
| | - <xsl:value-of select="$e" /> |
| | + <!-- |
| | + | "Muenchian Method" as described in |
| | + | XSLT Cookbook, by Sal Mangano, O'Reilly, pg195 |
| | + | for sorting tagged recipes into "chapters" |
| | + +--> |
| | + <xsl:for-each select="recipe[count(. | key('recipe-by-category',tags/tag[1])[1]) = 1]"> |
| | + <xsl:variable name="current-grouping-key" select="tags/tag[1]"/> |
| | + <xsl:if test="$current-grouping-key != ''"> |
| | + <xsl:text>
\recipecategory{</xsl:text> |
| | + <xsl:apply-templates select="$current-grouping-key" mode="escape-capitalize"/> |
| | + <xsl:text>}

</xsl:text> |
| | + <xsl:variable name="current-group" |
| | + select="key('recipe-by-category', $current-grouping-key)" /> |
| | + <xsl:for-each select="$current-group"> |
| | + <xsl:apply-templates select="." /> |
| | + </xsl:for-each> |
| | + </xsl:if> |
| | + </xsl:for-each> |
| | + |
| | + <!-- |
| | + | All untagged recipes to go into a "Recipes" chapter. |
| | + +--> |
| | + <xsl:if test="recipe[tags/tag = '' or not(tags/tag)]"> |
| | + <xsl:text>
\recipecategory{Recipes}

</xsl:text> |
| | + <xsl:apply-templates select="recipe[tags/tag = '' or not(tags/tag)]" /> |
| | + </xsl:if> |
| | + |
| | + <xsl:text>\backmatter
</xsl:text> |
| | + <xsl:text>\renewcommand{\clearforchapter}{\newpage}
</xsl:text> |
| | + <xsl:if test="$ingredient-index"> |
| | + <xsl:text>\renewcommand{\indexname}{Ingredients in Alphabetic Order}
</xsl:text> |
| | + <xsl:text>\printindex[flatingred]
</xsl:text> |
| | + <xsl:text>\renewcommand{\indexname}{Ingredients by Categories}
</xsl:text> |
| | + <xsl:text>\reduceindexindent
</xsl:text> |
| | + <xsl:text>\printindex[catingred]
</xsl:text> |
| | + </xsl:if> |
| | + <xsl:text>\end{document}
</xsl:text> |
| | +</xsl:template> |
| | + |
| | +<!-- |
| | + | Meta information about the book. |
| | + +--> |
| | +<xsl:template match="book"> |
| | + <xsl:text>\title{</xsl:text> |
| | + <xsl:apply-templates select="title" mode="escape-capitalize" /> |
| | + <xsl:text>}
</xsl:text> |
| | + <xsl:text>\date{</xsl:text> |
| | + <xsl:apply-templates select="created" mode="escape-capitalize" /> |
| | + <xsl:text>}
</xsl:text> |
| | + <xsl:text>\author{</xsl:text> |
| | + <xsl:apply-templates select="author" mode="escape-capitalize" /> |
| | + <xsl:text>}
</xsl:text> |
| | + <xsl:apply-templates select="photo" mode="book" /> |
| | +</xsl:template> |
| | + |
| | +<!-- |
| | + | Transforms the recipe from XML into LaTeX. |
| | + +--> |
| | +<xsl:template match="recipe"> |
| | + <!-- |
| | + | If a facing full-page photo is used, check whether |
| | + | a filler photo (advert) is required to make up |
| | + | the page number. |
| | + +--> |
| | + <xsl:if test="description/photo[@use='one-page' or @use='two-page']"> |
| | + <xsl:text>\checkfiller
</xsl:text> |
| | + </xsl:if> |
| | + <xsl:text>\begin{recipe}{</xsl:text> |
| | + <xsl:apply-templates select="description/title" /> |
| | + <xsl:text>}
</xsl:text> |
| | + <xsl:apply-templates select="description/photo[@use='recipe-inset']" mode="recipe" /> |
| | + <xsl:apply-templates /> |
| | + <xsl:apply-templates select="description/photo[@use='one-page' or @use='two-page']" mode="recipe" /> |
| | + <xsl:text>\end{recipe}

</xsl:text> |
| | +</xsl:template> |
| | + |
| | +<xsl:template match="photo[@use='front-cover']" mode="book"> |
| | + <xsl:apply-templates select="uri" mode="book" /> |
| | +</xsl:template> |
| | + |
| | +<xsl:template match="photo[@use='recipe-inset']" mode="recipe"> |
| | + <xsl:apply-templates select="uri" mode="recipe-inset" /> |
| | +</xsl:template> |
| | + |
| | +<xsl:template match="photo[@use='one-page']" mode="recipe"> |
| | + <xsl:apply-templates select="uri" mode="one-page" /> |
| | +</xsl:template> |
| | + |
| | +<xsl:template match="photo[@use='two-page']" mode="recipe"> |
| | + <xsl:apply-templates select="uri" mode="two-page" /> |
| | +</xsl:template> |
| | + |
| | +<xsl:template match="uri" mode="book"> |
| | + <xsl:if test="$book-front-cover"> |
| | + <xsl:text>\frontcoverphoto{</xsl:text> |
| | + <xsl:apply-templates /> |
| | + <xsl:text>}
</xsl:text> |
| | + </xsl:if> |
| | +</xsl:template> |
| | + |
| | +<xsl:template match="uri" mode="recipe-inset"> |
| | + <xsl:text>\insetphoto{</xsl:text> |
| | + <xsl:apply-templates /> |
| | + <xsl:text>}
</xsl:text> |
| | +</xsl:template> |
| | + |
| | +<xsl:template match="uri" mode="one-page"> |
| | + <xsl:text>\fullpagephoto{</xsl:text> |
| | + <xsl:apply-templates /> |
| | + <xsl:text>}
</xsl:text> |
| | +</xsl:template> |
| | + |
| | +<xsl:template match="uri" mode="two-page"> |
| | + <xsl:text>\twopagespreadphoto{</xsl:text> |
| | + <xsl:apply-templates /> |
| | + <xsl:text>}
</xsl:text> |
| | +</xsl:template> |
| | + |
| | + |
| | +<xsl:template match="description/title"> |
| | + <xsl:apply-templates mode="escape-capitalize" /> |
| | +</xsl:template> |
| | + |
| | +<!-- Pre-heated oven temperature, not to be confused with ingredient prep. --> |
| | +<xsl:template match="preparation"> |
| | + <xsl:apply-templates /> |
| | +</xsl:template> |
| | + |
| | +<xsl:template match="oven"> |
| | + <xsl:text>\oven{</xsl:text> |
| | + <xsl:value-of select="@temperature" /> |
| | + <xsl:text>\,</xsl:text> |
| | + <xsl:choose> |
| | + <xsl:when test="@unit='C' or @unit='F'"> |
| | + <xsl:text>\textdegree </xsl:text> |
| | + </xsl:when> |
| | + </xsl:choose> |
| | + <xsl:value-of select="@unit" /> |
| | + <xsl:text>}

</xsl:text> |
| | +</xsl:template> |
| | + |
| | +<xsl:template match="equipment[descendant::text()]"> |
| | + <xsl:text>\begin{equipment}
</xsl:text> |
| | + <!-- Select non-empty child nodes. --> |
| | + <xsl:for-each select="*[child::*]"> |
| | + <xsl:text> \item[</xsl:text> |
| | + <xsl:call-template name="escape-capitalize"> |
| | + <xsl:with-param name="ec" select="name()" /> |
| | + </xsl:call-template> |
| | + <xsl:text>] </xsl:text> |
| | + <xsl:call-template name="escape-capitalize"> |
| | + <xsl:with-param name="ec" select="." /> |
| | + </xsl:call-template> |
| | + <xsl:text>
</xsl:text> |
| | + </xsl:for-each> |
| | + <xsl:text>\end{equipment}

</xsl:text> |
| | +</xsl:template> |
| | + |
| | +<!-- |
| | + | Puts the value of all labels (ingredients and directions) in brackets. |
| | + +--> |
| | +<xsl:template match="@label"> |
| | + <xsl:text>[</xsl:text> |
| | + <xsl:call-template name="escape-capitalize"> |
| | + <xsl:with-param name="ec" select="." /> |
| | + </xsl:call-template> |
| | + <xsl:text>]</xsl:text> |
| | + <xsl:text>
</xsl:text> |
| | +</xsl:template> |
| | + |
| | +<xsl:template match="ingredients[parent::recipe]"> |
| | + <xsl:text>\begin{ingredients}
</xsl:text> |
| | + <xsl:apply-templates select="@label" /> |
| | + <xsl:apply-templates select="ingredient" /> |
| | + <xsl:text>\end{ingredients}

</xsl:text> |
| | + |
| | + <!-- |
| | + | If non-empty conditions exist, insert a preparation section. |
| | + +--> |
| | + <xsl:if test="ingredient[@condition != '']"> |
| | + <xsl:text>\begin{preparation}
</xsl:text> |
| | + <xsl:apply-templates select="ingredient[@condition != '']" mode="prep" /> |
| | + <xsl:text>\end{preparation}
</xsl:text> |
| | + </xsl:if> |
| | +</xsl:template> |
| | + |
| | +<xsl:template match="ingredient"> |
| | + <xsl:text> \ingred{</xsl:text> |
| | + |
| | + <xsl:call-template name="utf-fraction"> |
| | + <xsl:with-param name="quantity" select="@min-quantity" /> |
| | + </xsl:call-template> |
| | + |
| | + <xsl:if test="@max-quantity"> |
| | + <xsl:text>--</xsl:text> |
| | + |
| | + <xsl:call-template name="utf-fraction"> |
| | + <xsl:with-param name="quantity" select="@max-quantity" /> |
| | + </xsl:call-template> |
| | + </xsl:if> |
| | + <xsl:text>}{</xsl:text> |
| | + <xsl:value-of select="@unit" /> |
| | + <xsl:text>}{</xsl:text> |
| | + <xsl:apply-templates select="key('ingredient-id', @id)/@name" /> |
| | + <xsl:text>}%
</xsl:text> |
| | + |
| | + <!-- Put the ingredients in a categorized list. --> |
| | + <xsl:if test="key('ingredient-id',@id)/categories/category"> |
| | + <xsl:for-each select="key('ingredient-id',@id)/categories/category"> |
| | + <xsl:text> \index[catingred]{</xsl:text> |
| | + <xsl:call-template name="replace"> |
| | + <xsl:with-param name="pText" select="@name" /> |
| | + <xsl:with-param name="pToken" select="','" /> |
| | + <xsl:with-param name="pSubst" select="'!'" /> |
| | + </xsl:call-template> |
| | + <xsl:text>!</xsl:text> |
| | + <xsl:apply-templates select="../../@name" /> |
| | + <xsl:text>}%
</xsl:text> |
| | + </xsl:for-each> |
| | + </xsl:if> |
| | + |
| | + <!-- Put the ingredients in a flat list. --> |
| | + <xsl:text> \index[flatingred]{</xsl:text> |
| | + <xsl:apply-templates select="key('ingredient-id', @id)/@name" /> |
| | + <xsl:text>!</xsl:text> |
| | + <xsl:apply-templates select="ancestor::recipe/description/title" /> |
| | + <xsl:text>}%
</xsl:text> |
| | +</xsl:template> |
| | + |
| | +<!-- |
| | + | Extracts preparation instructions from ingredients. |
| | + +--> |
| | +<xsl:template match="ingredient[@condition != '']" mode="prep"> |
| | + <xsl:for-each select="."> |
| | + <xsl:text>\item </xsl:text> |
| | + <xsl:apply-templates select="@condition" mode="prep" /> |
| | + <xsl:text> the </xsl:text> |
| | + <xsl:apply-templates select="key('ingredient-id', @id)/@name" /> |
| | + <xsl:text>.
</xsl:text> |
| | + </xsl:for-each> |
| | +</xsl:template> |
| | + |
| | +<!-- |
| | + | Splits the ingredient preconditions using the Oxford serial comma: |
| | + | http://www.mhonarc.org/archive/html/xsl-list/2008-06/msg00401.html |
| | + | by Ronnie Royston |
| | + +--> |
| | +<xsl:template match="@condition" mode="prep"> |
| | + <xsl:variable name="actions" select="str:tokenize( ., ',' )" /> |
| | + <xsl:variable name="tokens" select="count( $actions )" /> |
| | + |
| | + <xsl:variable name="condition"> |
| | + <xsl:for-each select="$actions"> |
| | + <xsl:choose> |
| | + <xsl:when test="$tokens > 2"> |
| | + <xsl:choose> |
| | + <xsl:when test="position()=1"> |
| | + <xsl:value-of select="concat(' ', ., ',')"/> |
| | + </xsl:when> |
| | + <xsl:when test="position()=last()"> |
| | + <xsl:value-of select="concat(' and ', .)"/> |
| | + </xsl:when> |
| | + <xsl:otherwise> |
| | + <xsl:value-of select="concat(' ', .,',')"/> |
| | + </xsl:otherwise> |
| | + </xsl:choose> |
| | + </xsl:when> |
| | + <xsl:when test="$tokens = 2"> |
| | + <xsl:choose> |
| | + <xsl:when test="position()=1"> |
| | + <xsl:value-of select="concat(' ', .,' and')"/> |
| | + </xsl:when> |
| | + <xsl:otherwise> |
| | + <xsl:value-of select="concat(' ', .)"/> |
| | + </xsl:otherwise> |
| | + </xsl:choose> |
| | + </xsl:when> |
| | + <xsl:otherwise> |
| | + <xsl:value-of select="concat(' ', .)"/> |
| | + </xsl:otherwise> |
| | + </xsl:choose> |
| | + </xsl:for-each> |
| | + </xsl:variable> |
| | + |
| | + <xsl:call-template name="escape"> |
| | + <xsl:with-param |
| | + name="e" select="string:capitalize(normalize-space($condition))" /> |
| | + </xsl:call-template> |
| | +</xsl:template> |
| | + |
| | +<!-- |
| | + | Splits a delimited string by substitution text. |
| | + +--> |
| | +<xsl:template name="replace"> |
| | + <xsl:param name="pText"/> |
| | + <xsl:param name="pToken"/> |
| | + <xsl:param name="pSubst" /> |
| | + |
| | + <xsl:choose> |
| | + <!-- End of recursion. --> |
| | + <xsl:when test="string-length($pText) = 0" /> |
| | + |
| | + <!-- While there are more tokens... --> |
| | + <xsl:when test="contains($pText, $pToken)"> |
| | + <xsl:value-of select="substring-before($pText, $pToken)"/> |
| | + <xsl:value-of select="$pSubst"/> |
| | + |
| | + <xsl:call-template name="replace"> |
| | + <xsl:with-param name="pText" select="substring-after($pText, $pToken)"/> |
| | + <xsl:with-param name="pToken" select="$pToken"/> |
| | + <xsl:with-param name="pSubst" select="$pSubst" /> |
| | + </xsl:call-template> |
| | + </xsl:when> |
| | + <xsl:otherwise> |
| | + <!-- The text had no tokens, so display it without parsing. --> |
| | + <xsl:value-of select="$pText"/> |
| | + </xsl:otherwise> |
| | + </xsl:choose> |
| | +</xsl:template> |
| | + |
| | +<!-- |
| | + | Splits a space-delimited string, wrapping values in an "id" element. |
| | + +--> |
| | +<xsl:template name="output-tokens"> |
| | + <xsl:param name="list" /> |
| | + |
| | + <xsl:variable name="newlist" select="concat(normalize-space($list), ' ')" /> |
| | + <xsl:variable name="first" select="substring-before($newlist, ' ')" /> |
| | + <xsl:variable name="remaining" select="substring-after($newlist, ' ')" /> |
| | + <id> |
| | + <xsl:value-of select="$first" /> |
| | + </id> |
| | + <xsl:if test="$remaining"> |
| | + <xsl:call-template name="output-tokens"> |
| | + <xsl:with-param name="list" select="$remaining" /> |
| | + </xsl:call-template> |
| | + </xsl:if> |
| | +</xsl:template> |
| | + |
| | +<xsl:template match="directions[1]"> |
| | + <xsl:text>\startinstructions

</xsl:text> |
| | +</xsl:template> |
| | + |
| | +<xsl:template match="directions[descendant::text()]"> |
| | + <xsl:text>\begin{instructions}
</xsl:text> |
| | + <xsl:apply-templates select="@label" /> |
| | + <xsl:apply-templates select="step" /> |
| | + <xsl:text>\end{instructions}

</xsl:text> |
| | +</xsl:template> |
| | + |
| | +<xsl:template match="step"> |
| | + <xsl:text> \item </xsl:text> |
| | + <xsl:value-of select="normalize-space(string:capitalize(@action))" /> |
| | + <xsl:text> </xsl:text> |
| | + <xsl:apply-templates mode="escape" /> |
| | + <xsl:text>.
</xsl:text> |
| | +</xsl:template> |
| | + |
| | +<!-- |
| | + | Provides output escaping around user-defined text. This ensures that |
| | + | users cannot embed malicious LaTeX code inside their recipes. |
| | + | |
| | + | All text that must be escaped is transformed through this template. |
| | + | |
| | + | @param e - The string to escape. |
| | + | @see http://stackoverflow.com/a/2627303/59087 |
| | + | @see http://tex.stackexchange.com/q/97448/2148 |
| | + +--> |
| | +<xsl:template name="escape"> |
| | + <xsl:param name="e" /> |
| | + |
| | + <xsl:call-template name="replace"> |
| | + <xsl:with-param name="pText" select="$e" /> |
| | + <xsl:with-param name="pToken" select="'&'" /> |
| | + <xsl:with-param name="pSubst" select="'\&'" /> |
| | + </xsl:call-template> |
| | + |
| | + <!--xsl:value-of select="$e" /--> |
| | </xsl:template> |
| | |