<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>
|