| 
 
 <html>
 <head>
 <title>:: Tree Sample ::</title>
 
 <style type="text/css">
 body
 {
 padding: 0;
 margin: 0;
 }
 .tree ul, .tree li
 {
 list-style-type: none;
 margin: 0 0 0 2px;
 padding: 0;
 display: block;
 }
 .tree ul ul
 {
 margin-left: 16px;
 }
 .tree a.selected
 {
 background-color: lavender;
 }
 .tree .collapsed ul, .tree .collapsed span
 {
 display: none;
 }
 .tree span
 {
 display: block;
 margin: 0 0 0 16px;
 padding: 0;
 color: Gray;
 cursor: default;
 font-size: smaller;
 }
 .tree img
 {
 border: none;
 text-align: left;
 vertical-align: middle;
 margin-right: 2px;
 }
 .tree img.plusminus
 {
 width: 9px;
 height: 9px;
 }
 .tree a, .tree a:link, .tree a:visited, .tree a:active
 {
 font-size: 10pt;
 color: navy;
 font-family: Verdana;
 text-decoration: none;
 text-align: left;
 margin: 0 2px 0 2px;
 }
 .tree a:hover
 {
 text-decoration: underline;
 color: Blue;
 }
 
 </style>
 <script type="text/javascript">
 /*
 _______________________            ______________________
 XML DOM Tree Component             Browsers support:
 Version 1.5                -> Internet Explorer
 -> Mozilla
 _____________________________       -> Opera
 Features:                           -> Firefox
 -> Server Side Independency        -> Konqueror
 -> Cross Browser Support
 -> Dynamic Loading
 -> XML Source
 -> Easy Customization
 
 
 
 
 
 ______________________________
 Serghei Egoricev (c) 2006
 egoricev [at] gmail.com
 */
 
 // CSS import
 
 Tree = function() {}
 /*
 Use double click for navigate, single click for expand
 */
 Tree.useDblClicks = true; // NOT IMPLEMENTED
 Tree.saveNodesStateInCookies = true;
 /*
 CSS classes
 */
 Tree.expandedClassName = "";
 Tree.collapsedClassName = "collapsed";
 Tree.selectedClassName = "selected";
 Tree.plusMinusClassName = "plusminus";
 Tree.treeClass = "tree";
 
 /*
 Images
 */
 Tree.collapsedImage = "treeimg/collapsed.gif";
 Tree.expandedImage = "treeimg/expanded.gif";
 Tree.noChildrenImage = "treeimg/treenochild.gif";
 
 /*
 Xml Attributes
 */
 Tree.xmlCaption = "caption";
 Tree.xmlUrl = "url";
 Tree.xmlTarget = "target";
 Tree.xmlRetreiveUrl = "retreiveUrl";
 Tree.xmlIcon = "icon";
 Tree.xmlExpanded = "expanded";
 
 /*
 Text for loading
 */
 Tree.loadingText = "Loading ...";
 
 /*
 Private members
 */
 Tree.obj = null;
 Tree.instanceCount = 0;
 Tree.instancePrefix = "alder";
 Tree.cookiePrefix = "alder";
 Tree.dwnldQueue = new Array;
 Tree.dwnldCheckTimeout = 100;
 
 /*
 Interval handler. Ckecks for new nodes loaded.
 Adds loaded nodes to the tree.
 */
 Tree.checkLoad = function ()
 {
 var i, httpReq;
 for (i = 0; i<Tree.dwnldQueue.length; i++)
 if ((httpReq = Tree.dwnldQueue[i][0]).readyState == 4 /*COMPLETED*/)
 {
 var node = Tree.dwnldQueue[i][1];
 // unqueue loaded item
 Tree.dwnldQueue.splice(i, 1);
 Tree.appendLoadedNode(httpReq, node);
 if (Tree.saveNodesStateInCookies)
 Tree.openAllSaved(Tree.getId(node));
 } // if
 // will call next time, not all nodes were loaded
 if (Tree.dwnldQueue.length != 0)
 window.setTimeout(Tree.checkLoad, Tree.dwnldCheckTimeout);
 }
 
 /*
 Adds loaded node to tree.
 */
 Tree.appendLoadedNode = function (httpReq, node)
 {
 // create DomDocument from loaded text
 var xmlDoc = Tree.loadXml(httpReq.responseText);
 // create tree nodes from xml loaded
 var newNode = Tree.convertXml2NodeList(xmlDoc.documentElement);
 // Add loading error handling here must be added
 Tree.appendNode(node, newNode);
 }
 
 /*
 Event handler when node is clicked.
 Navigates node link, and makes node selected.
 */
 Tree.NodeClick = function (event)
 {
 var node = event.srcElement /*IE*/ || event.target /*DOM*/;
 // <li><a><img> - <img> is capturing the event
 while (node.tagName != "A")
 node = node.parentNode;
 node.blur();
 node = node.parentNode;
 Tree.obj = Tree.getObj(node);
 Tree.expandNode(node);
 Tree.selectNode(node);
 }
 
 /*
 Event handler when plus/minus icon is clicked.
 Desides whenever node should be expanded or collapsed.
 */
 Tree.ExpandCollapseNode = function (event)
 {
 var anchorClicked = event.srcElement /*IE*/ || event.target /*DOM*/;
 // <li><a><img> - <img> is capturing the event
 while (anchorClicked.tagName != "A")
 anchorClicked  = anchorClicked.parentNode;
 anchorClicked.blur();
 var node = anchorClicked.parentNode;
 // node has no children, and cannot be expanded or collapsed
 if (node.empty)
 return;
 Tree.obj = Tree.getObj(node);
 if (Tree.isNodeCollapsed(node))
 Tree.expandNode(node);
 else
 Tree.collapseNode(node);
 // cancelling the event to prevent navigation.
 if (event.preventDefault == undefined)
 { // IE
 event.cancelBubble = true;
 event.returnValue = false;
 } // if
 else
 { // DOM
 event.preventDefault();
 event.cancelBubble = true;
 } // else
 }
 
 /*
 Determines if specified node is selected.
 */
 Tree.isNodeSelected = function (node)
 {
 return (node.isSelected == true) || (Tree.obj.selectedNode == node);
 }
 
 /*
 Determines if specified node is expanded.
 */
 Tree.isNodeExpanded = function (node)
 {
 return (Tree.expandedClassName == node.className) || (node.expanded == true);
 }
 
 /*
 Determines if specified node is collapsed.
 */
 Tree.isNodeCollapsed = function (node)
 {
 return (Tree.collapsedClassName == node.className) || (node.collapsed == true);
 }
 
 /*
 Determines if node currently selected is at same
 level as node specified (has same root).
 */
 Tree.isSelectedNodeAtSameLevel = function (node)
 {
 if (Tree.obj.selectedNode == null) // no node currently selected
 return false;
 var i, currentNode, children = node.parentNode.childNodes; // all nodes at same level (li->ul->childNodes)
 for (i = 0; i < children.length; i++)
 if ((currentNode = children[i]) != node && Tree.isNodeSelected(currentNode))
 return true;
 return false;
 }
 
 /*
 Mark node as selected and unmark prevoiusly selected.
 Node is marked with attribute and <a> is marked with css style
 to avoid mark <li> twise with css style expanded and selected.
 */
 Tree.selectNode = function (node)
 {
 if (Tree.isNodeSelected(node)) // already marked
 return;
 if (Tree.obj.selectedNode != null)
 {// unmark previously selected node.
 Tree.obj.selectedNode.isSelected = false;
 // remove css style from anchor
 Tree.getNodeAnchor(Tree.obj.selectedNode).className = "";
 } // if
 // collapse selected node if at same level
 if (Tree.isSelectedNodeAtSameLevel(node))
 Tree.collapseNode(Tree.obj.selectedNode);
 // mark node as selected
 Tree.obj.selectedNode = node;
 node.isSelected = true;
 Tree.getNodeAnchor(node).className = Tree.selectedClassName;
 }
 
 /*
 Expand collapsed node. Loads children nodes if needed.
 */
 Tree.expandNode = function (node, avoidSaving)
 {
 if (node.empty)
 return;
 Tree.getNodeImage(node).src = Tree.expandedImage;
 node.className = Tree.expandedClassName;
 node.expanded = true;
 node.collapsed = false;
 if (Tree.areChildrenNotLoaded(node))
 Tree.loadChildren(node);
 if (Tree.saveNodesStateInCookies && !avoidSaving)
 Tree.saveOpenedNode(node);
 }
 
 /*
 Collapse expanded node.
 */
 Tree.collapseNode = function (node, avoidSaving)
 {
 if (node.empty)
 return;
 Tree.getNodeImage(node).src = Tree.collapsedImage;
 node.className = Tree.collapsedClassName;
 node.collapsed = true;
 node.expanded = false;
 if (Tree.saveNodesStateInCookies && !avoidSaving)
 Tree.saveClosedNode(node);
 }
 
 /*
 Returns plus/minus <img> for node specified.
 */
 Tree.getNodeImage = function (node)
 {
 return node.getElementsByTagName("IMG")[0];
 }
 
 /*
 Returns retreiveUrl for node specified.
 */
 Tree.getNodeRetreiveUrl = function (node)
 {
 return node.getElementsByTagName("A")[0].href;
 }
 
 /*
 Returns node link <a> element (<li><a><img></a><a>)
 */
 Tree.getNodeAnchor = function (node)
 {
 return node.getElementsByTagName("A")[1];
 }
 
 /*
 Cancel loading children nodes.
 */
 Tree.CancelLoad = function (event)
 {
 var i, node = event.srcElement /*IE*/ || event.target /*DOM*/;
 while (node.tagName != "LI")
 node = node.parentNode;
 // search node in queue
 for (i = 0; i<Tree.dwnldQueue.length; i++)
 if (Tree.dwnldQueue[i][1] == node)
 {
 // remove from queue
 Tree.dwnldQueue.splice(i, 1);
 // collapse node
 Tree.collapseNode(node);
 } // if
 }
 
 /*
 Loads text from url specified and returns it as result.
 */
 Tree.loadUrl = function (url, async)
 {
 // create request object
 var httpReq = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
 // prepare request
 httpReq.open("GET" /* method */, url /* url */, async == true /* async */, null /* login */, null /* password */);
 // send request
 httpReq.send(null);
 return async == true? httpReq : httpReq.responseText;
 }
 
 /*
 Creates XmlDom document from xml text string.
 */
 Tree.loadXml = function (xmlString)
 {
 var xmlDoc;
 if (window.DOMParser) /*Mozilla*/
 xmlDoc = new DOMParser().parseFromString(xmlString, "text/xml");
 else
 {
 if (document.implementation && document.implementation.createDocument)
 xmlDoc = document.implementation.createDocument("","", null); /*Konqueror*/
 else
 xmlDoc = new ActiveXObject("Microsoft.XmlDom"); /*IE*/
 
 xmlDoc.async = false;
 xmlDoc.loadXML(xmlString);
 } // else
 return xmlDoc;
 }
 
 /*
 Determines if children are loaded for node specified.
 */
 Tree.areChildrenNotLoaded = function (node)
 {
 return Tree.getNodeSpan(node) != null;
 }
 
 /*
 Finds loading span for node.
 */
 Tree.getNodeSpan = function (node)
 {
 var span = node.getElementsByTagName("SPAN");
 return (span.length > 0 && (span = span[0]).parentNode == node) ? span : null;
 }
 
 /*
 Enqueue load of children nodes for node specified.
 */
 Tree.loadChildren = function (node)
 {
 // get url with children
 var url = Tree.getNodeRetreiveUrl(node);
 // retreive xml text from url
 var httpReq = Tree.loadUrl(url, true);
 // enqueue node loading
 if (Tree.dwnldQueue.push(new Array (httpReq, node)) == 1)
 window.setTimeout(Tree.checkLoad, Tree.dwnldCheckTimeout);
 }
 
 /*
 Creates HTML nodes list from XML nodes.
 */
 Tree.convertXml2NodeList = function (xmlElement)
 {
 var ul = document.createElement("UL");
 var i, node, children = xmlElement.childNodes;
 var index = 0;
 for (i = 0; i<children.length; i++)
 if ((node = children[i]).nodeType == 1 /* ELEMENT_NODE */)
 ul.appendChild(Tree.convertXml2Node(node)).nodeIndex = index++;
 return ul;
 }
 
 /*
 Adds event handler
 */
 Tree.addEvent = function (obj, fn, ev)
 {
 if (ev == undefined) ev = "click"; // defaulting event to onclick
 if (obj.addEventListener)
 obj.addEventListener(ev, fn, false);
 else
 if (obj.attachEvent)
 obj.attachEvent("on"+ev, fn);
 else
 obj.onclick = fn;
 }
 
 /*
 Determines if xml node has child nodes inside.
 */
 Tree.hasXmlNodeChildren = function (xmlElement)
 {
 var i, children = xmlElement.childNodes;
 for (i = 0; i<children.length; i++)
 if ((node = children[i]).nodeType == 1 /* ELEMENT_NODE */)
 return true;
 return false;
 }
 
 /*
 Appends newly created node to node specified.
 Simply replace loading <span> at new node.
 */
 Tree.appendNode = function (node, newNode)
 {
 node.replaceChild(newNode, Tree.getNodeSpan(node));
 }
 
 /*
 Creates tree object. Loads it content from url specified.
 */
 Tree.prototype.Create = function (url, obj)
 {
 var div = document.createElement("DIV");
 div.id = Tree.instancePrefix + Tree.instanceCount++;
 div.className = Tree.treeClass;
 var xml = Tree.loadUrl(url, false);
 var xmlDoc = Tree.loadXml(xml);
 var newNode = Tree.convertXml2NodeList(xmlDoc.documentElement);
 div.appendChild(newNode);
 if (obj != undefined)
 {
 if (obj.appendChild) // is node
 obj.appendChild(div);
 else if (document.getElementById(obj)) // is node id
 document.getElementById(obj).appendChild(div);
 } // if
 else
 document.body.appendChild(div);
 if (Tree.saveNodesStateInCookies)
 Tree.openAllSaved(div.id);
 }
 
 /*
 Creates HTML tree node (<li>) from xml element.
 */
 Tree.convertXml2Node = function (xmlElement)
 {
 var li = document.createElement("LI");
 var a1 = document.createElement("A");
 var a2 = document.createElement("A");
 var i1 = document.createElement("IMG");
 var i2 = document.createElement("IMG");
 var hasChildNodes = Tree.hasXmlNodeChildren(xmlElement);
 var retreiveUrl = xmlElement.getAttribute(Tree.xmlRetreiveUrl);
 
 // plus/minus icon
 i1.className = Tree.plusMinusClassName;
 a1.appendChild(i1);
 Tree.addEvent(a1, Tree.ExpandCollapseNode);
 
 // plus/minus link
 a1.href = retreiveUrl != null && retreiveUrl.length != 0 ? retreiveUrl : "about:blank";
 li.appendChild(a1);
 
 // node icon
 i2.src = xmlElement.getAttribute(Tree.xmlIcon);
 a2.appendChild(i2);
 
 // node link
 a2.href = xmlElement.getAttribute(Tree.xmlUrl);
 a2.target = xmlElement.getAttribute(Tree.xmlTarget);
 a2.title = xmlElement.getAttribute(Tree.xmlCaption);
 a2.appendChild(document.createTextNode(xmlElement.getAttribute(Tree.xmlCaption)));
 Tree.addEvent(a2, Tree.NodeClick);
 
 li.appendChild(a2);
 
 // loading span
 if (!hasChildNodes && retreiveUrl != null && retreiveUrl.length != 0)
 {
 var span = document.createElement("SPAN");
 span.innerHTML = Tree.loadingText;
 Tree.addEvent(span, Tree.CancelLoad);
 li.appendChild(span);
 } // if
 
 // add children
 if (hasChildNodes)
 li.appendChild(Tree.convertXml2NodeList(xmlElement));
 if (hasChildNodes || retreiveUrl != null && retreiveUrl.length != 0)
 {
 if (xmlElement.getAttribute(Tree.xmlExpanded))
 Tree.expandNode(li, true);
 else
 Tree.collapseNode(li, true);
 } // if
 else
 {
 i1.src = Tree.noChildrenImage; // no children
 li.empty = true;
 } // else
 
 return li;
 }
 
 /*
 Retreives current tree object.
 */
 Tree.getObj = function (node)
 {
 var obj = node;
 while (obj != null && obj.tagName != "DIV")
 obj = obj.parentNode;
 return obj;
 }
 
 Tree.getId = function (node)
 {
 var obj = Tree.getObj(node);
 if (obj)
 return obj.id;
 return "";
 }
 
 /*
 Retreives unique id for tree node.
 */
 Tree.getNodeId = function (node)
 {
 var id = "";
 var obj = node;
 while (obj != null && obj.tagName != "DIV")
 {
 if (obj.tagName == "LI" && obj.nodeIndex != null)
 id = "_" + obj.nodeIndex + id;
 obj = obj.parentNode;
 } // while
 //  if (obj != null && obj.tagName == "DIV")
 //    id = obj.id + "_" + id;
 return id;
 }
 
 /*
 Saves node as opened for reload.
 */
 Tree.saveOpenedNode = function (node)
 {
 var treeId = Tree.getId(node);
 var state = Tree.getAllNodesSavedState(treeId);
 var nodeState = Tree.getNodeId(node) + ",";
 if (state.indexOf(nodeState) == -1)
 {
 state += nodeState;
 Tree.setAllNodesSavedState(treeId, state);
 } // if
 }
 
 /*
 Saves node as closed for reload.
 */
 Tree.saveClosedNode = function (node)
 {
 var treeId = Tree.getId(node);
 var state = Tree.getAllNodesSavedState(treeId);
 state = state.replace(new RegExp(Tree.getNodeId(node) + ",", "g"), "");
 Tree.setAllNodesSavedState(treeId, state);
 }
 
 Tree.getAllNodesSavedState = function (treeId)
 {
 var state = Tree.getCookie(Tree.cookiePrefix + "_" + treeId);
 return state == null ? "" : state;
 }
 
 Tree.setAllNodesSavedState = function (treeId, state)
 {
 Tree.setCookie(Tree.cookiePrefix + "_" + treeId, state);
 }
 
 /*
 Enques list of all opened nodes
 */
 Tree.openAllSaved = function(treeId)
 {
 var nodes = Tree.getAllNodesSavedState(treeId).split(",");
 var i;
 for (i=0; i<nodes.length; i++)
 {
 var node = Tree.getNodeById(treeId, nodes[i]);
 if (node && Tree.isNodeCollapsed(node))
 Tree.expandNode(node);
 } // for
 }
 
 Tree.getNodeById = function(treeId, nodeId)
 {
 var node = document.getElementById(treeId);
 if (!node)
 return null;
 var path = nodeId.split("_");
 var i;
 for (i=1; i<path.length; i++)
 {
 if (node != null)
 {
 node = node.firstChild;
 while (node != null && node.tagName != "UL")
 node = node.nextSibling;
 } // if
 if (node != null)
 node = node.childNodes[path[i]];
 else
 break;
 } // for
 return node;
 }
 
 Tree.setCookie = function(sName, sValue)
 {
 document.cookie = sName + "=" + escape(sValue) + ";";
 }
 
 Tree.getCookie = function(sName)
 {
 var a = document.cookie.split("; ");
 for (var i=0; i < a.length; i++)
 {
 var aa = a[i].split("=");
 if (sName == aa[0])
 return unescape(aa[1]);
 } // for
 return null;
 }
 
 </script>
 </head>
 <body>
 <div id="tree"></div>
 <hr/>
 <script type="text/javascript">
 new Tree().Create("tree.xml", "tree");
 new Tree().Create("tree.xml");
 </script>
 </body>
 </html>
 
 
 
 |