Introducing AJAXSLT – library for client side, JavaScript, XSLT transformations (good for RIA and AJAX)

While working on Rich HTML clients that could retrieve information from various sources on different web-servers using multiple AJAX requests, I came across the AJAXSLT project from Google Code. AJAXSLT is a JavaScript library that you can easily import to any HTML document. It allows you to create or retrieve an XML document – for example using an AJAX style XmlHttpRequest -, an XSLT document in similar fashion and perform the XSLT Transformation in the Client. The resulting XML document can be XHTML that is rendered in the browser (DHTML) or can be used to make additional requests or can be fed into an SVG object for graphical display. Or whatever else you would like to do with such an XML result.

I was sort of intrigued by the concept of AJAXSLT, so I downloaded the code from SourceForce – all of its 38Kb. In this article I will describe a simple application that I created using AJAXSLT: an HTML document that presents multiple links to RSS Feeds to the user. The user can select an RSS Feed, press a submit button and see a very readable summary of the RSS Feed’s contents. This is achieved by a Client Side XSLT transformation of the RSS Feed into XHTML that is rendered in situ. The XSLT document is visible to the user and he or she can make changes to the XSLT before re-rendering the RSS feed.

Introducing AJAXSLT - library for client side, JavaScript, XSLT transformations (good for RIA and AJAX)

....

Steps

To get to the application shown above is really quite simple. The most complex part of this probably is realizing that retrieving RSS feeds from an HTML document is not trivial: the RSS feeds are from different domains, different web server than the original HTML document. AJAX Requests must be to the same domain for security reasons. Hence we cannot simply make AJAX Requests directly to those RSS Feeds. Instead, we make use of a AjaxProxyServlet, a simple servlet that makes the request on behalf of the HTML client. This proxy is introduced in the article Proxy Servlet for AJAX requests to multiple, remote servers.

1. The first step is downloading the AJAXSLT libraries, currently the 0.4 release. I have downloaded the ZIP file (38Kb) and extracted the contents.

Introducing AJAXSLT - library for client side, JavaScript, XSLT transformations (good for RIA and AJAX)

The test directory contains two HTML files with associated JavaScript libraries that can be opened in a browser to immediately demonstrate the XSLT transformation as well as the XML Parsing and XPath retrieval functionality. Downloading, extracting and first trial run takes less than 10 minutes (probably less than five).

2. I have taken the xslt.html document from the AJAXSLT test directory as my starting point. I have added the AJAX request functions for calling the AjaxProxyServlet – these were introduced in the article on this proxy: Proxy Servlet for AJAX requests to multiple, remote servers. Then I have added the two hyperlinks for the two RSS Feeds – in this case the BBC World News RSS and the OraBlogs Aggregator RSS. These hyperlinks when clicked make a call to the loadXMLDoc function, passing the URL of the feed. This function makes the Ajax Request to the Proxy Servlet that is to retrieve the XML RSS contents and return it to the browser. The function processReqChange will process the response with the RSS Feed contents. It is pasted into the xml TEXTAREA.

When the Process button is clicked, the function test_xslt() is invoked. This function takes the XML textarea and parses the value into a variable. It then parses the XSLT textarea into another variable. Next it transforms the xsml variable using the xslt variable, storing the result in the html variable. The html variable is subsequently put in the html textarea and in the innerHTML propertyof the DIV htmldisplay:

    function test_xslt() {
      var xml = xmlParse(el('xml').value);
      var xslt = xmlParse(el('xslt').value);
      var html = xsltProcess(xml, xslt);
      el('html').value = html;
      el('htmldisplay').innerHTML = html;
    }

The XSLT engine in AJAXSLT is not complete yet. A few features that are required by the XSLT and XPath standards were left out. They are marked in the source code using alert() statements. The initial XSLT document used in this demo application is not extremely complex but at the same time it uses a number of key XSLT features:

<xsl:stylesheet>
  <xsl:template match="/">
     <div style="color:green">
        <xsl:value-of select="rss/channel/title/."/>
     </div>
     <ul>
     <xsl:for-each select="rss/channel/item">
       <li>
         <xsl:element name="a">
           <xsl:attribute name="target"><xsl:text>_blank</xsl:text></xsl:attribute>
           <xsl:attribute name="href"><xsl:value-of select="link" /></xsl:attribute>
           <xsl:value-of select="title" />
         </xsl:element>
       </li>
     </xsl:for-each>
     </ul>
  </xsl:template>
</xsl:stylesheet>

Note that:AJAXSLT is an implementation of XSL-T in JavaScript, intended for use in fat web pages, which are nowadays referred to as AJAX applications. Because XSL-T uses XPath, it is also an implementation of XPath that can be used independently of XSL-T. Both XPath and XSL-T are built into some of the modern browsers,  however not in all. This library runs on many browsers, including those that don’t support XSLT adaequately or at all, which is why the  library was written in the first place. Supported browsers:Firefox/1.0, Internet Explorer/6.0, Safari/1.2, Safari/1.3, Safari/2.0, Opera/7.5, Opera/8.0.

The entire HTML document looks as follows:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Simple XSLT test</title>
    <script src="misc.js" type="text/javascript"></script>
    <script src="dom.js" type="text/javascript"></script>
    <script src="xpath.js" type="text/javascript"></script>
    <script src="xslt.js" type="text/javascript"></script>
    <script src="test/xslt_script.js" type="text/javascript"></script>

<SCRIPT language="JavaScript">    

logging = true;
xsltdebug = true;
    var req;
    var xmldoc;

function el(id) {
  return document.getElementById(id);
}

function test_xslt() {
  var xml = xmlParse(el('xml').value);
  var xslt = xmlParse(el('xslt').value);
  var html = xsltProcess(xml, xslt);
  el('html').value = html;
  el('htmldisplay').innerHTML = html;
}

function cleanxml() {
  cleanvalue('xml');
  cleanvalue('xslt');
}

function cleanvalue(id) {
  var x = el(id);
  x.value = x.value.replace(/^\s*/, '').replace(/\n\s*/g, '\n');
}


  func
tion loadXMLDoc(url) {
    //
 branch for native XMLHttpRequest object
    if (window.XMLHttpRequest) {
        req = new XMLHttpRequest();
        req.onreadystatechange = processReqChange;
        req.open("GET", "proxy?remotehost="+url, true); // the third parameter - true - specified ASYNCHRONOUS processing i.e. not waiting for the response!
        req.send(null);
    // branch for IE/Windows ActiveX version
    } else if (window.ActiveXObject) {
        req = new ActiveXObject("Microsoft.XMLHTTP");
        if (req) {
            req.onreadystatechange = processReqChange;
            req.open("GET", "proxy?remotehost="+url, true); // the third parameter - true - specified ASYNCHRONOUS processing i.e. not waiting for the response!
            req.send();
        }
    }
  }        
  function processReqChange()  {
    // only if req shows "complete"
    if (req.readyState == 4) {
      // only if "OK"
      if (req.status == 200) {
          xmldoc=req.responseXML;
          document.xmlform.xml.value=req.responseText;
      }
      else {
        alert("Error: can not get the content ("+ req.statusText +")");
      }    
    }
  }
        
  </SCRIPT>
  </head>
  <body onload="loadXMLDoc('news.bbc.co.uk/rss/newsonline_world_edition/front_page/rss091.xml');">
  <a href="javascript:loadXMLDoc('news.bbc.co.uk/rss/newsonline_world_edition/front_page/rss091.xml');">BBC World News</a>
  <a href="javascript:loadXMLDoc('www.orablogs.com/orablogs/rss.xsql');">OraBlogs</a>

    <form name="xmlform" onsubmit="test_xslt();return false">
      <table>
        <tr>
          <td>RSS Feed<br />
            <textarea id="xml" cols="40" rows="10">
              <page>
                <message>
                  Hello World.
                </message>
              </page>
            </textarea>
          </td>
          <td>XSLT <br />
            <textarea id="xslt" cols="40" rows="10">
<xsl:stylesheet>
  <xsl:template match="/">
     <div style="color:green">
        <xsl:value-of select="rss/channel/title/."/>
     </div>
     <ul>
     <xsl:for-each select="rss/channel/item">
       <li>
         <xsl:element name="a">
           <xsl:attribute name="target"><xsl:text>_blank</xsl:text></xsl:attribute>
           <xsl:attribute name="href"><xsl:value-of select="link" /></xsl:attribute>
           <xsl:value-of select="title" />
         </xsl:element>
       </li>
     </xsl:for-each>
     </ul>
  </xsl:template>
</xsl:stylesheet>            </textarea>
          </td>
        </tr>
        <tr>
          <td colspan="2">
            <input type="submit" value="process"/>
          </td>
        </tr>
        <tr>
          <td valign="top">
            XML Result<br />
            <textarea id="html" cols="40" rows="10">
            </textarea>
          </td>
          <td>
            <div id="htmldisplay"></div>
          </td>
        </tr>
      </table>
    </form>
  </body>
</html>

When I click on the OraBlogs RSS Feed link, an Ajax request is made to the AjaxProxyServlet, passing a parameter called remotehost that contains the URL at which the OraBlogs RSS Feed can be found. The Proxy Servlet makes the request to get hold of the RSS feed (an XML Document) and sends it back to the browser. It is received in the ResponseXML on the HttpXmlRequest object and put in the textarea. When the Process button is pressed, the XML is read and parsed, the XSLT from the second textarea is read and parsed, then the XSLT transformation is performed and the resulting XML is put into the third textarea. Subsequently, the resulting XML is taken and placed in the innerHtml property of a DIV element:

el(‘htmldisplay’).innerHTML = html;

Introducing AJAXSLT - library for client side, JavaScript, XSLT transformations (good for RIA and AJAX)

Resources

Source code for the examples in this article: AjaXsltRss.zip.

Homepage for the Google Code AJAXSLT project

 

2 Comments

  1. Ramūnas February 8, 2006
  2. Andrew Collins January 17, 2006