Nano Tree : Tree « GUI Components « JavaScript DHTML

JavaScript DHTML
1. Ajax Layer
2. Data Type
3. Date Time
4. Development
5. Document
6. Event
7. Event onMethod
8. Form Control
9. GUI Components
10. HTML
11. Javascript Collections
12. Javascript Objects
13. Language Basics
14. Node Operation
15. Object Oriented
16. Page Components
17. Security
18. Style Layout
19. Table
20. Utilities
21. Window Browser
Microsoft Office Word 2007 Tutorial
Java
Java Tutorial
Java Source Code / Java Documentation
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
C# / C Sharp
C# / CSharp Tutorial
ASP.Net
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
PHP
Python
SQL Server / T-SQL
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
JavaScript DHTML » GUI Components » Tree 
Nano Tree

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>NanoTree</title>
<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">

<!-- std_treelook.css -->
<style REL="STYLESHEET" TYPE="text/css">
/* Tree Style */
.treetitle {
  padding:2px;
  cursor:default;
  
  font-family: Tahoma,Verdana,Arial, Helvetica, sans-serif;
  font-size: 11px;
  color: #000000;
}
.editednode {
  padding:2px;
  cursor:default;
  background-color: #FFFFFF;;
  color: #000000;
  
  font-family: Tahoma,Verdana,Arial, Helvetica, sans-serif;
  font-size: 11px;
}
.editednodeinput {
  background-color: #FFFFFF;;
  color: #000000;
  
  width: 150px;
  height: 17px;
  
  border-style: solid;
  border-width: 1px;
  border-color: #000000;
  
  font-family: Tahoma,Verdana,Arial, Helvetica, sans-serif;
  font-size: 11px;
}
.treetitleselectedfocused {
  padding:2px;
  cursor:default;
  background-color: highlight;
  color: highlighttext;
  
  font-family: Tahoma,Verdana,Arial, Helvetica, sans-serif;
  font-size: 11px;
}
.treetitleselectedblured {
  padding:2px;
  cursor:default;
  background-color: menu;
  color: windowtext;
  font-family: Tahoma,Verdana,Arial, Helvetica, sans-serif;
  font-size: 11px;
}
</style>

<!-- nanotree.js -->
<script type="text/javascript" language="JavaScript">
/**
* Original Author of this file: Martin Mouritzen. (martin@nano.dk)
*
*
* (Lack of) Documentation:
*
*
* If a finishedLoading method exists, it will be called when the tree is loaded.
* (good to display a div, etc.).
*
*
* You have to set the variable rootNode (as a TreeNode).
*
* You have to set a container element, this is the element in which the tree will be.
*
*
* TODO: 
* Save cookies better (only 1 cookie for each tree). Else the page will totally cookieclutter.
*
***********************************************************************
* Configuration variables.
************************************************************************/

// Should the rootNode be displayed.
var showRootNode = true;

// Should the dashed lines between nodes be shown.
var showLines = true;

// Should the nodes be sorted? (You can either specify a number, then it will be sorted by that, else it will
// be sorted alphabetically (by name).
var sortNodes = true;

// This is IMPORTANT... use an unique id for each document you use the tree in. (else they'll get mixed up).
var documentID = window.location.href;

// being read from cookie.
var nodesOpen = new Array();

// RootNode of the tree.
var rootNode;

// Container to display the Tree in.
var container;

// Shows/Hides subnodes on startup
var showAllNodesOnStartup = false;

// Is the roots dragable?
var dragable = false;


/************************************************************************
* The following is just instancevariables.
************************************************************************/
var href = '';

// rootNodeCallBack name (if null, it's not selectable).
var rootNodeCallBack = null;

// selectedNode
var selectedNode = null;

var states = '';
var statearray = new Array();

var treeNodeEdited = null;

var editaborted = false;

var floatDragElement = null;
var colouredElement = null;
var draggedNodeID = null;
var lastDraggedOnNodeID = null;


/**
* The TreeNode Object
@param id unique id of this treenode
@param name The title of this node
@param icon The icon if this node (Can also be an array with 2 elements, the first one will represent the closed state, and the next one the open state)
@param param A parameter, this can be pretty much anything. (eg. an array with information).
@param orderNumber an orderNumber If one is given the nodes will be sorted by this (else they'll be sorted alphabetically (If sorting is on).
*/
function TreeNode(id,name,icon,param,orderNumber) {
  this.id = id;
  this.childs = new Array();
  this.name = (name == null 'unset name' : name);
  this.icon = (icon == null '' : icon);
  this.parent = null;
  this.handler = null;
  this.param = (param == null '' : param);
  this.orderNumber = (orderNumber == null ? -: orderNumber);
  
  this.openeventlisteners = new Array();
  this.editeventlisteners = new Array();
  this.moveeventlisteners = new Array();
  this.haschilds = false;
  this.editable = false;
  this.linestring = '';
  
  this.nextSibling = null;
  this.prevSibling = null;
  
  this.childsHasBeenFetched = false;

  this.getID = function() {
    return this.id;
  }
  this.setName = function(newname) {
    this.name = newname;
  }
  this.getName = function() {
    return this.name;
  }
  this.getParam = function() {
    return this.param;
  }
  this.setIcon = function(icon) {
    this.icon = icon;
  }
  this.getIcon = function() {
    if (typeof(this.icon== 'object') {
      return this.icon[0];
    }
    return this.icon;
  }
  this.getOpenIcon = function() {
    if (typeof(this.icon== 'object') {
      return this.icon[1];
    }
    return this.icon;
  }
  this.hasIcon = function () {
    return this.icon != '';
  }
  this.getOrderNumber = function() {
    return this.orderNumber;
  }
  this.addOpenEventListener = function(event) {
    this.openeventlisteners[this.openeventlisteners.length= event;
  }
  this.gotOpenEventListeners = function() {
    return (this.openeventlisteners.length > 0);
  }
  this.addEditEventListener = function(event) {
    this.editeventlisteners[this.editeventlisteners.length= event;
  }
  this.gotEditEventListeners = function() {
    return (this.editeventlisteners.length > 0);
  }
  this.addMoveEventListener = function(event) {
    this.moveeventlisteners[this.moveeventlisteners.length= event;
  }
  this.gotMoveEventListeners = function() {
    return (this.moveeventlisteners.length > 0);
  }
  this.addChild = function(childNode) {
    var possiblePrevNode = this.childs[this.childs.length - 1]
    if (possiblePrevNode) {
      possiblePrevNode.nextSibling = childNode;
      childNode.prevSibling = possiblePrevNode;
      // alert(childNode.prevSibling);
    }

    this.childs[this.childs.length= childNode;
    childNode.setParent(this);

    if (sortNodes) {
      function sortByOrder(a,b) {
        var order1 = a.getOrderNumber();
        var order2 = b.getOrderNumber();
        if (order1 == -|| order2 == -1) {
          return a.getName().toLowerCase() > b.getName().toLowerCase() : -1;
        }
        else {
          if (order1 == order2) {
            // If they got the same order number, then we'll sort by their title.
            return a.getName().toLowerCase() > b.getName().toLowerCase() : -1;
          }
          else {
            return order1 - order2;
          }
        }
      }
      this.childs.sort(sortByOrder);
    }
  }
  this.removeChild = function(childNode) {
    var found = false;
    for (var i=0;i<this.childs.length;i++) {
      if (found) {
        this.childs[ithis.childs[i + 1];
      }
      if (this.childs[i== childNode) {
        if (i == (this.childs.length - 1)) {
          this.childs[inull;
        }
        else {
          this.childs[ithis.childs[i + 1];
        }
        found = true;
      }
    }
    if (found) {
      this.childs.length = this.childs.length-1;
    }
  }
  this.resetChilds = function() {
    this.childs = new Array();
  }
  this.setHasChilds = function(hasChilds) {
    this.haschilds = hasChilds;
  }
  this.hasChilds = function() {
    if (this.haschilds == true) {
      return true;
    }
    return (this.childs.length > 0);
  }
  this.getChildCount = function() {
    return this.childs.length;
  }
  this.getFirstChild = function() {
    if (this.hasChilds()) {
      return this.childs[0];
    }
    return null;
  }
  this.gotHandler = function() {
    return this.handler != null;
  }
  this.setHandler = function(handler) {
    this.handler = handler;
  }
  this.getHandler = function() {
    return this.handler;
  }
  this.setParent = function(parent) {
    this.parent = parent;
  }
  this.getParent = function() {
    return this.parent;
  }
  this.getLineString = function() {
    return this.linestring;
  }
  this.setLineString = function(string) {
    this.linestring = string;
  }
  this.isEditable = function() {
    return this.editable;
  }
  this.setEditable = function(editable) {
    this.editable = editable;
  }
  
}
function getTreeNode(nodeID) {
  return findNodeWithID(rootNode,nodeID);
}
function findNodeWithID(node,nodeID) {
  if (node.getID() == nodeID) {
    return node;
  }
  else {
    if (node.hasChilds()) {
      for(var i=0;i<node.getChildCount();i++) {
        var value = findNodeWithID(node.childs[i],nodeID);
        if (value != false) {
          return value;
        }
      }
    }
    return false;
  }
}
function readStates() {
  //setCookie('tree' + documentID,'');
  states = getCookie('tree' + documentID);
  if (states != null) {
    var array = states.split(';');
    for(var i=0;i<array.length;i++) {
      var singlestate = array[i].split('|');
      statearray[inew Array();
      statearray[i]["key"= singlestate[0];
      statearray[i]["state"]  = singlestate[1];
    }
  }
}
function getState(nodeID) {
  for(var i=0;i<statearray.length;i++) {
    if (statearray[i]["key"== nodeID) {
      state = statearray[i]["state"];
      if (state == null || state == '') {
        state = 'closed';
      }
      return state;
    }
  }
  return "closed";
}
function writeStates(nodeID,newstate) {
  //alert(nodeID);
  var str = '';
  var found = false;
  for(var i=0;i<statearray.length;i++) {
    if (statearray[i]["key"== nodeID) {
      statearray[i]["state"= newstate;
      found = true;
    }
    if (statearray[i]["state"!= null) {
      str += statearray[i]["key"'|' + statearray[i]["state"';';
    }
  }
  if (found == false) {
    statearray[statearray.lengthnew Array();
    statearray[statearray.length - 1]["key"= nodeID;
    statearray[statearray.length - 1]["state"= newstate;
    if (newstate != null) {
      str += nodeID + '|' + newstate + ';';
    }
  }
  setCookie('tree' + documentID,str);
}
function showTree(path) {
  readStates();
  
  href = path;
  window.focus();
  window.onblur = blurSelection;
  window.onfocus = focusSelection;
  var str = '';
  str = '<div id="node' + rootNode.getID() + '" class="treetitle" style="display:' + (showRootNode == true ? 'block' : 'none') + '">';
  str += '<nobr>';
  if (rootNode.hasIcon()) {
    str += '<img src="' + rootNode.getIcon() + '" style="vertical-align:middle;">';
  }
  str += '<span style="vertical-align:middle;">&nbsp;' + rootNode.getName() '</span>';
  str += '</nobr></div>';
  
  if (rootNode.hasChilds()) {
    for(i=0;i<rootNode.childs.length;i++) {
      nodeContents = showNode(rootNode.childs[i],(i == (rootNode.getChildCount() -1)));
      str = str + nodeContents;
    }
  }
  container.innerHTML = str;
  if (window.finishedLoading) {
    finishedLoading();
  }
}
/**
* Shows the given node, and subnodes.
*/
function showNode(treeNode,lastNode) {
  linestring = treeNode.getLineString();
  var state = getState(treeNode.getID());
  var str;
  str = '<div style="filter:alpha(opacity=100);" ondragenter="dragEnter(' + treeNode.getID() + ');" ondragleave="dragLeave();" ondragstart="startDrag(' + treeNode.getID() + ');" ondrag="dragMove();" ondragend="endDrag(' + treeNode.getID() + ')" id="node' + treeNode.getID() + '">';
  str += '<nobr>';
  for(var y=0;y<linestring.length;y++) {
    if (linestring.charAt(y== 'I') {
      str += '<img src="' + href + 'nanoImages/' + (showLines ? 'line' : 'white') + '.gif" style="width:19px;height:20px;vertical-align:middle;">';
    }
    else if (linestring.charAt(y== 'B') {
      str += '<img src="' + href + 'nanoImages/white.gif" style="width:19px;height:20px;vertical-align:middle;">';
    }
  }
  if (treeNode.hasChilds()) {
    // If this is the first child of the rootNode, and showRootNode is false, we want to display a different icon.
    if (!showRootNode && (treeNode.getParent() == rootNode&& (treeNode.getParent().getFirstChild() == treeNode)) {
      if (!lastNode) {
        str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'nanoImages/' + (state == 'open' ? (showLines ? 'minus_no_root' : 'minus_nolines') : (showLines ? 'plus_no_root' : 'plus_nolines')) + '.gif" style="width:19px;height:20px;vertical-align:middle;" OnClick="handleNode(' + treeNode.getID() + ');">';
      }
      else {
        str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'nanoImages/' + (state == 'open' ? 'minus_last' : 'plus_last') + '_no_root.gif" style="width:19px;height:20px;vertical-align:middle;" OnClick="handleNode(' + treeNode.getID() + ');">';
      }
    }
    else {
      if (!lastNode) {
        str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'nanoImages/' + (state == 'open' ? (showLines ? 'minus' : 'minus_nolines') : (showLines ? 'plus' : 'plus_nolines')) + '.gif" style="width:19px;height:20px;vertical-align:middle;" OnClick="handleNode(' + treeNode.getID() + ');">';
      }
      else {
        str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'nanoImages/' + (state == 'open' ? (showLines ? 'minus_last' : 'minus_nolines') : (showLines ? 'plus_last' : 'plus_nolines')) + '.gif" style="width:19px;height:20px;vertical-align:middle;" OnClick="handleNode(' + treeNode.getID() + ');">';
      }
    }
  }
  else {
    // If this is the first child of the rootNode, and showRootNode is false, we want to display a different icon.
    if (!showRootNode && (treeNode.getParent() == rootNode&& (treeNode.getParent().getFirstChild() == treeNode)) {
      if (!lastNode) {
        str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'nanoImages/' + (showLines ? 't_no_root' : 'white') + '.gif" style="width:19px;height:20px;vertical-align:middle;">';
      }
      else {
        str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'nanoImages/white.gif" style="width:19px;height:20px;vertical-align:middle;">';
      }
    }
    else {
      if (!lastNode) {
        str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'nanoImages/' + (showLines ? 't' : 'white') + '.gif" style="width:19px;height:20px;vertical-align:middle;">';
      }
      else {
        str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'nanoImages/' + (showLines ? 'lastnode' : 'white') + '.gif" style="width:19px;height:20px;vertical-align:middle;">';
      }
    }
  }
  iconStartImage = treeNode.getIcon();
  if (state != 'closed') {
    if (treeNode.hasChilds()) {
      iconStartImage = treeNode.getOpenIcon();
    }
  }
  
  str += '<img id="iconimage' + treeNode.getID() + '" src="' + iconStartImage + '" style="vertical-align:middle;" OnClick="selectNode(' + treeNode.getID() + ')">';
  str += '&nbsp;<span unselectable="ON" style="vertical-align:middle;" class="treetitle" ID="title' + treeNode.getID() + '" OnDblClick="handleNode(' + treeNode.getID() + ')" OnClick="selectNode(' + treeNode.getID() + ')">';
  str += treeNode.getName();
  str += '</span>';
  str += '</nobr>';
  str += '</div>';

  if (treeNode.hasChilds()) {
    if (state == 'open') {
      str += '<div id="node' + treeNode.getID() + 'sub" style="display:block;">';
      fireOpenEvent(treeNode);
      // alert('openevent: ' + treeNode.getName());
    }
    else {
      str += '<div id="node' + treeNode.getID() + 'sub" style="display:' + (showAllNodesOnStartup == true ? 'block;' : 'none;') + ';">';
    }
    var subgroupstr = '';
    var newChar = '';

    if (!lastNode) {
      newChar = 'I';
    }
    else {
      newChar = 'B';
    }
    for(var z=0;z<treeNode.getChildCount();z++) {
      treeNode.childs[z].setLineString(linestring + newChar);
    }
    for(var z=0;z<treeNode.getChildCount();z++) {
      subgroupstr += showNode(treeNode.childs[z],(z == (treeNode.getChildCount() -1)));
    }
    str += subgroupstr;
    str += '</div>';
  }
  else {
    str += '<div id="node' + treeNode.getID() + 'sub" style="display:none;">';
    str += '</div>';
  }
  return str;
}
/*
function mouseMove() {
  if (dragging) {
    alert('bob');
  }
}
function mouseUp() {
  if (dragging) {
    alert('dropped on something!');
  }
}
*/
function startDrag(nodeID) {
  if (!dragable) {
    return;
  }
  draggedNodeID = nodeID;
  
  var srcObj = window.event.srcElement;
  while(srcObj.tagName != 'DIV') {
    srcObj = srcObj.parentElement;
  }
  floatDragElement = document.createElement('DIV');

  floatDragElement.innerHTML = srcObj.innerHTML;
  floatDragElement.childNodes[0].removeChild(floatDragElement.childNodes[0].childNodes[0]);
  
  document.body.appendChild(floatDragElement);
  floatDragElement.style.zIndex = 100;
  floatDragElement.style.position = 'absolute';
  floatDragElement.style.filter='progid:DXImageTransform.Microsoft.Alpha(1,opacity=60);';
}
function findSpanChild(element) {
  if (element.tagName == 'SPAN') {
    return element;
  }
  else {
    if (element.childNodes) {
      for(var i=0;i<element.childNodes.length;i++) {
        var value = findSpanChild(element.childNodes[i]);
        if (value != false) {
          return value;
        }
      }
      return false;
    }
  }
}
function dragEnter(nodeID) {
  if (!dragable) {
    return;
  }
  lastDraggedOnNodeID = nodeID;
  
  if (colouredElement) {
    findSpanChild(colouredElement).className = 'treetitle';
  }
  colouredElement = window.event.srcElement;
  while(colouredElement.tagName != 'DIV') {
    colouredElement = colouredElement.parentElement;
    if (colouredElement.tagName == 'BODY') {
      // Something gone seriously wrong.
      alert('Drag failure, reached <BODY>!');
      return;
    }
  }  
  findSpanChild(colouredElement).className = 'treetitleselectedfocused';
}
function dragLeave() {
  if (!dragable) {
    return;
  }
}
function endDrag(nodeID) {
  if (!dragable) {
    return;
  }
  if (lastDraggedOnNodeID != null) {
    fireMoveEvent(getTreeNode(lastDraggedOnNodeID),draggedNodeID,lastDraggedOnNodeID);
  }
}
function dragProceed() {
  if (!dragable) {
    return;
  }
  var dragged = getTreeNode(draggedNodeID);
  var newparent = getTreeNode(lastDraggedOnNodeID);

  var oldparent = dragged.getParent();
  
  oldparent.removeChild(dragged);
  newparent.addChild(dragged);
  
  refreshNode(oldparent);
  refreshNode(newparent);
  
  _dragClean()
}
function dragCancel() {
  if (!dragable) {
    return;
  }
  _dragClean()
}
/**
* Don't call this yourself.
*/
function _dragClean() {
  if (!dragable) {
    return;
  }
  if (colouredElement) {
    findSpanChild(colouredElement).className = 'treetitle';
  }
  
  floatDragElement.parentElement.removeChild(floatDragElement);
  floatDragElement = null;
  colouredElement = null;
  draggedNodeID = null;
  lastDraggedOnNodeID = null;
}
function dragMove() {
  if (!dragable) {
    return;
  }
  floatDragElement.style.top = window.event.clientY;
  floatDragElement.style.left = window.event.clientX;
}
function editEnded() {
  if (treeNodeEdited != null) {
    // treeNodeEdited.getID();
    var editTitle = document.getElementById('title' + treeNodeEdited.getID());
    var input = editTitle.childNodes[0];
  
    var newValue = input.value;
    
    if (newValue == treeNodeEdited.getName()) {
      editTitle.innerHTML = newValue;
      treeNodeEdited = null;
      return;
    }
  
  &n