JavaScript Editor Free JavaScript Editor     JavaScript Debugger 




Main Page

Previous Page
Next Page

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.


Previous Page
Next Page

R7


JavaScript Editor Free JavaScript Editor     JavaScript Debugger


©