Web caching, Part 3: Database Integration
The last article in my Web caching series published by InformIT.com details the database integration methods. As most SQL databases don't give you record modification timestamps, you have to implement them yourself. In this article, you'll see how you can use SQL triggers to implement table-level, record-level and join-level timestamps that can be used to set the last-modified date on an HTTP response.
XSL: create optional class attributes in HTML output
When generating styled HTML output from XML, you might need to attach a special class to the first and/or the last element in a series (for example, first and last child). An only child would obviously have both classes attached to it (don't forget, class attribute can list multiple classes separated by a whitespace). The code to generate optimal list of HTML classes (or doing a generic join of list elements) is pretty simple:
<xsl:variable name="class">Please note the following:
<xsl:if test="position() = 1"> FirstInSeries</xsl:if>
<xsl:if test="position() = last()"> LastInSeries</xsl:if>
</xsl:variable>
<xsl:if test="$class != ''">
<xsl:attribute name="class">
<xsl:value-of select="substring-after($class,' ')" />
</xsl:attribute>
</xsl:if>
- Each value in the list has a leading whitespace. The list values are thus properly separated, but the result has an extra leading whitespace.
- The last xsl:if instruction tests if the class attribute is needed (detailed description of this trick).
- The substring-after function removes the leading whitespace, resulting in the desired value for the class attribute.
XSL: avoid empty attributes in output stream
Whenever you create an attribute with an xsl:attribute instruction containing conditional instructions (xsl:if or xsl:choose), the result might be empty, resulting in an empty attribute in the output stream. For example, both my simplistic WordProcessingML to HTML stylesheet or similar code published on OpenXML Developer could create <span style=""> elements.
To avoid this problem, capture the conditional results into a variable and create the attribute only if the variable is non-empty, for example:
To avoid this problem, capture the conditional results into a variable and create the attribute only if the variable is non-empty, for example:
<xsl:variable name="style">
<xsl:if test="w:rPr/w:i">font-style: italic;;</xsl:if>
<xsl:if test="w:rPr/w:b">font-weight: bold;</xsl:if>
</xsl:variable>
<xsl:if test="$style != ''">
<xsl:attribute name="style"><xsl:value-of select="$style" /></xsl:attribute>
</xsl:if>
XSL: transform an element only if it's a descendant of another element
I recently had to work on WordProcessingML documents and wanted to transform only those w:p elements that were within the subtree of the w:body element. My initial solution was "a bit" complex: match all w:p elements that have a w:body ancestor.
<xsl:template match="w:p[ancestor::w:body]">There is (as always) a much more elegant solution:
<xsl:template match="w:body//w:p">
XSL: Detect first-of-type element in a list
Sometimes you want your XSL transformation to process first element of a type in a child list in different manner. For example, using the following data ...
(A) Write two transformation rules, one for the first element, one for the remaining ones:
<?xml version="1.0" encoding="UTF-8" ?>... you might want to process the first editor in a slightly different manner. There are two simple solutions:
<list>
<author>John Brown</author>
<author>Jim Small</author>
<editor>Jane Doe</editor>
<editor>Grace Kelly</editor>
</list>
(A) Write two transformation rules, one for the first element, one for the remaining ones:
<xsl:template match="editor[1]">(B) Use preceding-sibling axis to check whether an element is the first of its type:
<!-- transform the first editor in list -->
</xsl:template>
<xsl:template match="editor">
<!-- transform the remaining editor elements -->
</xsl:template>
<xsl:template match="editor">You should use the first method when the handling of the first element is radically different from the rest and the second one when you only need to set a few attributes or write a lead-in text.
<xsl:if test="not(preceding-sibling::editor)">Editors:</xsl:if>
<xsl:value-of select="text()" /> (<xsl:value-of select="position()" />)
</xsl:template>
IE7 breaks some AJAX libraries
Probably this is very old news (and Internet is full of related blog entries), but somehow I've managed to miss it ... If you use XMLHTTP Request object and initialize it similar to this:
Note: If you use Sarissa (which broke my AJAX application), download the latest version, it contains all relevant IE7 fixes.
if (IE) {
x = new ActiveXObject("Microsoft.XMLHTTP")
} else {
x = new XMLHttpRequest();
}... Internet Explorer 7 will constantly complain about the page trying to use ActiveX controls (at least IE6 was silent unless you've disabled ActiveX in which case AJAX broke anyway). The proper way to deal with this particular quirk of IE is to test for window.XMLHttpRequest first (instead of relying on browser type) and use ActiveX only if needed.Note: If you use Sarissa (which broke my AJAX application), download the latest version, it contains all relevant IE7 fixes.
XPath position() function returns unexpected results
If you process XML documents generated "manually" (including documents composed within a program without proper XML tools), you might be surprised by the results returned by with position() function. For example, the input XML document ...
<?xml version="1.0" encoding="UTF-8" ?>... processed with a simple XML stylesheet ...
<list>
<author>John Brown</author>
<editor>Jane Doe</editor>
<author>Jim Small</author>
<editor>Grace Kelly</editor>
</list>
<?xml version="1.0" encoding="utf-8" ?>... returns surprising results:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" />
<xsl:template match="list">
Results: <xsl:apply-templates />
</xsl:template>
<xsl:template match="author|editor">
<xsl:value-of select="text()" /> (<xsl:value-of select="position()" />)
</xsl:template>
</xsl:stylesheet>
Results:The reason for this unexpected behavior are whitespace text nodes between the author and editor elements which are also counted by the position() function. To skip them, either create XML documents without extra whitespace or use more specific xsl:apply-template statements, for example:
John Brown (2)
Jane Doe (4)
Jim Small (6)
Grace Kelly (8)
<xsl:template match="list">The select="*" option in the last example selects only child nodes of the current XML node and thus skips over text fragments (including whitespace nodes).
Results: <xsl:apply-templates select="*"/>
</xsl:template>
Web Caching, Part 2: Reduce the Download Time
In my second article in the Web caching series published by InformIT.com, I've focused on caching dynamically generated pages in the browser cache. The article explains the in-depth details of browser-side HTTP caching and the HTTP headers you have to process in server scripts to make your dynamic pages cacheable.
When properly implemented, this solution can drastically reduce the amount of information downloaded to visitor's browser, thus increasing the overall responsiveness of your web application.
When properly implemented, this solution can drastically reduce the amount of information downloaded to visitor's browser, thus increasing the overall responsiveness of your web application.
Web Caching, Part 1: Explicit Content Expiration
InformIT.com has published Reap the Benefits of Web Caching, Part 1: Explicit Content Expiration; the first article in my three-part series covering usage of web caching mechanisms in dynamic web pages.
Flash and Search Engines
This article gives you an excellent idea how to use Flash in your web sites while still having them completely visible to the search engines.
Detect included libraries in ASP
My ASP applications rely a lot on included libraries. As I usually end up with a complex hierarchy of libraries, it's not always possible to include the required modules in the dependent library. This makes debugging a bit more interesting, as you cannot be sure all the prerequisites have been included in the main .asp page unless you test all code paths.
To work around this problem, I wrote a small routine that checks for existence of prerequisite functions and throws an exception documenting what went wrong.
You would use this routine in a way similar to the example below:
To work around this problem, I wrote a small routine that checks for existence of prerequisite functions and throws an exception documenting what went wrong.
'
' CheckIncludedLibrary - checks if the incRoutine is available,
' otherwise throws an exception documenting that srcLibrary
' needs incLibrary
'
Sub CheckIncludedLibrary(incRoutine,srcLibrary,incLibrary)
Dim routineRef
On Error Resume Next
Set routineRef = GetRef(incRoutine)
If Err.Number <> 0 Then On Error Goto 0 : _
Err.Raise vbObjectError+1,srcLibrary, _
"You have to include " & incLibrary
End Sub
You would use this routine in a way similar to the example below:
'
' This library requires inclusion of /forms/xmlLibrary.asp,
' /forms/lib/editLibrary.asp and /asp/incPostingPreview.asp
'
CheckIncludedLibrary "NewXMLTextElement", _
"listRoutesLibrary","/forms/xmlLibrary"
CheckIncludedLibrary "EnrichElementText", _
"listRoutesLibrary","/forms/lib/editLibrary"
CheckIncludedLibrary "PostToPreview", _
"listRoutesLibrary","/asp/incPostingPreview"
“You've asked for it” series
Analyzing Google query strings that brought visitors to my blog (StatCounter is an excellent free tool to do this job), I usually find interesting (oft repeating) queries that are not yet answered in my blog. Obviously there are not too many good answers on other web sites, otherwise Google users would probably not click on a hit on the second or third page (where my blog usually appears for more generic queries).
So, to help my fellow programmers, I've started a series of “You've asked for it” posts answering the questions that brought many of you to my site in the first place (and, don't forget, you can always send me an interesting question with the Send a message link on my bio page.
So, to help my fellow programmers, I've started a series of “You've asked for it” posts answering the questions that brought many of you to my site in the first place (and, don't forget, you can always send me an interesting question with the Send a message link on my bio page.
Use XSLT to generate RSS item description
RSS specifications do not allow HTML markup in title or description elements. If you want to include HTML markup in RSS elements, it has to be quoted, for example
The following XSLT templates solve the problem: call the outputQuotedTree template in the context of input node containing HTML markup and it will generate quoted contents of its child nodes.
<xsl:template name="outputQuotedTree">
<xsl:for-each select="node() ¦ text()">
<xsl:call-template name="outputTextNode" />
</xsl:for-each>
</xsl:template>
<xsl:template name="outputTextNode">
<xsl:choose>
<xsl:when test="name() = ''">
<xsl:value-of select="." />
</xsl:when>
<xsl:otherwise>
<!-- Emit the opening tag -->
<xsl:text><</xsl:text><xsl:value-of select="name()" />
<!-- Emit the attributes of the opening tag -->
<xsl:for-each select="@*">
<xsl:text> </xsl:text>
<xsl:value-of select="name()"/><xsl:text>='</xsl:text>
<xsl:value-of select="."/>
<xsl:text>'</xsl:text>
</xsl:for-each>
<xsl:choose>
<xsl:when test="node() ¦ text()">
<!-- If there are children, close the start tag and
process the children -->
<xsl:text>></xsl:text>
<xsl:for-each select="node() ¦ text()">
<xsl:call-template name="outputTextNode" />
</xsl:for-each>
<!-- Emit the closing tag -->
<xsl:text></</xsl:text>
<xsl:value-of select="name()" />
<xsl:text>></xsl:text>
</xsl:when>
<xsl:otherwise>
<!-- No children, emit the self-closing tag -->
<xsl:text>/></xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Centering CSS-based layouts, Detecting text resize and more
The latest collection of links by Meryl Evans contains real gems:
- The article by Kris Hadlock has some great tips on creating CSS-based window-heigth or centered content.
- This article explains how to detect browser font size changes.
- CSS support in various browsers
- The anatomy of an RSS feed
MSXML firstChild property in VBScript
To complement a bit vague Microsoft MSDN document: firstChild property returns nothing (not null) if the XML node has no children. To test whether an XML node has children, use:
If Not (node.firstChild Is Nothing) Then ...
or, alternatively
If node.childNodes.length > 0 Then ...
Subscribe to:
Posts (Atom)