XSLT global replace

I found a code to do a global replace (similar to VBScript Replace function) in XSLT. It's a great example of important XSLT 1.0 concepts: setting variable value with other XSLT tags (function call emulation) and changing a loop into a recursion (as loops are not available in XSLT 1.0). Here is the slightly cleaned-up code. First we define the function (named template) and its parameters:
<xsl:template name="globalReplace">
  <xsl:param name="value"/>
  <xsl:param name="from"/>
  <xsl:param name="to"/>
The xsl:choose is the if-then-else/select replacement. We test if the source substring is present in the value …
  <xsl:choose>
    <xsl:when test="contains($value,$from)">
… if it is, we do tail recursion first, replacing all the remaining values and storing the result in $rest …
      <xsl:param name="rest">
        <xsl:call-template name="globalReplace">
          <xsl:with-param name="outputString" select="substring-after($value,$from)"/>
          <xsl:with-param name="from" select="$from"/>
          <xsl:with-param name="to" select="$to"/>
        </xsl:call-template>
      </xsl:param>
… and then concatenate the substring before the first from value with the to value and the results of the tail recursion.
      <xsl:value-of select="concat(substring-before($value,$from),$to,$rest)" />
    </xsl:when>
If the source string does not contain the substring to be replaced, we just return it (this also ends the tail recursion) …
    <xsl:otherwise>
      <xsl:value-of select="$outputString"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

A few must-read JavaScript documents

If you're serious about JavaScript programming, read these first:

XSL: Copy input elements into output stream

The usual answer to the question “how do I copy input XML elements into output XSLT stream?” is “use xsl:copy or xsl:copy-of for deep copy.” But, as always, the devil is in the details. For example, if you're faced with a product catalog structure similar to this one …
<catalog>
  <product id="123">
    <name>Sample product</name>
    <text>A <strong>marvellous</strong>
      <span class='red'>red</span> book</text>
  </product>
</catalog>
… and use a simplistic approach to the copy-of
<xsl:template match="product">
  <h3><xsl:value-of select="name" /></h3>
  <xsl:if test="text">
    <xsl:copy-of select="text" />
  </xsl:if>
</xsl:template>
… you'd end up with an extra non-HTML-compliant text node in the output stream …
<h3>Sample product</h3>
<text>A <strong>marvellous</strong>
 <span class="red">red</span> book </text>
To get just the contents you'd like to get, you should still use the xsl:copy-of instruction, but select just the child element (child::*) and the text nodes (text()) of the text element. To add icing on the cake, we'll enclose the contents of the text element in a P tag:
<xsl:template match="product">
  <h3><xsl:value-of select="name" /></h3>
  <xsl:for-each select="text">
    <p><xsl:copy-of select="text() | child::*" /></p>
  </xsl:for-each>
</xsl:template>

Displaying element's ancestors

If you need to process the whole path from the root element to the current element, the ancestor:: axis provides a convenient means of doing that, more so as it lists elements from the root element toward the current one (the opposite direction from what you'd get with the parent:: axis). For example, assume you have an online book with the following structure:
<book title="Sample book">
  <chapter title="First chapter">
    <section title="Section in first chapter">
      <section title="Chunk of text within a section">
        <para>My paragraph</para>
      </section>
    </section>
  </chapter>
</book>

The section elements can be nested

You could use the following template to display the breadcrumbs in front of each heading:

<xsl:template match="chapter|section">
  <p class='crumbs'>

Walk through all the ancestors, starting from the root element

    <xsl:for-each select="ancestor::*">

If the current element has an ancestor, we've obviously printed something already, so insert a breadcrumb separator …

      <xsl:if test="ancestor::*"><xsl:text>, </xsl:text></xsl:if>

… and display the title of the ancestor element.

      <xsl:value-of select="@title" />
    </xsl:for-each>
  </p>

At the end, display the current heading and recursively process child elements.

  <h2><xsl:value-of select="@title" /></h2>
  <xsl:apply-templates />
</xsl:template>

Max function in ASP

The VBScript (at least its ASP dialect) is not very strong in math functions. For example, the simple Max function is missing. It's not hard to write, though:
Function Max(A,B)
  Max = A : If B > A Then Max = B
End Function
Things get a bit more complex if you want to take in account the NULL values (assuming non-NULL is always greater than NULL):
Function Max(A,B)
  Max = A : If IsNull(B) Then Exit Function
  If B > A Or IsNull(A) Then Max = B
End Function
However, I prefer the multi-value function that can take as many arguments as you like (unfortunately in an array, as VBScript functions can't take variable number of arguments):
Function MaxArray(A)
  Dim I
  MaxArray = A(LBound(A))
  For I = LBound(A) + 1 To UBound(A)
    If A(I) > MaxArray Or IsNull(MaxArray) Then MaxArray = A(I)
  Next
End Function

The code uses a VBScript quirk: function name without parenthesis when used within the function refers to the current function's value, not a recursive call

To call the MaxArray function, you don't have to construct an array beforehand, you can also use the array VBScript function, for example:

mxv = MaxArray(array(A,B,C,D))

Keyboard shortcuts in web user interface

Keyboard shortcuts can make an application significantly quicker to use; however, if you want to implement then in your web applications, you'll have to wade through murky waters of non-standard keyboard events (the only major browser event type not standardized in currently supported DOM standards). I've documented most of the pitfalls (and the workarounds) in my InformIT article Adding Keyboard Shortcuts to Your Web User Interface.

Firefox vulnerabilities when using data: or jar: protocols

Just stumbled across two articles describing vulnerabilities in the ways Firefox handles the data: and jar: protocols. If you're at least a bit interested in securing web applications (or your network), read them (and try to understand what's going on). It looks like the data: protocol vulnerability is a bug, but the jar: thing is a clear FAD (Functions-As-Designed).

XSL function to find parent node name

If you want to find the node name of the current element's parent, use local-name(parent::*):
  • The local-name function returns the name of its argument (without the namespace) or the name of the current element if it's called without an argument;
  • The parent:: axis selects the current element's parent;
  • The parent::* matches any node that is the parent of the current element.
For example, given the following input data …
<book>
  <chapter>
    <section>
      <title>My section</title>
      <para>My paragraph</para>
    </section>
  </chapter>
</book>
… this XSL template will output “My parent is: section
<xsl:template match="para">
  My parent is: <xsl:value-of select="local-name(parent::*)" />
</xsl:template>

This post is part of You've asked for it series of articles.

Fading background made simple

I wanted to implement fading background for HTML elements that are changed in the background based on XMLHttpRequest calls made in my AJAX applications, the idea being that the user's attention would shift toward the changed element without the visual cue being too intrusive. As I've had excellent experience with the X library, I started my search there … and found the xAnimation.rgb function that does exactly what I've needed. The following short function changes the element's background from page default (white) to dark yellow and back in a second:
function setFading (e) {
  e.style.backgroundColor = "#FFFFFF";
  if (!e.xa) e.xa = new xAnimation();
  e.xa.rgb(e,"background-color","#EDC226",1000,1,1);
}

Emulating activeElement property with DOM browsers

Sometimes you need to know which page element has the focus. Internet Explorer solves the problem nicely with the document.activeElement property, which is unfortunately a proprietary extension and thus not available in other DOM-compliant browsers.

All the solutions I've found with Google were kludges along the lines of “scan all the elements and test each one to see whether it has focus”. It's much better to use the capture phase of the DOM event model and track the focus and blur events with the document object:
function _dom_trackActiveElement(evt) {
  if (evt && evt.target) {
    document.activeElement =
        evt.target == document ? null : evt.target;
  }
}
 
function _dom_trackActiveElementLost(evt) {
  document.activeElement = null; }
 
if (document.addEventListener) {
  document.addEventListener("focus",_dom_trackActiveElement,true);
  document.addEventListener("blur",_dom_trackActiveElementLost,true);
}

This implementation is slightly over-pessimistic; if the browser window loses focus, the activeElement is set to null (as the input control loses focus as well). If your application needs the activeElement value even when the browser window doesn't have the focus, you could remove the blur event listener.