XLST via JavaScript

I’ve recently been working on a prototype that makes NBS Create systems readable in the web browser. This isn’t really a new concept as our NBS Create product and Revit plugin actually use an embedded WebBrowser .NET control (which is basically a wrapper around Internet Explorer).

Most of our products store their data in XML and most transform XML to HTML to provide a rich editing experience (fonts, bullets, hyperlinks, symbols, etc). XML is a bit out of favour now, with JSON being the preferred format. That said, XML still brings something to the table – with schemas, xpath and xsl transforms.

The data I was using for my prototype was in XML format. I was originally planning on letting .NET transform the XML and stream HTML to the client web browser. Out of curiosity, I Googled whether it was possible to do the transform via JavaScript. My thinking was that I could get the client webbrowser to do the work rather than the server. It’s quite an old topic actually, and to my surprise some browsers can natively do the transform – but many tutorials or StackOverflow answers recommended using JavaScript. So I thought….”why not give it a shot!”.

In modern WebBrowsers (Chrome, Firefox, Safari) it was a trivial task:

Step 1: Load XML

My XML documents are sent to the client via an API as a string – the client must convert this string to an XMLDocument. JQuery makes this a breeze:

function parseXmlString(xml) {
  var xmlDoc = $.parseXML(xml);

  if (xmlDoc) {
    return xmlDoc;
  }
}

Step 2: Load XSLT

The XSLT is a resource on the server, the client must load it like any other web resource. I chose to use JQuery to send an Ajax request:

function loadXmlDocument(url, callback) {
  $.ajax({
    url: url,
    dataType: "xml",
    success: function (data) {
      callback(data);
    }
  });
}

Step 3: Use the XSLTProcessor to transform

We just do a quick check of the browsers capabilities to make sure it supports the XSLTProcessor.

if (typeof (XSLTProcessor) !== "undefined") { // FF, Safari, Chrome etc
  xsltProcessor = new XSLTProcessor();

  xsltProcessor.importStylesheet(xsl);
  xsltProcessor.setParameter(null, "resPath", configSettings.areaPath + 'Content/img/CreateResources');

  resultDocument = xsltProcessor.transformToFragment(xml, document);
  var contentNode = document.getElementById("clause-content");
  contentNode.appendChild(resultDocument);
}

Also worth highlighting, is that my XSL requires some parameters passing to it, this is easily done via that setParameter() method.

Internet Explorer quirks

But things are never just that easy are they? Internet Explorer 9-11 don’t support the XSLTProcessor, instead they use an ActiveXObject to do the transform.

Again we need to test the browsers capabilities, but there’s another quirk. IE 9-10 will pass a test for window.ActiveXObject; IE11 however has a bug and will report a fail, so we must check for “ActiveXObject” in window too.

We also have another issue, XML has to be loaded in to the ActiveXObject as a string (but we read it in as an XMLDocument). Frustratingly, the only workaround I could find, was to serialise the XMLDocument to a string so it can be loaded in to the ActiveXObject *sigh*.

if (window.ActiveXObject || "ActiveXObject" in window) {
  var xslt = new ActiveXObject("Msxml2.XSLTemplate");
  var xslDoc = new ActiveXObject("Msxml2.FreeThreadedDOMDocument");

  var serializer = new XMLSerializer();
  var text = serializer.serializeToString(xsl);

  xslDoc.loadXML(text);
  xslt.stylesheet = xslDoc;

  var xslProc = xslt.createProcessor();
  xslProc.input = xml;
  xslProc.addParameter("resPath", configSettings.areaPath + 'Content/img/CreateResources');
  xslProc.transform();

  var output = xslProc.output;
  document.getElementById("clause-content").innerHTML = output;
}

Fortunately the IE 9-11 XSLT processor also supports the passing of parameter values.

And the final result

 

Advertisements