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 WebFXTreeItems and WebFXLoadTreeItems 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