12.2. JavaScript Objects
Although it's not an object-oriented language, JavaScript is an object-based language. This means that, although it might not be as powerful as PHP, Ruby, or Java, it is still pretty darn powerful. Add the fact that it is currently the best/only choice available, and you'll quickly understand why objects are important.
Although there are several ways to create objects in JavaScript, I usually use only two. The first method of creating an object in JavaScript is simply a matter of writing a function and assigning it to a variable using the new operator to create an instance, as shown in Listing 12-1.
Listing 12-1. Example function Class Constructor
function word() {
var _setCount = 0; // Protected variable
this.theWord; // Public property
this.setWord = _setWord; // Public method setWord
this.getWord = _getWord; // Public method getWord
this.count = _getSetCount; // Public method count
function _setWord(theWord) {
// Public exposed as getWord
this.theWord = theWord;
_incrementCount();
}
function _getWord() { // Public exposed as setWord
return(this.theWord);
}
function _getSetCount() { // Public exposed as count
return(_setCount);
}
function _incrementCount() { // Private method
++_setCount;
}
}
var myInstance = new word();
|
Now we have an instance of the property word assigned to the variable myInstance, and the only question is, how do we use it? Thankfully, the notation for addressing properties and methods is a relatively standard instancename.property or instancename.method(). If you're looking at the constructor, the way to distinguish them is that they are all preceded by the this keyword. The way to tell which are properties and which are methods is that methods always are equal to a function. It is important to point out that the parentheses are omitted because including them would cause the method to be invoked as well as exposed.
Although the previous class constructor is essentially useless, it does show the details of how to create a constructor. It has private members, _setCount, and private methods, _incrementCount. Also, as explained previously, it has both public properties, as in theWord, and public methods, as in setWord, getWord, and getSetCount. Of course, an example that is actually useful might not have all of these.
12.2.1. Collections
I might be wrong, but I am of the opinion that the most useful type of data structure that has ever been conceived, excluding the DOM, is perhaps an associative array. If you're unfamiliar with this type of data structure, information is stored in name/value pairs. If you know the name, you can find the value. And the value isn't limited to any particular data type; come to think of it, neither is the name. A good use would be to cache XSL style sheets because they usually don't change very often. After they're cached, it is no longer necessary to bother the web server to get them; all that is necessary is to retrieve them from the cache. However, there is one danger, and that danger is caching information that shouldn't be cached because someone else might change it, as in the results of database queries.
Listing 12-2 is an example of a constructor for a lightweight cache/associative array. The single private property, _cache, is a JavaScript object that is the cache itself. There are three public methods to handle inserting name/value pairs, retrieving values, and purging either selected name/value pairs or the entire contents of the cache.
Listing 12-2. Cache Class Constructor (Associative Array)
<!-- <![CDATA[
/*
Class: Cache
Function: Cache
Purpose: To act as a client-side cache(associative array).
Data are stored as name/value pairs.
*/
function Cache() {
var _cache = new Object();
// Object to store information
var _namesArray = new Array(); // Array for names
this.insert = _insert; // Method: cache an object
this.retrieve = _retrieve; // Method: retrieve object
this.purge = _purge; // Method: purge object(s)
this.names = _names; // Method: return names
/*
Function: _insert
Method: insert
Purpose: Inserts a name/value pair into the cache.
*/
function _insert(name,value) {
_cache[name] = value; // Cache object
_namesArray.push(name); // Store name
}
/*
Function: _retrieve
Method: retrieve
Purpose: Retrieves a value from the cache using a name.
*/
function _retrieve(name) {
if(typeof(_cache[name]) == 'undefined')
return(null); // Object not cached
else
return(_cache[name]); // Return object
}
/*
Function: _purge
Method: purge
Purpose: Purges one or more name/value pairs from
the cache.
*/
function _purge() {
if(arguments.length == 0) {
_cache = new Object(); // Create new cache object
_namesArray = new Array(); // Create new names array
} else {
var singleName;
_namesArray = new Array(); // Create new names array
for(var i=0;i < arguments.length;i++)
_cache[arguments[i]] = null;
for(singleName in _cache)
if(_cache[singleName] != null)
_namesArray.push(singleName);
}
}
/*
Function: _names
Method: names
Purpose: Returns an array consisting of the names from the
cache.
*/
function _names() {
return(_namesArray);
}
}
// ]]> -->
|
As with the previous example, it is necessary to create an instance of the object before using it. Listing 12-3 shows the object being put through its paces, along with the expected results shown in the comments.
Listing 12-3. Listing Head Here
var magicWords = new Cache();
magicWords.insert(1,'xyzzy'); // Insert key = 1, value = 'xyzzy'
magicWords.insert(2,'plugh'); // Insert key = 2, value = 'plugh'
magicWords.insert(3,'plover');
// Insert key = 3, value = 'plover'
alert(magicWords.names()); // 1,2,3
alert(magicWords.retrieve(1)); // 'xyzzy'
alert(magicWords.retrieve(2)); // 'plugh'
magicWords.purge(3);
// Purge key/value pair - key = 3
alert(magicWords.retrieve(3)); // null
alert(magicWords.names()); // 1,2
magicWords.purge(); // Purge all key/value pairs
alert(magicWords.retrieve(1)); // null
|
The caching class is pretty straightforward; it is only a wrapper around a JavaScript object that has public methods that allow for changes to the object and retrieval from the object.
12.2.2. XML
Without a doubt, my biggest complaint concerning client-side XML is the lack of a single cross-browser way to create an XML document. This is one of those areas in which cross-browser coding can be a real drag because I have a tendency to create a page using a single browser. Only when I get it working in my browser of choice do I go back and try to make it work for Internet Explorer. In case you are wondering, this makes for some really ugly JavaScript, all sewn together from various mismatched parts. I may be a mad scientist, but there is something to be said for reusability.
That's the reason I cobbled together a few class constructors to neaten things up around the old lab. It's not like I'm using coasters or anything. I'm just trying to make sure that I can understand what I wrote six months from now. They say that the memory is the first thing to goor is it the hair? Whatever, I can't even remember who "they" are anyway, so it can't be important.
The first of these class constructors is to handle the details involved with using the XMLHttpRequest object. It deals with whether the browser is Microsoft Internet Explorer or any other browser, and then it creates the XMLHTTPRequest object using the syntax appropriate to the specific browser. In addition, it handles readyState changes for asynchronous requests. Unlike the previous example, which was created in much the same manner as a regular JavaScript class, this time a prototype object is created. Although they're not used for these constructors, prototypes offer the advantage of allowing for the possibility of inheritance if it is deemed necessary in the future. Listing 12-4 shows what the constructor looks like.
Listing 12-4. Cross-Browser (Gecko and IE) XMLHttp Class Constructor
<!-- <![CDATA[
XMLHttpRequest.prototype = new XMLHttpRequest;
XMLHttpRequest.prototype.constructor = XMLHttpRequest;
/*
Class: XMLHttpRequest
Function: XMLHttpRequest
Method: n/a
Description: Constructor for this class.
*/
function XMLHttpRequest() {
try {
var x = new DOMParser();
this._IE = false;
}
catch(e) { this._IE = true; };
this._XMLHttp; // XMLHttp request object
this._requestHeader = new Cache();
if(this._IE)
this._XMLHttp = new ActiveXObject('Microsoft.XMLHttp');
else
this._XMLHttp = new XMLHttpRequest();
}
// Property: GET, POST or HEAD
XMLHttpRequest.prototype.action = 'GET';
// Property: true/false
XMLHttpRequest.prototype.asynchronous = true;
// Property: package to send
XMLHttpRequest.prototype.envelope = null
/*
Class: XMLHttpRequest
Function: XMLHttpRequest_readyState
Method: readyState
Description: Returns the readyState for the XMLHttpRequest
object.
*/
function XMLHttpRequest_readyState() {
return(this._XMLHttp.readyState);
}
XMLHttpRequest.prototype.readyState = XMLHttpRequest_readyState;
/*
Class: XMLHttpRequest
Function: XMLHttpRequest_getResponseHeader
Method: getResponseHeader
Description: Returns a single response header from the last
XMLHttpRequest.
*/
function XMLHttpRequest_getResponseHeader(name) {
return(this._XMLHttp.getResponseHeader(name));
}
XMLHttpRequest.prototype.getResponseHeader =
XMLHttpRequest_getResponseHeader;
/*
Class: XMLHttpRequest
Function: XMLHttpRequest_getAllResponseHeaders
Method: getAllResponseHeaders
Description: Returns all of the response headers from
the last XMLHttpRequest.
*/
function XMLHttpRequest_getAllResponseHeaders() {
return(this._XMLHttp.getAllResponseHeaders());
}
XMLHttpRequest.prototype.getAllResponseHeaders =
XMLHttpRequest_getAllResponseHeaders;
/*
Class: XMLHttpRequest
Function: XMLHttpRequest_responseText
Method: responseText
Description: Returns the text response from the last
XMLHttpRequest.
*/
function XMLHttpRequest_responseText() {
return(this._XMLHttp.responseText);
}
XMLHttpRequest.prototype.responseText =
XMLHttpRequest_responseText;
/*
Class: XMLHttpRequest
Function: XMLHttpRequest_responseXML
Method: responseXML
Description: Returns the XML DOM document response from
the last XMLHttpRequest.
*/
function XMLHttpRequest_responseXML() {
if(this._IE) {
var xml =
new ActiveXObject('MSXML2.FreeThreadedDOMDocument.3.0');
xml.async = true;
xml.loadXML(this._XMLHttp.responseText);
return(xml);
} else
return(this._XMLHttp.responseXML);
}
XMLHttpRequest.prototype.responseXML =
XMLHttpRequest_responseXML;
/*
Class: XMLHttpRequest
Function: XMLHttpRequest_stateChangeHandler
Method: n/a
Description: Dummy state change handler for
asynchronous requests.
*/
function XMLHttpRequest_stateChangeHandler() { }
XMLHttpRequest.prototype.stateChangeHandler =
XMLHttpRequest_stateChangeHandler;
/*
Class: setRequestHeader
Function: XMLHttpRequest_setRequestHeader
Method: setRequestHeader
Description: Inserts to the cache of HTTP request headers.
*/
function XMLHttpRequest_setRequestHeader(name,value) {
this.removeRequestHeader(name);
this._requestHeader.insert(name,value);
}
XMLHttpRequest.prototype.setRequestHeader =
XMLHttpRequest_setRequestHeader;
/*
Class: setRequestHeader
Function: XMLHttpRequest_removeRequestHeader
Method: n/a
Description: Removes from the cache of HTTP
request headers.
*/
function XMLHttpRequest_removeRequestHeader(name) {
this._requestHeader.purge(name);
}
XMLHttpRequest.prototype.removeRequestHeader =
XMLHttpRequest_removeRequestHeader;
/*
Class: XMLHttpRequest
Function: XMLHttpRequest_send
Method: send
Description: Sends XMLHttpRequest.
*/
function XMLHttpRequest_send() {
var successful = false;
if(arguments.length != 0)
this.envelope = arguments[0];
switch(this._XMLHttp.readyState) {
case(4):
case(0):
try {
if(this._IE)
this._XMLHttp.onreadystatechange =
this.stateChangeHandler;
else
this._XMLHttp.stateChangeHandler =
this.XMLHttpRequest_stateChangeHandler;
this._XMLHttp.open(this.action,this.uri,this.asynchronous);
var names = this._requestHeader.names();
for(var i=0;i < names.length;i++)
this._XMLHttp.setRequestHeader(names[i],this._requestHeader.retrieve(names
[i]));
this._XMLHttp.send(this.envelope);
successful = true;
}
catch(e) { }
break;
default:
break;
}
return(successful);
}
XMLHttpRequest.prototype.send = XMLHttpRequest_send;
// ]]> -->
|
The constructor shown does exactly what the handwritten code from the beginning of Chapter 8, "AJAX Using XML and XMLHttpRequest," does. In a nutshell, it sends an XMLHttpRequest to the server, waits for the response, and then acts upon the response. This is not a big deal; just create an instance, and it takes care of everythingunless, of course, you're paid by the line.
Now that we've got a constructor to handle the getting of XML, it might be a good idea to figure out a place to put it. What's needed, as if you didn't already know, is a generic XML document object. It doesn't have to be perfect; it only has to workand by "work," I mean offer a single set of properties and methods. From the previous chapters, you're already aware that this is written, so let's take a gander at it in Listing 12-5.
Listing 12-5. Cross-Browser XML Document Class Constructor
<!-- <![CDATA[
XMLDocument.prototype = new XMLDocument;
XMLDocument.prototype.constructor = XMLDocument;
/*
Class: XMLDocument
Function: XMLDocument
Method: n/a
Description: Constructor for this class.
*/
function XMLDocument() {
try {
var x = new DOMParser();
this._IE = false;
}
catch(e) { this._IE = true; };
this._XMLHttpRequest = new XMLHttpRequest();
this._XML; // XML DOM document
this._DOMParser; // XML DOM parser (Gecko only)
this._XMLSerializer; // XML serializer (Gecko only)
this._state = 0; // Pseudo readyState
if(!this._IE) {
this._DOMParser = new DOMParser();
this._XMLSerializer = new XMLSerializer();
this._XML =
document.implementation.createDocument("", "", null);
}
}
/*
Class: XMLDocument
Function: XMLDocument_load
Method: load
Description: Loads the specified XML document.
*/
function XMLDocument_load(xml) {
var isXMLText = false;
var isXMLDocument = (typeof(xml) == 'object');
try { // Test for elements
isXMLText = (new RegExp('<','g')).test(xml);
}
catch(e) { }
switch(true) {
case(this._IE && isXMLText): // Internet Explorer & text
this._XML =
new ActiveXObject('MSXML2.FreeThreadedDOMDocument.3.0');
this._XML.async = true;
this._XML.loadXML(xml);
this._state = 4; // Ready state complete
break;
case(!this._IE && isXMLText): // Not IE & text
this._XML =
this._DOMParser.parseFromString(xml,"text/xml");
this._state = 4; // Ready state is complete
break;
case(this._IE && isXMLDocument):
// Internet Explorer & XML DOM
this._XML =
new ActiveXObject('MSXML2.FreeThreadedDOMDocument.3.0');
this._XML.async = true;
try {
this._XML.loadXML(xml.serialize());
}
catch(e) {
this._XML = xml;
}
this._state = 4; // Ready state complete
break;
case(!this._IE && isXMLDocument): // Not IE & XML DOM
try {
this._XML = xml.DOMDocument();
}
catch(e) {
this._XML = xml;
}
this._state = 4; // Ready state is complete
break;
default:
this._XMLHttpRequest.uri = xml;
try {
this._XMLHttpRequest.send();
this._state = 1;
}
catch(e) {
if(this._IE) {
this._XML =
new ActiveXObject('MSXML2.FreeThreadedDOMDocument.3.0');
this._XML.async = true;
} else
this._XML =
this._DOMParser.parseFromString(' ','text/xml');
this._state = 4; // Error - force complete
}
}
if(this._state == 4)
this._XMLHttpRequest = new XMLHttpRequest();
}
XMLDocument.prototype.load = XMLDocument_load;
/*
Class: XMLDocument
Function: XMLDocument_serialize
Method: serialize
Description: Returns the result of the prior transformation
as a serialize XML DOM document (text).
*/
function XMLDocument_serialize() {
try {
if(this.readyState() == 4) {
if(this._XMLHttpRequest.readyState() == 4)
this.load(this._XMLHttpRequest.responseXML());
if(this._IE)
return(this._XML.xml)
else
return(this._XMLSerializer.serializeToString(this._XML));
} else
return(null); // Not loaded
}
catch(e) {
return(null); // Invalid document
}
}
XMLDocument.prototype.serialize = XMLDocument_serialize;
/*
Class: XMLDocument
Function: XMLDocument_DOMDocument
Method: DOMDocument
Description: Returns the result of the prior transformation
as a Browser-native XML DOM document.
*/
function XMLDocument_DOMDocument() {
try {
if(this.readyState() == 4) {
if(this._XMLHttpRequest.readyState() == 4)
this.load(this._XMLHttpRequest.responseXML());
return(this._XML);
} else
return(null); // Document not loaded
}
catch(e) {
return(null); // Invalid document
}
}
XMLDocument.prototype.DOMDocument = XMLDocument_DOMDocument;
/*
Class: XMLDocument
Function: XMLDocument_readyState
Method: readyState
Description: Returns the readyState for the XML document.
*/
function XMLDocument_readyState() {
if(this._XMLHttpRequest.readyState() == 0)
return(4);
else
return(this._XMLHttpRequest.readyState());
}
XMLDocument.prototype.readyState = XMLDocument_readyState;
/*
Class: XMLDocument
Function: XMLHttpRequest_setRequestHeader
Method: n/a
Description: Inserts to the cache of HTTP request headers.
*/
function XMLDocument_setRequestHeader(name,value) {
this._XMLHttpRequest.setRequestHeader(name,value);
}
XMLDocument.prototype.setRequestHeader =
XMLDocument_setRequestHeader;
/*
Class: XMLDocument
Function: XMLDocument_getResponseHeader
Method: getResponseHeader
Description: Returns a single response header from the last
XMLHttpRequest.
*/
function XMLDocument_getResponseHeader(name) {
return(this._XMLHttpRequest.getResponseHeader(name));
}
XMLDocument.prototype.getResponseHeader =
XMLDocument_getResponseHeader;
/*
Class: XMLDocument
Function: XMLDocument_getAllResponseHeaders
Method: getAllResponseHeaders
Description: Returns all of the response headers from
the last XMLHttpRequest.
*/
function XMLDocument_getAllResponseHeaders() {
return(this._XMLHttpRequest.getAllResponseHeaders());
}
XMLDocument.prototype.getAllResponseHeaders =
XMLDocument_getAllResponseHeaders;
/*
Class: XMLDocument
Function: XMLDocument_setEnvelope
Method: setEnvelope
Description: Sets the envelope for an XMLHttpRequest.
*/
function XMLDocument_setEnvelope(value) {
this._XMLHttpRequest.envelope = value;
this._XMLHttpRequest.action = 'POST';
}
XMLDocument.prototype.setEnvelope = XMLDocument_setEnvelope;
/*
Class: XMLDocument
Function: XMLDocument_selectNodes
Method: selectNodes
Description: Returns an array of XMLDocument based upon
an XPath statement.
*/
function XMLDocument_selectNodes(xpath) {
var results;
var resultArray = new Array(); // XML Document result array
if(this.readyState() == 4)
if(this._XMLHttpRequest.readyState() == 4)
this.load(this._XMLHttpRequest.responseXML());
if(_IE) {
results = this._XML.selectNodes(xpath);
for(var i=0;i < results.length;i++) {
resultArray.push(new XMLDocument());
resultArray[i].load(results[i].xml);
}
} else { // XPath evaluator
var evaluator = new XPathEvaluator();
var resolver =
evaluator.createNSResolver(this._XML.documentElement);
var result; // Single XPath result
var xml;
var i = 0; // Counter
results =
evaluator.evaluate(xpath,this._XML,resolver,XPathResult.ANY_TYPE,null);
while(result = results.iterateNext()) {
xml = document.implementation.createDocument("", "",null);
xml.appendChild(xml.importNode(result,true));
resultArray.push(new XMLDocument());
resultArray[i].load(this._XMLSerializer.serializeToString(xml));
++i;
}
}
return(resultArray);
}
XMLDocument.prototype.selectNodes = XMLDocument_selectNodes;
/*
Class: XMLDocument
Function: XMLDocument_selectSingleNode
Method: selectSingleNode
Description: Returns a single XML document based upon an
XPath statement.
*/
function XMLDocument_selectSingleNode(xpath) {
return(this.selectNodes(xpath)[0]);
}
XMLDocument.prototype.selectSingleNode =
XMLDocument_selectSingleNode;
// ]]> -->
|
Now that there is a generic constructor for XML documents and a constructor for the XSLT Request object, the next task is to ask the nice web service for an XML document. To do this, a quick and easy way of producing a SOAP envelope is required. In writing this constructor, I learned something about SOAP that I hadn't realized in the past: SOAP is, in some ways, like a car. With a car, there is a base model, and, regardless of the options, the base model remains the same. Oh, sure, some cars have better sound systems and some have bigger engines, but underneath all the little extras, the cars are essentially the same. Take my car, for example; with the exception of the dirt and the dent on the hood from a flower pot, when you get past the options, it is just like the other car from that model year.
This same approach was used when writing the SOAPEnvelope constructor. A basic template serves as a starting point, and all of the other options are then added on. These options consist of things such as the operator, content, and namespaceall required, but very often different from request to request. Listing 12-6 shows the inner workings of this constructor.
Listing 12-6. Cross-Browser SOAPEnvelope Class Constructor That Uses Regular Expressions
<!-- <![CDATA[
SOAPEnvelope.prototype = new SOAPEnvelope;
SOAPEnvelope.prototype.constructor = SOAPEnvelope;
/*
Class: SOAPEnvelope
Function: SOAPEnvelope
Method: n/a
Description: Constructor for this class.
*/
<!-- <![CDATA[
function SOAPEnvelope() {
this._template = '<?xml version="1.0" encoding="utf-8"?>';
this._template += '<soap:Envelope
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">';
this._template += '<soap:Body>';
this._template += '<_operator xmlns="_namespace">';
this._template += '_package';
this._template += '</_operator>';
this._template += '</soap:Body>';
this._template += '</soap:Envelope>';
}
SOAPEnvelope.prototype.operator = null;
SOAPEnvelope.prototype.namespace = 'http://tempuri.org/';
SOAPEnvelope.prototype.content = null;
/*
Class: SOAPEnvelope
Function: SOAPEnvelope_envelope
Method: envelope
Description: Returns the readyState for the XMLHttpRequest
object.
*/
function SOAPEnvelope_envelope() {
var work;
work = this._template.replace(/_operator/g,this.operator);
work = work.replace(/_namespace/g,this.namespace);
work = work.replace(/_package/g,this.content);
return(work);
}
SOAPEnvelope.prototype.envelope = SOAPEnvelope_envelope;
// ]]> -->
|
12.2.3. XSLT
The final constructor that was used in the examples was the XSLTProcessor constructor, which serves as the poster child for code reuse. It has two instances of XMLDocument objects, one for the XML document and one for the XSL style sheet. It also serves fairly well to show some of the difference between Gecko-based browsers such as Firefox, Mozilla, and Netscape, and Microsoft Internet Explorer.
These differences range from Internet Explorer needing a template to create a processor to something as simple as Firefox needing a serializer to obtain the text representation of an XML document. Listing 12-7 shows the constructor for the XSLTProcessor.
Listing 12-7. Cross-Browser XSLTProcessor Class, Used for Transformations
<!-- <![CDATA[
XsltProcessor.prototype = new XsltProcessor;
XsltProcessor.prototype.constructor = XsltProcessor;
/*
Class: XsltProcessor
Function: XsltProcessor
Method: n/a
Description: Constructor for this class.
*/
function XsltProcessor() {
try {
var x = new DOMParser();
this._IE = false;
}
catch(e) { this._IE = true; };
this._xsl = new XMLDocument(); // Input XSL style sheet
this._xml = new XMLDocument(); // Input XML document
this._output; // Output (text)
this._XMLSerializer; // XML serializer (Gecko only)
this._XSLTemplate; // XSLT template (IE only)
this._XsltProcessor; // XSLT processor
if(!this._IE)
this._XMLSerializer = new XMLSerializer();
}
/*
Class: XsltProcessor
Function: XsltProcessor_initialize
Method: _initialize
Description: Initializes/re-initializes the XSLT processor.
*/
function XsltProcessor_initialize() {
if(this._IE) {
this._XSLTemplate =
new ActiveXObject('MSXML2.XSLTemplate.3.0');
this._XSLTemplate.stylesheet = this._xsl.DOMDocument();
this._XsltProcessor = this._XSLTemplate.createProcessor;
} else
this._XsltProcessor = new XSLTProcessor();
}
XsltProcessor.prototype._initialize = XsltProcessor_initialize;
/*
Class: XsltProcessor
Function: XsltProcessor_setParameter
Method: setParameter
Description: Inserts an XSLT parameter to the parameter
cache.
*/
function XsltProcessor_setParameter(name,value) {
try {
if(this._IE)
this._XsltProcessor.addParameter(name,value);
else
this._XsltProcessor.setParameter(null,name,value);
}
catch(e) {
this._initialize();
this.setParameter(name,value);
}
}
XsltProcessor.prototype.setParameter =
XsltProcessor_setParameter;
/*
Class: XsltProcessor
Function: XsltProcessor_load
Method: load
Description: Loads the XML document to be transformed.
*/
function XsltProcessor_load(xml) {
try {
this._xml.load(xml);
}
catch(e) {
this._initialize();
}
}
XsltProcessor.prototype.load = XsltProcessor_load;
/*
Class: XsltProcessor
Function: XsltProcessor_importStylesheet
Method: importStylesheet
Description: Loads the XSL style sheet for the
transformation.
*/
function XsltProcessor_importStylesheet(xsl) {
try {
this._xsl.load(xsl);
}
catch(e) {
this._initialize();
}
}
XsltProcessor.prototype.importStylesheet =
XsltProcessor_importStylesheet;
/*
Class: XsltProcessor
Function: XsltProcessor_readyState
Method: readyState
Description: Returns the readyState for a combination of
the XML document and the XSL style sheet.
*/
function XsltProcessor_readyState() {
switch(true) {
case((this._xsl.readyState() == 0) && (this._xsl.readyState() == 0)):
return(this._xsl.readyState());
break;
case((this._xsl.readyState() > 0) && (this._xsl.readyState() < 4)):
return(this._xsl.readyState());
break;
case((this._xml.readyState() > 0) && (this._xml.readyState() < 4)):
return(this._xml.readyState());
break;
default:
return(4);
break;
}
}
XsltProcessor.prototype.readyState = XsltProcessor_readyState;
/*
Class: XsltProcessor
Function: XsltProcessor_transform
Method: transform
Description: Performs the XSL transformation using the
supplied XML document and XSL style sheet.
Returns the result as an XML document.
*/
function XsltProcessor_transform() {
if(this._IE) {
this._XsltProcessor.input = this._xml.DOMDocument();
this._XsltProcessor.transform();
this._output = this._XsltProcessor.output;
} else {
this._XsltProcessor.importStylesheet(this._xsl.DOMDocument());
this._output =
this._XMLSerializer.serializeToString(this._XsltProcessor.transformToDocum
ent(this._xml.DOMDocument(),document));
}
this._initialize();
return(this._output);
}
XsltProcessor.prototype.transform = XsltProcessor_transform;
/*
Class: XsltProcessor
Function: XsltProcessor_serialize
Method: serialize
Description: Returns the result of the prior transformation
as a serialize XML document (text).
*/
function XsltProcessor_serialize() {
return(this._output);
}
XsltProcessor.prototype.serialize = XsltProcessor_serialize;
// ]]> -->
|
12.2.4. Serialization Without Berries
One common item that you'll notice throughout each of the previous constructors is that serialization plays a big part in handling XML. Several reasons account for this, the first being that XML was designed to be human readable, and humans read text, not binary. For example, when was the last time you heard, "ASCII 65, uppercase 'A'"? I'm the one who was called a mad scientist, and I don't deal with that stuff, so I can't imagine the more mundane members of humanity doing things like that.
The second reason for serialization is the underlying architecture of the web, the Hypertext Transfer Protocol, or HTTP, for short. The HTML, XHTML, JavaScript, CSS, XML, and XSL travel back and forth from the server to the client as text. Without serialization, all of the "X-stuff," as an old supervisor of mine put it, wouldn't be going anywhere.
Another reason for serialization is that, unlike an XML object, very little overhead is associated with text. An XML DOM document requires between three and ten times the memory of the equivalent text document. This overhead could cause some issues in the client's browser on older machines. Of course, the issue of overhead has to be weighted against parsing the text to load a document.
My final reason for serialization is that it is just so easy to load an XML document from a text document. In Microsoft Internet Explorer, it is simply a matter of using the loadXML method. With Firefox, a little more work is necessary, but not too much. Just use the DOMParser's parseFromString method and reconstituted XML, just like freeze-dried coffee or freeze-dried minions.
|