1 /* This code is based on the one originally provided by
2 Geir Landrö in his dTree 2.05 package. You can get it
3 at : www.destroydrop.com/javascript/tree/.
5 Therefore, the DTDDoc team considers that this code is
6 Copyright (c) 2002-2003 Geir Landrö. Since the original
7 author didn't clearly forbids copies of this part, we
8 assume we're not doing anything wrong in porviding it
9 to you, in a modified or non-modified form.
13 Geir Landrö : Orignal version, for dTree.
15 Michael Koehrsen (10/2004) : Original modification to
16 allow DTDDoc to use this.
18 Stefan Champailler (10/2004) : Make sure that the first
19 level of the tree is not shown (aesthetic stuff).
22 //----------------------------------------------------------------
24 // Implements a DHTML tree with the following features:
25 // - Supports a general directed graph as the underlying model.
26 // - Supports a concept of an "always open" node.
27 //----------------------------------------------------------------
29 // Private class _CCTreeModelNode
30 function _CCTreeModelNode(id,label,link,alwaysOpen,initiallyOpen)
35 this.alwaysOpen = alwaysOpen;
36 this.initiallyOpen = initiallyOpen;
38 // children and childLabels are parallel arrays.
39 // By default, childLabels[i] == children[i].label,
40 // but the link operation may optionally specify
41 // a child label that is specific to that parent-child
43 this.children = new Array();
44 this.childLabels = new Array();
47 _CCTreeModelNode.prototype.addChild = function(child,childLabel)
49 this.children.push(child);
50 if (childLabel) this.childLabels.push(childLabel);
51 else this.childLabels.push(child.label);
54 // Private class _CCDisplayNode
55 function _CCDisplayNode(modelNode,parentNode,treeId,label)
57 this.modelNode = modelNode;
58 this.parentNode = parentNode;
61 if (label) this.label = label;
62 else this.label = modelNode.label;
64 this.isLastChild = false;
66 if (this.parentNode) {
67 this.id = this.parentNode.id + ":" + this.modelNode.id;
69 /* Stefan Champailler : This little fix is clever ! Let's check the
72 alpha (1) -+-> beta (69)
75 This describes a tree with three nodes. Two of them are links
76 (beta). In that case, both the "beta" node have an id of "1:69".
77 So if one calls openNode on any of them, what happens is that only
78 one actually gets opened. To prevent that, I change the id of the
79 node on the fly to make sure there are only unique id's.
81 Please note this code is not very efficient (and yes, the right
82 number of slash will be added :)) */
84 for( var k=0; k<parentNode.children.length; k++) {
85 if( parentNode.children[k].id == this.id)
86 this.id = this.id + "/";
91 else this.id = this.modelNode.id;
93 CCTree.trees[this.treeId].allDisplayNodes[this.id] = this;
95 this.isOpen = this.modelNode.alwaysOpen || this.modelNode.initiallyOpen;
98 this.constructChildren();
102 _CCDisplayNode.prototype.toInnerHTML = function()
106 if (this.isOpen && !this.children) this.constructChildren();
108 if (this.isLastChild)
110 if (this.modelNode.alwaysOpen)
111 indent = this.imageTag("joinbottom.gif");
112 else if (this.isOpen)
113 indent = this.imageTag("minusbottom.gif","close")
114 else if (this.modelNode.children.length)
115 indent = this.imageTag("plusbottom.gif","open");
117 indent = this.imageTag("joinbottom.gif");
121 if (this.modelNode.alwaysOpen)
122 indent = this.imageTag("join.gif");
123 else if (this.isOpen)
124 indent = this.imageTag("minus.gif","close");
125 else if (this.modelNode.children.length)
126 indent = this.imageTag("plus.gif","open");
128 indent = this.imageTag("join.gif");
131 /* Construct a horizontal line of the tree */
133 var currAnc = this.parentNode;
136 if (currAnc.isLastChild)
137 indent = this.imageTag("empty.gif") + indent;
139 indent = this.imageTag("line.gif") + indent;
140 currAnc = currAnc.parentNode;
143 var result = indent + this.nodeContent();
145 /* Recurse deeper in the tree */
147 if (this.isOpen && this.children)
150 for (ix in this.children)
152 result += this.children[ix];
159 _CCDisplayNode.prototype.toString = function()
161 return "<div class='cctree-node' id='" + this.divId() + "'>" + this.toInnerHTML() + "</div>";
164 _CCDisplayNode.prototype.constructChildren = function()
166 if (this.modelNode.children.length > 0)
168 this.children = new Array();
170 for (ix in this.modelNode.children)
172 this.children.push(new _CCDisplayNode(this.modelNode.children[ix],
175 this.modelNode.childLabels[ix]));
177 this.children[this.children.length-1].isLastChild = true;
181 _CCDisplayNode.prototype.imageTag = function(imgName,action)
185 if (action == "open") href="CCTree.trees[" + this.treeId + "].openNode('" + this.id + "')";
186 if (action == "close") href="CCTree.trees[" + this.treeId + "].closeNode('" + this.id + "')";
188 if (href) return "<a href=\"javascript:" + href + "\"><img src='img/" + imgName + "' border='0'></a>";
189 else return "<img src='img/" + imgName + "'>";
192 _CCDisplayNode.prototype.divId = function()
194 return "CCTree_" + this.treeId + "_" + this.id;
197 _CCDisplayNode.prototype.nodeContent = function()
201 if (CCTree.trees[this.treeId].linkTarget) target = " target='" + CCTree.trees[this.treeId].linkTarget + "'";
203 if (this.modelNode.link)
204 return "<a href='" + this.modelNode.link + "'" + target + ">" + this.label + "</a>";
205 else return this.label;
208 _CCDisplayNode.prototype.open = function()
211 // document.all is known to work on IE but not on Konqueror or Mozilla.
212 // So I've changed it to something more portable.
214 //document.all[this.divId()].innerHTML = this.toInnerHTML();
215 document.getElementById(this.divId()).innerHTML = this.toInnerHTML();
218 _CCDisplayNode.prototype.close = function()
221 //document.all[this.divId()].innerHTML = this.toInnerHTML();
222 document.getElementById(this.divId()).innerHTML = this.toInnerHTML();
225 // Public class CCTree
226 CCTree = function(linkTarget)
228 // may have multiple roots:
229 this.rootModelNodes = new Array();
230 this.allModelNodes = new Array();
231 this.treeId = CCTree.trees.length;
232 this.allDisplayNodes = new Array(); // indexed by id
233 this.linkTarget = linkTarget;
234 CCTree.trees.push(this);
238 CCTree.trees = new Array();
240 CCTree.prototype.addRootNode = function (id,label,link,alwaysOpen,initiallyOpen)
242 this.rootModelNodes[id] = this.addNode(id,label,link,alwaysOpen,initiallyOpen);
245 CCTree.prototype.addNode = function(id,label,link,alwaysOpen,initiallyOpen)
247 var newNode = new _CCTreeModelNode(id,label,link,alwaysOpen,initiallyOpen);
248 this.allModelNodes[id] = newNode;
252 CCTree.prototype.linkNodes = function(parentId,childId,childLabel)
254 this.allModelNodes[parentId].addChild(this.allModelNodes[childId],childLabel);
257 CCTree.prototype.constructDisplayNodes = function()
259 this.rootDisplayNodes = new Array();
261 for (ix in this.rootModelNodes)
263 this.rootDisplayNodes.push(new _CCDisplayNode(this.rootModelNodes[ix],null,this.treeId));
265 this.rootDisplayNodes[this.rootDisplayNodes.length-1].isLastChild = true;
268 CCTree.prototype.openNode = function(displayNodeId)
270 this.allDisplayNodes[displayNodeId].open();
273 CCTree.prototype.closeNode = function(displayNodeId)
275 this.allDisplayNodes[displayNodeId].close();
278 CCTree.prototype.toString = function()
280 this.constructDisplayNodes();
285 for (ix in this.rootDisplayNodes)
287 result += this.rootDisplayNodes[ix].toString();