/******************************************************************************
 * dtree v2.0 (25 Feb 2004) - DHTML Tree-View implementation in JavaScript    *
 *     - tested to work on MSIE 6.0, Mozilla 1.6 and Netscape 7.1             *
 *     - much more simpler than v1.1 - does not contain browser specific code *
 *     - new features (see http://personal.sirma.bg/stenly/dtree_nav.html)    *
 *     - free for non-commercial purposes                                     *
 *     - v2.2 available for commercial purposes, features:                    *
 *         - hi-lighting of focused items                                     *
 *         - item-specific context menues                                     *
 *         - costs EUR 200 / $ 240 (contact author)                           *
 *                                                                            *
 * dtree v1.1 (19 Feb 1999) - DHTML Tree-View implementation in JavaScript    *
 *     - works on Netscape 4.+ or MSIE 4.+ but has problems with the latest   *
 *       versions of the popular browsers                                     *
 *     - v1.1 (but not v2.0 or later) is distributed under GPL                *
 *     - download at: http://personal.sirma.bg/stenly/dtree-v1.1.js           *
 *                                                                            *
 * author: Stanislav Jordanov  e-mail: stenly@REMOVEMEsirma.bg                *
 *                             http://personal.sirma.bg/stenly/               *
 ******************************************************************************/
// array containing all the trees in the document
var treesArray		= null;
// the name of the form containing each tree's state
var formName		= "hiddenOntologyTreeForm";
// the URL of the directory containing the images
var strImagePath	= "images/";
// all images are sized to 16x22
var tagIMG			= "<img width=16 height=22 ";

var imgUrlChecked        = strImagePath + "dt_checked.gif";
var imgUrlUnchecked      = strImagePath + "dt_unchecked.gif";
var imgUrlNode           = strImagePath + "dt_node.gif";
var imgUrlLastNode       = strImagePath + "dt_lastnode.gif";
var imgUrlOpenedNode     = strImagePath + "dt_mnode.gif";
var imgUrlClosedNode     = strImagePath + "dt_pnode.gif";
var imgUrlOpenedLastNode = strImagePath + "dt_mlastnode.gif";
var imgUrlClosedLastNode = strImagePath + "dt_plastnode.gif";
var imgUrlDefItem        = strImagePath + "dt_doc.gif";
var imgUrlDefOpened      = strImagePath + "dt_folderopen.gif";
var imgUrlDefClosed      = strImagePath + "dt_folderclosed.gif";

var htmlVertline= tagIMG + "src='"+strImagePath+"dt_vertline.gif'>";
var htmlBlank   = tagIMG + "src='"+strImagePath+"dt_blank.gif'>";


if (Array.prototype.push == null)
  Array.prototype.push =
  function  push(data) {
	this[this.length] = data;
  };
if (Array.prototype.pop == null)
  Array.prototype.pop  =
  function  pop() {
	var data = this[this.length-1];
	this.length--;
	return data;
  };


function  Item(strLabel, imgUrlItem)
{
  this.id          = null;  // set in Tree_setup
  this.layer       = null;  // set in Item/Folder_renderLayer
  this.parent      = null;  // set in Folder_insertItem
  this.strLabel    = strLabel;
  this.imgUrlItem  = (imgUrlItem ? imgUrlItem : imgUrlDefItem);

  this.renderLayer   = Item_renderLayer;   // (Tree tree) virtual
  this.makeVisible   = Item_makeVisible;   // (void)
  this.makeInvisible = Item_makeInvisible; // (void)
  this.isLastChild   = Item_isLastChild;   // (void)
  this.vShift        = Item_vShift;        // (int nPixels)
  this.vPlace        = Item_vPlace;        // (int atPixel)
  this.vPos          = Item_vPos;          // (void)
}


function  Folder(strLabel, imgUrlClosed, imgUrlOpened)
{
  // properties inherited from Item:
  this.id          = null;     // set in Tree_setup
  this.strLabel    = strLabel;
  this.layer       = null;     // set in Item/Folder_renderLayer
  this.parent      = null;     // set in Folder_insertItem
  if (imgUrlClosed) {
	this.imgUrlClosed = imgUrlClosed;
	this.imgUrlOpened =(imgUrlOpened ? imgUrlOpened : imgUrlClosed);
   }
   else {
	 this.imgUrlClosed = imgUrlDefClosed;
	 this.imgUrlOpened = imgUrlDefOpened;
   }

  // folder specific properties:
  this.isFolder    = true;
  this.children    = new Array();
  this.isOpened    = false;
  this.strLeftSide = null;     // set in Folder_renderLayer

  // methods inherited from Item:
  this.makeVisible   = Item_makeVisible;   // (void)
  this.makeInvisible = Item_makeInvisible; // (void)
  this.isLastChild   = Item_isLastChild;   // (void)
  this.vShift        = Item_vShift;        // (int nPixels)
  this.vPlace        = Item_vPlace;        // (int atPixel)
  this.vPos          = Item_vPos;          // (void)

  // folder specific methods:
  this.renderLayer    = Folder_renderLayer;            // (Tree tree) virtual
  this.collectVisibleSubs = Folder_collectVisibleSubs; // (Item-Array arrSubs)
  this.I = Folder_insertItem;                          // (String itemLabel)
  this._ = Folder_insertChild;                         // (Item child)
  this.getFolderImgUrl = Folder_getFolderImgUrl;         // (void)
  this.getNodeImgUrl   = Folder_getNodeImgUrl;           // (void)
}


function  Item_renderLayer(tree)
{
  if (this.layer != null)
	return;

  var tabId = "tab"+tree.id+"."+this.id;
  var htmlContent = "<table style='position:absolute;left:0;visibility:hidden' id='"+tabId+"'"
	+ " border=0 cellspacing=0 cellpadding=0><tr><td nowrap>"
	+ this.parent.strLeftSide
	+ (this.isLastChild() ? tagIMG + "src='"+imgUrlLastNode+"'>"
						  : tagIMG + "src='"+imgUrlNode+"'>")
	+ tagIMG + "id='itemIcon"+this.id+"' src='"+this.imgUrlItem+"'>"
  if (tree.ITEM_CB_VISIBLE) {
	// render the checkbox:
	this.isChecked = tree.isItemChecked(this);
	htmlContent += tagIMG + "id='checkBox"+this.id
				+           "' src='" + (this.isChecked ? imgUrlChecked : imgUrlUnchecked)
				+           "' onclick='javascript:clickOnCB("+tree.id+","+this.id
				+           ")'>";
  }
  htmlContent += "</td><td valign=middle nowrap>&nbsp;"+this.strLabel+"</td></tr></table>";

  this.layerId = tabId;
  return htmlContent;
}


function  Folder_renderLayer(tree)
{
  if (this.layer != null)
	return;

  if (this.parent != null)
	this.strLeftSide = this.parent.strLeftSide;
  else
	this.strLeftSide = "";

  var tabId = "tab"+tree.id+"."+this.id;
  var onclick = "' onclick='javascript:clickOnNode("+tree.id+","+this.id+")'"
			  + "  onmouseover=\"this.style.cursor='hand'\">";
  var hasChildren = this.children.length != 0;
  var htmlContent = "<table style='position:absolute;left:0;visibility:hidden' id='"+tabId+"'"
	+ " border=0 cellspacing=0 cellpadding=0><tr><td nowrap>"
	+ this.strLeftSide
	  // render the +/- box only if the folder has children:
	+(hasChildren ? (tagIMG + "id='nodeIcon"+this.id+"' src='"+this.getNodeImgUrl() + onclick)
				  : (this.isLastChild() ? tagIMG + "src='"+imgUrlLastNode+"'>"
										: tagIMG + "src='"+imgUrlNode+"'>"))
	  // render the folder icon:
	+ tagIMG + "id='itemIcon"+this.id+"' src='"+this.getFolderImgUrl()
			 + (hasChildren ? onclick : "'>");

  if (tree.FOLDER_CB_VISIBLE) {
	// render the checkbox:
	this.isChecked = tree.isItemChecked(this);
	htmlContent += tagIMG + "id='checkBox"+this.id
				+           "' src='" + (this.isChecked ? imgUrlChecked : imgUrlUnchecked)
				+           "' onclick='javascript:clickOnCB("+tree.id+","+this.id
				+           ")'>";
  }
  htmlContent += "</td><td valign=middle nowrap>&nbsp;" + this.strLabel + "</td></tr></table>";

  // update strLeftSide as children will use it:
  this.strLeftSide += (this.isLastChild() ? htmlBlank : htmlVertline);

  this.layerId = tabId;
  if (this.id == 0) {
	tree.pane.innerHTML += htmlContent;
	var doc = tree.frame.document;
	this.layer       = doc.getElementById(tabId);
	this.iconImg     = doc.getElementById("itemIcon"+this.id);
	this.nodeImg     = doc.getElementById("nodeIcon"+this.id);
	this.checkBoxImg = doc.getElementById("checkBox"+this.id);
  }
  return htmlContent;
}


function  Folder_getNodeImgUrl()
{
  if (this.isLastChild()) {
	return (this.isOpened ? imgUrlOpenedLastNode : imgUrlClosedLastNode);
  }
  else {
	return (this.isOpened ? imgUrlOpenedNode : imgUrlClosedNode);
  }
}


function  Folder_getFolderImgUrl()
{
  if (this.isOpened && this.children.length != 0)
	return this.imgUrlOpened;
  else
	if (!this.isOpened && this.children.length == 0)
		return this.imgUrlOpened;
	else
		return this.imgUrlClosed;
}


function  Folder_insertChild(child)
{
  this.children.push(child);
  child.parent = this;
  return this;
}

function  Folder_insertItem(strLabel, imgUrlItem) { return this._(new Item(strLabel, imgUrlItem)); }

function  F(strLabel, imgUrlClosed, imgUrlOpened) { return new Folder(strLabel, imgUrlClosed, imgUrlOpened); }


function  Item_makeVisible()   { this.layer.style.visibility = "inherit"; }
function  Item_makeInvisible() { this.layer.style.visibility = "hidden";  }

function  Item_vShift(nPixels) { this.layer.style.top = (parseInt(this.layer.style.top) + nPixels) + "px" }
function  Item_vPlace(pixel)   { this.layer.style.top = pixel + "px";    }
function  Item_vPos()          { return parseInt(this.layer.style.top); }

function  getLayerHeight(layer) { return layer.style.pixelHeight; }
function  getLayerWidth(layer)  { return layer.style.pixelWidth; }


function  Item_isLastChild()
{
  if (this.parent == null)
	return true;
  var siblings = this.parent.children;
  if (siblings[siblings.length-1] == this)
	return true;
  // add logic for folder-only view:
  // ???
  return false;
}


function  Folder_collectVisibleSubs(arrSubs)
{
  for (var i=0; i < this.children.length; i++) {
	var child = this.children[i];
	arrSubs.push(child);
	if (child.isFolder && child.isOpened) {
	  child.collectVisibleSubs(arrSubs);
	}
  }
}


function  clickOnNode(treeId, fldId)
{
  var tree  = treesArray[treeId];
  var fld   = tree.items[fldId];
  var vsubs = new Array();

  fld.collectVisibleSubs(vsubs);
  var shift = vsubs.length * tree.itemHeight;

  fld.isOpened = !fld.isOpened;
  // change node's & folder's image:
  fld.nodeImg.src = fld.getNodeImgUrl();
  fld.iconImg.src = fld.getFolderImgUrl();

  if (fld.isOpened) { // closed -> opened
	// shift down all the visibles bellow fld
	// to make room for its visible subs:
	var fvi; // (fld's visible index)
	for (fvi=tree.visibles.length; 0 < fvi--; ) {
	  var item = tree.visibles[fvi];
	  if (item == fld) {
		break;
	  }
	  item.vShift(shift);
	}
	// display fld's visible subs and place them appropriately:
	var fld_top = fld.vPos();
	var additionalContent = "";
	for (var i=0; i < vsubs.length; ) {
	  var sub = vsubs[i++];
	  if (sub.layer == null) {
		additionalContent += sub.renderLayer(tree);
	  }
	}
	if (additionalContent != "") {
	  tree.pane.innerHTML += additionalContent;
	  // old IEs may screw referencies to elements
	  // if innerHTML is changed so restore them:
	  var doc = tree.frame.document;
	  for (var i=tree.items.length; 0 < i--; ) {
		var item = tree.items[i];
		var actualLayer = doc.getElementById(item.layerId);
		if (item.layer != actualLayer) { // doc was re-rendered, rescan elements:
		  item.layer       = actualLayer;
		  item.iconImg     = doc.getElementById("itemIcon"+item.id);
		  item.nodeImg     = doc.getElementById("nodeIcon"+item.id);
		  item.checkBoxImg = doc.getElementById("checkBox"+item.id);
		}
	  }
	}

	// now update the tree's visibles array:
	var slice1 = tree.visibles.slice(0, fvi + 1); // the elements up to (+) fld
	var slice2 = tree.visibles.slice(fvi + 1);    // the elements after fld
	tree.visibles = slice1.concat(vsubs).concat(slice2);

	// refresh al items
	for (var i=0; i < tree.visibles.length; ) {
	  var sub = tree.visibles[i++];
	  sub.vPlace((i-1)*tree.itemHeight);
	  sub.makeVisible();
	}
  }
  else { // opened -> closed
	// hide all visible subs of fld:
	for (var i=vsubs.length; 0 < i--; ) {
	  vsubs[i].makeInvisible();
	}
	// shift up all the visibles bellow fld
	// to fill the freed room:
	var fvi; // (fld's visible index)
	for (fvi=tree.visibles.length; 0 < fvi--; ) {
	  if (fld == tree.visibles[fvi])
		break;
	}
	// assert(0 <= fvi);
	// remove vsubs from tree's visibles array:
	var slice1 = tree.visibles.slice(0, fvi+1);
	var slice2 = tree.visibles.slice(fvi+1 + vsubs.length);
	tree.visibles = slice1.concat(slice2);
	// shift up the rest of tree.visibles:
	for (var i=fvi+1; i < tree.visibles.length; i++) {
	  tree.visibles[i].vShift(-shift);
	}
  }
  // notify tree to update the form:
  tree.setFolderOpened(fld, fld.isOpened);
}


function  clickOnCB(treeId, itemId)
{
  //do nothing so far
  var  tree = treesArray[treeId];
  var  item = tree.items[itemId];
  if (item.isFolder && tree.FOLDER_CB_ENABLED ||
	 !item.isFolder && tree.ITEM_CB_ENABLED) {
	item.isChecked = !item.isChecked;
	item.checkBoxImg.src = (item.isChecked ? imgUrlChecked : imgUrlUnchecked);
	tree.setItemChecked(item, item.isChecked);
  }
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// $Tree
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function  Tree(rootFld)
{
  if (treesArray == null)
	treesArray = new Array();

  this.id = treesArray.length;
  treesArray.push(this);
  this.rootFld = rootFld;
  this.paneId  = "tree"+this.id+".pane";
  this.stateId = "tree"+this.id+".state";

  this.FOLDER_CB_VISIBLE = true;
  this.FOLDER_CB_ENABLED = true;
  this.ITEM_CB_VISIBLE   = true;
  this.ITEM_CB_ENABLED   = true;
  this.SHOW_ITEMS        = true;

  this.setup = Tree_setup;                     //(String paneId, Folder rootFld)
  this.isItemChecked   = Tree_isItemChecked;   //(Item item)
  this.setItemChecked  = Tree_setItemChecked;  //(Item item, bool isChecked)
  this.isFolderOpened  = Tree_isFolderOpened;  //(Folder fld)
  this.setFolderOpened = Tree_setFolderOpened; //(Folder fld, bool isOpened)
  this.getProperty     = Tree_getProperty;     //(item, sPropName) -> str
  this.setProperty     = Tree_setProperty;     //(item, sPropName, sPropVal)
  this.createState     = Tree_createState;     //(String state)
  this.createPane      = Tree_createPane;      //(int width, int height)
}


function  Tree_getProperty(sOwnerId, sPropName, defaultValue)
{
  if (this.state != null) {
	var s = this.state.value;
	var key = sOwnerId + "." + sPropName + "=";
	var i = s.indexOf("|" + key);
	if (i == -1 && s.indexOf(key) == 0) { // no need of '|' here
	  i = 0;
	}
	if (i != -1) {
	  i = s.indexOf("=", i) + 1;
	  return s.substring(i, s.indexOf("|", i));
	}
  }
  return defaultValue;
}


function  Tree_setProperty(sOwnerId, sPropName, sPropVal)
{
  if (this.state != null) {
	var s = this.state.value;
	var key = sOwnerId + "." + sPropName + "=";
	var i = s.indexOf("|" + key);
	if (i == -1 && s.indexOf(key) == 0) { // no need of '|' here
	  i = 0;
	}
	if (i == -1) { // append the new property:
	  this.state.value += key + sPropVal + "|";
	}
	else {
	  i = s.indexOf("=", i);
	  this.state.value = s.substring(0,i+1)
							 + sPropVal
							 + s.substring(s.indexOf("|", i));
	}
	return true; //property set
  }
  return false;  //property not set
}


function  Tree_isFolderOpened(fld)             { return (this.getProperty(fld.id, "o") == "t"); }
function  Tree_setFolderOpened(fld, isOpened)  { this.setProperty(fld.id, "o", (isOpened?"t":"f")); }

function  Tree_isItemChecked(item)             { return (this.getProperty(item.id, "c") == "t"); }
function  Tree_setItemChecked(item, isChecked) { this.setProperty(item.id, "c", (isChecked?"t":"f")); }


function  Tree_setup()
{
  this.frame = frames[this.paneId];
  this.frame.document.open();
  this.frame.document.write("<div id='TreeDiv'></div>");
  this.frame.document.close();
  this.pane = this.frame.document.getElementById("TreeDiv");

  // export the event handlers to the frame :
  this.frame.clickOnNode = clickOnNode;
  this.frame.clickOnCB   = clickOnCB;

  if (this.state == null) {
	var form = document.forms[formName ? formName : 0];
	if (form != null) {
	  this.state = form[this.stateId];
	}
  }
  this.paneWidth  = getLayerWidth(this.pane);
  this.paneHeight = getLayerHeight(this.pane);
  this.itemWidth  = this.paneWidth;
  this.itemHeight = 22;

  this.items = new Array();
  var  stack = new Array();
  stack.push(this.rootFld);

  while (stack.length != 0) {
	var item = stack.pop();
	item.id   = this.items.length;
	this.items.push(item);
	if (item.isFolder) {
	  item.isOpened = this.isFolderOpened(item);
	  for (var i=item.children.length; 0 < i--; ) {
		stack.push(item.children[i]);
	  }
	}
  }

  // exclude items from the tree view if specified:
  if (!this.SHOW_ITEMS) {
	for (var i = this.items.length; 0 < i--; ) {
	  var fld = this.items[i];
	  if (fld.isFolder) {
		var  subFlds = new Array();
		for (var j = 0; j < fld.children.length; j++)
		  if (fld.children[j].isFolder)
			subFlds.push(fld.children[j]);
		fld.children = subFlds;
	  }
	}
  }

  // display the root fld:
  this.visibles = new Array();
  this.visibles.push(this.rootFld);
  var  openRoot = this.rootFld.isOpened;
  this.rootFld.isOpened = false;
  this.rootFld.renderLayer(this);
  this.rootFld.vPlace(0);
  this.rootFld.makeVisible();
  if (openRoot || this.state == null || this.state.value == "")
	clickOnNode(this.id, 0);
}

function  setupTrees()
{
  for (var i = 0; i < treesArray.length; i++) {
	treesArray[i].setup();
  }
}

function  Tree_createPane(width, height)
{
  document.write("<iframe name='"+this.paneId+"' width="+width+" height="+height+" frameborder='1' scrolling='auto'></iframe>");
}

function  Tree_createState(state)
{
  document.write("<form name='"+formName+"'><input type='hidden' name='"+this.stateId+"' value='"+state+"'></form>");
}
