From the latest weekly release of Opera (v 9.01 apparently) we discover,
8518,3466,383 - Desktop Team - by Desktop Team
Added support for exls:node-set in XSLT
NOTE: I can only hope that by exls:nose-set, they mean exsl:node-set. If no, then this could does present a problem, but an easily fixed problem, which is always a good thing.
Of course, the ability to combine the document function with the EXSLT node-set() function can be a powerful tool, as this allows the ability to access data that is outside the current context document being processed, to then dice and splice this data into variouses pieces, processing dynamically generated XML fragments via the exsl:node-set function. None-the-less, this (exsl:node-set) is still an EXTREMELY nice addition in which I am truly grateful to the folks at Opera for adding to the XSLT mix.
That said,
I do find myself wondering why the document function, who’s only function is to use the standard HTTP GET method to access an external document, is having such a tough time making the weekly build ‘cut’ for a web browser company. I mean, if theres ANYTHING a web browser is required to be able to accomplish above and beyond ANYTHING else, its that of implementing support for the HTTP GET method such that it can access external documents.
But, I also would rather praise Opera for showing signs of XSLT life, than suggest by ANY stretch of the imagination that their work in this area is underappreciated. It most certainly is not, and in fact it is VERY appreciated, as this adds to each of our web development tool bags some powerful functionality that simply is unmatched in terms of performance, reliabilty, capabilty, functionality, and for those that implement complete XPath/XSLT 1.0 support, a cross-platform code base that allows the ability to dump a solid 90% of the Javascript stranglehold code who’s only purpose is to check to see what platform is running, and attempt to gracefully adapt to this particular platforms Javascript implementation.
And this doesn’t even touch the hair loss that comes when dealing with the various discrepancies of CSS implementations, although this is VASTLY improved over the last six-twelve months thanks to the work of the folks behind the Web Standards Project, something I don’t think we could ever be grateful enough for their work and efforts thus far.
For now, however, lets focus on the performance side of things, as the CSS stuff, while annoying when attempting to deal with the various discrepancies between browsers, doesn’t effect performance all that much (although, in some cases it can when dealing with css pseudo class, especially :hover, depending on what element its applied to and, of course, which browser is performing the rendering, and dynamic effects that take place when the :hover pseudo class is invoked), and instead is a visual issue. An important visual issue, but one that in most case doesn’t effect performance.
With this in mind, moving back to Javascript…
Even when dealing with what is in my own personal opinion, as well as tens of thousands of other developers who use Sarissa on a daily basis, the VERY BEST code there is to gracefully handle the various discrepancies between platforms, providing a cross-platform API in the process for processing XML and XSLT, sits at (with comments of which I am guessing is probably 75-100 lines at first glance) 576 lines of uncompiled Javascript code for the primary sarissa.js file which covers XML DOM and XSLT processing.
Of course, for transit, this can be compressed, but that doesn’t have anything to do with increasing performance once its uncompressed on the client, and while small, in fact increases the processing cost on the client as a result.
To contrast, the entire overbloated XML/XSLT solution that not only performs the same functionality, but also renders the basic XHTML page layout is, in total, 88 lines.
So the breakdown:
Javascript (first code block below) 576 - ~75-100 lines of comments = between 475 - 501 lines of code
XML - 28 lines of bloated (read: could easily be reduced to between 4 and 6 lines) code
XSLT - 60 lines of bloated (read: could easily be reduced to between 15-20 lines ) code
NOTE: For some reason the rendering of this code in Fx is causing additional spaces between the lines, when in fact they don’t exist in the underlying source. Please see this same script inside of the ExtensibleForge.Net Trac code browser for a more realistic size representation, with line numbers. To be fair, the code below seems even longer than it actually is due to the offset, so please keep this in mind as you scroll down through the code base.)
fixed this problem, but without CSS diving, I’m not sure why the ad’s to the right are breaking to the bottom. I prefer to use white text Kermit Green (I actually prefer white, but from what I understand Kermit Green helps provide better viewable contrast (credit: Scott Hanselman, someone who knows a thing or two about this kind of stuff ;) on a blue background for contrast, but I made need to pull out my inline css to see if this fixes that problem.
Nope! Not sure what could be causing this, as even with plain old pre tags it still seems to break the right colum to the bottom of the post. Don’t have access to the underlying CSS files, so not sure what I can do to fix this at the moment. Will find out, and fix when/if possible
Fixed :)
/**
* ====================================================================
* About
* ====================================================================
* Sarissa is an ECMAScript library acting as a cross-browser wrapper for native XML APIs.
* The library supports Gecko based browsers like Mozilla and Firefox,
* Internet Explorer (5.5+ with MSXML3.0+), Konqueror, Safari and a little of Opera
* @version 0.9.7
* @author: Manos Batsis, mailto: mbatsis at users full stop sourceforge full stop net
* ====================================================================
* Licence
* ====================================================================
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 or
* the GNU Lesser General Public License version 2.1 as published by
* the Free Software Foundation (your choice between the two).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License or GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* or GNU Lesser General Public License along with this program; if not,
* write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* or visit http://www.gnu.org
*
*/
/**
* <p>Sarissa is a utility class. Provides "static" methods for DOMDocument and
* XMLHTTP objects, DOM Node serializatrion to XML strings and other goodies.</p>
* @constructor
*/
function Sarissa(){};
/** @private */
Sarissa.PARSED_OK = "Document contains no parsing errors";
Sarissa.PARSED_EMPTY = "Document is empty"
Sarissa.PARSED_UNKNOWN_ERROR = "Not well-formed or other error";
var _sarissa_iNsCounter = 0;
var _SARISSA_IEPREFIX4XSLPARAM = "";
var _SARISSA_HAS_DOM_IMPLEMENTATION = document.implementation && true;
var _SARISSA_HAS_DOM_CREATE_DOCUMENT = _SARISSA_HAS_DOM_IMPLEMENTATION && document.implementation.createDocument;
var _SARISSA_HAS_DOM_FEATURE = _SARISSA_HAS_DOM_IMPLEMENTATION && document.implementation.hasFeature;
var _SARISSA_IS_MOZ = _SARISSA_HAS_DOM_CREATE_DOCUMENT && _SARISSA_HAS_DOM_FEATURE;
var _SARISSA_IS_SAFARI = (navigator.userAgent && navigator.vendor && (navigator.userAgent.toLowerCase().indexOf("applewebkit") != -1 || navigator.vendor.indexOf("Apple") != -1));
var _SARISSA_IS_IE = document.all && window.ActiveXObject && navigator.userAgent.toLowerCase().indexOf("msie") > -1 && navigator.userAgent.toLowerCase().indexOf("opera") == -1;
if(window && (!window.Node || !window.Node.ELEMENT_NODE)){
window.Node = {ELEMENT_NODE: 1, ATTRIBUTE_NODE: 2, TEXT_NODE: 3, CDATA_SECTION_NODE: 4, ENTITY_REFERENCE_NODE: 5, ENTITY_NODE: 6, PROCESSING_INSTRUCTION_NODE: 7, COMMENT_NODE: 8, DOCUMENT_NODE: 9, DOCUMENT_TYPE_NODE: 10, DOCUMENT_FRAGMENT_NODE: 11, NOTATION_NODE: 12};
};
// IE initialization
if(_SARISSA_IS_IE){
// for XSLT parameter names, prefix needed by IE
_SARISSA_IEPREFIX4XSLPARAM = "xsl:";
// used to store the most recent ProgID available out of the above
var _SARISSA_DOM_PROGID = "";
var _SARISSA_XMLHTTP_PROGID = "";
var _SARISSA_DOM_XMLWRITER = "";
/**
* Called when the Sarissa_xx.js file is parsed, to pick most recent
* ProgIDs for IE, then gets destroyed.
* @private
* @param idList an array of MSXML PROGIDs from which the most recent will be picked for a given object
* @param enabledList an array of arrays where each array has two items; the index of the PROGID for which a certain feature is enabled
*/
Sarissa.pickRecentProgID = function (idList){
// found progID flag
var bFound = false;
for(var i=0; i < idList.length && !bFound; i++){
try{
var oDoc = new ActiveXObject(idList[i]);
o2Store = idList[i];
bFound = true;
}catch (objException){
// trap; try next progID
};
};
if (!bFound)
throw "Could not retreive a valid progID of Class: " + idList[idList.length-1]+". (original exception: "+e+")";
idList = null;
return o2Store;
};
// pick best available MSXML progIDs
_SARISSA_DOM_PROGID = null;
_SARISSA_THREADEDDOM_PROGID = null;
_SARISSA_XSLTEMPLATE_PROGID = null;
_SARISSA_XMLHTTP_PROGID = null;
if(!window.XMLHttpRequest){
/**
* Emulate XMLHttpRequest
* @constructor
*/
XMLHttpRequest = function() {
if(!_SARISSA_XMLHTTP_PROGID){
_SARISSA_XMLHTTP_PROGID = Sarissa.pickRecentProgID(["Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"]);
};
return new ActiveXObject(_SARISSA_XMLHTTP_PROGID);
};
};
// we dont need this anymore
//============================================
// Factory methods (IE)
//============================================
// see non-IE version
Sarissa.getDomDocument = function(sUri, sName){
if(!_SARISSA_DOM_PROGID){
_SARISSA_DOM_PROGID = Sarissa.pickRecentProgID(["Msxml2.DOMDocument.5.0", "Msxml2.DOMDocument.4.0", "Msxml2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"]);
};
var oDoc = new ActiveXObject(_SARISSA_DOM_PROGID);
// if a root tag name was provided, we need to load it in the DOM object
if (sName){
// create an artifical namespace prefix
// or reuse existing prefix if applicable
var prefix = "";
if(sUri){
if(sName.indexOf(":") > 1){
prefix = sName.substring(0, sName.indexOf(":"));
sName = sName.substring(sName.indexOf(":")+1);
}else{
prefix = "a" + (_sarissa_iNsCounter++);
};
};
// use namespaces if a namespace URI exists
if(sUri){
oDoc.loadXML('<' + prefix+':'+sName + " xmlns:" + prefix + "="" + sUri + """ + " />");
} else {
oDoc.loadXML('<' + sName + " />");
};
};
return oDoc;
};
// see non-IE version
Sarissa.getParseErrorText = function (oDoc) {
var parseErrorText = Sarissa.PARSED_OK;
if(oDoc.parseError.errorCode != 0){
parseErrorText = "XML Parsing Error: " + oDoc.parseError.reason +
"nLocation: " + oDoc.parseError.url +
"nLine Number " + oDoc.parseError.line + ", Column " +
oDoc.parseError.linepos +
":n" + oDoc.parseError.srcText +
"n";
for(var i = 0; i < oDoc.parseError.linepos;i++){
parseErrorText += "-";
};
parseErrorText += "^n";
}
else if(oDoc.documentElement == null){
parseErrorText = Sarissa.PARSED_EMPTY;
};
return parseErrorText;
};
// see non-IE version
Sarissa.setXpathNamespaces = function(oDoc, sNsSet) {
oDoc.setProperty("SelectionLanguage", "XPath");
oDoc.setProperty("SelectionNamespaces", sNsSet);
};
/**
* Basic implementation of Mozilla's XSLTProcessor for IE.
* Reuses the same XSLT stylesheet for multiple transforms
* @constructor
*/
XSLTProcessor = function(){
if(!_SARISSA_XSLTEMPLATE_PROGID){
_SARISSA_XSLTEMPLATE_PROGID = Sarissa.pickRecentProgID(["Msxml2.XSLTemplate.5.0", "Msxml2.XSLTemplate.4.0", "MSXML2.XSLTemplate.3.0"]);
};
this.template = new ActiveXObject(_SARISSA_XSLTEMPLATE_PROGID);
this.processor = null;
};
/**
* Impoprts the given XSLT DOM and compiles it to a reusable transform
* @argument xslDoc The XSLT DOMDocument to import
*/
XSLTProcessor.prototype.importStylesheet = function(xslDoc){
if(!_SARISSA_THREADEDDOM_PROGID){
_SARISSA_THREADEDDOM_PROGID = Sarissa.pickRecentProgID(["Msxml2.FreeThreadedDOMDocument.5.0", "MSXML2.FreeThreadedDOMDocument.4.0", "MSXML2.FreeThreadedDOMDocument.3.0"]);
_SARISSA_DOM_XMLWRITER = Sarissa.pickRecentProgID(["Msxml2.MXXMLWriter.5.0", "Msxml2.MXXMLWriter.4.0", "Msxml2.MXXMLWriter.3.0", "MSXML2.MXXMLWriter", "MSXML.MXXMLWriter", "Microsoft.XMLDOM"]);
};
// convert stylesheet to free threaded
var converted = new ActiveXObject(_SARISSA_THREADEDDOM_PROGID);
converted.loadXML(xslDoc.xml);
this.template.stylesheet = converted;
this.processor = this.template.createProcessor();
// (re)set default param values
this.paramsSet = new Array();
};
/**
* Transform the given XML DOM
* @argument sourceDoc The XML DOMDocument to transform
* @return The transformation result as a DOM Document
*/
XSLTProcessor.prototype.transformToDocument = function(sourceDoc){
this.processor.input = sourceDoc;
var outDoc = new ActiveXObject(_SARISSA_DOM_XMLWRITER);
this.processor.output = outDoc;
this.processor.transform();
var oDoc = new ActiveXObject(_SARISSA_DOM_PROGID);
oDoc.loadXML(outDoc.output+"");
return oDoc;
/*
this.processor.input = sourceDoc;
var outDoc = Sarissa.getDomDocument();
this.processor.output = outDoc;
this.processor.transform();
return outDoc;*/
};
/**
* Set global XSLT parameter of the imported stylesheet
* @argument nsURI The parameter namespace URI
* @argument name The parameter base name
* @argument value The new parameter value
*/
XSLTProcessor.prototype.setParameter = function(nsURI, name, value){
/* nsURI is optional but cannot be null */
if(nsURI){
this.processor.addParameter(name, value, nsURI);
}else{
this.processor.addParameter(name, value);
};
/* update updated params for getParameter */
if(!this.paramsSet[""+nsURI]){
this.paramsSet[""+nsURI] = new Array();
};
this.paramsSet[""+nsURI][name] = value;
};
/**
* Gets a parameter if previously set by setParameter. Returns null
* otherwise
* @argument name The parameter base name
* @argument value The new parameter value
* @return The parameter value if reviously set by setParameter, null otherwise
*/
XSLTProcessor.prototype.getParameter = function(nsURI, name){
nsURI = nsURI || "";
if(this.paramsSet[nsURI] && this.paramsSet[nsURI][name]){
return this.paramsSet[nsURI][name];
}else{
return null;
};
};
}else{ /* end IE initialization, try to deal with real browsers now ;-) */
if(_SARISSA_HAS_DOM_CREATE_DOCUMENT){
/**
* <p>Ensures the document was loaded correctly, otherwise sets the
* parseError to -1 to indicate something went wrong. Internal use</p>
* @private
*/
Sarissa.__handleLoad__ = function(oDoc){
Sarissa.__setReadyState__(oDoc, 4);
};
/**
* <p>Attached by an event handler to the load event. Internal use.</p>
* @private
*/
_sarissa_XMLDocument_onload = function(){
Sarissa.__handleLoad__(this);
};
/**
* <p>Sets the readyState property of the given DOM Document object.
* Internal use.</p>
* @private
* @argument oDoc the DOM Document object to fire the
* readystatechange event
* @argument iReadyState the number to change the readystate property to
*/
Sarissa.__setReadyState__ = function(oDoc, iReadyState){
oDoc.readyState = iReadyState;
oDoc.readystate = iReadyState;
if (oDoc.onreadystatechange != null && typeof oDoc.onreadystatechange == "function")
oDoc.onreadystatechange();
};
Sarissa.getDomDocument = function(sUri, sName){
var oDoc = document.implementation.createDocument(sUri?sUri:null, sName?sName:null, null);
if(!oDoc.onreadystatechange){
/**
* <p>Emulate IE's onreadystatechange attribute</p>
*/
oDoc.onreadystatechange = null;
};
if(!oDoc.readyState){
/**
* <p>Emulates IE's readyState property, which always gives an integer from 0 to 4:</p>
* <ul><li>1 == LOADING,</li>
* <li>2 == LOADED,</li>
* <li>3 == INTERACTIVE,</li>
* <li>4 == COMPLETED</li></ul>
*/
oDoc.readyState = 0;
};
if(!oDoc.parseError){
oDoc.parseError = new SarissaParseError();
};
oDoc.addEventListener("load", _sarissa_XMLDocument_onload, false);
return oDoc;
};
if(window.XMLDocument){
//if(window.XMLDocument) , now mainly for opera
}// TODO: check if the new document has content before trying to copynodes, check for error handling in DOM 3 LS
else if(document.implementation && document.implementation.hasFeature && document.implementation.hasFeature('LS', '3.0')){
/**
* <p>Factory method to obtain a new DOM Document object</p>
* @argument sUri the namespace of the root node (if any)
* @argument sUri the local name of the root node (if any)
* @returns a new DOM Document
*/
Sarissa.getDomDocument = function(sUri, sName){
var oDoc = document.implementation.createDocument(sUri?sUri:null, sName?sName:null, null);
return oDoc;
};
}
else {
Sarissa.getDomDocument = function(sUri, sName){
var oDoc = document.implementation.createDocument(sUri?sUri:null, sName?sName:null, null);
// looks like safari does not create the root element for some unknown reason
if(oDoc && (sUri || sName) && !oDoc.documentElement){
oDoc.appendChild(oDoc.createElementNS(sUri, sName));
};
return oDoc;
};
};
};//if(_SARISSA_HAS_DOM_CREATE_DOCUMENT)
};
//==========================================
// Common stuff
//==========================================
if(!window.DOMParser){
if(_SARISSA_IS_SAFARI){
/*
* DOMParser is a utility class, used to construct DOMDocuments from XML strings
* @constructor
*/
DOMParser = function() {
};
/**
* Construct a new DOM Document from the given XMLstring
* @param sXml the given XML string
* @param contentType the content type of the document the given string represents (one of text/xml, application/xml, application/xhtml+xml).
* @return a new DOM Document from the given XML string
*/
DOMParser.prototype.parseFromString = function(sXml, contentType){
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", "data:text/xml;charset=utf-8," + encodeURIComponent(sXml), false);
xmlhttp.send(null);
return xmlhttp.responseXML;
};
}else if(Sarissa.getDomDocument && Sarissa.getDomDocument() && Sarissa.getDomDocument(null, "bar").xml){
DOMParser = function() {
};
DOMParser.prototype.parseFromString = function(sXml, contentType){
var doc = Sarissa.getDomDocument();
doc.loadXML(sXml);
return doc;
};
};
};
if(!window.document.importNode && _SARISSA_IS_IE){
try{
/**
* Implements importNode for the current window document in IE using innerHTML.
* Testing showed that DOM was multiple times slower than innerHTML for this,
* sorry folks. If you encounter trouble (who knows what IE does behind innerHTML)
* please gimme a call.
* @param oNode the Node to import
* @param bChildren whether to include the children of oNode
* @returns the imported node for further use
*/
window.document.importNode = function(oNode, bChildren){
var importNode = document.createElement("div");
if(bChildren){
importNode.innerHTML = new XMLSerializer().serializeToString(oNode);
}else{
importNode.innerHTML = new XMLSerializer().serializeToString(oNode.cloneNode(false));
};
return importNode.getElementsByTagName("*")[0];
};
}catch(e){};
};
if(!window.Sarissa.getParseErrorText){
/**
* <p>Returns a human readable description of the parsing error. Usefull
* for debugging. Tip: append the returned error string in a <pre>
* element if you want to render it.</p>
* <p>Many thanks to Christian Stocker for the initial patch.</p>
* @argument oDoc The target DOM document
* @returns The parsing error description of the target Document in
* human readable form (preformated text)
*/
Sarissa.getParseErrorText = function (oDoc){
var parseErrorText = Sarissa.PARSED_OK;
if(!oDoc.documentElement){
parseErrorText = Sarissa.PARSED_EMPTY;
}
else if(oDoc.documentElement.tagName == "parsererror"){
parseErrorText = oDoc.documentElement.firstChild.data;
parseErrorText += "n" + oDoc.documentElement.firstChild.nextSibling.firstChild.data;
}
else if(oDoc.getElementsByTagName("parsererror").length > 0){
var parsererror = oDoc.getElementsByTagName("parsererror")[0];
parseErrorText = Sarissa.getText(parsererror, true)+"n";
}else if(oDoc.parseError && oDoc.parseError.errorCode != 0){
parseErrorText = Sarissa.PARSED_UNKNOWN_ERROR;
};
return parseErrorText;
};
};
Sarissa.getText = function(oNode, deep){
var s = "";
var nodes = oNode.childNodes;
for(var i=0; i < nodes.length; i++){
var node = nodes[i];
var nodeType = node.nodeType;
if(nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE){
s += node.data;
}else if(deep == true
&& (nodeType == Node.ELEMENT_NODE
|| nodeType == Node.DOCUMENT_NODE
|| nodeType == Node.DOCUMENT_FRAGMENT_NODE)){
s += Sarissa.getText(node, true);
};
};
return s;
};
if(!window.XMLSerializer
&& Sarissa.getDomDocument
&& Sarissa.getDomDocument("","foo", null).xml){
/**
* Utility class to serialize DOM Node objects to XML strings
* @constructor
*/
XMLSerializer = function(){};
/**
* Serialize the given DOM Node to an XML string
* @param oNode the DOM Node to serialize
*/
XMLSerializer.prototype.serializeToString = function(oNode) {
return oNode.xml;
};
};
/**
* strips tags from a markup string
*/
Sarissa.stripTags = function (s) {
return s.replace(/<[^>]+>/g,"");
};
/**
* <p>Deletes all child nodes of the given node</p>
* @argument oNode the Node to empty
*/
Sarissa.clearChildNodes = function(oNode) {
// need to check for firstChild due to opera 8 bug with hasChildNodes
while(oNode.firstChild){
oNode.removeChild(oNode.firstChild);
};
};
/**
* <p> Copies the childNodes of nodeFrom to nodeTo</p>
* <p> <b>Note:</b> The second object's original content is deleted before
* the copy operation, unless you supply a true third parameter</p>
* @argument nodeFrom the Node to copy the childNodes from
* @argument nodeTo the Node to copy the childNodes to
* @argument bPreserveExisting whether to preserve the original content of nodeTo, default is false
*/
Sarissa.copyChildNodes = function(nodeFrom, nodeTo, bPreserveExisting) {
if((!nodeFrom) || (!nodeTo)){
throw "Both source and destination nodes must be provided";
};
if(!bPreserveExisting){
Sarissa.clearChildNodes(nodeTo);
};
var ownerDoc = nodeTo.nodeType == Node.DOCUMENT_NODE ? nodeTo : nodeTo.ownerDocument;
var nodes = nodeFrom.childNodes;
if(/*(!_SARISSA_IS_IE) && */ownerDoc.importNode) {
for(var i=0;i < nodes.length;i++) {
nodeTo.appendChild(ownerDoc.importNode(nodes[i], true));
};
}else{
for(var i=0;i < nodes.length;i++) {
nodeTo.appendChild(nodes[i].cloneNode(true));
};
};
};
/**
* <p> Moves the childNodes of nodeFrom to nodeTo</p>
* <p> <b>Note:</b> The second object's original content is deleted before
* the move operation, unless you supply a true third parameter</p>
* @argument nodeFrom the Node to copy the childNodes from
* @argument nodeTo the Node to copy the childNodes to
* @argument bPreserveExisting whether to preserve the original content of nodeTo, default is
*/
Sarissa.moveChildNodes = function(nodeFrom, nodeTo, bPreserveExisting) {
if((!nodeFrom) || (!nodeTo)){
throw "Both source and destination nodes must be provided";
};
if(!bPreserveExisting){
Sarissa.clearChildNodes(nodeTo);
};
var nodes = nodeFrom.childNodes;
// if within the same doc, just move, else copy and delete
if(nodeFrom.ownerDocument == nodeTo.ownerDocument){
while(nodeFrom.firstChild){
nodeTo.appendChild(nodeFrom.firstChild);
};
}else{
var ownerDoc = nodeTo.nodeType == Node.DOCUMENT_NODE ? nodeTo : nodeTo.ownerDocument;
if(ownerDoc.importNode /*&& (!_SARISSA_IS_IE)*/) {
for(var i=0;i < nodes.length;i++) {
nodeTo.appendChild(ownerDoc.importNode(nodes[i], true));
};
}else{
for(var i=0;i < nodes.length;i++) {
nodeTo.appendChild(nodes[i].cloneNode(true));
};
};
Sarissa.clearChildNodes(nodeFrom);
};
};
/**
* <p>Serialize any object to an XML string. All properties are serialized using the property name
* as the XML element name. Array elements are rendered as <code>array-item</code> elements,
* using their index/key as the value of the <code>key</code> attribute.</p>
* @argument anyObject the object to serialize
* @argument objectName a name for that object
* @return the XML serializationj of the given object as a string
*/
Sarissa.xmlize = function(anyObject, objectName, indentSpace){
indentSpace = indentSpace?indentSpace:'';
var s = indentSpace + '<' + objectName + '>';
var isLeaf = false;
if(!(anyObject instanceof Object) || anyObject instanceof Number || anyObject instanceof String
|| anyObject instanceof Boolean || anyObject instanceof Date){
s += Sarissa.escape(""+anyObject);
isLeaf = true;
}else{
s += "n";
var itemKey = '';
var isArrayItem = anyObject instanceof Array;
for(var name in anyObject){
s += Sarissa.xmlize(anyObject[name], (isArrayItem?"array-item key=""+name+""":name), indentSpace + " ");
};
s += indentSpace;
};
return s += (objectName.indexOf(' ')!=-1?"</array-item>n":"</" + objectName + ">n");
};
/**
* Escape the given string chacters that correspond to the five predefined XML entities
* @param sXml the string to escape
*/
Sarissa.escape = function(sXml){
return sXml.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
};
/**
* Unescape the given string. This turns the occurences of the predefined XML
* entities to become the characters they represent correspond to the five predefined XML entities
* @param sXml the string to unescape
*/
Sarissa.unescape = function(sXml){
return sXml.replace(/'/g,"'")
.replace(/"/g,""")
.replace(/>/g,">")
.replace(/</g,"<")
.replace(/&/g,"&");
};
// EOF
And here’s the equivalent, and in fact overinflated (see this post from a while back to understand why I chose to overinflate this code… there is a purpose, but it could just as easily be avoided, and the XML file that is sent to the browser wouldn’t even require 1/8 of this code base before being able to move forward with its ultra fast page rendering using each platforms (for the most part) ultra fast XSLT processor.) version of XML/XSLT that allows the ability to determine what the requesting browser happens to be, accessing a browser specific XML data file to then access the browser specific CSS and Javascript files specific to that platform.
XML file (dynamically sets the claimed browser on the server to then validate this through the system-property(’xsl:vendor’) to combat user-agent spoofing… again, see the above linked post from last December for more detail as to why this was implemented, and why its not all that necessary.)
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="SessionConfig.xsl"?>
<my:session xmlns:my="http://channelxml.com/explorations/session">
<my:validation>
<my:UserAgentInvalidDoc>./UserAgentInvalidDoc.xml</my:UserAgentInvalidDoc>
<my:browser>Firefox</my:browser>
<bVend:bVendors xmlns:bVend="http://channelxml.com/explorations/browservendors">
<bVend:vBrowser name="IE">
<bVend:vendor sourceXML="validIE.xml">Microsoft</bVend:vendor>
</bVend:vBrowser>
<bVend:vBrowser name="Firefox">
<bVend:vendor sourceXML="validFirefox.xml">Transformiix</bVend:vendor>
</bVend:vBrowser>
<bVend:vBrowser name="Mozilla">
<bVend:vendor sourceXML="validMozilla.xml">Transformiix</bVend:vendor>
</bVend:vBrowser>
<bVend:vBrowser name="Netscape">
<bVend:vendor sourceXML="validNetscape.xml">Transformiix</bVend:vendor>
</bVend:vBrowser>
<bVend:vBrowser name="AppleMAC-Safari">
<bVend:vendor sourceXML="validSafari.xml">libxslt</bVend:vendor>
</bVend:vBrowser>
<bVend:vBrowser name="Opera">
<bVend:vendor sourceXML="validOpera.xml">Opera</bVend:vendor>
</bVend:vBrowser>
</bVend:bVendors>
</my:validation>
</my:session>
Total lines of XML - 28
SessionConfig.xsl file
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="http://channelxml.com/explorations/session" xmlns="http://www.w3.org/1999/xhtml"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:bVend="http://channelxml.com/explorations/browservendors"
xmlns:page="http://channelxml.com/page/output" exclude-result-prefixes="my html page bVend">
<xsl:output doctype-system="/resources/dtd/xhtml1-strict.dtd"
doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" cdata-section-elements="script"
indent="yes" method="xml"/>
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="my:session">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="my:validation">
<xsl:variable name="browser" select="my:browser"/>
<xsl:apply-templates select="bVend:bVendors/bVend:vBrowser[@name = $browser]">
<xsl:with-param name="invalidDoc" select="document(my:UserAgentInvalidDoc)"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="bVend:vBrowser">
<xsl:param name="invalidDoc"/>
<xsl:variable name="vendor" select="system-property('xsl:vendor')"/>
<xsl:choose>
<xsl:when test="bVend:vendor = $vendor">
<xsl:apply-templates select="document(bVend:vendor/@sourceXML)"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="document($invalidDoc)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="page:output">
<xsl:apply-templates select="page:html"/>
</xsl:template>
<xsl:template match="page:html">
<html>
<xsl:apply-templates/>
</html>
</xsl:template>
<xsl:template match="page:head">
<head>
<xsl:apply-templates/>
</head>
</xsl:template>
<xsl:template match="page:title">
<title>
<xsl:apply-templates/>
</title>
</xsl:template>
<xsl:template match="page:body">
<body>
<xsl:apply-templates/>
</body>
</xsl:template>
<xsl:template match="page:heading">
<h1 style="font-size:{@size}">
<xsl:apply-templates/>
</h1>
</xsl:template>
</xsl:stylesheet>
Total lines of XSLT - 60
[UPDATE] I should add to this the XML file that the above process will access once it has determined which file is should access using the above code base. However, please keep in mind that the three of these files combined represent a completed (although quite simple) transformation and resulting page, where as the Javascript only represents the potential to access the (same) XML and XSLT file to then process these to gain the same resulting output.
That said, the magic that is really responsible for making this all happen is, in fact, only one line of code, and it looks like this,
<?xml-stylesheet type="text/xsl" href="SessionConfig.xsl"?>
The accessed XML file if it was determined that Mozilla (more accurately, Sea Monkey) is the client browser,
<?xml version="1.0" encoding="UTF-8"?>
<page:output xmlns:page="http://channelxml.com/page/output">
<page:html>
<page:head>
<page:title>Hooray, You're a Valid Mozilla-based Browser (Mozilla Application Suite, to be more specific)! Here's a Cookie</page:title>
</page:head>
<page:body>
<page:heading size="large">Hooray, You're a Valid Mozilla-based Browser (Mozilla Application Suite, to be more specific)! Here's a Cookie.</page:heading>
</page:body>
</page:html>
</page:output>
[/UPDATE]
Please note: I am not suggesting that you make XSLT the core or your web applications, and instead use it as a way to pre-filter the client browser using a manageable and EASILY understandable code base, with tool support that is *VAST* to say the least.
Of course, for those willing to bite the bullet and learn the language, it does present an EXTREMELY powerful solution which in all honesty is one of the simplest languages ever developed, but because its forces most developers to think differently than they do now, it can be a bit tricky to get used to at first. Given its DSSSL roots, which has its roots directly embedded into Lisp, its understandable why this is, but once the light clicks on…
It’s all just smiles and happy thoughts from that point forward. :D
But again, thats not the point. The point instead is this…
By using XSLT as as a way of building the framework of each page, building from a default standards based Javascript file (which, of course, if/when the platforms provide better standards supports, can be used instead without a single line of additional code, and only a minor change in a master XML definition file.) that is then modified to allow for the peculiarities of each individual platform, it then becomes a simple matter of using a declarative style approach to web page development in which the same XML framework can be used to make simple declarations in regards to what needs to do (e.g. write out a <script …> element referencing the proper script file for a code base particular to the client platform), and then let the specifics of each browsers modified code base deal with the each platforms peculiar nature, while still using the same cross-platform API so masterfully crafter by Manos as part of his Sarissa project.
In fact, I have in my posession a framework I developed a while back that extends from the above XML/XSLT code base that does just that, using the same API that Manos developed, but limiting the Javascript to just the code necessary to implement each various function on each various platform using a declarative style approach. While I do plan to eventually release this code, its still undetermined how and when I will go about doing this.
But I do see some potential here to help provide some incentive to the folks at Opera to provide the necessary functonality for this platform to work and work well in the 9.x release that meets the XPath 1.0/XSLT 1.0 criteria. So I guess we’ll see how that goes.
One thing I will mention… This platform not only makes HEAVY use of CSS, but in fact without CSS, it wouldn’t even work. If and when it is seen just how much I mean by HEAVY, I have no doubt in my mind many a smiles from the CSS zealots of the world will be the result.
But for now, however, until at very least the document function finds its way into a weekly build, it won’t even work at all (on Opera I should add… it works FANTASTIC on IE 5+, Mozilla, and Safari ;)
Let the countdown to document function support begin.
Oh, and one last thing…
Opera… Thanks for the exsl:node-set implementation. In all honesty and sincerity, it is VERY MUCH appreciated!


> Opera... Thanks for the exsl:node-set implementation.
Yeah right. Though you seem to hail the corss-browser abilities of using XSLT, this one is not amongst them, considering:
Firefox: not supported. Why? I don't know. They don't provide their own alternative either. In fact, it gives a pretty ugly error:
[Exception... "Component returned failure code: 0x8060000e [nsIXSLTProcessor.transformToDocument]" nsresult: "0x8060000e (<unknown>)" location: "JS frame :: "....
Microsoft has its own extension functions (sounds familiar? JS stories repeat themselves for new standards... alas) and for exslt it will return the following:
Namespace 'http://exslt.org/common' does not contain any functions.
So, here we go again. When we want to use something useful, exslt:node-set() in this case, we will need to check the vendor. There it is, bouncing back at you, that stuff you thought you lost when moving from Javascript to XSLT (and still using bits of Javascript). This is what it will look like
<xsl:if test="system-property('xsl:vendor') = 'Opera'">
... you can use exslt:node-set() here....
</xsl:if>
Opera supporting exslt? Nice gesture, but will be close to useless as long as Firefox, and more specifically, IE, has no normal way of dealing with it.
But I do have another suggestion: using document() the other way in Opera. I haven't tested this myself, but knowing that XSLT is in fact valid XML, you can turn the external document into a XSLT stylesheet, with one large variable. Now you can use the combination of or together with exslt:node-set($yourdoc-variable) to get to your document. For other browsers you can still access the same data, just change your root-XPath a bit.
Ok, there's one glitch that will proof unsurpassable: and cannot be used with AVT (Attribute Value Templates). Hence, the variability of using document() falls back because you have to hardcode your documents for use in Opera (or you have to resort to some sophisticated tricks, which are all well-documented on the xsl-list).
Best wishes,
Abel Braaksma
Nuntia B.V.
@Abel,
>> using document() the other way in Opera
Which other way would that be? What you are refering to is,
<xsl:variable select="document('')"/>
which would then allow you to reference the transformation file itself.
There's one problem, though: There *IS NO* document() function in Opera.
>> (or you have to resort to some sophisticated tricks, which are all well-documented on the xsl-list).