xslt self match child node but with different html div id - xslt

I am transforming xml to html using saxon.
I have xml like this:
<abc>
level-1
<abc>
level-2
<abc>
level-x
</abc>
</abc>
</abc>
in html i want to do something like this:
<div class="abc-1">
level-1
<div class="abc-2">
level-2
<div class="abc-3">
level-3
</div>
</div>
</div>
Now in my xslt i want to do somthing like following, so i can have different class names in child node with same node match, but not sure what could be good and optimized way to do this in xslt.
<xsl:template match = "abc">
<div class="abc<x>">
<xsl:apply-templates />
</div>
</xsl:template>

<xsl:template match="abc">
<div class="abc-{count(ancestor-or-self::abc)}">
<xsl:apply-templates />
</div>
</xsl:template>

Related

Is it possible to add an attribute to literal result elements?

I have a big set of XSLT templates that generate <div> elements with assorted content. Matches look like these:
<xsl:template match="block[#name = 'block_blah']">
<div>
blah
<div>foooo</div>
</div>
</xsl:template>
<xsl:template match="block[#name = 'block2']">
<div>
<div>xyz</div>
abc
</div>
</xsl:template>
I need to add an attribute to every first level <div>. So the output will become:
<div data-blockname="block_blah">
blah
<div>foooo</div>
</div>
<div data-blockname="block2">
<div>xyz</div>
abc
</div>
Do I have to insert data-blockname="{#name}" manually in every case? Or is there a way to inject it globally?
There is no way to do this "globally" as you say, but there are ways to restructure your XSLT and avoid repetition, like this:
<xsl:template match="block[#name]">
<div name="{#name}">
<xsl:apply-templates select="." mode="content" />
</div>
<xsl:template>
<xsl:template match="block[#name = 'block_blah']" mode="content">
blah
<div>foooo</div>
</xsl:template>
<xsl:template match="block[#name = 'block2']" mode="content">
<div>xyz</div>
abc
</xsl:template>

XSLT/Diazo: create element for each pair of elements

I'm using Diazo/XSLT for theming a Plone website. On the homepage, Plone gives me the following structure:
<dl>
<dt>
The news item title
</dt>
<dd>
The content of the news item
</dd>
(and so on for the following ones)
</dl>
Which I want to transform into something like that:
<div id="news_items">
<div>
<h2>The news item title</h2>
The content of the news item.
<a class="readmore" href="<the HREF taken from the dt/a tag)">Read more</a>
</div>
(and so on for the following items)
</div>
I'm not really familiar with XSLT and Diazo (and more used to rewrite some pieces of existing themes) but I tried a few solutions.
The first one was to do this in two times. First look for each "dd" tags, creates the structure and after that update by parsing all "dt" tags:
<copy css:theme="#news_items">
<xsl:for-each css:select="#content-core dl dd">
<xsl:element name="div">
<xsl:element name="h2">
</xsl:element>
<xsl:copy-of select="./*" />
<xsl:element name="a">
<xsl:attribute name="class">readmore</xsl:attribute>
Read more
</xsl:element>
</xsl:element>
</xsl:for-each>
</copy>
It creates the structure correctly, but I don't know how to write the second part. The idea would be something like that:
<xsl:for-each css:select="#content-core dl dt">
<!-- Copy the link in the 'dd' tag into the h2 tag, based on the position -->
<copy css:theme="#news_item div:nth-child(position()) h2" css:select="a" />
<!-- Copy the a tag's href in the 'Read more' tag -->
<copy css:theme="#news_item div:nth-child(position()) a.readmore">
<xsl:attribute name="class">
<xsl:value-of select="#class" />
</xsl:attribute>
</copy>
</xsl:for-each>
I know it does not make that much sense, but I hope you get the idea of it:
I look into each "dd" tag
I find, based on the position in the loop, the 'h2' tag created in the previous step and I copy the link inside the "dd" tag.
I find (based on the position again) the "a.readmore" tag and copy the href.
The second solution I've been thinking about is a bit dirtier (and does not work either). The idea is to create the content in one step:
<xsl:for-each css:select="#content-core dl > *">
<xsl:if test="name = dt">
<!-- That the dt tag, we start the 'div' tag and add the h2 tag -->
</xsl:if>
<xsl:if test="name = dt">
<!-- That the dt tag, we copy the content and close the 'div' tag -->
</xsl:if>
</xsl:foreach>
But I really don't like the solution (and I don't see how to create the 'Read more' link).
Do you think the first solution can be done ? (especially the second step to populate the "h2" tag and add the "href" attribute on the "Read more" link).
Is there a way better solution available ?
I'd prefer to use only Diazo, but if it's not doable I can directly override the view in Plone (which would be simpler I think, at least I know how to do that)
Thanks for your help.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<div id="news_items">
<xsl:apply-templates select="dt"/>
</div>
</xsl:template>
<xsl:template match="dt">
<div>
<h2><xsl:copy-of select="a"/></h2>
<xsl:apply-templates select="following-sibling::dd[1]"/>
<a class="readmore" href="{a/#href}">Read more</a>
</div>
</xsl:template>
</xsl:stylesheet>
when applied on the following XML document (with the same structure as the provided one but with more items):
<dl>
<dt>
The news item1 title
</dt>
<dd>
The content of the news item1
</dd>
<dt>
The news item2 title
</dt>
<dd>
The content of the news item2
</dd>
</dl>
produces the wanted, correct result:
<div id="news_items">
<div>
<h2>
The news item1 title
</h2>
The content of the news item1
<a class="readmore" href="someUrl1">Read more</a>
</div>
<div>
<h2>
The news item2 title
</h2>
The content of the news item2
<a class="readmore" href="someUrl2">Read more</a>
</div>
</div>

XSLT replace tag by generated one

I'm quite new to XSLT.
This is the problem I'm trying to solve for hours now:
I auto-generate a table of contents for a xml document which works great so far. However I'd like to replace a placeholder tag in my source xml with that just generated toc code.
So the output should include the whole document with replaced placeholder-toc-tag with the auto-generated toc xml.
This is what I've tried:
Let's say I have my placeholderTag anywhere in the document and want to replace this/those. I thought that I could loop through all nodes by node() and check if the node name equals my placeholder tag:
<xsl:template match="node()">
<xsl:choose>
<xsl:when test="divGen">
<!-- apply other template to generate toc-->
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="node()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
However the if statement won't match like this.
edit:
Ok, here's the source document (TEI coded - TEI namespace removed):
<TEI>
<teiHeader>
<fileDesc>
<titleStmt>
<title>Title</title>
</titleStmt>
<publicationStmt>
<p>Publication information</p>
</publicationStmt>
<sourceDesc>
<p>Information about the source</p>
</sourceDesc>
</fileDesc>
</teiHeader>
<text>
<front>
<titlePage>
<byline>title page details</byline>
</titlePage>
</front>
<body>
<divGen type="toc"/>
<div type="part">
<div type="section">
<head>heading1</head>
</div>
<div type="section">
<head>heading2</head>
</div>
</div>
<div type="part">
<div type="section">
<head>heading3</head>
</div>
<div type="section">
<head>heading4</head>
</div>
<div type="section">
<head>heading5</head>
</div>
</div>
</body>
<back> </back>
</text>
I'd like to auto-generate the toc from the head values and replace the divGen tag by the auto-produced toc code. However please notice that the divGen tag can be anywhere in the document but not outside of body.
Any ideas?
Chris
Good question, +1.
Here is a complete transformation (with mock TOC generation to be replaced by real one) that shows how to do this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vTOC">
<xsl:apply-templates mode="TOC"/>
<mockTOC>
<xsl:comment>The real TOC generated here</xsl:comment>
</mockTOC>
</xsl:variable>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="divGen[#type='toc']">
<xsl:copy-of select="$vTOC"/>
</xsl:template>
<xsl:template match="text()" mode="TOC"/>
</xsl:stylesheet>
when this transformation is applied on the provided XML document:
<TEI>
<teiHeader>
<fileDesc>
<titleStmt>
<title>Title</title>
</titleStmt>
<publicationStmt>
<p>Publication information</p>
</publicationStmt>
<sourceDesc>
<p>Information about the source</p>
</sourceDesc>
</fileDesc>
</teiHeader>
<text>
<front>
<titlePage>
<byline>title page details</byline>
</titlePage>
</front>
<body>
<divGen type="toc"/>
<div type="part">
<div type="section">
<head>heading1</head>
</div>
<div type="section">
<head>heading2</head>
</div>
</div>
<div type="part">
<div type="section">
<head>heading3</head>
</div>
<div type="section">
<head>heading4</head>
</div>
<div type="section">
<head>heading5</head>
</div>
</div>
</body>
<back>
</back>
</text>
</TEI>
the correct, wanted output is produced, in which any occurences of <divGen type="toc"/> are replaced by the generated TOC:
<TEI>
<teiHeader>
<fileDesc>
<titleStmt>
<title>Title</title>
</titleStmt>
<publicationStmt>
<p>Publication information</p>
</publicationStmt>
<sourceDesc>
<p>Information about the source</p>
</sourceDesc>
</fileDesc>
</teiHeader>
<text>
<front>
<titlePage>
<byline>title page details</byline>
</titlePage>
</front>
<body>
<mockTOC><!--The real TOC generated here--></mockTOC>
<div type="part">
<div type="section">
<head>heading1</head>
</div>
<div type="section">
<head>heading2</head>
</div>
</div>
<div type="part">
<div type="section">
<head>heading3</head>
</div>
<div type="section">
<head>heading4</head>
</div>
<div type="section">
<head>heading5</head>
</div>
</div>
</body>
<back/>
</text>
</TEI>
Explanation: Use of modes to pre-generate the TOC in a variable, then overriding the identity rule for any TOC placeholder.
Im guessing somewhere u have a template like
<xsl:template match="/">
<xsl:apply-templates select="*"/>
</xsl:template>
then u want the template to only match your placeholderTag
then the others will default to your other nodes!
<xsl:template match="placeholderTag">
<!-- applying generate toc thing-->
</xsl:template>
<xsl:template match="node()">
<xsl:copy-of select="node()"/>
</xsl:template>

XSLT - How to evaluate a single node in multiple places

I am building an image rotator in XSLT that requires the following markup:
<div class="wrapper">
<div class="overlay"></div>
<div id="slider" class="slider">
[IMAGE FROM NODE A GOES HERE]
[IMAGE FROM NODE B GOES HERE]
...
</div>
<div id="htmlcaption" class="html-caption">
[CAPTION FOR NODE A GOES HERE]
[CAPTION FOR NODE B GOES HERE]
...
</div>
</div>
I need help constructing the XSLT so that Node A would get evaluated inside #slider then get re-evaluated in #htmlcaption, then Node B, and so on.
Any help would be greatly appreciated.
Thanks!
First, it is perfectly possible to evaluate a source element multiple times. Just use the same selector.
For example, considering the following XML:
<images>
<node id="a" image="foo.png" caption="foo" />
<node id="b" image="bar.png" caption="bar" />
</images>
This XSLT will repeatedly output stuff from the first node:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="images">
<div id="images">
<img><xsl:value-of select="node[#id='a']/#image"/></img>
<img><xsl:value-of select="node[#id='a']/#caption"/></img>
<img><xsl:value-of select="node[#id='a']/#image"/></img>
<img><xsl:value-of select="node[#id='a']/#caption"/></img>
</div>
</xsl:template>
Output:
<div id="images">
<img>foo.png</img>
<img>foo</img>
<img>foo.png</img>
<img>foo</img>
</div>
However it looks like what you really want is to loop over a bunch of nodes containing image and caption.
You could use a for-each loop to avoid selecting the nodes by name:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="images">
<div id="images">
<xsl:for-each select="node">
<img><xsl:value-of select="#image"/></img>
</xsl:for-each>
</div>
<div id="captions">
<xsl:for-each select="node">
<div><xsl:value-of select="#caption"/></div>
</xsl:for-each>
</div>
</xsl:template>
</xsl:stylesheet>
Which will produce:
<div id="images">
<img>foo.png</img>
<img>bar.png</img>
</div>
<div id="captions">
<div>foo</div>
<div>bar</div>
</div>

Using XSLT to process an HTML page and move a DIV from one place to another

I have a HTML page (fully valid) that is post-processed by XSLT.
Let's say the relevant section of code looks like this:
<div id="content"> ... </div>
...
<div id="announcement"> ... </div>
The XSLT needs to transform it to look like this:
<div id="content"> <div id="announcement"> ... </div> ... </div>
Any ideas? I'm stuck.
Edit: Indicated that <div id="content"> and <div id="announcement"> are separated by other code.
If the announcement div directly follows the content div, use:
<xsl:template match="div[#id='content' and following-sibling::div[1][#id='announcement']">
<!-- copy the content div and its attributes -->
<xsl:copy>
<xsl:copy-of select="#*" />
<!-- now make a copy of the directly following announcement div -->
<xsl:copy-of select="following-sibling::div[1][#id='announcement']" />
<!-- process the rest of the contents -->
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<!-- the empty template mutes an announcement div that follows a content div -->
<xsl:template match="div[#id='announcement' and preceding-sibling::div[1][#id='content']" />
The above is specific enough not to touch any other divs that might be in your document. If your situation allows, you can make it simpler/less specific to increase readability.