XLoadTree Implementation (WebFX)

## Implementation

The main idea is to create sub classes to `WebFXTree` and `WebFXTreeItem` and overload the `expand` methods to start the loading of an xml file. Once the loading is done the xml file is tranformed into `WebFXTreeItem`s and `WebFXLoadTreeItem`s and inserted.

## WebFXLoadTree

First we create a new constructor and inside this we call the super constructor to make sure that the instance will be correctly initiated. After that we set some property values and finally we check whether the tree is open, if it is we start to load the sub trees. If not, we add a dummy tree item that displays the loading text.

After the constructor is created we set the protype to a new `WebFXTree`.

    function WebFXLoadTree(sText, sXmlSrc, sAction, sBehavior, sIcon, sOpenIcon) {
       // call super
       this.WebFXTree = WebFXTree;
       this.WebFXTree(sText, sAction, sBehavior, sIcon, sOpenIcon);

       // setup default property values
       this.src = sXmlSrc;
       this.loading = false;
       this.loaded = false;
       this.errorText = "";

       // check start state and load if open
       if (this.open)
          _startLoadXmlTree(this.src, this);
       else {
          // and create loading item if not
          this._loadingItem = new WebFXTreeItem(webFXTreeConfig.loadingText);
          this.add(this._loadingItem);
       }
    }

    WebFXLoadTree.prototype = new WebFXTree;

The constructor is fairly straight forward and does not do much. Notice however how super is called by binding `WebFXTree` as a method and then calling it.

Next we need to override the `expand` method but since we still need to be able to call the original `expand` method we create a new method called `_webfxtree_expand` that points to the function object used by `WebFXTree` objects. This is the standard way to access super methods but the first few times it might look a bit odd.

The logic in the `expand` method is really simple. We just check if we should start loading the xml file and then we expand it using the super expand (`_webfxtree_expand`) method.

    // override the expand method to load the xml file
    WebFXLoadTree.prototype._webfxtree_expand = WebFXTree.prototype.expand;
    WebFXLoadTree.prototype.expand = function() {
       if (!this.loaded && !this.loading) {
          // load
          _startLoadXmlTree(this.src, this);
       }
       this._webfxtree_expand();
    };

## WebFXLoadTreeItem

This class is too similar to `WebFXLoadTree` for me to be entirely comfortable. Since JavaScript does not support multiple inheritance, and I did not want to fake it using expandos, we just have to repeat the code. For everyone that are interested, the code for this can be found in xloadtree.js.

## Loading the Tree

As you can see in the code above there is a function called `_startLoadXmlTree` that is called to load the actual xml file. This function uses an `XmlHttp` object to do the actual loading. The loading of the xml file is done asynchronously to prevent the UI to lock up while the file is being loaded and therefore we wait for the `onreadystatechange` event to fire before we continue. See the Xml Extras article for a more detailed description about the `XmlHttp` object.

    // creates the xmlhttp object and starts the load of the xml document
    function _startLoadXmlTree(sSrc, jsNode) {
       jsNode.loading = true;
       var xmlHttp = XmlHttp.create();
       xmlHttp.open("GET", sSrc, true);	// async
       xmlHttp.onreadystatechange = function () {
          if (xmlHttp.readyState == 4)
             _xmlFileLoaded(xmlHttp.responseXML, jsNode);
       };
       // call in new thread to allow ui to update
       window.setTimeout(function () {
          xmlHttp.send(null);
       }, 10);
    }

Once the xml file has finished loading we call the function `_xmlFileLoaded`. This function checks that we got an xml document back and if we did it goes through the document and recursively converts the xml elements to js `WebFXTreeItem` and inserts them. Once the xml elements have been converted and inserted we remove the dummy tree item that was only used to show that we were loading the contents.

    // Inserts an xml document as a subtree to the provided node
    function _xmlFileLoaded(oXmlDoc, jsParentNode) {
       var bIndent = false;
       var bAnyChildren = false;
       jsParentNode.loaded = true;
       jsParentNode.loading = false;

       // check that the load of the xml file went well
       if( oXmlDoc == null || oXmlDoc.documentElement == null) {
          jsParentNode.errorText = parseTemplateString(webFXTreeConfig.loadErrorTextTemplate,
                                                       jsParentNode.src);
       }
       else {
          // there is one extra level of tree elements
          var root = oXmlDoc.documentElement;

          // loop through all tree children
          var cs = root.childNodes;
          var l = cs.length;
          for (var i = 0; i < l; i++) {
             if (cs[i].tagName == "tree") {
                bAnyChildren = true;
                bIndent = true;
                jsParentNode.add( _xmlTreeToJsTree(cs[i]), true);
             }
          }

          // if no children we got an error
          if (!bAnyChildren)
             jsParentNode.errorText = parseTemplateString(webFXTreeConfig.emptyErrorTextTemplate,
                                                          jsParentNode.src);
       }

       // remove dummy
       if (jsParentNode._loadingItem != null) {
          jsParentNode._loadingItem.remove();
          bIndent = true;
       }

       if (bIndent) {
          // indent now that all items are added
          jsParentNode.indent();
       }

       // show error in status bar
       if (jsParentNode.errorText != "")
          window.status = jsParentNode.errorText;
    }

A few more things happen in this function but nothing really important. There is some code that checks the errors and a few properties are set to reflect the state of the `WebFXLoadTree` or `WebFXLoadTreeItem` object.

## Converting the Xml

The only important thing left to do is to convert the xml tree item to a js `WebFXTreeItem`. This is done in the function `_xmlTreeToJsTree`. Here we retreive the xml attributes and if there was a `src` attribute defined we create a new `WebFXLoadTreeItem` and otherwise a `WebFXTreeItem`. Once that is created we go through all the `childNodes` of the xml node and convert and add those as well.

    // Converts an xml tree to a js tree. See article about xml tree format
    function _xmlTreeToJsTree(oNode) {
    	// retreive attributes
       var text = oNode.getAttribute("text");
       var action = oNode.getAttribute("action");
       var parent = null;
       var icon = oNode.getAttribute("icon");
       var openIcon = oNode.getAttribute("openIcon");
       var src = oNode.getAttribute("src");

       // create jsNode
       var jsNode;
       if (src != null && src != "")
          jsNode = new WebFXLoadTreeItem(text, src, action, parent, icon, openIcon);
       else
          jsNode = new WebFXTreeItem(text, action, parent, icon, openIcon);

       // go through childNOdes
       var cs = oNode.childNodes;
       var l = cs.length;
       for (var i = 0; i < l; i++) {
          if (cs[i].tagName == "tree")
             jsNode.add( _xmlTreeToJsTree(cs[i]), true );
       }

       return jsNode;
    }

Introduction
Usage
API
Implementation
Demo
Download

Author: Erik Arvidsson