Xslt and multi-line text (Sitecore) - xslt

I have a field in item (multi-line text) which i output in my xslt rendering. The problem is that carrigae return are not shown in my output - what do i need to do to make my xslt output show the carrigae returns?

Use this template to substitude newlines:
<xsl:template name="br">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text,'
')">
<xsl:value-of select="substring-before($text,'
')"/>
<br/>
<xsl:call-template name="br">
<xsl:with-param name="text">
<xsl:value-of select="substring-after($text,'
')"/>
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
And call it upon your text item like this:
<xsl:call-template name="br">
<xsl:with-param name="text" select="somenode/mytext"/>
</xsl:call-template>

A carriage return in XML source is ignored as whitespace. (Really, all consecutive whitespace characters are condensed to one space.) However, perhaps one of these will work instead of a plain carriage return?
<xsl:text>This text has
a newline</xsl:text>
Or
This text has
a newline

Related

XSL Using replace and matches to update values

I am trying to update an old form that uses a data model that has changed, so any reference to the old model I want to replace with the new. I currently use the function matches to tell if I should preform the replace on the current string and then use the replace function to replace the value with the new one, the problem is that the regex used in the matches does not work with the regex in the replace.
<xsl:template
match="//*[contains(#*:default,'instance(''document'')/')
mode="pass">
<xsl:variable
name="regex"
as="element()*">
<regex>instance('document')/doc_type/description</regex>
<regex>anotherRegex</regex>
</xsl:variable>
<xsl:variable
name="replacement"
as="element()*">
<replacement>xxf:get-request-parameter('documentDesc')</replacement>
<replacement>replacedRegex</replacement>
</xsl:variable>
<xsl:element name="{name()}">
<xsl:for-each select="#*">
<xsl:choose>
<xsl:when test="name() = ('xxf:default')">
<xsl:attribute name="xxf:default">
<xsl:analyze-string
regex="{concat('(',$regex[1],'|',$regex[2],')')}"
select=".">
<xsl:matching-substring>
<xsl:if test="matches(.,$regex[1])">
<xsl:value-of select="replace(.,$regex[1],$replacement[1])" />
</xsl:if>
<xsl:if test="matches(.,$regex[2])">
<xsl:value-of select="replace(.,$regex[2],$replacement[2])" />
</xsl:if>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="." />
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="{local-name()}"><xsl:value-of select="." /></xsl:attribute>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:element>
</xsl:template>
Current XML:
<xf:bind id="clinic-bind"
name="clinic"
xxf:default="instance('document')/doc_type/description"
type="xf:string"/>
What I want to turn it into:
<xf:bind id="clinic-bind"
name="clinic"
xxf:default="xxf:get-request-parameter('documentDesc')"
type="xf:string"/>
So the Problem was basicaally I had to escape the '()' characters in the regex variable.
So I ended up with
<regex>instance\('document'\)/doc_type/description</regex>
In the regex part.

XSLT - Replace multiple new line with single new line

I'm looking to replace multiple consecutive new line characters with a single new line character in the result of an XSLT.
Within an xml node I could have the following:
<description>
A description that goes over multiple lines, because:
- someone inputted the data in another system
- and there are gaps between lines
Another gap above this
</description>
I'd like the text out of this node to appear as so:
A description that goes over multiple lines, because:
- someone inputted the data in another system
- and there are gaps between lines
Another gap above this
Is there a way to do this with an XSLT?
Using XSLT 1.0 (libxslt)
How about:
<xsl:template match="description">
<xsl:call-template name="normalize-returns">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template name="normalize-returns">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text, '
')">
<!-- recursive call -->
<xsl:call-template name="normalize-returns">
<xsl:with-param name="text">
<xsl:value-of select="substring-before($text, '
')"/>
<xsl:text>
</xsl:text>
<xsl:value-of select="substring-after($text, '
')"/>
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

XSL: replace single and double quotes with &apos; and "

I have a XSL which I am using to transform one XML to another like:
<xsl:value-of select="somestatement"/>
where some statement has single quotes and double quotes. Ex: This is an "example" string. I have 'single quotes'
I want to replace single quotes with &apos; and double quotes with " so that output string will be :
This is an "example" string. I have &apos;single quotes&apos;
Can someone please suggest a solution for this?
Thanks for the help in advance.
You need to call a named recursive template for this. Try:
<xsl:template name="escape-quotes">
<xsl:param name="text"/>
<xsl:param name="searchString">'</xsl:param>
<xsl:param name="replaceString">&apos;</xsl:param>
<xsl:variable name="apos">'</xsl:variable>
<xsl:choose>
<xsl:when test="contains($text,$searchString)">
<xsl:call-template name="escape-quotes">
<xsl:with-param name="text" select="concat(substring-before($text,$searchString), $replaceString, substring-after($text,$searchString))"/>
<xsl:with-param name="searchString" select="$searchString"/>
<xsl:with-param name="replaceString" select="$replaceString"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$searchString=$apos">
<xsl:call-template name="escape-quotes">
<xsl:with-param name="text" select="$text"/>
<xsl:with-param name="searchString">"</xsl:with-param>
<xsl:with-param name="replaceString">&quot;</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text" disable-output-escaping="yes"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Example of calling the template:
<output>
<xsl:call-template name="escape-quotes">
<xsl:with-param name="text">This is an "example" string. I have 'single quotes'.</xsl:with-param>
</xsl:call-template>
</output>
Result:
<output>This is an "example" string. I have &apos;single quotes&apos;.</output>

Calculating string length of text node when parent has other child nodes too

I am stuck with XSLT 1.0 making a hyphenation script for verbatim output to PDF. (note it is PDF so I can't use clever CSS to hellp me out.
I would like to be able to access both text() nodes and child text() nodes to compare stringlength as part of the script.
For example:
<programlisting>This text will be displayed without word-wrap
I can measure length of string between end line chars '
'
and break long lines. That works great until
<emphasis>this gets added</emphasis> then my counting loop doesn't
count the text in the emphasis tags.
</programlisting>
I am looping through the text() using a template called screen. It detects the end of line character and decides if the line is too long. Long lines are broken and the remainder recursed through the template until the remainder is less than the max line length... then onto the next line of text...
... that works great, but now i have some children in the and I can't figure out how I can access the text() and any child node's text() at the same time to calculate string length.
Sorry for the long example code...
The template to match looks like this...
<xsl:template match="programlisting/text()">
<xsl:variable name="max_width">100</xsl:variable>
<xsl:variable name="min_width">80</xsl:variable>
<xsl:call-template name="screen">
<xsl:with-param name="text" select="."/>
<xsl:with-param name="max_width" select="$max_width"/>
<xsl:with-param name="min_width" select="$min_width"/>
</xsl:call-template>
</xsl:template>
The template to decide if string before linebreak is too long:
<xsl:template name="screen">
<xsl:param name="text" select="."/>
<xsl:param name="max_width"/>
<xsl:param name="min_width"/>
<xsl:variable name="fixed_text"> <!-- add end linebreak if missing -->
<xsl:choose>
<xsl:when test="substring($text,string-length($text),1) = '
'">
<xsl:value-of select="$text"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat($text,'
')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="first_line">
<xsl:value-of select="substring-before(concat($fixed_text,'
'),'
')"/>
</xsl:variable>
<xsl:variable name="other_lines">
<xsl:value-of select="substring-after($fixed_text,concat($first_line,'
'))"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="string-length(normalize-space($first_line)) < 1"><!-- blank line (just trim and copy)-->
<xsl:value-of select="concat($verbatim_padding,substring($first_line,1,100),'
')"/>
</xsl:when>
<xsl:when test="string-length($first_line) < 100"> <!-- short line (just copy)-->
<xsl:value-of select="concat($verbatim_padding,$first_line,'
')"/>
</xsl:when>
<xsl:otherwise>
<!-- Line is too long!! -->
<xsl:variable name="wrapped_lines">
<xsl:call-template name="break_line">
<xsl:with-param name="long_string" select="$first_line"/>
<xsl:with-param name="max_chars" select="100"/>
<xsl:with-param name="min_chars" select="80"/>
<xsl:with-param name="break_chars" select="' ,/;'"/>
<xsl:with-param name="linebreak_string" select="'~'"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="concat($verbatim_padding,$wrapped_lines)"/>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="string-length($other_lines) > 0">
<xsl:call-template name="screen">
<xsl:with-param name="text" select="$other_lines"/>
<xsl:with-param name="max_width" select="$max_width"/>
<xsl:with-param name="min_width" select="$min_width"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
The template to break up long lines:
<xsl:template name="break_line">
<xsl:param name="long_string"/>
<xsl:param name="max_chars"/> <!--max chars allowed on a line -->
<xsl:param name="min_chars"/> <!--max char position foa soft linebreak (else we hard break at the max chars!) -->
<xsl:param name="break_chars"/> <!-- chars used for soft breaking -->
<xsl:param name="linebreak_string"/> <!-- add this to end of a broken line -->
<xsl:choose>
<xsl:when test="(string-length($long_string) < $max_chars) or (string-length($long_string) < $min_chars) or (string-length($long_string) < 1)">
<xsl:value-of select="concat($long_string,'
')"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="trim_x_by">
<xsl:call-template name="CheckLastChar">
<xsl:with-param name="string" select="$long_string"/>
<xsl:with-param name="start" select="$max_chars"/>
<xsl:with-param name="stop" select="$min_chars"/>
<xsl:with-param name="break_chars" select="$break_chars"/>
<xsl:with-param name="max_chars" select="$max_chars"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="first_x_chars"><xsl:value-of select="substring($long_string,1,$trim_x_by)"/></xsl:variable>
<xsl:variable name="remaining_chars"><xsl:value-of select="substring-after($long_string,$first_x_chars)"/></xsl:variable>
<xsl:value-of select="concat($first_x_chars,' ',$linebreak_string)"/>
<xsl:call-template name="break_line">
<xsl:with-param name="long_string" select="$remaining_chars"/>
<xsl:with-param name="max_chars" select="$max_chars"/>
<xsl:with-param name="min_chars" select="$min_chars"/>
<xsl:with-param name="break_chars" select="$break_chars"/>
<xsl:with-param name="linebreak_string" select="$linebreak_string"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
And finally the recursive checking of string chars to see if its a safe place to line-break (soft break)
<xsl:template name="CheckLastChar">
<xsl:param name="string"/>
<xsl:param name="stop" select="1"/>
<xsl:param name="start" select="string-length($string)"/>
<xsl:param name="count" select="$start"/>
<xsl:param name="break_chars"/>
<xsl:param name="max_chars"/>
<xsl:choose>
<xsl:when test="($count < $stop) or (string-length($string) < $stop)">
<!-- gone so far into the line that a linebreak would look weird! So return the max-length and that will
force a 'hard' linebreak exactly at the max-length point regardless of the character -->
<xsl:value-of select="$max_chars"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="last_char"><xsl:value-of select="substring($string,$count,1)"/></xsl:variable>
<xsl:choose>
<xsl:when test="contains($break_chars,$last_char)">
<xsl:value-of select="$count"/>
</xsl:when>
<xsl:otherwise>
<!-- keep looking -->
<xsl:call-template name="CheckLastChar">
<xsl:with-param name="string" select="$string"/>
<xsl:with-param name="stop" select="$stop"/>
<xsl:with-param name="start" select="$start"/>
<xsl:with-param name="count" select="$count - 1"/>
<xsl:with-param name="break_chars" select="$break_chars"/>
<xsl:with-param name="max_chars" select="$max_chars"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
To get all of the text in an element (including its descendants' text), just use string(Element):
<xsl:template match="programlisting">
<xsl:variable name="max_width">100</xsl:variable>
<xsl:variable name="min_width">80</xsl:variable>
<xsl:call-template name="screen">
<xsl:with-param name="text" select="string(.)"/>
<xsl:with-param name="max_width" select="$max_width"/>
<xsl:with-param name="min_width" select="$min_width"/>
</xsl:call-template>
</xsl:template>
Note the removal of text() from the match attribute.
XPath Spec
The string-value of an element node is the concatenation of the string-values of all text node descendants of the element node in document order.
The string() function converts an object to a string as follows:
A node-set is converted to a string by returning the string-value of the node in the node-set that is first in document order. If the node-set is empty, an empty string is returned.

Freemind to JSON to Protovis xlst transformation draft

I have a very simple taxonomy I'm editing in freemind, and want to visualize it in protovis as a sunburst visualisation. The depth of the taxonomy is unknown.
I've produced an attempt to built a XLST transformation that can be used with Freemind's export via xsl script functionality - to output data in the exact JSON format needed by Protovis to produce a sunburst - the idea being no further transforms are needed in javascript.
An example of the output JSON format I'm looking for is here:
http://mbostock.github.com/protovis/ex/sunburst.html
Effectively the freemind .mm file format is the input.
Running my alpha code (shown below) in stylus studio builds up a json format (badly formatted but seems legal) which feeds protovis ok when I save the output generated from stylus studio directly to a .js file manually. For some reason Freemind doesn't seem to export data using this code though...
Is there something I'm missing?
Any help appreciated.
Many thanks, Andrew
===========UPDATE=============
I've corrected the code, the problem was that some of my xsl wasn't supported by the xslt engine used by freemind. I corrected the code and moved it to github under a liberal license and removed it from here.
The adaptor is available here:
https://github.com/minkymorgan/Freemind2JSON#readme
Andrew
Here is my attempt. I based it of your version, and added a few more features.
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/map">
<xsl:text>var Map = {
</xsl:text>
<xsl:apply-templates select="node">
<xsl:with-param name="indent">
<xsl:text> </xsl:text>
</xsl:with-param>
</xsl:apply-templates>
<xsl:text>
};
</xsl:text>
</xsl:template>
<xsl:template match="node">
<xsl:param name="indent"/>
<xsl:if test="position() != 1">
<xsl:text>,
</xsl:text>
</xsl:if>
<xsl:value-of select="$indent"/>
<xsl:text>"</xsl:text>
<xsl:call-template name="escape-javascript">
<xsl:with-param name="string"
select="descendant-or-self::node/#TEXT"/>
</xsl:call-template>
<xsl:text>": </xsl:text>
<xsl:choose>
<xsl:when test="node">
<xsl:text>{
</xsl:text>
<xsl:apply-templates select="node">
<xsl:with-param name="indent">
<xsl:value-of select="$indent"/>
<xsl:text> </xsl:text>
</xsl:with-param>
</xsl:apply-templates>
<xsl:text>
</xsl:text>
<xsl:value-of select="$indent"/>
<xsl:text>}</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>10</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--
Javascript string escape template by Jeni Tennison
Source: http://holytshirt.blogspot.com/2008/06/xslt-javascript-escaping.html
Author page: http://www.jenitennison.com/
-->
<xsl:template name="escape-javascript">
<xsl:param name="string" />
<xsl:choose>
<xsl:when test='contains($string, "&apos;")'>
<xsl:call-template name="escape-javascript">
<xsl:with-param name="string"
select='substring-before($string, "&apos;")' />
</xsl:call-template>
<xsl:text>\'</xsl:text>
<xsl:call-template name="escape-javascript">
<xsl:with-param name="string"
select='substring-after($string, "&apos;")' />
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($string, '
')">
<xsl:call-template name="escape-javascript">
<xsl:with-param name="string"
select="substring-before($string, '
')" />
</xsl:call-template>
<xsl:text>\n</xsl:text>
<xsl:call-template name="escape-javascript">
<xsl:with-param name="string"
select="substring-after($string, '
')" />
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($string, '\')">
<xsl:value-of select="substring-before($string, '\')" />
<xsl:text>\\</xsl:text>
<xsl:call-template name="escape-javascript">
<xsl:with-param name="string"
select="substring-after($string, '\')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise><xsl:value-of select="$string" /></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
If run on the Freemind test file, it produces the following output:
var Map = {
"Notetest": {
"Notetest": 10,
"This is a node": {
"with a linbreak \n subnode": 10,
"and another subnode": 10,
"and some folded subnodes": {
"fold1": 10,
"fold2": 10,
"fold3": 10
}
},
"Attributes": 10
}
};
In case it's of interest ... I've just pushed an XSLT script for converting FreeMind to JSON.
My script is a bit simpler and does not yet support Javascript escaping.
It's also not designed for use in Protovis
https://github.com/tohagan/freemind-to-json