2009-06-22 18:18:56 -04:00
|
|
|
|
/* This code is based on the one originally provided by
|
|
|
|
|
Geir Landr<EFBFBD> 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<EFBFBD>. 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<EFBFBD> : 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;
|
|
|
|
|
}
|