XSLT - loop, count, position () with variables

I apologize in advance - there are a lot of newbies here and is struggling after reading almost all XSLT threads. So I desperately need some aspirins and your leadership!

I have three input parameters / variables that need to be processed using xslt version 1.0.

<!-- Variables in the XSL --> <xsl:variable name="tw">125</xsl:variable> <xsl:variable name="rows">4</xsl:variable> <xsl:variable name="cols">6</xsl:variable> 

I want to get the calculated values (pixels) in the " background-position " HTML, as shown below: -

 <div style="background-position:-0px -0px;"><img src="images/thumbs/1.jpg" alt="one" /></div> <div style="background-position:-125px -0px;"><img src="images/thumbs/2.jpg" alt="two" /></div> <div style="background-position:-250px -0px;"><img src="images/thumbs/3.jpg" alt="three" /></div> <div style="background-position:-375px -0px;"><img src="images/thumbs/4.jpg" alt="four" /></div> <div style="background-position:-500px -0px;"><img src="images/thumbs/5.jpg" alt="five" /></div> <div style="background-position:-625px -0px;"><img src="images/thumbs/6.jpg" alt="six" /></div> <div style="background-position:-0px -125px;"><img src="images/thumbs/7.jpg" alt="seven" /></div> <div style="background-position:-125px -125px;"><img src="images/thumbs/8.jpg" alt="eight" /></div> <div style="background-position:-250px -125px;"><img src="images/thumbs/9.jpg" alt="nine" /></div> <div style="background-position:-375px -125px;"><img src="images/thumbs/10.jpg" alt="ten" /></div> <div style="background-position:-500px -125px;"><img src="images/thumbs/11.jpg" alt="11" /></div> <div style="background-position:-625px -125px;"><img src="MIB/images/thumbs/12.jpg" alt="12" /></div> <div style="background-position:-0px -250px;"><img src="images/thumbs/13.jpg" alt="13" /></div> <div style="background-position:-125px -250px;"><img src="/mages/thumbs/14.jpg" alt="14" /></div> <div style="background-position:-250px -250px;"><img src="images/thumbs/15.jpg" alt="15" /></div> <div style="background-position:-375px -250px;"><img src="images/thumbs/16.jpg" alt="16" /></div> <div style="background-position:-500px -250px;"><img src="images/thumbs/17.jpg" alt="17" /></div> <div style="background-position:-625px -250px;"><img src="images/thumbs/18.jpg" alt="18" /></div> <div style="background-position:-0px -375px;"><img src="images/thumbs/19.jpg" alt="19" /></div> <div style="background-position:-125px -375px;"><img src="images/thumbs/20.jpg" alt="20" /></div> <div style="background-position:-250px -375px;"><img src="images/thumbs/21.jpg" alt="21" /></div> <div style="background-position:-375px -375px;"><img src="images/thumbs/22.jpg" alt="22" /></div> <div style="background-position:-500px -375px;"><img src="images/thumbs/23.jpg" alt="23" /></div> <div style="background-position:-625px -375px;"><img src="images/thumbs/24.jpg" alt="24" /></div> 

I understand that I need to go in cycles (somehow) and save the count (somehow), and having seen numerous examples of loop and counting, I expected the process to be direct, but then I read about position() and number and so many other things that my head rotates. I don’t know how and where to position the count loop in the for-each statement or even if for-each is the best solution. None of my weak attempts have created a well-formed XSL, so you can see that I am not getting quick access.

So here, hoping that one of the gurus here can help me start the process and help in my understanding of xslt.

Image data comes from an XML file, and I can process img src and alt without problems, so this is just pixel calculations, loop, count, etc. This is problem

 <xsl:for-each select="DATASET/ITEM"> <div style="background-position:-{rowpos}px -{colpos}px;"><img src="{thumbnailimage}" alt="{imagealttext}" /></div> </xsl:for-each> <!-- rowpos and colpos are the calculated values that are generated from whatever loop and count process is used --> 

So for clarification:

The calculation. There are 3 variables that will result in 6 calculated values. TW = 125. The width is used for background positions, therefore, depending on the row / column, the calculated values ​​are given in the example - for example. tw-tw, tw, tw * 2, tw * 3, tw * 4 and tw * 5 (a total of 6 calculations). The number of rows and the number of columns determines how and where each thumbnail is positioned using the calculated pixel values, which in the above case are 0px, -125px, -250px, -375px, -500px and -675px.

Further expansion of calculations: - The positions of rows and columns are determined by the width of the thumbnail. The position of each sketch is determined by the number of rows and the number of columns. A grid of 3 columnar columns with thumbs of 150 pixels wide will require 9 thumbnails, therefore 9 sets of px values, but only 2 unique calculations, for example, will be required. tw * 2 and tw * 3 as shown below: -

 <div style="background-position: -0px -0px;" /><img src="image 1.jpg" alt="Alt text 1" /></div> <div style="background-position: -125px -0px;" /><img src="image 2.jpg" alt="Alt text 2" /></div> <div style="background-position: -250px -0px;" /><img src="image 3.jpg" alt="Alt text 3" /></div> <div style="background-position: -375px -0px;" /><img src="image 4.jpg" alt="Alt text 4" /></div> <div style="background-position: -500px -0px;" /><img src="image 5.jpg" alt="Alt text 5" /></div> <div style="background-position: -0px -125px;" /><img src="image 6.jpg" alt="Alt text 6" /></div> <div style="background-position: -125px -125px;" /><img src="image 7.jpg" alt="Alt text 7" /></div> <div style="background-position: -250px -125px;" /><img src="image 8.jpg" alt="Alt text 8" /></div> <div style="background-position: -375px -125px;" /><img src="image 9.jpg" alt="Alt text 9" /></div> <div style="background-position: -500px -125px;" /><img src="image 10.jpg" alt="Alt text 10" /></div> <div style="background-position: -0px -250px;" /><img src="image 11.jpg" alt="Alt text 11" /></div> <div style="background-position: -125px -250px;" /><img src="image 12.jpg" alt="Alt text 12" /></div> <div style="background-position: -250px -250px;" /><img src="image 13.jpg" alt="Alt text 13" /></div> <div style="background-position: -375px -250px;" /><img src="image 14.jpg" alt="Alt text 14" /></div> <div style="background-position: -500px -250px;" /><img src="image 15.jpg" alt="Alt text 15" /></div> <div style="background-position: -0px -375px;" /><img src="image 16.jpg" alt="Alt text 16" /></div> <div style="background-position: -125px -375px;" /><img src="image 17.jpg" alt="Alt text 17" /></div> <div style="background-position: -250px -375px;" /><img src="image 18.jpg" alt="Alt text 18" /></div> <div style="background-position: -375px -375px;" /><img src="image 19.jpg" alt="Alt text 19" /></div> <div style="background-position: -500px -375px;" /><img src="image 20.jpg" alt="Alt text 20" /></div> 

XML file The src and alt text image comes from an XML file: -

 <DATASET> <ITEM> <THUMBNAILIMAGE>image1.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 1</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image2.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 2</IMAGEALTTEXT> </ITEM> ...... <ITEM> <THUMBNAILIMAGE>image20.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 20</IMAGEALTTEXT> </ITEM> </DATASET> 
+4
source share
3 answers

Here is a complete, shorter and simpler (only one recursive template), recursive, XSLT 1.0 conversion :

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:param name="pTw" select="125"/> <xsl:param name="pNumRows" select="4"/> <xsl:param name="pNumCols" select="5"/> <xsl:variable name="vImageItems" select="/*/*"/> <xsl:template match="/"> <xsl:call-template name="generateDiv"/> </xsl:template> <xsl:template name="generateDiv"> <xsl:param name="pRow" select="1"/> <xsl:param name="pCol" select="1"/> <xsl:if test="not($pRow > $pNumRows)"> <xsl:variable name="vImageItem" select= "$vImageItems[($pRow -1)*$pNumCols + $pCol]"/> <div style="background-position: -{($pCol -1)*$pTw}px -{($pRow -1)*$pTw}px;"> <img src="{$vImageItem/THUMBNAILIMAGE}" alt="{$vImageItem/IMAGEALTTEXT}" /> </div> <xsl:variable name="vnewCol"> <xsl:choose> <xsl:when test="$pCol = $pNumCols">1</xsl:when> <xsl:otherwise><xsl:value-of select="$pCol+1"/></xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:variable name="vnewRow"> <xsl:choose> <xsl:when test="$vnewCol = 1"><xsl:value-of select="$pRow+1"/></xsl:when> <xsl:otherwise><xsl:value-of select="$pRow"/></xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:call-template name="generateDiv"> <xsl:with-param name="pRow" select="$vnewRow"/> <xsl:with-param name="pCol" select="$vnewCol"/> </xsl:call-template> </xsl:if> </xsl:template> </xsl:stylesheet> 

When this conversion is applied to the provided XML document :

 <DATASET> <ITEM> <THUMBNAILIMAGE>image1.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 1</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image2.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 2</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image3.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 3</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image4.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 4</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image5.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 5</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image6.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 6</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image7.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 7</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image8.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 8</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image9.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 9</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image10.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 10</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image11.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 11</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image12.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 12</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image13.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 13</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image14.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 14</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image15.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 15</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image16.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 16</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image17.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 17</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image18.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 18</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image19.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 19</IMAGEALTTEXT> </ITEM> <ITEM> <THUMBNAILIMAGE>image20.jpg</THUMBNAILIMAGE> <IMAGEALTTEXT>Alt text 20</IMAGEALTTEXT> </ITEM> </DATASET> 

the desired, correct result is output:

 <div style="background-position: -0px -0px;"> <img src="image1.jpg" alt="Alt text 1"/> </div> <div style="background-position: -125px -0px;"> <img src="image2.jpg" alt="Alt text 2"/> </div> <div style="background-position: -250px -0px;"> <img src="image3.jpg" alt="Alt text 3"/> </div> <div style="background-position: -375px -0px;"> <img src="image4.jpg" alt="Alt text 4"/> </div> <div style="background-position: -500px -0px;"> <img src="image5.jpg" alt="Alt text 5"/> </div> <div style="background-position: -0px -125px;"> <img src="image6.jpg" alt="Alt text 6"/> </div> <div style="background-position: -125px -125px;"> <img src="image7.jpg" alt="Alt text 7"/> </div> <div style="background-position: -250px -125px;"> <img src="image8.jpg" alt="Alt text 8"/> </div> <div style="background-position: -375px -125px;"> <img src="image9.jpg" alt="Alt text 9"/> </div> <div style="background-position: -500px -125px;"> <img src="image10.jpg" alt="Alt text 10"/> </div> <div style="background-position: -0px -250px;"> <img src="image11.jpg" alt="Alt text 11"/> </div> <div style="background-position: -125px -250px;"> <img src="image12.jpg" alt="Alt text 12"/> </div> <div style="background-position: -250px -250px;"> <img src="image13.jpg" alt="Alt text 13"/> </div> <div style="background-position: -375px -250px;"> <img src="image14.jpg" alt="Alt text 14"/> </div> <div style="background-position: -500px -250px;"> <img src="image15.jpg" alt="Alt text 15"/> </div> <div style="background-position: -0px -375px;"> <img src="image16.jpg" alt="Alt text 16"/> </div> <div style="background-position: -125px -375px;"> <img src="image17.jpg" alt="Alt text 17"/> </div> <div style="background-position: -250px -375px;"> <img src="image18.jpg" alt="Alt text 18"/> </div> <div style="background-position: -375px -375px;"> <img src="image19.jpg" alt="Alt text 19"/> </div> <div style="background-position: -500px -375px;"> <img src="image20.jpg" alt="Alt text 20"/> </div> 

Explanation

  • This is a recursive solution . A non-recursive solution (the so-called Piez method ) is possible if it is guaranteed that the number of iterations will not exceed the total number of available nodes (in the source XML documents and XSLT style sheets).

  • Stop condition : the current value of the line number becomes greater than the number of lines specified by the parameter ( $pRow > $pNumRows ).

  • Action . Create the necessary new div element, then get the values ​​for the column number and row number, with which the next action will be performed.

0
source

I think this can be done using the code below ( XSLT 1.0 ). The following conversion should go with any XML file.

 <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xsl:output indent="yes" /> <xsl:variable name="TW">125</xsl:variable> <xsl:variable name="ROWS">4</xsl:variable> <xsl:variable name="COLS">6</xsl:variable> <xsl:variable name="TOTAL"><xsl:value-of select="$ROWS * $COLS"/></xsl:variable> <xsl:template match="/"> <xsl:call-template name="iterate_rows" /> </xsl:template> <xsl:template name="iterate_rows"> <xsl:param name="num">0</xsl:param> <xsl:if test="not($num = $ROWS)"> <xsl:call-template name="iterate_cols"> <xsl:with-param name="rows" select="$num"/> </xsl:call-template> <xsl:call-template name="iterate_rows"> <xsl:with-param name="num" select="$num + 1"/> </xsl:call-template> </xsl:if> </xsl:template> <xsl:template name="iterate_cols"> <xsl:param name="num">0</xsl:param> <xsl:param name="rows" /> <xsl:if test="not($num = $COLS)"> <xsl:element name="div"> <xsl:attribute name="style" ><xsl:value-of select="concat('background-position:-', $TW*$num, 'px -', $TW*$rows, 'px;')" /></xsl:attribute> <xsl:variable name="number" select="$rows*$COLS + $num + 1" /> <xsl:element name="img"> <xsl:attribute name="src"> <xsl:value-of select="concat('images/thumbs/', $number, '.jpg')"/> </xsl:attribute> <xsl:attribute name="alt"> <xsl:value-of select="$number"/> </xsl:attribute> </xsl:element> </xsl:element> <xsl:call-template name="iterate_cols"> <xsl:with-param name="num" select="$num + 1"/> <xsl:with-param name="rows" select="$rows" /> </xsl:call-template> </xsl:if> </xsl:template> </xsl:stylesheet> 

EDIT:

In XSLT, it’s not possible to create loops like those in C ++ or Java, because it is not possible to change the value of a variable (so it is impossible to have a counter). But you can repeat the iteration with repetition.

In my solution, I defined two repeating patterns - the first one is named iterate_rows , and the second is iterate_cols . The entry point for the entire "program" is:

 <xsl:template match="/"> <xsl:call-template name="iterate_rows" /> </xsl:template> 

This is called first. The named iterate_rows template has one parameter num and has a default value of 0. An important condition is <xsl:if ...> , which checks if we have reached the end of the loop (in this case it will be $ROWS ). Inside the condition block, we have two calls: one executes the iterate_cols template, and the second calls the iterate_rows template iterate_rows (note: we call it with the num parameter added). The second template works in a similar way. Hope this helps.

+3
source

In XSLT 2.0 it is simple: it is something like:

 <xsl:for-each select="1 to $rows"> <xsl:variable name="row" select="position()"/> <xsl:for-each select="1 to $columns"> <xsl:variable name="column" select="position()"/> <div style="background-position: -{$row*$tw}px -{$column*$tw}px"/> </ </ 

In 1.0, you don’t have the luxury of select="1 to $rows" , but a popular workaround is to use select="(//*)[position() &lt;= $rows]" , which works as long as the source document There are at least $ lines. You are not actually interested in the selected nodes, only in how many there are, which determines how many times you run the loop.

+1
source

Source: https://habr.com/ru/post/1415722/


All Articles