Grouping between different Xpath in XSLT - xslt

I am having concerns in grouping the nodes from different XPath.
My XML is :-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<rfs>
<rf id="cc_3_case_33">
<ref>P028</ref>
<rcs>
<rc id="cc_3_rc_199_1022">
<kws/>
</rc>
</rcs>
<kws>
<kw>
<name>AAKAB</name>
<type>CC</type>
</kw>
<kw>
<name>Bakery</name>
<type>IS</type>
</kw>
</kws>
</rf>
<rf id="cc_5_case_85">
<ref>P086</ref>
<rcs>
<rc id="cc_5_rc_199_3772">
<kws>
<kw>
<name>AAKSweAB</name>
<type>CC</type>
</kw>
<kw>
<name>Tefac</name>
<type>IS</type>
</kw>
</kws>
</rc>
</rcs>
<kws/>
</rf>
<rf id="cc_3_case_8">
<ref>P013</ref>
<rcs>
<rc id="cc_3_rc_199_1179">
<kws>
<kw>
<name>AAKAB</name>
<type>CC</type>
</kw>
<kw>
<name>AAK Denmark A/S</name>
<type>CC</type>
</kw>
<kw>
<name>Processes</name>
<type>IS</type>
</kw>
</kws>
</rc>
</rcs>
<kws>
<kw>
<name>AAKAB</name>
<type>CC</type>
</kw>
<kw>
<name>Processes</name>
<type>IS</type>
</kw>
</kws>
</rf>
<rf id="cc_3_case_89">
<ref>P085</ref>
<rcs>
<rc id="cc_3_rc_199_4057">
<kws>
<kw>
<name>AAKAB</name>
<type>CC</type>
</kw>
<kw>
<name>PB</name>
<type>IS</type>
</kw>
<kw>
<name>PBM</name>
<type>IS>PB</type>
</kw>
<kw>
<name>Coasun</name>
<type>Product Category</type>
</kw>
<kw>
<name>Akobisc</name>
<type>Product Category>Coasun</type>
</kw>
</kws>
</rc>
</rcs>
<kws>
<kw>
<name>AAKAB</name>
<type>CC</type>
</kw>
</kws>
</rf>
<rf id="cc_8_case_58">
<ref>P055</ref>
<rcs>
<rc id="cc_8_rc_199_2333">
<kws>
<kw>
<name>AAKNetBV</name>
<type>CC</type>
</kw>
<kw>
<name>Special Nutrition</name>
<type>IS</type>
</kw>
</kws>
</rc>
</rcs>
<kws>
<kw>
<name>AAKSweAB</name>
<type>CC</type>
</kw>
<kw>
<name>Special Nutrition</name>
<type>IS</type>
</kw>
</kws>
</rf>
<rf id="cc_5_case_46">
<ref>P041</ref>
<rcs>
<rc id="cc_5_rc_199_1572">
<kws>
<kw>
<name>AAKAB</name>
<type>CC</type>
</kw>
<kw>
<name>AAKSweAB</name>
<type>CC</type>
</kw>
<kw>
<name>PC</name>
<type>IS</type>
</kw>
</kws>
</rc>
<rc id="cc_5_rc_199_2254">
<kws>
<kw>
<name>AAKSweAB</name>
<type>CC</type>
</kw>
<kw>
<name>PC</name>
<type>IS</type>
</kw>
</kws>
</rc>
</rcs>
<kws>
<kw>
<name>AAKSweAB</name>
<type>CC</type>
</kw>
<kw>
<name>PC</name>
<type>IS</type>
</kw>
</kws>
</rf>
<rf id="cc_0_case_76">
<ref>P075</ref>
<rcs>
<rc id="cc_0_rc_199_3186">
<kws/>
</rc>
</rcs>
<kws>
<kw>
<name>AAKKamPL</name>
<type>CC</type>
</kw>
</kws>
</rf>
<rf id="cc_0_case_72">
<ref>P071</ref>
<rcs>
<rc id="cc_0_rc_199_3170">
<kws>
<kw>
<name>CCF</name>
<type>IS</type>
</kw>
</kws>
</rc>
</rcs>
<kws>
<kw>
<name>AAKKamPL</name>
<type>CC</type>
</kw>
</kws>
</rf>
</rfs>
The XSLT I wrote is below which is able to group between nodes of similar Xpath..and if the data doesn't exist in same hierarchy, the values are not coming up.
My XSLT :-
<xsl:template match="/">
<ccwc id="a">
<cccs>
<xsl:choose>
<xsl:when test="rfs/rf/rcs/rc/kws=''">
<xsl:for-each-group select="rfs/rf/kws/kw[type='CC'][1]" group-by="name">
<xsl:variable name="ccName1" select="current-group()[1]/name"/>
<ccc>
<xsl:attribute name="id"><xsl:value-of select="$ccName1"/></xsl:attribute>
<name><xsl:value-of select="$ccName1"/></name>
<cccs>
<xsl:for-each-group select="current-group()/../kw[type='IS']" group-by="name">
<xsl:variable name="ccName2" select="current-group()[1]/name"/>
<ccc>
<xsl:attribute name="id"><xsl:value-of select="concat($ccName1,'_',$ccName2)"/></xsl:attribute>
<name><xsl:value-of select="$ccName2"/></name>
</ccc>
</xsl:for-each-group>
</cccs>
</ccc>
</xsl:for-each-group>
</xsl:when>
<xsl:when test="rfs/rf/rcs/rc/kws!=''">
<xsl:for-each-group select="rfs/rf/rcs/rc/kws/kw[type='CC'][1]" group-by="name">
<xsl:variable name="ccName1" select="current-group()[1]/name"/>
<ccc>
<xsl:attribute name="id"><xsl:value-of select="$ccName1"/></xsl:attribute>
<name><xsl:value-of select="$ccName1"/></name>
<cccs>
<xsl:for-each-group select="current-group()/../kw[type='IS']" group-by="name">
<xsl:variable name="ccName2" select="current-group()[1]/name"/>
<ccc>
<xsl:attribute name="id"><xsl:value-of select="concat($ccName1,'_',$ccName2)"/></xsl:attribute>
<name><xsl:value-of select="$ccName2"/></name>
</ccc>
</xsl:for-each-group>
</cccs>
</ccc>
</xsl:for-each-group>
</xsl:when>
</xsl:choose>
</cccs>
</ccwc>
</xsl:template>
</xsl:transform>
My Output XML is coming -
<ccwc id="a">
<cccs>
<ccc id="AAKAB">
<name>AAKAB</name>
<cccs>
<ccc id="AAKAB_Bakery">
<name>Bakery</name>
</ccc>
<ccc id="AAKAB_Processes">
<name>Processes</name>
</ccc>
</cccs>
</ccc>
<ccc id="AAKSweAB">
<name>AAKSweAB</name>
<cccs>
<ccc id="AAKSweAB_Special Nutrition">
<name>Special Nutrition</name>
</ccc>
<ccc id="AAKSweAB_PC">
<name>PC</name>
</ccc>
</cccs>
</ccc>
<ccc id="AAKKamPL">
<name>AAKKamPL</name>
<cccs/>
</ccc>
</cccs>
</ccwc>
Whereas I want the Output XML as follows:-
<?xml version="1.0" encoding="UTF-8"?>
<ccwc xmlns:aak="http://www.tridan.it/aak" id="aak">
<defaultComplexity>NORMAL</defaultComplexity>
<cccs>
<ccc id="AAKAB">
<name>AAKAB</name>
<cccs>
<ccc id="AAKAB_Bakery">
<name>Bakery</name>
</ccc>
<ccc id="AAKAB_Processes">
<name>Processes</name>
</ccc>
</cccs>
</ccc>
<ccc id="AAKSweAB">
<name>AAKSweAB</name>
<cccs>
<ccc id="AAKSweAB_Special Nutrition">
<name>Special Nutrition</name>
</ccc>
<ccc id="AAKSweAB_PC">
<name>PC</name>
</ccc>
</cccs>
</ccc>
<ccc id="AAKKamPL">
<name>AAKKamPL</name>
<cccs>
<ccc id="AAKKamPL_CCF">
<name>CCF</name>
</ccc>
</cccs>
</ccc>
</cccs>
</ccwc>
i.e , concatenating the CC and IS first occurrence values whether both are in rfs/rf/kws/kw or both in rfs/rf/rcs/rc/kws/kw or one in either of the XPaths.
Any help would be appreciable.Thanks!

Related

XSLT: Repeat input n-times, copy input but change some attributes, depending on passed parameters

I'm a XSLT beginner and need some help (XSLT 1.0).
Basically I need to repeat input given number of times, copying it as-is but changing values of some attributes.
I've seen that the "standard way" of doing this is by using apply-templates and xsl:copy, but the problem is I need to change values of those attributes depending on parameters passed from the "upper level", so I think I need to use call-template, but I just can't figure this out :)
Input:
<Repeater repeatCount="3">
<!-- Only one direct child of Repeater (e.g. Panel or TextBox).
Types of children are unknown but all children have id attribute.
Everything should be copied as-is except for:
1) All Repeater's children id's should be suffixed with row number, e.g.:
LastName becomes LastName_1, LastName_2, and so on...
2) Repeater's direct child (here: MainPanel) top attribute value should be accumulated from previous top+height -->
<Panel id="MainPanel" top="0" left="0" width="160" height="12">
<Panel id="FirstChildPanel" top="0" left="0" width="80" height="12">
<TextBox id="FirstName" top="0" left="0" width="30" height="12" />
<TextBox id="LastName" top="0" left="35" width="30" height="12" />
</Panel>
<Panel id="SecondChildPanel" top="12" left="80" width="80" height="12">
<TextBox id="Address" top="0" left="0" width="70" height="12" />
</Panel>
</Panel>
</Repeater>
Desired output:
<!-- Repeater's content is repeated 3 times.
All ids are suffixed with a row number.
The outermost repeated element (e.g. MainPanel) has increasing top value -->
<Panel id="MainPanel_1" top="0" left="0" width="160" height="12">
<Panel id="FirstChildPanel_1" top="0" left="0" width="80" height="12">
<TextBox id="FirstName_1" top="0" left="0" width="30" height="12" />
<TextBox id="LastName_1" top="0" left="35" width="30" height="12" />
</Panel>
<Panel id="SecondChildPanel_1" top="12" left="80" width="80" height="12">
<TextBox id="Address_1" top="0" left="0" width="70" height="12" />
</Panel>
</Panel>
<Panel id="MainPanel_2" top="12" left="0" width="160" height="12">
<Panel id="FirstChildPanel_2" top="0" left="0" width="80" height="12">
<TextBox id="FirstName_2" top="0" left="0" width="30" height="12" />
<TextBox id="LastName_2" top="0" left="35" width="30" height="12" />
</Panel>
<Panel id="SecondChildPanel_2" top="12" left="80" width="80" height="12">
<TextBox id="Address_2" top="0" left="0" width="70" height="12" />
</Panel>
</Panel>
<Panel id="MainPanel_3" top="24" left="0" width="160" height="12">
<Panel id="FirstChildPanel_3" top="0" left="0" width="80" height="12">
<TextBox id="FirstName_3" top="0" left="0" width="30" height="12" />
<TextBox id="LastName_3" top="0" left="35" width="30" height="12" />
</Panel>
<Panel id="SecondChildPanel_3" top="12" left="80" width="80" height="12">
<TextBox id="Address_3" top="0" left="0" width="70" height="12" />
</Panel>
</Panel>
XSLT (wrong):
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="Repeater">
<xsl:apply-templates />
</xsl:template>
<!-- repeat first direct child of Repeater -->
<xsl:template match="Repeater/*[1]">
<xsl:call-template name="ContentTemplate">
<xsl:with-param name="count" select="../#repeatCount" />
<xsl:with-param name="top" select="#top" />
</xsl:call-template>
</xsl:template>
<xsl:template name="ContentTemplate">
<xsl:param name="index" select="1"></xsl:param>
<xsl:param name="count" select="1"></xsl:param>
<xsl:param name="top"></xsl:param>
<!-- generate suffix for id's from current iteration index -->
<xsl:variable name="rowId" select="concat('_', $index)"></xsl:variable>
<xsl:variable name="calculatedTop">
<xsl:choose>
<xsl:when test="$index = 1">
<xsl:value-of select="$top"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$top + ./*[1]/#height" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- iterate Repeater/#repeatCount times. -->
<xsl:if test="$index <= $count">
<!-- should just be copying Repeater's direct child as-is (unknown element type), but change id and top -->
<Panel id="{concat(#id, $rowId)}"
top="{$calculatedTop}"
left="{#left}"
width="{#width}"
height="{#height}">
<!-- should copy all children as-is, but change their id -->
<xsl:call-template name="CopyTemplate">
<xsl:with-param name="currentNode" select="."></xsl:with-param>
<xsl:with-param name="rowId" select="$rowId"></xsl:with-param>
</xsl:call-template>
</Panel>
<!-- call recursively passing over new row index and calculatedTop -->
<xsl:call-template name="ContentTemplate">
<xsl:with-param name="index" select="$index + 1" />
<xsl:with-param name="count" select="$count" />
<xsl:with-param name="top" select="$calculatedTop" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<!-- this is all wrong :) -->
<xsl:template name="CopyTemplate" match="#*|node()" mode="copy">
<xsl:param name="currentNode"></xsl:param>
<xsl:param name="rowId"></xsl:param>
<xsl:for-each select="$currentNode/node()">
<xsl:choose>
<xsl:when test="name(current()) = 'id'">
<xsl:attribute name="id">
<xsl:value-of select="concat(#id, $rowId)"/>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="current()"/>
</xsl:otherwise>
</xsl:choose>
<xsl:call-template name="CopyTemplate">
<xsl:with-param name="currentNode" select="current()"/>
<xsl:with-param name="rowId" select="$rowId"></xsl:with-param>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
So i got the iteration part, but can't exactly figure out how to do the copy part, so that all id's are suffixed with current row number.
Any help much appreciated :)
I'd suggest you try it this way:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/Repeater">
<root>
<xsl:call-template name="repeat">
<xsl:with-param name="n" select="#repeatCount" />
</xsl:call-template>
</root>
</xsl:template>
<xsl:template name="repeat">
<xsl:param name="n"/>
<xsl:if test="$n > 0">
<!-- recursive call -->
<xsl:call-template name="repeat">
<xsl:with-param name="n" select="$n - 1" />
</xsl:call-template>
<!-- apply templates with current $n -->
<xsl:apply-templates>
<xsl:with-param name="n" select="$n" />
<xsl:with-param name="h" select="*/#height" />
</xsl:apply-templates>
</xsl:if>
</xsl:template>
<!-- identity transform (modified to carry parameters) -->
<xsl:template match="#*|node()">
<xsl:param name="n"/>
<xsl:param name="h"/>
<xsl:copy>
<xsl:apply-templates select="#*|node()">
<xsl:with-param name="n" select="$n" />
<xsl:with-param name="h" select="$h" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<!-- append $n to all ids -->
<xsl:template match="#id">
<xsl:param name="n"/>
<xsl:attribute name="id">
<xsl:value-of select="concat(., '_', $n)"/>
</xsl:attribute>
</xsl:template>
<!-- increase top -->
<xsl:template match="/Repeater/*/#top">
<xsl:param name="n"/>
<xsl:param name="h"/>
<xsl:attribute name="top">
<xsl:value-of select="$h * ($n - 1)"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Here's another option. It's not as clean as Michael's answer if you need to add further transformation, but it's pretty simple.
XML Input
<Repeater repeatCount="3">
<!-- Only one direct child of Repeater (e.g. Panel or TextBox).
Types of children are unknown but all children have id attribute.
Everything should be copied as-is except for:
1) All Repeater's children id's should be suffixed with row number, e.g.:
LastName becomes LastName_1, LastName_2, and so on...
2) Repeater's direct child (here: MainPanel) top attribute value should be accumulated from previous top+height -->
<Panel id="MainPanel" top="0" left="0" width="160" height="12">
<Panel id="FirstChildPanel" top="0" left="0" width="80" height="12">
<TextBox id="FirstName" top="0" left="0" width="30" height="12" />
<TextBox id="LastName" top="0" left="35" width="30" height="12" />
</Panel>
<Panel id="SecondChildPanel" top="12" left="80" width="80" height="12">
<TextBox id="Address" top="0" left="0" width="70" height="12" />
</Panel>
</Panel>
</Repeater>
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="repeat" select="/Repeater/#repeatCount"/>
<xsl:template match="/Repeater">
<results>
<xsl:apply-templates select="*[1]">
<xsl:with-param name="repeatCount" select="1"/>
<xsl:with-param name="topLevel" select="true()"/>
</xsl:apply-templates>
</results>
</xsl:template>
<xsl:template match="*">
<xsl:param name="repeatCount"/>
<xsl:param name="topLevel"/>
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:if test="#id"><!--Override id attribute if it exists.-->
<xsl:attribute name="id">
<xsl:value-of select="concat(#id,'_',$repeatCount)"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="$topLevel and #top"><!--Override top attribute if it exists.-->
<xsl:attribute name="top">
<xsl:value-of select="($repeatCount - 1) * #height"/>
</xsl:attribute>
</xsl:if>
<xsl:apply-templates select="*">
<xsl:with-param name="repeatCount" select="$repeatCount"/>
<xsl:with-param name="topLevel" select="false()"/>
</xsl:apply-templates>
</xsl:copy>
<xsl:if test="$topLevel and $repeat > $repeatCount">
<xsl:apply-templates select=".">
<xsl:with-param name="repeatCount" select="$repeatCount + 1"/>
<xsl:with-param name="topLevel" select="true()"/>
</xsl:apply-templates>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
XML Output
<results>
<Panel id="MainPanel_1" top="0" left="0" width="160" height="12">
<Panel id="FirstChildPanel_1" top="0" left="0" width="80" height="12">
<TextBox id="FirstName_1" top="0" left="0" width="30" height="12"/>
<TextBox id="LastName_1" top="0" left="35" width="30" height="12"/>
</Panel>
<Panel id="SecondChildPanel_1" top="12" left="80" width="80" height="12">
<TextBox id="Address_1" top="0" left="0" width="70" height="12"/>
</Panel>
</Panel>
<Panel id="MainPanel_2" top="12" left="0" width="160" height="12">
<Panel id="FirstChildPanel_2" top="0" left="0" width="80" height="12">
<TextBox id="FirstName_2" top="0" left="0" width="30" height="12"/>
<TextBox id="LastName_2" top="0" left="35" width="30" height="12"/>
</Panel>
<Panel id="SecondChildPanel_2" top="12" left="80" width="80" height="12">
<TextBox id="Address_2" top="0" left="0" width="70" height="12"/>
</Panel>
</Panel>
<Panel id="MainPanel_3" top="24" left="0" width="160" height="12">
<Panel id="FirstChildPanel_3" top="0" left="0" width="80" height="12">
<TextBox id="FirstName_3" top="0" left="0" width="30" height="12"/>
<TextBox id="LastName_3" top="0" left="35" width="30" height="12"/>
</Panel>
<Panel id="SecondChildPanel_3" top="12" left="80" width="80" height="12">
<TextBox id="Address_3" top="0" left="0" width="70" height="12"/>
</Panel>
</Panel>
</results>

XSLT: Suggest for otherwise code, while all 'when' conditions are not met

Please suggest, I am working on MathML code, in 'subsup' element, it should have three elements. But my previous scripts are inserted some space tags after the 'sub' or 'subsup' elements (that is required for internal process). By this existing 'subsup' will have more than three child. However all conditions I wrote, but unable to code the 'Otherwise' code, if none of the 'when' conditions are not met. If space found, then my code will enclose 'space and following or precedings' with one 'mrow', by that ensuring the msubsup having three childs only.
XML:
<article>
<body>
<p>Graphene <inline-formula>
<math>
<mtext>yyyy</mtext>
<msubsup>
<mi>H</mi>
<mn>1</mn>
<mn>2</mn>
</msubsup>
<msubsup>
<mi>A</mi>
<mspace width="0.04"/>
<mn>1</mn>
<mspace width="0.04"/>
<mn>2</mn>
<mspace width="0.04"/>
<mn>2</mn>
</msubsup>
<msubsup>
<mi>B</mi>
<mn>1</mn>
<mspace width="0.04"/>
<mn>2</mn>
<mspace width="0.04"/>
</msubsup>
<msubsup>
<mi>C</mi>
<mn>1</mn>
<mn>7</mn>
<mspace width="0.04"/>
</msubsup>
<msubsup>
<mi>D</mi>
<mn>1</mn>
<mn>2</mn>
<mspace width="0.04"/>
</msubsup>
<msubsup>
<mi>E</mi>
<mspace width="0.04"/>
<mn>1</mn>
<mspace width="0.04"/>
<mn>2</mn>
</msubsup>
</math>
<inline-graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="fdr495521ieqn220a39.gif"/>
</inline-formula>
</p>
</body>
</article>
XSLT:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="msubsup[count(child::*) > 3]">
<xsl:element name="{name()}">
<xsl:for-each select="*"><!--Cheking with each child, grouping mspace with preceded or followed element, ensuring three child with subsup element-->
<xsl:choose>
<xsl:when test="position()=1 and name()='mspace' and not(following-sibling::*[name()='mspace'])"><!--Child 1 having space, then it has to enclose by 'mrow' with immediate following element-->
<xsl:element name="mrow">
<xsl:apply-templates select="self::node()"/>
<xsl:apply-templates select="following-sibling::*[1]"/>
</xsl:element>
<xsl:apply-templates select="following-sibling::*[2]"/>
<xsl:apply-templates select="following-sibling::*[3]"/>
<xsl:apply-templates select="following-sibling::*[4]"/>
<xsl:apply-templates select="following-sibling::*[5]"/>
<xsl:apply-templates select="following-sibling::*[6]"/>
<xsl:apply-templates select="following-sibling::*[7]"/>
</xsl:when>
<xsl:when test="position()=2 and name()='mspace' and not(following-sibling::*[name()='mspace'])"><!--Child 2 having space, then it has to enclose by 'mrow' with immediate preceding element-->
<xsl:element name="mrow">
<xsl:apply-templates select="preceding-sibling::*"/>
<xsl:apply-templates select="self::node()"/>
</xsl:element>
<xsl:apply-templates select="following-sibling::*|node()"/>
</xsl:when>
<xsl:when test="position()=2 and name()='mspace' and following-sibling::*[2][name()='mspace']">
<xsl:element name="mrow">
<xsl:apply-templates select="preceding-sibling::*"/>
<xsl:apply-templates select="self::node()"/>
</xsl:element>
<xsl:element name="mrow">
<xsl:apply-templates select="following-sibling::*[1]"/>
<xsl:apply-templates select="following-sibling::*[2]"/>
</xsl:element>
<xsl:element name="mrow">
<xsl:apply-templates select="following-sibling::*[3]"/>
<xsl:apply-templates select="following-sibling::*[4]"/>
<xsl:apply-templates select="following-sibling::*[5]"/>
<xsl:apply-templates select="following-sibling::*[6]"/>
<xsl:apply-templates select="following-sibling::*[7]"/>
</xsl:element>
</xsl:when>
<xsl:when test="position()=2 and name()='mspace' and following-sibling::*[3][name()='mspace']">
<xsl:element name="mrow">
<xsl:apply-templates select="preceding-sibling::*"/>
<xsl:apply-templates select="self::node()"/>
</xsl:element>
<xsl:apply-templates select="following-sibling::*[1]"/>
<xsl:element name="mrow">
<xsl:apply-templates select="following-sibling::*[2]"/>
<xsl:apply-templates select="following-sibling::*[3]"/>
<xsl:apply-templates select="following-sibling::*[4]"/>
<xsl:apply-templates select="following-sibling::*[5]"/>
<xsl:apply-templates select="following-sibling::*[6]"/>
<xsl:apply-templates select="following-sibling::*[7]"/>
</xsl:element>
</xsl:when>
<xsl:when test="position()=3 and name()='mspace' and not(preceding-sibling::*[name()='mspace'])">
<xsl:apply-templates select="preceding-sibling::*[2]"/>
<xsl:element name="mrow">
<xsl:apply-templates select="preceding-sibling::*[1]"/>
<xsl:apply-templates select="self::node()"/>
</xsl:element>
<xsl:element name="mrow">
<xsl:apply-templates select="following-sibling::*|node()"/>
</xsl:element>
</xsl:when>
<xsl:when test="(position()=4 or position() > 4) and name()='mspace' and not(preceding-sibling::*[name()='mspace'])">
<xsl:apply-templates select="preceding-sibling::*[3]"/>
<xsl:apply-templates select="preceding-sibling::*[2]"/>
<xsl:element name="mrow">
<xsl:apply-templates select="preceding-sibling::*[1]"/>
<xsl:apply-templates select="self::node()"/>
<xsl:apply-templates select="following-sibling::*|node()"/>
</xsl:element>
</xsl:when>
<!--Please suggest, what may be the 'otherwise' code if top all 'when' conditions not met-->
<!--xsl:otherwise>
<xsl:apply-templates select="self::node()"/>
</xsl:otherwise-->
</xsl:choose>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Required code: See my xsl, suggest for 'Otherwise' code. I am using XSL2 version
Required OutPut:
enter code here <?xml version="1.0" encoding="UTF-8"?>
<article>
<body>
<p>Graphene <inline-formula>
<math>
<mtext>yyyy</mtext>
<msubsup>
<mi>H</mi>
<mn>1</mn>
<mn>2</mn>
</msubsup>
<msubsup>
<mrow>
<mi>A</mi>
<mspace width="0.04"/>
</mrow>
<mrow>
<mn>1</mn>
<mspace width="0.04"/>
</mrow>
<mrow>
<mn>2</mn>
<mspace width="0.04"/>
<mspace width="0.04"/>
<mn>2</mn>
</mrow>
</msubsup>
<msubsup>
<mi>B</mi>
<mrow>
<mn>1</mn>
<mspace width="0.04"/>
</mrow>
<mrow>
<mn>2</mn>
<mspace width="0.04"/>
</mrow>
</msubsup>
<msubsup>
<mi>C</mi>
<mn>1</mn>
<mrow>
<mn>7</mn>
<mspace width="0.04"/>
</mrow>
</msubsup>
<msubsup>
<mi>D</mi>
<mn>1</mn>
<mrow>
<mn>2</mn>
<mspace width="0.04"/>
</mrow>
</msubsup>
<msubsup>
<mrow>
<mi>E</mi>
<mspace width="0.04"/>
</mrow>
<mrow>
<mn>1</mn>
<mspace width="0.04"/>
</mrow>
<mrow>
<mn>2</mn>
</mrow>
</msubsup>
</math>
<inline-graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="fdr495521ieqn220a39.gif"/>
</inline-formula>
</p>
</body>
</article>

XSLT combine node when attribute's value are different

During transformation, who I can combine one node into another. for example, when Attributes/Attribute/Type=ComplexAttr then it should go under Attributes/Attribute/Type=Common only.
Below is the sample XML & XSLT that I'm trying to use which is not working. TIA (Thanks in Advance)
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="type" match="Attribute" use="Type"/>
<xsl:template match="/">
<Data Schema="XML A">
<xsl:apply-templates select="XML/Attributes/Attribute">
<xsl:sort select="Type" order="descending"/>
</xsl:apply-templates>
<errorCodes>
<xsl:apply-templates select="XML/Attributes/Attribute"
mode="errors"/>
</errorCodes>
</Data>
</xsl:template>
<xsl:template
match="Attribute[generate-id()=generate-id(key('type', Type)[1])]">
<xsl:if test="Type != 'ComplexAttr'">
<Attributes type="{Type}">
<xsl:if test="Type = 'ComplexAttr'">
<xsl:value-of select="Common"/>
</xsl:if>
<xsl:apply-templates select="../Attribute[Type=current()/Type]" mode="out"/>
</Attributes>
</xsl:if>
</xsl:template>
<xsl:template match="Attribute" mode="out">
<Attr id="{id}" name="{Name}" value="{Value}"/>
</xsl:template>
<xsl:template match="Attribute"/>
<xsl:template match="Attribute" mode="errors"/>
<xsl:template match="Attribute[Value='']" mode="errors">
<errorCode>"value for <xsl:value-of select="Name"/> is missing."</errorCode>
</xsl:template>
<xsl:template match="/Attribute">
<xsl:if test="Type = 'ComplexAttr'">
<Attributes type="Common">
<xsl:apply-templates select="../Attribute[Type=current()/Type]" mode="out"/>
<!--<Attr id="{id}" name="{Name}" value="{Value}"/>-->
</Attributes>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
---- Source XML ----
<?xml version="1.0" encoding="windows-1252"?>
<XML>
<Attributes>
<Attribute>
<id>5</id>
<Name>Buyer ID</Name>
<Type>common</Type>
<Value>Lee</Value>
</Attribute>
<Attribute>
<id>331</id>
<Name>Enviornment</Name>
<Type>common</Type>
<Value>Development</Value>
</Attribute>
<Attribute>
<id>79</id>
<Name>Retail</Name>
<Type>common</Type>
<Value></Value>
</Attribute>
<Attribute>
<id>402</id>
<Name>Gender</Name>
<Type>category</Type>
<Value>Men</Value>
</Attribute>
<Attribute>
<id>1197</id>
<Name>UPC</Name>
<Type>ComplexAttr</Type>
<Value>Testing</Value>
<Path />
</Attribute>
</Attributes>
</XML>
---- Transformed XML output
<?xml version="1.0" encoding="utf-8"?>
<Data Schema="XML A">
<Attributes type="common">
<Attr id="5" name="Buyer ID" value="Lee" />
<Attr id="331" name="Enviornment" value="Development" />
<Attr id="79" name="Retail" value="" />
<Attr id="41" name="PlusShip" value="False" />
<Collection id="" name="test">
<ComplexAttr refId="0">
<MaskValue />
<Attr id="1197" name="UPC" value="Testing" />
</ComplexAttr>
</Collection>
</Attributes>
<Attributes type="category">
<Attr id="402" name="Gender" value="Men" />
<Attr id="433" name="HeelHeight" value="" />
</Attributes>
<errorCodes>
<errorCode>"value for Retail is missing."</errorCode>
</errorCodes>
</Data>
If you want to group Attribute by Type with 'common' and 'ComplexAttr' in the same group, then you need to change the key value expression into something like:
<xsl:key name="type"
match="Attribute"
use="concat(
Type[. != 'ComplexAttr'],
substring(
'common',
1 div (Type = 'ComplexAttr')
)
)"/>
<xsl:template match="Attribute[
generate-id()
= generate-id(
key('type',
concat(
Type[. != 'ComplexAttr'],
substring(
'common',
1 div (Type = 'ComplexAttr')
)
)
)[1]
)
]">
EDIT: And in the group templates applying:
<xsl:apply-templates select="key('type',
concat(
Type[. != 'ComplexAttr'],
substring(
'common',
1 div (Type = 'ComplexAttr')
)
)
)"
mode="out"/>
EDIT: Full example. This stylesheet:
<!DOCTYPE xsl:stylesheet [
<!ENTITY key "concat(Type[. != 'ComplexAttr'],substring('common',1 div (Type = 'ComplexAttr')))">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="type" match="Attribute" use="&key;"/>
<xsl:template match="/">
<Data Schema="XML A">
<xsl:apply-templates
select="XML/Attributes/Attribute[
generate-id() = generate-id(key('type', &key;)[1])
]">
<xsl:sort select="&key;" order="descending"/>
</xsl:apply-templates>
<errorCodes>
<xsl:apply-templates select="XML/Attributes/Attribute"
mode="errors"/>
</errorCodes>
</Data>
</xsl:template>
<xsl:template match="Attribute">
<xsl:variable name="vCurrent-Grouping-Key" select="&key;"/>
<Attributes type="{$vCurrent-Grouping-Key}">
<xsl:apply-templates select="key('type',$vCurrent-Grouping-Key)"
mode="out"/>
</Attributes>
</xsl:template>
<xsl:template match="Attribute" mode="out" name="makeAttr">
<Attr id="{id}" name="{Name}" value="{Value}"/>
</xsl:template>
<xsl:template match="Attribute[Type='ComplexAttr']" mode="out">
<Collection id="" name="test">
<ComplexAttr refId="0">
<MaskValue />
<xsl:call-template name="makeAttr"/>
</ComplexAttr>
</Collection>
</xsl:template>
<xsl:template match="Attribute" mode="errors"/>
<xsl:template match="Attribute[Value='']" mode="errors">
<errorCode>"value for <xsl:value-of select="Name"/> is missing."</errorCode>
</xsl:template>
</xsl:stylesheet>
Output:
<Data Schema="XML A">
<Attributes type="common">
<Attr id="5" name="Buyer ID" value="Lee" />
<Attr id="331" name="Enviornment" value="Development" />
<Attr id="79" name="Retail" value="" />
<Collection id="" name="test">
<ComplexAttr refId="0">
<MaskValue />
<Attr id="1197" name="UPC" value="Testing" />
</ComplexAttr>
</Collection>
</Attributes>
<Attributes type="category">
<Attr id="402" name="Gender" value="Men" />
</Attributes>
<errorCodes>
<errorCode>"value for Retail is missing."</errorCode>
</errorCodes>
</Data>

XSLT to output plain text table

I'm working on an XSL template to convert an XHTML/hResume document to plain text, and I'm having trouble with the table layout (no, not layout tables). At the moment I've got the following, using the excellent Dave Pawson's padding template:
<variable name="newline" select="'
'"/>
<template match="xhtml:table">
<variable name="maxWidth">
<for-each select="xhtml:tr/xhtml:th | xhtml:tr/xhtml:td">
<sort select="string-length(child::text()|child::node())" order="descending" data-type="number"/>
<if test="position() = 1">
<value-of select="string-length(child::text()|child::node())"/>
</if>
</for-each>
</variable>
<for-each select="xhtml:tr">
<for-each select="xhtml:th|xhtml:td">
<variable name="string">
<for-each select="child::text()|child::node()">
<value-of select="."/>
</for-each>
</variable>
<value-of select="$string"/>
<call-template name="append-pad">
<with-param name="length" select="$maxWidth - string-length($string)"/>
</call-template>
<text> </text>
</for-each>
<value-of select="$newline"/>
</for-each>
<value-of select="$newline"/>
</template>
This produces columns of equal width, but I'd like to improve it in a couple ways:
Find and use the max width of each column. For that it's necessary to store a flexible number of values. I can change maxWidth to do this in the simple cases, but how do you handle spanning columns?
Center the contents of spanning columns.
Are there any templates to do something like this?
With a "global" (for every cell in table) $maxWith you could handle colspans like this stylesheet (preserving your own logic):
<stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml">
<output method="text"/>
<variable name="newline" select="'
'"/>
<template match="xhtml:table">
<variable name="maxWidth">
<for-each select="xhtml:tr/xhtml:th | xhtml:tr/xhtml:td">
<sort select="string-length(child::text()|child::node())" order="descending" data-type="number"/>
<if test="position() = 1">
<value-of select="string-length(child::text()|child::node())"/>
</if>
</for-each>
</variable>
<for-each select="xhtml:tr">
<for-each select="xhtml:th|xhtml:td">
<variable name="string">
<for-each select="child::text()|child::node()">
<value-of select="."/>
</for-each>
</variable>
<variable name="padding">
<choose>
<when test="#colspan">
<value-of select="$maxWidth * #colspan + #colspan - 1 - string-length($string)"/>
</when>
<otherwise>
<value-of select="$maxWidth - string-length($string)"/>
</otherwise>
</choose>
</variable>
<value-of select="$string"/>
<call-template name="append-pad">
<with-param name="length" select="$padding"/>
</call-template>
<text> </text>
</for-each>
<value-of select="$newline"/>
</for-each>
<value-of select="$newline"/>
</template>
<template name="append-pad">
<param name="length" select="0"/>
<if test="$length != 0">
<value-of select="' '"/>
<call-template name="append-pad">
<with-param name="length" select="$length - 1"/>
</call-template>
</if>
</template>
</stylesheet>
Input:
<table xmlns="http://www.w3.org/1999/xhtml">
<tr>
<th>First</th>
<th>Second</th>
<th>Third</th>
</tr>
<tr>
<td>One</td>
<td>Two</td>
<td>Three</td>
</tr>
<tr>
<td colspan="2">Uno</td>
<td>Tres</td>
</tr>
</table>
Output:
First Second Third
One Two Three
Uno Tres
EDIT: In order to center the cells with colspan, use this stylesheet (now with my own logic):
<stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml">
<output method="text"/>
<variable name="newline" select="'
'"/>
<template match="xhtml:table">
<apply-templates>
<with-param name="maxWidth">
<for-each select="xhtml:tr/xhtml:th | xhtml:tr/xhtml:td">
<sort select="string-length(.)" order="descending" data-type="number"/>
<if test="position() = 1">
<value-of select="string-length(.)"/>
</if>
</for-each>
</with-param>
</apply-templates>
<value-of select="$newline"/>
</template>
<template match="xhtml:tr">
<param name="maxWidth"/>
<apply-templates>
<with-param name="maxWidth" select="$maxWidth"/>
</apply-templates>
<value-of select="$newline"/>
</template>
<template match="xhtml:th|xhtml:td">
<param name="maxWidth"/>
<variable name="string">
<for-each select="child::text()|child::node()">
<value-of select="."/>
</for-each>
</variable>
<variable name="padding">
<choose>
<when test="#colspan">
<value-of select="($maxWidth * #colspan + #colspan - 1 - string-length($string)) div 2"/>
</when>
<otherwise>
<value-of select="$maxWidth - string-length($string)"/>
</otherwise>
</choose>
</variable>
<if test="#colspan">
<call-template name="append-pad">
<with-param name="length" select="floor($padding)"/>
</call-template>
</if>
<value-of select="$string"/>
<call-template name="append-pad">
<with-param name="length" select="ceiling($padding)"/>
</call-template>
<text> </text>
</template>
<template name="append-pad">
<param name="length" select="0"/>
<if test="$length != 0">
<value-of select="' '"/>
<call-template name="append-pad">
<with-param name="length" select="$length - 1"/>
</call-template>
</if>
</template>
</stylesheet>
Output:
First Second Third
One Two Three
Uno Tres

Creating a menu using xslt for Umbraco

I've created a menu in umbraco using XSLT. The menu is using the usual ul and li elements and I'm displaying only the first level of the menu. The aim is to create a menu that expands to show the sub menu when I click a parent node (in the top level).
I am after the xslt I would need to expose the sub menu when clicked.
I think I would need to make use of ancestor-or-self to detect the current menu and parent menu and display them and also the $currentPage variable.
I have the following xslt:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp " "> ]>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt"
xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:tagsLib="urn:tagsLib" xmlns:urlLib="urn:urlLib"
exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets tagsLib urlLib ">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:param name="currentPage"/>
<xsl:template match="/">
<div id="kb-categories">
<h3>Categories</h3>
<xsl:call-template name="drawNodes">
<xsl:with-param name="parent" select="$currentPage/ancestor-or-self::node [#level=1]"/>
</xsl:call-template>
</div>
</xsl:template>
<xsl:template name="drawNodes">
<xsl:param name="parent"/>
<xsl:if test="(umbraco.library:IsProtected($parent/#id, $parent/#path) = 0 or (umbraco.library:IsProtected($parent/#id, $parent/#path) = 1)) and $parent/#level = 1">
<ul class="kb-menuLevel1" >
<xsl:for-each select="$parent/node [string(./data [#alias='showInMenu']) = 1]">
<li>
<a href="/kb{umbraco.library:NiceUrl(#id)}">
<xsl:value-of select="#nodeName"/>
</a>
<xsl:variable name="level" select="#level" />
<xsl:if test="(count(./node [string(./data [#alias='showInMenu']) = '1']) > 0)">
<xsl:call-template name="drawNodes">
<xsl:with-param name="parent" select="."/>
</xsl:call-template>
</xsl:if>
</li>
</xsl:for-each>
</ul>
</xsl:if>
<xsl:if test="(umbraco.library:IsProtected($parent/#id, $parent/#path) = 0 or (umbraco.library:IsProtected($parent/#id, $parent/#path) = 1)) and $parent/#level > 1">
<ul class="kb-menuLevel{#level}" style="display: none;">
<xsl:for-each select="$parent/node [string(./data [#alias='showInMenu']) = 1]">
<li>
<a href="/kb{umbraco.library:NiceUrl(#id)}">
<xsl:value-of select="#nodeName"/>
</a>
<xsl:variable name="level" select="#level" />
<xsl:if test="(count(./node [string(./data [#alias='showInMenu']) = '1']) > 0)">
<xsl:call-template name="drawNodes">
<xsl:with-param name="parent" select="."/>
</xsl:call-template>
</xsl:if>
</li>
</xsl:for-each>
</ul>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
I suspect this could be improved using apply-templates, but I'm not yet up to speed with that (this being only the second day of my learning xslt).
My menu:
Menu Item 1
Menu Item 2
Menu Item 3
Menu Item 4
when I click on Menu Item 2 I will be taken to the page for menu Item 2 and the submenu will also be displayed:
Menu Item 1
Menu Item 2
-- Menu Item 2.1
-- Menu Item 2.2
Menu Item 3
Menu Item 4
and so on down the nested menu.
Here is some sample xml for the above.
<root>
<node id="1" nodeTypeAlias="kbHomepage" nodeName="Home" level="1">
<data alias="introduction">
<![CDATA[<p>Welcome</p>]]>
</data>
<node id="2" nodeTypeAlias="guide" nodeName="Menu Item 1" level="2">
<data alias="bodyText">
<![CDATA[<p>This is some text</p>]]>
</data>
<data alias="showInMenu">1</data>
<data alias="menuName">Menu Item 1</data>
</node>
<node id="3" nodeTypeAlias="guide" nodeName="Menu Item 2" level="2">
<data alias="bodyText">
<![CDATA[<p>This is some text</p>]]>
</data>
<data alias="showInMenu">1</data>
<data alias="menuName">Menu Item 2</data>
<node id="4" nodeTypeAlias="guide" nodeName="Menu Item 2.1" level="3">
<data alias="bodyText">
<![CDATA[<p>Some Text</p>]]>
</data>
<data alias="showInMenu">1</data>
<data alias="menuName">Menu Item 2.1</data>
</node>
<node id="5" nodeTypeAlias="guide" nodeName="Menu Item 2.2" level="3">
<data alias="bodyText">
<![CDATA[<p>Some Text</p>]]>
</data>
<data alias="showInMenu">1</data>
<data alias="menuName">Menu Item 2.2</data>
<node id="6" nodeTypeAlias="guide" nodeName="Item 2.2.1 Guide" level="4">
<data alias="bodyText">
<![CDATA[<p>Some Text</p>]]>
</data>
<data alias="showInMenu">0</data>
<data alias="menuName"></data>
</node>
</node>
</node>
<node id="8" nodeTypeAlias="guide" nodeName="Menu Item 3" level="2">
<data alias="bodyText">
<![CDATA[<p>This is some text</p>]]>
</data>
<data alias="showInMenu">1</data>
<data alias="menuName">Menu Item 3</data>
</node>
<node id="9" nodeTypeAlias="guide" nodeName="Menu Item 4" level="2">
<data alias="bodyText">
<![CDATA[<p>This is some text</p>]]>
</data>
<data alias="showInMenu">1</data>
<data alias="menuName">Menu Item 4</data>
</node>
</node>
<node id="7" nodeTypeAlias="someAlias" nodeName="Some Other Page" level="1">
<data alias="bodyText">
<![CDATA[<p>This is some text</p>]]>
</data>
</node>
</root>
edit: the following almost does what I need :
<xsl:variable name="visibleChidren" select="node[data[#alias='showInMenu'] = 1 and (#level = 2 or descendant-or-self::*[generate-id($currentPage) = generate-id(.)] or preceding-sibling::*[generate-id($currentPage) = generate-id(.)] or following-sibling::*[generate-id($currentPage) = generate-id(.)])]" />
I just need to also include the direct children from the current page.
I tried (with my very limited knowledge about Umbraco) to clean up your code a bit and remove the redundancy. It looks as though it would work with the XML sample you provided, but I cannot really test it against Umbraco.
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp " "> ]>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt"
xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:tagsLib="urn:tagsLib" xmlns:urlLib="urn:urlLib"
exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets tagsLib urlLib ">
<xsl:output method="xml" omit-xml-declaration="yes" encoding="utf-8" />
<xsl:param name="currentPage" />
<xsl:template match="/">
<div id="kb-categories">
<h3>Categories</h3>
<xsl:apply-templates mode="list" select="/root/node[#nodeTypeAlias='kbHomepage']" />
</div>
</xsl:template>
<!-- matches anything with <node> children and creates an <ul> -->
<xsl:template match="*[node]" mode="list">
<!-- prepare a list of all visible children -->
<xsl:variable name="visibleChidren" select="node[
data[#alias='showInMenu'] = 1
and (
not(umbraco.library:IsProtected(#id, #path))
or umbraco.library:IsLoggedOn()
)
]" />
<!-- prepare a CSS class for the "selected path" -->
<xsl:variable name="display">
<xsl:if test=".//node[generate-id() = generate-id($currentPage)]">
<xsl:text>visible</xsl:text>
</xsl:if>
</xsl:variable>
<xsl:if test="$visibleChidren">
<ul class="menu kb-menuLevel{$visibleChidren[1]/#level} {$display}">
<xsl:apply-templates mode="item" select="$visibleChidren" />
</ul>
</xsl:if>
</xsl:template>
<!-- matches <node> elements and turns them into list items -->
<xsl:template match="node" mode="item">
<li>
<xsl:if test="generate-id() = generate-id($currentPage)">
<xsl:attribute name="class">selected</xsl:attribute>
</xsl:if>
<a href="/kb{{umbraco.library:NiceUrl(#id)}}">
<xsl:value-of select="#nodeName" />
</a>
<!-- if there are any child nodes, render them -->
<xsl:if test="node">
<xsl:apply-templates mode="list" select="." />
</xsl:if>
</li>
</xsl:template>
</xsl:stylesheet>
Gives you the following. Note that I have escaped the attribute value template in <a href... - remove the double curlies above to enable them again:
<div id="kb-categories">
<h3>Categories</h3>
<ul class="menu kb-menuLevel2 visible">
<li>
Menu Item 1
</li>
<li>
Menu Item 2
<ul class="menu kb-menuLevel3 visible">
<li class="selected">
Menu Item 2.1
</li>
<li>
Menu Item 2.2
</li>
</ul>
</li>
<li>
Menu Item 3
</li>
<li>
Menu Item 4
</li>
</ul>
</div>
Now you could do in CSS:
ul.menu {
display: hidden;
}
ul.menu.visible {
display: block;
}
ul.menu li.selected {
font-weight: bold;
}
Does that help you?
I figured out what I need to do what I want. The key line being:
<xsl:variable name="visibleChidren" select="node[data[#alias='showInMenu'] = 1 and (#level = 2 or descendant-or-self::*[generate-id($currentPage) = generate-id(.)] or preceding-sibling::*[generate-id($currentPage) = generate-id(.)] or following-sibling::*[generate-id($currentPage) = generate-id(.)] or parent::*[generate-id($currentPage) = generate-id(.)])]" />
From the entire xslt:
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp " "> ]>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt"
xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:tagsLib="urn:tagsLib" xmlns:urlLib="urn:urlLib"
exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets tagsLib urlLib ">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:param name="currentPage"/>
<xsl:variable name="currentLevel" select="$currentPage/#level" />
<xsl:template match="/">
<div id="kb-categories">
<h3>Categories</h3>
<xsl:apply-templates mode="list" select="$currentPage/ancestor-or-self::node [#nodeTypeAlias = 'kbHomepage']" />
</div>
</xsl:template>
<!-- matches anything with <node> children and makes a list out of them -->
<xsl:template match="node" mode="list">
<!-- select only sub-nodes that have 'showInMenu' = 1 -->
<xsl:variable name="visibleChidren" select="node[data[#alias='showInMenu'] = 1 and (#level = 2 or descendant-or-self::*[generate-id($currentPage) = generate-id(.)] or preceding-sibling::*[generate-id($currentPage) = generate-id(.)] or following-sibling::*[generate-id($currentPage) = generate-id(.)] or parent::*[generate-id($currentPage) = generate-id(.)])]" />
<xsl:if test="$visibleChidren">
<ul>
<xsl:apply-templates mode="item" select="$visibleChidren" />
</ul>
</xsl:if>
</xsl:template>
<xsl:template match="node" mode="item">
<li>
<a href="/kb{umbraco.library:NiceUrl(#id)}">
<xsl:value-of select="#nodeName"/>
</a>
<xsl:apply-templates mode="list" select="." />
</li>
</xsl:template>
</xsl:stylesheet>
Or you could solve yourself a lot of hacking about in XSLT and use the following navigation package from our.umbraco.org
This I think does everything you need and no need to get your hands dirty in the murky world of XSLT.
http://our.umbraco.org/projects/cogworks---flexible-navigation