| /* This code is based on the one originally provided by |
| Geir Landrö in his dTree 2.05 package. You can get it |
| at : www.destroydrop.com/javascript/tree/. |
| |
| Therefore, the DTDDoc team considers that this code is |
| Copyright (c) 2002-2003 Geir Landrö. Since the original |
| author didn't clearly forbids copies of this part, we |
| assume we're not doing anything wrong in porviding it |
| to you, in a modified or non-modified form. |
| */ |
| |
| /* |
| Geir Landrö : Orignal version, for dTree. |
| |
| Michael Koehrsen (10/2004) : Original modification to |
| allow DTDDoc to use this. |
| |
| Stefan Champailler (10/2004) : Make sure that the first |
| level of the tree is not shown (aesthetic stuff). |
| */ |
| |
| //---------------------------------------------------------------- |
| // CCTree |
| // Implements a DHTML tree with the following features: |
| // - Supports a general directed graph as the underlying model. |
| // - Supports a concept of an "always open" node. |
| //---------------------------------------------------------------- |
| |
| // Private class _CCTreeModelNode |
| function _CCTreeModelNode(id,label,link,alwaysOpen,initiallyOpen) |
| { |
| this.id = id; |
| this.label = label; |
| this.link = link; |
| this.alwaysOpen = alwaysOpen; |
| this.initiallyOpen = initiallyOpen; |
| |
| // children and childLabels are parallel arrays. |
| // By default, childLabels[i] == children[i].label, |
| // but the link operation may optionally specify |
| // a child label that is specific to that parent-child |
| // relationship. |
| this.children = new Array(); |
| this.childLabels = new Array(); |
| } |
| |
| _CCTreeModelNode.prototype.addChild = function(child,childLabel) |
| { |
| this.children.push(child); |
| if (childLabel) this.childLabels.push(childLabel); |
| else this.childLabels.push(child.label); |
| } |
| |
| // Private class _CCDisplayNode |
| function _CCDisplayNode(modelNode,parentNode,treeId,label) |
| { |
| this.modelNode = modelNode; |
| this.parentNode = parentNode; |
| this.treeId = treeId; |
| |
| if (label) this.label = label; |
| else this.label = modelNode.label; |
| |
| this.isLastChild = false; |
| |
| if (this.parentNode) { |
| this.id = this.parentNode.id + ":" + this.modelNode.id; |
| |
| /* Stefan Champailler : This little fix is clever ! Let's check the |
| following tree: |
| |
| alpha (1) -+-> beta (69) |
| \-> beta (69). |
| |
| This describes a tree with three nodes. Two of them are links |
| (beta). In that case, both the "beta" node have an id of "1:69". |
| So if one calls openNode on any of them, what happens is that only |
| one actually gets opened. To prevent that, I change the id of the |
| node on the fly to make sure there are only unique id's. |
| |
| Please note this code is not very efficient (and yes, the right |
| number of slash will be added :)) */ |
| |
| for( var k=0; k<parentNode.children.length; k++) { |
| if( parentNode.children[k].id == this.id) |
| this.id = this.id + "/"; |
| } |
| |
| /* end of fix */ |
| } |
| else this.id = this.modelNode.id; |
| |
| CCTree.trees[this.treeId].allDisplayNodes[this.id] = this; |
| |
| this.isOpen = this.modelNode.alwaysOpen || this.modelNode.initiallyOpen; |
| if (this.isOpen) |
| { |
| this.constructChildren(); |
| } |
| } |
| |
| _CCDisplayNode.prototype.toInnerHTML = function() |
| { |
| var indent = ""; |
| |
| if (this.isOpen && !this.children) this.constructChildren(); |
| |
| if (this.isLastChild) |
| { |
| if (this.modelNode.alwaysOpen) |
| indent = this.imageTag("joinbottom.gif"); |
| else if (this.isOpen) |
| indent = this.imageTag("minusbottom.gif","close") |
| else if (this.modelNode.children.length) |
| indent = this.imageTag("plusbottom.gif","open"); |
| else |
| indent = this.imageTag("joinbottom.gif"); |
| } |
| else |
| { |
| if (this.modelNode.alwaysOpen) |
| indent = this.imageTag("join.gif"); |
| else if (this.isOpen) |
| indent = this.imageTag("minus.gif","close"); |
| else if (this.modelNode.children.length) |
| indent = this.imageTag("plus.gif","open"); |
| else |
| indent = this.imageTag("join.gif"); |
| } |
| |
| /* Construct a horizontal line of the tree */ |
| |
| var currAnc = this.parentNode; |
| while (currAnc) |
| { |
| if (currAnc.isLastChild) |
| indent = this.imageTag("empty.gif") + indent; |
| else |
| indent = this.imageTag("line.gif") + indent; |
| currAnc = currAnc.parentNode; |
| } |
| |
| var result = indent + this.nodeContent(); |
| |
| /* Recurse deeper in the tree */ |
| |
| if (this.isOpen && this.children) |
| { |
| var ix; |
| for (ix in this.children) |
| { |
| result += this.children[ix]; |
| } |
| } |
| |
| return result; |
| } |
| |
| _CCDisplayNode.prototype.toString = function() |
| { |
| return "<div class='cctree-node' id='" + this.divId() + "'>" + this.toInnerHTML() + "</div>"; |
| } |
| |
| _CCDisplayNode.prototype.constructChildren = function() |
| { |
| if (this.modelNode.children.length > 0) |
| { |
| this.children = new Array(); |
| var ix; |
| for (ix in this.modelNode.children) |
| { |
| this.children.push(new _CCDisplayNode(this.modelNode.children[ix], |
| this, |
| this.treeId, |
| this.modelNode.childLabels[ix])); |
| } |
| this.children[this.children.length-1].isLastChild = true; |
| } |
| } |
| |
| _CCDisplayNode.prototype.imageTag = function(imgName,action) |
| { |
| var href = null; |
| |
| if (action == "open") href="CCTree.trees[" + this.treeId + "].openNode('" + this.id + "')"; |
| if (action == "close") href="CCTree.trees[" + this.treeId + "].closeNode('" + this.id + "')"; |
| |
| if (href) return "<a href=\"javascript:" + href + "\"><img src='img/" + imgName + "' border='0'></a>"; |
| else return "<img src='img/" + imgName + "'>"; |
| } |
| |
| _CCDisplayNode.prototype.divId = function() |
| { |
| return "CCTree_" + this.treeId + "_" + this.id; |
| } |
| |
| _CCDisplayNode.prototype.nodeContent = function() |
| { |
| var target = ""; |
| |
| if (CCTree.trees[this.treeId].linkTarget) target = " target='" + CCTree.trees[this.treeId].linkTarget + "'"; |
| |
| if (this.modelNode.link) |
| return "<a href='" + this.modelNode.link + "'" + target + ">" + this.label + "</a>"; |
| else return this.label; |
| } |
| |
| _CCDisplayNode.prototype.open = function() |
| { |
| this.isOpen = true; |
| // document.all is known to work on IE but not on Konqueror or Mozilla. |
| // So I've changed it to something more portable. |
| |
| //document.all[this.divId()].innerHTML = this.toInnerHTML(); |
| document.getElementById(this.divId()).innerHTML = this.toInnerHTML(); |
| } |
| |
| _CCDisplayNode.prototype.close = function() |
| { |
| this.isOpen = false; |
| //document.all[this.divId()].innerHTML = this.toInnerHTML(); |
| document.getElementById(this.divId()).innerHTML = this.toInnerHTML(); |
| } |
| |
| // Public class CCTree |
| CCTree = function(linkTarget) |
| { |
| // may have multiple roots: |
| this.rootModelNodes = new Array(); |
| this.allModelNodes = new Array(); |
| this.treeId = CCTree.trees.length; |
| this.allDisplayNodes = new Array(); // indexed by id |
| this.linkTarget = linkTarget; |
| CCTree.trees.push(this); |
| } |
| |
| // static variables |
| CCTree.trees = new Array(); |
| |
| CCTree.prototype.addRootNode = function (id,label,link,alwaysOpen,initiallyOpen) |
| { |
| this.rootModelNodes[id] = this.addNode(id,label,link,alwaysOpen,initiallyOpen); |
| } |
| |
| CCTree.prototype.addNode = function(id,label,link,alwaysOpen,initiallyOpen) |
| { |
| var newNode = new _CCTreeModelNode(id,label,link,alwaysOpen,initiallyOpen); |
| this.allModelNodes[id] = newNode; |
| return newNode; |
| } |
| |
| CCTree.prototype.linkNodes = function(parentId,childId,childLabel) |
| { |
| this.allModelNodes[parentId].addChild(this.allModelNodes[childId],childLabel); |
| } |
| |
| CCTree.prototype.constructDisplayNodes = function() |
| { |
| this.rootDisplayNodes = new Array(); |
| var ix; |
| for (ix in this.rootModelNodes) |
| { |
| this.rootDisplayNodes.push(new _CCDisplayNode(this.rootModelNodes[ix],null,this.treeId)); |
| } |
| this.rootDisplayNodes[this.rootDisplayNodes.length-1].isLastChild = true; |
| } |
| |
| CCTree.prototype.openNode = function(displayNodeId) |
| { |
| this.allDisplayNodes[displayNodeId].open(); |
| } |
| |
| CCTree.prototype.closeNode = function(displayNodeId) |
| { |
| this.allDisplayNodes[displayNodeId].close(); |
| } |
| |
| CCTree.prototype.toString = function() |
| { |
| this.constructDisplayNodes(); |
| |
| var ix; |
| var result = ""; |
| |
| for (ix in this.rootDisplayNodes) |
| { |
| result += this.rootDisplayNodes[ix].toString(); |
| } |
| |
| return result; |
| } |