mrscheme
Version:
Scheme didactic subset interpreter in JavaScript.
1,032 lines (903 loc) • 33.8 kB
JavaScript
import { jQuery } from './jquerylight.mjs';
/*-------------------------------------------------------------------------------------------
| ECOTree.js
|--------------------------------------------------------------------------------------------
| (c) 2006 Emilio Cortegoso Lobato
|
| ECOTree is a javascript component for tree drawing. It implements the node positioning
| algorithm of John Q. Walker II "Positioning nodes for General Trees".
|
| Basic features include:
| - Layout features: Different node sizes, colors, link types, alignments, separations
| root node positions, etc...
| - Nodes can include a title and an hyperlink, and a hidden metadata.
| - Subtrees can be collapsed and expanded at will.
| - Single and Multiple selection modes.
| - Search nodes using title and metadata as well.
|
| This code is free source, but you will be kind if you don't distribute modified versions
| with the same name, to avoid version collisions. Otherwise, please hack it!
|
| References:
|
| Walker II, J. Q., "A Node-Positioning Algorithm for General Trees"
| Software — Practice and Experience 10, 1980 553-561.
| (Obtained from C++ User's journal. Feb. 1991)
|
| Last updated: June 21, 2010, Bob Good
| Version: 1.5 - Couple of more Firefox issues with text offset
|
| Last updated: June 21, 2010, Bob Good
| Version: 1.4 - fixed some FireFox issues
|
| Last updated: June 4, 2010, Bob Good
| Version: 1.3 - fixed color problem caused by "change all" ECOlor => eColor
|
| Last updated: May 11, 2010, Bob Good
| Version: 1.2 - added thirdwalk correction factors because was drawing nodes off the screen
|
| Last updated: October 26th, 2006
| Version: 1.0
|
| Modifications from version 1.5 made July 15, 2010 by
| Kevin Dorff of the
| Institute for Computational Biomedicine
| Weill Cornell Medical College
| http://icb.med.cornell.edu
|
| Original code from:
| http://www.codeproject.com/KB/scripting/graphic_javascript_tree.aspx
|
| Changes from 1.5
| * Requires jQuery (tested with 1.4.2)
| * Requires excanvas (tested with r3, assuming you want to support Internet Explorer)
| * Now that you can use Canvas in IE (with excanvas), Firefox, Chrome, Safari,
| this code ONLY supports rendering to Canvas (removed VML).
| It is possible this version won't work properly with old browsers, as it has
| not been extensively tested.
| * When adding a node, if you specify a width or height of -1 it will
| "measure" the text and size the box and size it accordingly
| * Size the div surrounding the canvas so it fit the constraints
| of the visible graph and hide the overflow
| * Ability to specify the radius of the node corners via config.nodeRadius
| * Reformatted code (braces and such).
|
\------------------------------------------------------------------------------------------*/
const ECONode = function (id, pid, dsc, w, h, c, bc, target, meta) {
this.id = id;
this.pid = pid;
this.dsc = dsc;
this.w = w;
this.h = h;
this.c = c;
this.bc = bc;
this.target = target;
this.meta = meta;
this.siblingIndex = 0;
this.dbIndex = 0;
this.XPosition = 0;
this.YPosition = 0;
this.prelim = 0;
this.modifier = 0;
this.leftNeighbor = null;
this.rightNeighbor = null;
this.nodeParent = null;
this.nodeChildren = [];
this.isCollapsed = false;
this.canCollapse = false;
this.isSelected = false;
}
ECONode.prototype._getLevel = function () {
if (this.nodeParent.id == -1) {
return 0;
}
else return this.nodeParent._getLevel() + 1;
}
ECONode.prototype._isAncestorCollapsed = function () {
if (this.nodeParent.isCollapsed) {
return true;
} else {
if (this.nodeParent.id == -1) {
return false;
} else {
return this.nodeParent._isAncestorCollapsed();
}
}
}
ECONode.prototype._setAncestorsExpanded = function () {
if (this.nodeParent.id == -1) {
return;
} else {
this.nodeParent.isCollapsed = false;
return this.nodeParent._setAncestorsExpanded();
}
}
ECONode.prototype._getChildrenCount = function () {
if (this.isCollapsed) {
return 0;
}
if (this.nodeChildren == null) {
return 0;
} else {
return this.nodeChildren.length;
}
}
ECONode.prototype._getLeftSibling = function () {
if (this.leftNeighbor != null && this.leftNeighbor.nodeParent == this.nodeParent) {
return this.leftNeighbor;
} else {
return null;
}
}
ECONode.prototype._getRightSibling = function () {
if (this.rightNeighbor != null && this.rightNeighbor.nodeParent == this.nodeParent) {
return this.rightNeighbor;
} else {
return null;
}
}
ECONode.prototype._getChildAt = function (i) {
return this.nodeChildren[i];
}
ECONode.prototype._getChildrenCenter = function (tree) {
const node = this._getFirstChild();
const node1 = this._getLastChild();
return node.prelim + ((node1.prelim - node.prelim) + tree._getNodeSize(node1)) / 2;
}
ECONode.prototype._getFirstChild = function () {
return this._getChildAt(0);
}
ECONode.prototype._getLastChild = function () {
return this._getChildAt(this._getChildrenCount() - 1);
}
ECONode.prototype._drawChildrenLinks = function (tree) {
var s = [];
var xa = 0, ya = 0, xb = 0, yb = 0, xc = 0, yc = 0, xd = 0, yd = 0;
var node1 = null;
switch (tree.config.iRootOrientation) {
case ECOTree.RO_TOP:
xa = this.XPosition + (this.w / 2);
ya = this.YPosition + this.h;
break;
case ECOTree.RO_BOTTOM:
xa = this.XPosition + (this.w / 2);
ya = this.YPosition;
break;
case ECOTree.RO_RIGHT:
xa = this.XPosition;
ya = this.YPosition + (this.h / 2);
break;
case ECOTree.RO_LEFT:
xa = this.XPosition + this.w;
ya = this.YPosition + (this.h / 2);
break;
}
for (var k = 0; k < this.nodeChildren.length; k++) {
node1 = this.nodeChildren[k];
switch (tree.config.iRootOrientation) {
case ECOTree.RO_TOP:
xd = xc = node1.XPosition + (node1.w / 2);
yd = node1.YPosition;
xb = xa;
switch (tree.config.iNodeJustification) {
case ECOTree.NJ_TOP:
yb = yc = yd - tree.config.iLevelSeparation / 2;
break;
case ECOTree.NJ_BOTTOM:
yb = yc = ya + tree.config.iLevelSeparation / 2;
break;
case ECOTree.NJ_CENTER:
yb = yc = ya + (yd - ya) / 2;
break;
}
break;
case ECOTree.RO_BOTTOM:
xd = xc = node1.XPosition + (node1.w / 2);
yd = node1.YPosition + node1.h;
xb = xa;
switch (tree.config.iNodeJustification) {
case ECOTree.NJ_TOP:
yb = yc = yd + tree.config.iLevelSeparation / 2;
break;
case ECOTree.NJ_BOTTOM:
yb = yc = ya - tree.config.iLevelSeparation / 2;
break;
case ECOTree.NJ_CENTER:
yb = yc = yd + (ya - yd) / 2;
break;
}
break;
case ECOTree.RO_RIGHT:
xd = node1.XPosition + node1.w;
yd = yc = node1.YPosition + (node1.h / 2);
yb = ya;
switch (tree.config.iNodeJustification) {
case ECOTree.NJ_TOP:
xb = xc = xd + tree.config.iLevelSeparation / 2;
break;
case ECOTree.NJ_BOTTOM:
xb = xc = xa - tree.config.iLevelSeparation / 2;
break;
case ECOTree.NJ_CENTER:
xb = xc = xd + (xa - xd) / 2;
break;
}
break;
case ECOTree.RO_LEFT:
xd = node1.XPosition;
yd = yc = node1.YPosition + (node1.h / 2);
yb = ya;
switch (tree.config.iNodeJustification) {
case ECOTree.NJ_TOP:
xb = xc = xd - tree.config.iLevelSeparation / 2;
break;
case ECOTree.NJ_BOTTOM:
xb = xc = xa + tree.config.iLevelSeparation / 2;
break;
case ECOTree.NJ_CENTER:
xb = xc = xa + (xd - xa) / 2;
break;
}
break;
}
// Canvas Rendering
tree.ctx.save();
tree.ctx.strokeStyle = tree.config.linkColor;
tree.ctx.beginPath();
switch (tree.config.linkType) {
case "M":
tree.ctx.moveTo(xa, ya);
tree.ctx.lineTo(xb, yb);
tree.ctx.lineTo(xc, yc);
tree.ctx.lineTo(xd, yd);
break;
case "B":
tree.ctx.moveTo(xa, ya);
tree.ctx.bezierCurveTo(xb, yb, xc, yc, xd, yd);
break;
}
tree.ctx.stroke();
tree.ctx.restore();
}
return s.join('');
}
export const ECOTree = function (obj, elm) {
this.config = {
iMaxDepth : 100,
iLevelSeparation : 40,
iSiblingSeparation : 40,
iSubtreeSeparation : 80,
iRootOrientation : ECOTree.RO_TOP,
iNodeJustification : ECOTree.NJ_TOP,
topXCorrection : 0,
topYCorrection : 0,
topXAdjustment : 0,
topYAdjustment : 0,
linkType : "M",
linkColor : "blue",
nodeColor : "#CCCCFF",
nodeFill : ECOTree.NF_GRADIENT,
nodeBorderColor : "blue",
nodeSelColor : "#FFFFCC",
nodeRadius : 5,
levelColors : ["#5555FF","#8888FF","#AAAAFF","#CCCCFF"],
levelBorderColors : ["#5555FF","#8888FF","#AAAAFF","#CCCCFF"],
colorStyle : ECOTree.CS_NODE,
useTarget : true,
searchMode : ECOTree.SM_DSC,
selectMode : ECOTree.SL_MULTIPLE,
defaultNodeWidth : 80,
defaultNodeHeight : 40,
defaultTarget : 'javascript:void(0);',
//expandedImage : './img/less.gif',
//collapsedImage : './img/plus.gif',
//transImage : './img/trans.gif'
};
//if (jQuery("#eco_tree_ruler_span").length == 0) {
if ( !jQuery("#eco_tree_ruler_span") ) {
const b = jQuery("#EvaluationResults");
const span = jQuery.createElement('span', {
id: 'eco_tree_ruler_span',
'class': 'econode',
'style': 'visibility:hidden' });
b.append(span);
//b.append("<span id='eco_tree_ruler_span' class='econode' style='visibility:hidden'></span>");
}
this.version = "1.5-icb";
this.obj = obj;
this.treeContainer = jQuery("#" + elm);
this.__elm__ = elm; // QNC
this.self = this;
this.ctx = null;
this.canvasoffsetTop = 0;
this.canvasoffsetLeft = 0;
this.chartHeight = -1;
this.chartWidth = -1;
this.maxLevelHeight = [];
this.maxLevelWidth = [];
this.previousLevelNode = [];
this.rootYOffset = 0;
this.rootXOffset = 0;
this.nDatabaseNodes = [];
this.mapIDs = {};
this.root = new ECONode(-1, null, null, 2, 2);
this.iSelectedNode = -1;
this.iLastSearch = 0;
}
//Constant values
//Tree orientation
ECOTree.RO_TOP = 0;
ECOTree.RO_BOTTOM = 1;
ECOTree.RO_RIGHT = 2;
ECOTree.RO_LEFT = 3;
//Level node alignment
ECOTree.NJ_TOP = 0;
ECOTree.NJ_CENTER = 1;
ECOTree.NJ_BOTTOM = 2;
//Node fill type
ECOTree.NF_GRADIENT = 0;
ECOTree.NF_FLAT = 1;
//Colorizing style
ECOTree.CS_NODE = 0;
ECOTree.CS_LEVEL = 1;
//Search method: Title, metadata or both
ECOTree.SM_DSC = 0;
ECOTree.SM_META = 1;
ECOTree.SM_BOTH = 2;
//Selection mode: single, multiple, no selection
ECOTree.SL_MULTIPLE = 0;
ECOTree.SL_SINGLE = 1;
ECOTree.SL_NONE = 2;
//CANVAS functions...
ECOTree._roundedRect = function (ctx, x, y, width, height, radius) {
ctx.beginPath();
ctx.moveTo(x, y + radius);
ctx.lineTo(x, y + height - radius);
ctx.quadraticCurveTo(x, y + height, x + radius, y + height);
ctx.lineTo(x + width - radius, y + height);
ctx.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
ctx.lineTo(x + width, y + radius);
ctx.quadraticCurveTo(x + width, y, x + width - radius, y);
ctx.lineTo(x + radius, y);
ctx.quadraticCurveTo(x, y, x, y + radius);
ctx.fill();
ctx.stroke();
}
ECOTree._canvasNodeClickHandler = function (tree, target, nodeid) {
if (target != nodeid) {
return;
}
tree.selectNode(nodeid, true);
}
//Layout algorithm
ECOTree._firstWalk = function (tree, node, level) {
var leftSibling = null;
node.XPosition = 0;
node.YPosition = 0;
node.prelim = 0;
node.modifier = 0;
node.leftNeighbor = null;
node.rightNeighbor = null;
tree._setLevelHeight(node, level);
tree._setLevelWidth(node, level);
tree._setNeighbors(node, level);
if (node._getChildrenCount() == 0 || level == tree.config.iMaxDepth) {
leftSibling = node._getLeftSibling();
if (leftSibling != null) {
node.prelim = leftSibling.prelim + tree._getNodeSize(leftSibling) + tree.config.iSiblingSeparation;
} else {
node.prelim = 0;
}
} else {
var n = node._getChildrenCount();
for (var i = 0; i < n; i++) {
var iChild = node._getChildAt(i);
ECOTree._firstWalk(tree, iChild, level + 1);
}
var midPoint = node._getChildrenCenter(tree);
midPoint -= tree._getNodeSize(node) / 2;
leftSibling = node._getLeftSibling();
if (leftSibling != null) {
node.prelim = leftSibling.prelim + tree._getNodeSize(leftSibling) + tree.config.iSiblingSeparation;
node.modifier = node.prelim - midPoint;
ECOTree._apportion(tree, node, level);
} else {
node.prelim = midPoint;
}
}
}
ECOTree._apportion = function (tree, node, level) {
var firstChild = node._getFirstChild();
var firstChildLeftNeighbor = firstChild.leftNeighbor;
var j = 1;
for (var k = tree.config.iMaxDepth - level; firstChild != null && firstChildLeftNeighbor != null && j <= k;) {
var modifierSumRight = 0;
var modifierSumLeft = 0;
var rightAncestor = firstChild;
var leftAncestor = firstChildLeftNeighbor;
for (var l = 0; l < j; l++) {
rightAncestor = rightAncestor.nodeParent;
leftAncestor = leftAncestor.nodeParent;
modifierSumRight += rightAncestor.modifier;
modifierSumLeft += leftAncestor.modifier;
}
var totalGap = (firstChildLeftNeighbor.prelim + modifierSumLeft +
tree._getNodeSize(firstChildLeftNeighbor) + tree.config.iSubtreeSeparation) -
(firstChild.prelim + modifierSumRight);
if (totalGap > 0) {
var subtreeAux = node;
var numSubtrees = 0;
for (; subtreeAux != null && subtreeAux != leftAncestor; subtreeAux = subtreeAux._getLeftSibling()) {
numSubtrees++;
}
if (subtreeAux != null) {
var subtreeMoveAux = node;
var singleGap = totalGap / numSubtrees;
for (; subtreeMoveAux != leftAncestor; subtreeMoveAux = subtreeMoveAux._getLeftSibling()) {
subtreeMoveAux.prelim += totalGap;
subtreeMoveAux.modifier += totalGap;
totalGap -= singleGap;
}
}
}
j++;
if (firstChild._getChildrenCount() == 0) {
firstChild = tree._getLeftmost(node, 0, j);
} else {
firstChild = firstChild._getFirstChild();
}
if (firstChild != null) {
firstChildLeftNeighbor = firstChild.leftNeighbor;
}
}
}
ECOTree._secondWalk = function (tree, node, level, X, Y) {
if (level <= tree.config.iMaxDepth) {
var xTmp = tree.rootXOffset + node.prelim + X;
var yTmp = tree.rootYOffset + Y;
var maxsizeTmp = 0;
var nodesizeTmp = 0;
var corrTmp = 0;
var flag = false;
switch (tree.config.iRootOrientation) {
case ECOTree.RO_TOP:
case ECOTree.RO_BOTTOM:
maxsizeTmp = tree.maxLevelHeight[level];
nodesizeTmp = node.h;
break;
case ECOTree.RO_RIGHT:
case ECOTree.RO_LEFT:
maxsizeTmp = tree.maxLevelWidth[level];
flag = true;
nodesizeTmp = node.w;
break;
}
switch (tree.config.iNodeJustification) {
case ECOTree.NJ_TOP:
node.XPosition = xTmp;
node.YPosition = yTmp;
break;
case ECOTree.NJ_CENTER:
node.XPosition = xTmp;
node.YPosition = yTmp + (maxsizeTmp - nodesizeTmp) / 2;
break;
case ECOTree.NJ_BOTTOM:
node.XPosition = xTmp;
node.YPosition = (yTmp + maxsizeTmp) - nodesizeTmp;
break;
}
if (flag) {
var swapTmp = node.XPosition;
node.XPosition = node.YPosition;
node.YPosition = swapTmp;
}
switch (tree.config.iRootOrientation) {
case ECOTree.RO_BOTTOM:
node.YPosition = -node.YPosition - nodesizeTmp;
break;
case ECOTree.RO_RIGHT:
node.XPosition = -node.XPosition - nodesizeTmp;
break;
}
if (node._getChildrenCount() != 0) {
ECOTree._secondWalk(tree, node._getFirstChild(), level + 1, X + node.modifier,
Y + maxsizeTmp + tree.config.iLevelSeparation);
}
var rightSibling = node._getRightSibling();
if (rightSibling != null) {
ECOTree._secondWalk(tree, rightSibling, level, X, Y);
}
// adjust for large tree, where "left:' offset has gone negative (off the screen)
if (node.XPosition < 0) {
corrTmp = 0 - node.XPosition;
if (corrTmp > tree.config.topXCorrection) {
tree.config.topXCorrection = corrTmp;
}
}
// adjust for large tree, where "top:' offset has gone negative (off the screen)
if (node.YPosition < 0) {
corrTmp = 0 - node.YPosition;
if (corrTmp > tree.config.topYCorrection) {
tree.config.topYCorrection = corrTmp;
}
}
}
}
ECOTree._thirdWalk = function (tree, node, level) {
if (level <= tree.config.iMaxDepth) {
node.XPosition = node.XPosition + tree.config.topXCorrection;
node.YPosition = node.YPosition + tree.config.topYCorrection;
if (node._getChildrenCount() != 0) {
ECOTree._thirdWalk(tree, node._getFirstChild(), level + 1);
}
var rightSibling = node._getRightSibling();
if (rightSibling != null) {
ECOTree._thirdWalk(tree, rightSibling, level);
}
}
}
ECOTree.prototype._positionTree = function () {
this.maxLevelHeight = [];
this.maxLevelWidth = [];
this.previousLevelNode = [];
ECOTree._firstWalk(this.self, this.root, 0);
switch (this.config.iRootOrientation) {
case ECOTree.RO_TOP:
case ECOTree.RO_LEFT:
this.rootXOffset = this.config.topXAdjustment + this.root.XPosition;
this.rootYOffset = this.config.topYAdjustment + this.root.YPosition;
break;
case ECOTree.RO_BOTTOM:
case ECOTree.RO_RIGHT:
this.rootXOffset = this.config.topXAdjustment + this.root.XPosition;
this.rootYOffset = this.config.topYAdjustment + this.root.YPosition;
}
this.config.topXCorrection = 0;
this.config.topYCorrection = 0;
ECOTree._secondWalk(this.self, this.root, 0, 0, 0);
// Adjust for very large trees off of the screen
if ((this.config.topXCorrection > 0) || (this.config.topYCorrection > 0)) {
ECOTree._thirdWalk(this.self, this.root, 0);
}
}
ECOTree.prototype._setLevelHeight = function (node, level) {
if (this.maxLevelHeight[level] == null) {
this.maxLevelHeight[level] = 0;
}
if (this.maxLevelHeight[level] < node.h) {
this.maxLevelHeight[level] = node.h;
}
}
ECOTree.prototype._setLevelWidth = function (node, level) {
if (this.maxLevelWidth[level] == null) {
this.maxLevelWidth[level] = 0;
}
if (this.maxLevelWidth[level] < node.w) {
this.maxLevelWidth[level] = node.w;
}
}
ECOTree.prototype._setNeighbors = function(node, level) {
node.leftNeighbor = this.previousLevelNode[level];
if (node.leftNeighbor != null) {
node.leftNeighbor.rightNeighbor = node;
}
this.previousLevelNode[level] = node;
}
ECOTree.prototype._getNodeSize = function (node) {
switch (this.config.iRootOrientation) {
case ECOTree.RO_TOP:
case ECOTree.RO_BOTTOM:
return node.w;
case ECOTree.RO_RIGHT:
case ECOTree.RO_LEFT:
return node.h;
}
return 0;
}
ECOTree.prototype._getLeftmost = function (node, level, maxlevel) {
if (level >= maxlevel) {
return node;
}
if (node._getChildrenCount() == 0) {
return null;
}
var n = node._getChildrenCount();
for (var i = 0; i < n; i++) {
var iChild = node._getChildAt(i);
var leftmostDescendant = this._getLeftmost(iChild, level + 1, maxlevel);
if (leftmostDescendant != null) {
return leftmostDescendant;
}
}
return null;
}
ECOTree.prototype._selectNodeInt = function (dbindex, flagToggle) {
if (this.config.selectMode == ECOTree.SL_SINGLE) {
if ((this.iSelectedNode != dbindex) && (this.iSelectedNode != -1)) {
this.nDatabaseNodes[this.iSelectedNode].isSelected = false;
}
this.iSelectedNode = (this.nDatabaseNodes[dbindex].isSelected && flagToggle) ? -1 : dbindex;
}
this.nDatabaseNodes[dbindex].isSelected = (flagToggle) ? !this.nDatabaseNodes[dbindex].isSelected : true;
}
ECOTree.prototype._collapseAllInt = function (flag) {
var node = null;
for (var n = 0; n < this.nDatabaseNodes.length; n++) {
node = this.nDatabaseNodes[n];
if (node.canCollapse) {
node.isCollapsed = flag;
}
}
this.UpdateTree();
}
ECOTree.prototype._selectAllInt = function (flag) {
var node = null;
for (var k = 0; k < this.nDatabaseNodes.length; k++) {
node = this.nDatabaseNodes[k];
node.isSelected = flag;
}
this.iSelectedNode = -1;
this.UpdateTree();
}
ECOTree.prototype._drawTree = function () {
var s = [];
var node = null;
var color = "";
var border = "";
for (var n = 0; n < this.nDatabaseNodes.length; n++) {
node = this.nDatabaseNodes[n];
switch (this.config.colorStyle) {
case ECOTree.CS_NODE:
color = node.c;
border = node.bc;
break;
case ECOTree.CS_LEVEL:
var iColor = node._getLevel() % this.config.levelColors.length;
color = this.config.levelColors[iColor];
iColor = node._getLevel() % this.config.levelBorderColors.length;
border = this.config.levelBorderColors[iColor];
break;
}
if (!node._isAncestorCollapsed()) {
// Canvas rendering
this.ctx.save();
this.ctx.strokeStyle = border;
switch (this.config.nodeFill) {
case ECOTree.NF_GRADIENT:
var lgradient = this.ctx.createLinearGradient(node.XPosition, 0, node.XPosition + node.w, 0);
lgradient.addColorStop(0.0, ((node.isSelected) ? this.config.nodeSelColor : color));
lgradient.addColorStop(1.0, "#F5FFF5");
this.ctx.fillStyle = lgradient;
break;
case ECOTree.NF_FLAT:
this.ctx.fillStyle = ((node.isSelected) ? this.config.nodeSelColor : color);
break;
}
ECOTree._roundedRect(this.ctx, node.XPosition, node.YPosition, node.w, node.h, this.config.nodeRadius);
this.ctx.restore();
//HTML elements for text ... DIVs to be placed over the Canvas
s.push('<div id="' + node.id + '" class="econode" style="position:absolute;top:' + (node.YPosition + this.canvasoffsetTop) + 'px; left:' + (node.XPosition + this.canvasoffsetLeft) + 'px; width:' + node.w + '; height:' + node.h + ';z-index:100;" ');
if (this.config.selectMode != ECOTree.SL_NONE) {
// QNC useless:
//s.push('onclick="javascript:ECOTree._canvasNodeClickHandler(' + this.obj + ',event.target.id,\'' + node.id + '\');" ');
}
s.push('>');
s.push('<font face=Verdana size=1>');
// if (node.canCollapse) {
// s.push('<a id="c' + node.id + '" href="javascript:' + this.obj + '.collapseNode(\'' + node.id + '\', true);" >');
// s.push('<img border=0 src="' + ((node.isCollapsed) ? this.config.collapsedImage : this.config.expandedImage) + '" >');
// s.push('</a>');
// s.push('<img src="' + this.config.transImage + '" >');
// }
if (node.target && this.config.useTarget) {
s.push('<a id="t' + node.id + '" href="' + node.target + '">');
s.push(node.dsc);
s.push('</a>');
} else {
s.push(node.dsc);
}
s.push('</font>');
s.push('</div>');
if (!node.isCollapsed) {
s.push(node._drawChildrenLinks(this.self));
}
}
}
return s.join('');
}
ECOTree.prototype._calcWidthAndHeight = function () {
this.chartWidth = 0;
this.chartHeight = 0;
for (var n = 0; n < this.nDatabaseNodes.length; n++) {
let node = this.nDatabaseNodes[n];
if (!node._isAncestorCollapsed()) {
this.chartWidth = Math.max(this.chartWidth, node.XPosition + node.w);
this.chartHeight = Math.max(this.chartHeight, node.YPosition + node.h);
}
}
this.chartWidth += 2;
this.chartHeight += 2;
}
// ECOTree API begins here...
ECOTree.prototype.UpdateTree = function () {
// Empty the tree container so we can refill it
this.treeContainer.empty();
this._positionTree();
this._calcWidthAndHeight();
// Set the size on the tree container
this.treeContainer.css({
'position': 'relative',
'width': this.chartWidth,
'height': this.chartHeight,
'overflow': 'hidden'
});
// Make the <canvas> element.
var canvas = document.createElement('canvas');
jQuery(canvas).attr('width', this.chartWidth)
.attr('height', this.chartHeight)
.attr('id', 'ECOTreecanvas_' + this.__elm__)
.appendTo(this.treeContainer);
//jQuery(canvas).attr('width', 2000).attr('height', 7000).attr('id', 'ECOTreecanvas').appendTo(this.treeContainer);
if (jQuery.browser.msie) {
// For Internet Explorer, have excanvas initialize the canvas method
canvas = G_vmlCanvasManager.initElement(canvas);
}
if (canvas && canvas.getContext) {
this.ctx = canvas.getContext("2d");
var h = this._drawTree();
this.treeContainer.append(h);
}
}
ECOTree.prototype.add = function (id, pid, dsc, w, h, c, bc, target, meta) {
var nw = w || this.config.defaultNodeWidth; //Width, height, colors, target and metadata defaults...
var nh = h || this.config.defaultNodeHeight;
// See if we need to calculate the width or the height
if (nw == -1 || nh == -1) {
var ruler = jQuery("#eco_tree_ruler_span");
ruler.css({'width' : '', 'height' : ''});
if (nw != -1) {
ruler.css({'width' : nw});
}
if (nh != -1) {
ruler.css({'height' : nh});
}
ruler.html(dsc);
if (nw == -1) {
nw = ruler.innerWidth() + 18;
}
if (nh == -1) {
nh = ruler.innerHeight() + 4;
}
}
var color = c || this.config.nodeColor;
var border = bc || this.config.nodeBorderColor;
var tg = (this.config.useTarget) ? ((typeof target == "undefined") ? (this.config.defaultTarget) : target) : null;
var metadata = (typeof meta != "undefined") ? meta : "";
var pnode = null; //Search for parent node in database
if (pid == -1) {
pnode = this.root;
} else {
for (var k = 0; k < this.nDatabaseNodes.length; k++) {
if (this.nDatabaseNodes[k].id == pid) {
pnode = this.nDatabaseNodes[k];
break;
}
}
}
var node = new ECONode(id, pid, dsc, nw, nh, color, border, tg, metadata); //New node creation...
node.nodeParent = pnode; //Set it's parent
pnode.canCollapse = true; //It's obvious that now the parent can collapse
var i = this.nDatabaseNodes.length; //Save it in database
node.dbIndex = this.mapIDs[id] = i;
this.nDatabaseNodes[i] = node;
var h = pnode.nodeChildren.length; //Add it as child of it's parent
node.siblingIndex = h;
pnode.nodeChildren[h] = node;
}
ECOTree.prototype.searchNodes = function (str) {
var node = null;
var m = this.config.searchMode;
var sm = (this.config.selectMode == ECOTree.SL_SINGLE);
if (typeof str == "undefined") return;
if (str == "") return;
var found = false;
var n = (sm) ? this.iLastSearch : 0;
if (n == this.nDatabaseNodes.length) n = this.iLastSeach = 0;
str = str.toLocaleUpperCase();
for (; n < this.nDatabaseNodes.length; n++) {
node = this.nDatabaseNodes[n];
if (node.dsc.toLocaleUpperCase().indexOf(str) != -1 && ((m == ECOTree.SM_DSC) || (m == ECOTree.SM_BOTH))) {
node._setAncestorsExpanded();
this._selectNodeInt(node.dbIndex, false);
found = true;
}
if (node.meta.toLocaleUpperCase().indexOf(str) != -1 && ((m == ECOTree.SM_META) || (m == ECOTree.SM_BOTH))) {
node._setAncestorsExpanded();
this._selectNodeInt(node.dbIndex, false);
found = true;
}
if (sm && found) {
this.iLastSearch = n + 1;
break;
}
}
this.UpdateTree();
}
ECOTree.prototype.selectAll = function () {
if (this.config.selectMode != ECOTree.SL_MULTIPLE) {
return;
}
this._selectAllInt(true);
}
ECOTree.prototype.unselectAll = function () {
this._selectAllInt(false);
}
ECOTree.prototype.collapseAll = function () {
this._collapseAllInt(true);
}
ECOTree.prototype.expandAll = function () {
this._collapseAllInt(false);
}
ECOTree.prototype.collapseNode = function (nodeid, upd) {
var dbindex = this.mapIDs[nodeid];
this.nDatabaseNodes[dbindex].isCollapsed = !this.nDatabaseNodes[dbindex].isCollapsed;
if (upd) {
this.UpdateTree();
}
}
ECOTree.prototype.selectNode = function (nodeid, upd) {
this._selectNodeInt(this.mapIDs[nodeid], true);
if (upd) {
this.UpdateTree();
}
}
ECOTree.prototype.setNodeTitle = function (nodeid, title, upd) {
var dbindex = this.mapIDs[nodeid];
this.nDatabaseNodes[dbindex].dsc = title;
if (upd) {
this.UpdateTree();
}
}
ECOTree.prototype.setNodeMetadata = function (nodeid, meta, upd) {
var dbindex = this.mapIDs[nodeid];
this.nDatabaseNodes[dbindex].meta = meta;
if (upd) {
this.UpdateTree();
}
}
ECOTree.prototype.setNodeTarget = function (nodeid, target, upd) {
var dbindex = this.mapIDs[nodeid];
this.nDatabaseNodes[dbindex].target = target;
if (upd) {
this.UpdateTree();
}
}
ECOTree.prototype.setNodeColors = function (nodeid, color, border, upd) {
var dbindex = this.mapIDs[nodeid];
if (color) {
this.nDatabaseNodes[dbindex].c = color;
}
if (border) {
this.nDatabaseNodes[dbindex].bc = border;
}
if (upd) {
this.UpdateTree();
}
}
ECOTree.prototype.getSelectedNodes = function () {
var node = null;
var selection = [];
var selnode = null;
for (var n = 0; n < this.nDatabaseNodes.length; n++) {
node = this.nDatabaseNodes[n];
if (node.isSelected) {
selnode = {
"id" : node.id,
"dsc" : node.dsc,
"meta" : node.meta
};
selection[selection.length] = selnode;
}
}
return selection;
}