Using yui-ext widgets from markup

I’ve written several posts about using yui-ext in Virtualmin’s new GUI, and I recommend it whole-heartedly to folks looking for a really nice set of JavaScript widgets. It, like the YUI library it is based on, is targeted to widgets useful in general applications, rather than very simple task-focused applications. It’s great for the kind of applications that have a lot of data to present, lots of pages to navigate via roughly random access, and if care isn’t taken with presentation could place quite heavy demands on the end user. We’ve got that kind of application. Virtualmin does a lot of widely varied tasks. So, yui-ext is a great fit.

But, one place where yui-ext falls down, that some other libs do better, is in building widgets from markup. It pretty much completely lacks the capacity to build any of its complex widgets from HTML markup. They must be generated with JavaScript. There are clever things like a loader function that’ll load a JSON object automagically…but nobody who’s making an app that can target a wide array of client devices, like we are, is going to be able to deliver data in that form (at least, not without major re-writes of significant portions of the application, and a new layer of abstraction).

Nonetheless, the widgets are so nice that I’ve looked past that, and with some help from Jack Slocum, I’ve begun building functions that will parse the tree and replace the elements with the yui-ext elements.

Ext.tree.TreePanel.readMarkup = function(root, el){
    function readBranch(pnode, ul){
        var cn = ul.childNodes;
        for(var i = 0, len = cn.length; i < len; i++){
            var li = cn[i];
            if(li.tagName && li.tagName.toLowerCase() == 'li'){
                var a = li.getElementsByTagName('a')[0];
                var cul = li.getElementsByTagName('ul')[0];
                var node = pnode.appendChild(new Ext.tree.TreeNode({
                    id : a.id,
                    text : a.innerHTML,
                    cls: a.className,
                    href : a.href
                }));
                if(cul){
                    readBranch(node, cul);
                }
            }
        }
    }
    readBranch(root, YAHOO.util.Dom.get(el));
    return root;
};
 
treeMenu = function(el){
  // create the hidden root
  var root = new Ext.tree.TreeNode('');
 
  // get the existing markup element
  var prenl = el.getElementsByTagName('UL');
  var nl = getEl(prenl.item(0).id);
 
  // read it
  Ext.tree.TreePanel.readMarkup(root, nl.dom);
 
  // remove the markup
  nl.remove();
 
  // initialize the tree
  var tree = new Ext.tree.TreePanel(el, {rootVisible:false, enableDD:false, animate:true});
  tree.setRootNode(root);
  tree.render();
};
 
var Trees = function(){
  return {
    init : function() {
      // Get all of the div ids of class treemenu
      var elements = YAHOO.util.Dom.getElementsByClassName('treemenu', 'div');
      YAHOO.util.Dom.batch ( elements, treeMenu );
    }
  };
}();
 
YAHOO.ext.EventManager.onDocumentReady(Trees.init, Trees, true);

Trees.init, which is run once the document has finished loading searches the DOM for elements of class “treemenu” and then uses a YUI batch to iterate over all of them with the treeMenu function. The treeMenu function is merely a wrapper for the hard work being done by readParse, which runs through the tree to figure out the structure of the list and put it into a TreePanel.

The markup read by this function looks like:

<div id="nav" class="treemenu">
  <ul id="navul1" class="nav-list">
    <li><a href="...">Leaf Node</a></li>
    <li><a href="...">Node with children</a>
      <ul>
        <li><a href="woot">Chillun 1</a></li>
        <li><a href="wooty">Chillun 2</a></li>
      </ul>
    </li>
  </ul>
</div>
 
<div id="nav2" class="treemenu">
  <ul id="navul2" class="nav-list">
    <li><a href="...">Another Leaf Node</a></li>
    <li><a href="...">Second Node with children</a>
      <ul>
        <li><a href="woot">Chillun 1</a></li>
        <li><a href="wooty">Chillun 2</a></li>
      </ul>
    </li>
  </ul>
</div>

The same kind of technique can be used for all of the Ext functions, though I believe I’ll be able to use a mix of markup and JavaScript for the remainder of my conversion…the tree menus were particularly challenging because of the way they are generated in our code.