In XSLT 2.0 disable-output-escaping is deprecated. New facility for the problem is called Character Maps. Character maps are simpler, better and don't mess with data model. Formal definition:
A character map allows a specific character appearing in a text or attribute node in the final result tree to be substituted by a specified string of characters during serialization. The effect of character maps is defined in [XSLT and XQuery Serialization].
This is basically declarative replace for XSLT output.
Let's say you want to generate this XUL fragment (real world question):
<menuitem label="&context.add.building;"/>
XSLT 2.0 solution would be to define a character map, which maps ampersand character to ugh... ampersand character :) For this to make sense you should realize that mapping a character effectively disables its XML or HTML escaping. It's like you say "I want this character to be outputted as this string and don't mess with it you, the engine". That's powerful enough even to disable escaping of reserved characters such as & and <.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output use-character-maps="xul" />
<xsl:character-map name="xul">
<xsl:output-character character="&" string='&'/>
</xsl:character-map>
<xsl:template match="contextMenu">
<menuitem label="&context.add.{name()}"/>
</xsl:template>
</xsl:stylesheet>
Awesome. I can't wait, I want this in XSLT 1.0 too. I think I'm going to implement character maps for nxslt.exe tool. The only problem is how to provide mapping info. I'll post about it next time.
[Tested with Saxon 8.8B for .NET].
1. Install Archive Date Header Plugin from Adam Kalsey. This allows to generate year headers.
2. Insert this piece of Javascript somewhere - if you have common js file that would be the best, but at worse just paste it before </head> in your template. It's a function to handle expand/collapse year clicks.
<script type="text/javascript">
function ec(e) {
var targ;
if (e.target) targ = e.target;
else if (e.srcElement) targ = e.srcElement;
if (targ) {
rows = targ.parentNode.getElementsByTagName("DIV");
collapsed= targ.className == "year-hdr collapsed";
targ.className = collapsed? "year-hdr expanded" : "year-hdr collapsed";
for(i=0; i < rows.length; i++)
rows[i].style.display = collapsed? "block" : "none";
}
}
</script>
3. Use the following snippet in your template to generate expandable archive list. It generates nested list of archives with year headers.
<div class="side">
<div>
<MTArchiveList archive_type="Monthly">
<MTArchiveDateHeader>
</div><div>
<p class="year-hdr collapsed"
onclick="ec(event)"
id="archive<MTArchiveDate format="%Y">">
<MTArchiveDate format="%Y">
<p>
</MTArchiveDateHeader>
<div class="month-row">
<img src="images/folder.gif" align="middle"/> <a
href="<$MTArchiveLink$>"><MTArchiveDate format="%B"></a>
</div>
</MTArchiveList>
</div>
</div>
4. If you prefer the current year to be expanded by default, insert this piece of javascript after above template:
<script type="text/javascript">
year = document.getElementById("archive" + (new Date()).getFullYear());
if (year) {
year.className = "year-hdr expanded";
rows = year.parentNode.getElementsByTagName("DIV");
for(i=0; i < rows.length; i++)
rows[i].style.display = "block";
}
</script>
5. Define CSS styles. Here are my definitions:
.year-hdr {
cursor: pointer;
cursor:hand;
font-size: 120%;
font-weight: bold;
background-repeat: no-repeat;
background-position: top left;
padding:2px;
}
.collapsed {
background-image: url(images/plus.gif);
}
.expanded {
background-image: url(images/solid.gif);
}
.month-row {
display: none;
padding-left: 2em;
}
Done.
Well, having XSLT stylesheets externally no doubt has many benefits. But embedding XSLT stylesheets into deploy units (be it dll, exe or jar) is also done not without a reason. After all two the most often used in the wild XSLT stylesheets are embedded. I'm talking about res://msxml.dll/defaultss.xsl (technically not XSLT stylesheet) and chrome://global/content/xml/XMLPrettyPrint.xsl - respectively Internet Explorer and Firefox XML pretty printers.
Embedding stylesheet doesn't necessarily mean coupling presentation and logic layers, in fact it has nothing to do with any application design issues. It's just deployment strategy and in that sense XSLT stylesheets aren't different from images or scripts.
Sure, when your XSLT file is just laying around you can tweak it without reinstalling application (sometimes even without restarting it). Sometimes that's useful and sometimes you don't want users to play with your stylesheet, because allowing that means:
Again, sometimes deployment is easier if you don't have to bother about all that little files to be placed into the right place.
Some people embed, obfuscate and encrypt XSLT stylesheets trying to prevent reverse engineering. Well, apparently they do it for a reason too.
Performance is another interesting point. I think loading string resources should be a little bit faster than loading from the disk. Didn't test it though.
But the whole new era of XSLT embedding is going to start when XSLT finally becomes widely compilable into executable code. Next Visual Studio (codename Orcas, expected later this year) will include XSLTC.EXE - XSLT to MSIL compiler. That would add another benefit - save on XSLT compilation, which is time and resource hog.
If you do embed your XSLT stylesheets, I wonder what are your reasons?