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">
<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>
Please note the following:
  • 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:
<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 ...
<?xml version="1.0" encoding="UTF-8" ?>
<list>
<author>John Brown</author>
<author>Jim Small</author>
<editor>Jane Doe</editor>
<editor>Grace Kelly</editor>
</list>
... you might want to process the first editor in a slightly different manner. There are two simple solutions:

(A) Write two transformation rules, one for the first element, one for the remaining ones:
<xsl:template match="editor[1]">
<!-- transform the first editor in list -->
</xsl:template>

<xsl:template match="editor">
<!-- transform the remaining editor elements -->
</xsl:template>
(B) Use preceding-sibling axis to check whether an element is the first of its type:
<xsl:template match="editor">
<xsl:if test="not(preceding-sibling::editor)">Editors:</xsl:if>
<xsl:value-of select="text()" /> (<xsl:value-of select="position()" />)
</xsl:template>
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.

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:
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" ?>
<list>
<author>John Brown</author>
<editor>Jane Doe</editor>
<author>Jim Small</author>
<editor>Grace Kelly</editor>
</list>
... processed with a simple XML stylesheet ...
<?xml version="1.0" encoding="utf-8" ?>
<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>
... returns surprising results:
  Results:
John Brown (2)

Jane Doe (4)

Jim Small (6)

Grace Kelly (8)
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:
<xsl:template match="list">
Results: <xsl:apply-templates select="*"/>
</xsl:template>
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).

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.

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.
'
' 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.

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 <b>bold</b>. Doing this in any server-side scripting language is easy, for example, ASP provides Server.HTMLEncode function. If you use XSLT to transform internal XML documents into RSS feeds, the task gets trickier, as XSLT provides no equivalent function.

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>&lt;</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>&gt;</xsl:text>
<xsl:for-each select="node() ¦ text()">
<xsl:call-template name="outputTextNode" />
</xsl:for-each>
<!-- Emit the closing tag -->
<xsl:text>&lt;/</xsl:text>
<xsl:value-of select="name()" />
<xsl:text>&gt;</xsl:text>
</xsl:when>
<xsl:otherwise>
<!-- No children, emit the self-closing tag -->
<xsl:text>/&gt;</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:

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 ...

Windows that resize to fit their content

This article contains a great description of JavaScript code that lets you resize a pop-up window to perfectly fit its content. Highly recommended!

Another way of writing less-than

In one of my projects, I had to check the version number of included JavaScript library to detect out-of-date browser cache. However, that's hard if you generate web pages with XSLT transformations. Finally, this trick worked for me - instead of writing
if (window.libraryVersion > minimumVersion) ...
... I wrote ...
if (Math.max(window.libraryVersion,minimumVersion) ==  minimumVersion) ...

Embedding JavaScript in XSL documents

Summary: Do not use complex inline JavaScript in XSLT-generated web pages
If you're using XSLT to produce full-blown (X)HTML pages from XML documents (more about that in one of my upcoming articles for www.InformIT.com), you'll sooner or later need a bit of inline JavaScript code in the final web page. As you know, you cannot use bare less-than-sign (<) in an XML document and you cannot use &lt; quoting in JavaScript embedded in <script> tags in traditional HTML page.

Apart from migrating all JavaScript code in a separate .js file (which is the best solution anyway) or using tricks to avoid < and & in the JavaScript code, the only way to make things work (at least in some modern XHTML-aware browsers) is to structure the web page to be XHTML-compliant with proper <!DOCTYPE> declaration and <html xmlns="http://www.w3.org/1999/xhtml"> root element.  When the browsers recognize a page as XHTML-compliant page, they might be willing to transform &lt; escape within the <script> tag into less-than sign (works for Opera, but not for Firefox).

Reading the XSLT-related standards, one would get an impression that using the <xsl:output method="html" /> command should force the transformation engine to write pure text instead of XML escapes within the <script> tag. However, it's almost impossible to force all the XSLT transformation engines being used today to do it properly (for example, it looks like MSXML engine on the IIS server simply ignores it).

Setting text property of MSXML Node object destroys its children

The Microsoft's documentation of the IXMLDOMNode.text property skips an important detail: if the node has non-text children and you set its text property, it loses all children. The node.text = string statement deletes all children and creates a single IXMLDOMText child.

Manipulating innerHtml disables event handlers set with JavaScript

If you manipulate innerHtml property of an HTML element with children and you've previously set any event handlers on those children with JavaScript (not with onxxxx attributes), they're gone (as the changing of innerHTML effectively erases all the children and recreates them). If you want to retain children event handlers, you should add new children with DOM calls (node.appendChild).

For example, if you set up onclick handler like this:
<div id="outerDiv">
<p id="para1">Click here</p>
</div>
<script>
var x = document.getElementById("para1");
x.onclick = function () { alert("Clicked"); }
</script>

... the onclick handler will stop working if you change innerHtml of outerDiv, for example to add another paragraph:
<script>
var outerDiv = document.getElementById("outerDiv") ;
outerDiv.innerHtml += "<p id='para2'>Another paragraph</p>" ;
</script>

Browser-independent Object Fading

The author of this article (courtesy of InformIT.com) did a great job documenting ways to fade an object in a wide range of incompatible browser implementations. Highly recommended.

Detecting cached CSS style sheets

Have you ever deployed an improved version of your web pages, just to find out that the visitors have to press Ctrl-R (Reload) to get them right as their browsers have cached the Cascaded Style Sheet (.css) files used in your web site. Renaming the style sheet files every time you change them definitely solves the problem, but requires changing all the related web pages as well.
It's infinitely better if you can detect that the style sheets are cached by the browser (and thus obsolete) and respond with a forced reload. In the simplest case (more about version control in another post), you can detect that the style sheets referenced by your web page are missing a class (or id) selector that you need. Include the browser-independent stylesheet library in your web page and add the following code somewhere in the body of the page:
<script>if (xHasStyleSheets() && ! xHasStyleSelector('.myClass')) location.reload(true);</script>
If you're looking for an ID selector,  replace '.myClass' with '#myID'.

Detecting out-of-date JavaScript libraries

A major challenge of writing rapid-changing AJAX applications is the asynchronous nature of browser page fetching. It's almost impossible to ensure that the browser will fetch the new version of all the components of your application (for example, JavaScript library) when it changes on the server.

You might solve this problem by renaming the JavaScript libraries (.js files) every time you make a significant change that could break the client-side application. While being bullet-proof, this approach requires republishing of all pages referencing the renamed library (which could be your entire web site).

It's way better to include the cache-checking code in your application:
  • Within your library, set a global variable (or a property of window object) to library's version number. The version number could be an increasing number, the date the change was made or (ideally) a version number from your source code control system (SCCS for Unix fans or Visual SourceSafe for Windows)
  • In every web page that relies on particular functionality of that library, check the version number and force a reload if there's a mismatch.
For example, in your JavaScript file, include a statement setting the version number ...
window.version_MyLibrary = 20060810; // library last changed on August 10th, 2006
... and at the beginning of the web pages requiring new library functionality, check the version number just after the library has been included with the <script> tag:
<script>if (window.version_MyLibrary < 20060810) location.reload(true);</script>
So, if your web page detects that the library included during the load process has an incompatible version number (due to cached version being used by the browser), it forces a hard reload, downloading all components from the web server.

Browser-independent style sheet Javascript code

In a recent project, I had to check whether the stylesheets attached to a document contain a particular class name. The X library (www.cross-browser.com) does not contain anything handling the stylesheets, so here's the necessary code (works in Internet Explorer and Firefox, Opera is broken).
 
Usage example: 
xHasStyleSelector('.menuButton') will return true if any of the stylesheets attached to the document (including the inline stylesheets) contain definition of .menuButton.

// browser-independent stylesheet handling
// Copyright 2006 Ivan Pepelnjak (
www.zaplana.net)
// Distributed under the terms of the GNU LGPL
 
//
// xGetStyleSheetFromLink - extracts style sheet object from the HTML LINK object (IE vs. DOM CSS level 2)
//
function xGetStyleSheetFromLink(cl) { return cl.styleSheet ? cl.styleSheet : cl.sheet; }
 
//
// xGetCSSRules - extracts CSS rules from the style sheet object (IE vs. DOM CSS level 2)
//
function xGetCSSRules(ss) { return ss.rules ? ss.rules : ss.cssRules; }
 
//
// xHasStyleSheets - checks browser support for stylesheet related objects (IE or DOM compliant)
//
function xHasStyleSheets() {
  return document.styleSheets ? true : false ;
}
   
//
// xTraverseStyleSheet (stylesheet, callback)
//   traverses all rules in the stylesheet, calling callback function on each rule.
//   recursively handles stylesheets imported with @import CSS directive
//   stops when the callback function returns true (it has found what it's been looking for)
//
// returns:
//   undefined - problems with CSS-related objects
//   true      - callback function returned true at least once
//   false     - callback function always returned false
//
function xTraverseStyleSheet(ss,cb) {
  if (!ss) return false;
  var rls = xGetCSSRules(ss) ; if (!rls) return undefined ;
  var result;
  
  for (var j = 0; j < rls.length; j++) {
    var cr = rls[j];
    if (cr.selectorText) { result = cb(cr); if (result) return true; }
    if (cr.type && cr.type == 3 && cr.styleSheet) xTraverseStyleSheet(cr.styleSheet,cb);
  }
  if (ss.imports) {
    for (var j = 0 ; j < ss.imports.length; j++) {
      if (xTraverseStyleSheet(ss.imports[j],cb)) return true;
    }
  }
  return false;
}
   
//
// xTraverseDocumentStyleSheets(callback)
//   traverses all stylesheets attached to a document (linked as well as internal)
//   
function xTraverseDocumentStyleSheets(cb) {
  var ssList = document.styleSheets; if (!ssList) return undefined;
     
  for (i = 0; i < ssList.length; i++) {
    var ss = ssList[i] ; if (! ss) continue;
    if (xTraverseStyleSheet(ss,cb)) return true;
  }
  return false;
}
 
//
// xHasStyleSelector(styleSelectorString)
//   checks whether any of the stylesheets attached to the document contain the definition of the specified
//   style selector (simple string matching at the moment)
//
// returns:
//   undefined  - style sheet scripting not supported by the browser
//   true/false - found/not found
//
function xHasStyleSelector(ss) {
  if (! xHasStyleSheets()) return undefined ;
 
  function testSelector(cr) {
    return cr.selectorText.indexOf(ss) >= 0;
  }
  return xTraverseDocumentStyleSheets(testSelector);
}

Improve your search engine rankings with AJAX

My article describing how to use AJAX to split navigational elements from the main page content, resulting in improve search engine ranking and reduced download time has been published on InformIT.com.

Computing server time zone difference in classic ASP ... take 2

Antic from Jodohost pointed me to an even more elegant solution: use mixed JavaScript/VBScript to get the result. JavaScript can compute the timezone difference with a built-in function and VBScript can then use the results.

Here is the minimum code to get the desired result:
<script runat="server" language="jscript">
var TZDiff = new Date().getTimezoneOffset();

</script>
<% Response.Write TZDiff %>

Of course, if you care about ASP performance, you might not want to invoke two script interpreter engines in a single page. In that case, a more optimized solution is:

GetTZ.asp:
<%@ LANGUAGE=JScript %>
<% Application("TZOffset") = new Date().getTimezoneOffset() %>

Any other ASP page:
<%
If Not IsNumeric(Application("TZOffset")) Then Server.Execute("GetTZ.asp")
' use Application("TZOffset") from here on
%>

Computing Timezone difference from GMT in ASP VBScript

If you want to be nice to your web visitors, you will eventually have to start considering the "last-modified" HTTP headers in order to make sure that:
  • the browsers always get the most accurate data and
  • the dial-up users don't wait for data they have already cached.

Before getting there, you'll hit a major obstacle - all HTTP timestamps are in GMT (and in an obscure format, on top of that) and the time you get with the now() function is local to your timezone. No big deal in most languages - you just use system calls or DLLs to figure out the difference between local time and GMT ... but you can't do that in ASP. Writing a custom COM module is always an option if you own the web server (in which case you can hardcode the timezone offset anyway), but for those of us who rely on hosting (and distributing code), things get almost impossibly complex.

But, as always, there is a trick - using the XMLHTTPRequest object, the ASP script can execute a request on its own server (http://127.0.0.1) and compare the GMT timestamp in the reply with the current time to get the local timezone difference.

Here is the code to do it:

'
' FromHTTPDate - helper function that converts HTTP date
' into a date value
'
Function FromHTTPDate (SDate)
Dim n,LC,S
n=InStr(1,SDate,";",vbTextCompare)
If (n>0) Then
S = Left(SDate,n-5)
Else
If SDate > "9" Then S = Left(SDate,Len(SDate) - 4)
End If
n=InStr(1,S," ",vbTextCompare)
If (n>0) Then S = Mid(S,n+1,Len(S))
LC = Response.LCID : Response.LCID = 1033
FromHTTPDate=CDate(S) : Response.LCID = LC
End Function

'
' GetTimezoneDifference - returns hourly difference between
' local time and GMT
'
Function GetTimezoneDifference
Dim TZD
TZD = Application ("TimezoneDifference") ' reuse the result
If TypeName(TZD) = "Integer" Then ' if available
GetTimezoneDifference = TZD : Exit Function
End If

Dim HttpReq,Port,URL,RDT

Set HttpReq = Server.CreateObject("MSXML2.ServerXMLHTTP")
Port = Request.ServerVariables("SERVER_PORT")
URL = "http://127.0.0.1" & ":" & Port & "/"
HttpReq.open "GET", URL , False
HttpReq.send

RDT = FromHTTPDate(HttpReq.getResponseHeader("Date"))
TZD = CInt((Now() - RDT) * 24 + 0.05)
Application ("TimezoneDifference") = TZD ' cache the result
GetTimezoneDifference = TZD
End Function

Handling Variant byte array in ASP

This is yet another of slightly undocumented ASP features - you can find a lot of examples telling you how to read and write binary data (with, among other things, Response.binaryWrite or ADODB_recordset("blob").value), but when you try to manipulate individual bytes (I had to compute MD5 hash), uncle Google or Microsoft search gives you nothing.

The trick is (as always) very simple: you treat variant byte array as string, using regular string functions (like Len, Mid or Asc). However, due to Unicode support in VBScript, you have to use their byte versions. So, to dump the variant byte array, use this code:

For I = 1 to LenB(varArray)
Response.Write AscB(MidB(varArray,I,1))
Next