assert = function(expr)
{
  if(!expr)
  alert("Asserting failed");
}

__DBG_OUT__ = function(msg) {
	return;
   document.getElementsByTagName("body")[0].appendChild(document.createElement("div")).
   appendChild(document.createTextNode(msg));
}

//BEGIN class TVDefaultRenderer
TVDefaultRenderer = function()
{
  this.root = null;
  this.id = TVDefaultRenderer.next_id++;
  this.node_id_pfx = 'tv_';
  this.css_pfx = 'Tv';
  this.rendered = false;
  this.on_demand_rendering = true;
  this.container = null;
  TVDefaultRenderer.objects.push(this);
}

TVDefaultRenderer.objects = [];

// --------------------------------------------------------
TVDefaultRenderer.next_id = 0;
TVDefaultRenderer.CSS_DOCUMENT = "Doc";
TVDefaultRenderer.CSS_NODE = "Nd";
TVDefaultRenderer.CSS_NODE_NAME = "NdNa";
TVDefaultRenderer.CSS_NODE_ICON = "NdIc";
TVDefaultRenderer.CSS_INDENT = "In";
TVDefaultRenderer.CSS_INDENT_EMPTY = "InE";
TVDefaultRenderer.CSS_INDENT_HERE = "InH";
TVDefaultRenderer.CSS_INDENT_LAST = "InL";
TVDefaultRenderer.CSS_INDENT_EXPANDED = "InX";
TVDefaultRenderer.CSS_INDENT_COLLAPSED = "InC";
TVDefaultRenderer.CSS_INDENT_LAST_EXPANDED = "InLX";
TVDefaultRenderer.CSS_INDENT_LAST_COLLAPSED = "InLC";
TVDefaultRenderer.CSS_LEAF = "Lf";
TVDefaultRenderer.CSS_LEAF_ICON = "LfI";
TVDefaultRenderer.CSS_ATTRIBUTE = "At";
TVDefaultRenderer.CSS_ATTRIBUTE_ICON = "At";

// --------------------------------------------------------
TVDefaultRenderer.prototype.setRoot = function(root)
{
  this.root = root;
}

// --------------------------------------------------------
TVDefaultRenderer.prototype.draw = function(pos, on_demand_rendering)
{
  if(on_demand_rendering != null)
    this.on_demand_rendering = on_demand_rendering;
  var id = this.node_id_pfx + this.id;
  if(pos)
  {
    this.container = document.createElement("div")
    this.container.id = id;
    this.container.className = this.css_pfx + "Doc";
    if(typeof(pos) == "object")
    {
      pos.appendChild(this.container);
    }
    else
    {
      document.getElementById(pos).appendChild(this.container);
    }
  }
  else
  {
    document.write("<div id=\"" + id + "\" class=\"" + this.css_pfx + "Doc" + "\"></div>");
    this.container = document.getElementById(id);
  }
  this.root.addRenderer(this, 0);
  this.rendered = true;
}

// --------------------------------------------------------
TVDefaultRenderer.prototype.updateIdent = function(node, level)
{
  var container = document.getElementById(this.node_id_pfx + this.id + "_" + node.id);
  if(!container)
    return;
//   alert(node.id);
  //           Div    -> table   -> tbody   -> tr      -> td[content] -> td[icon] -> td[ident]
  container = container.firstChild.firstChild.firstChild.lastChild.previousSibling.previousSibling;
//   alert(container);
  this.updateIdentPriv(container, node, level, true);
}

// --------------------------------------------------------
TVDefaultRenderer.prototype.updateIdentPriv = function(container, node, level, is_top)
{
  var have_succ = node.nextSibling();
  var cls_name = this.css_pfx;
  var me = this;
  if(have_succ)
  {
    if(is_top && node.hasChildren())
    {
      var ch_showing = node.renderers[this.id].expanded;
      if(!ch_showing ||  node.expand_hook != null)
        cls_name += TVDefaultRenderer.CSS_INDENT_COLLAPSED;
      else
        cls_name += TVDefaultRenderer.CSS_INDENT_EXPANDED;
      container.renderer = this;
      container.onclick = function()
        { node.toggleShowChildren(this.renderer, false); }
      container.ondblclick = function()
        { /*if(node.renderers[me.id].expanded)*/  node.show(me, true); /*else node.hide(me, true);*/ }
    }
    else if(is_top)
      cls_name += TVDefaultRenderer.CSS_INDENT_HERE;
    else
      cls_name += TVDefaultRenderer.CSS_INDENT;
  }
  else
  {
    if(is_top && node.hasChildren())
    {
      var ch_showing = node.renderers[this.id].expanded;
      if(!ch_showing || node.expand_hook != null)
        cls_name += TVDefaultRenderer.CSS_INDENT_LAST_COLLAPSED;
      else
        cls_name += TVDefaultRenderer.CSS_INDENT_LAST_EXPANDED;
      container.renderer = this;
      container.onclick = function()
        { node.toggleShowChildren(this.renderer, false); }
      container.ondblclick = function()
        { /*if(node.renderers[me.id].expanded)*/ node.show(me, true); /*else node.hide(me, true);*/ }
    }
    else if(is_top)
      cls_name += TVDefaultRenderer.CSS_INDENT_LAST;
    else
      cls_name += TVDefaultRenderer.CSS_INDENT_EMPTY;
  }
  if(container.className != cls_name)
    container.className = cls_name;
  if(level > 0)
    this.updateIdentPriv(container.previousSibling, node.parent_node, level - 1, false);
}

// --------------------------------------------------------
TVDefaultRenderer.prototype.drawNode = function(node, level)
{
  __DBG_OUT__(this.id + " .. drawing " + node.id + " " + level);
  var rid = this.node_id_pfx + this.id;
  var id = rid + "_" + node.id;
  var pfx;
  if(node.css_pfx)
    pfx = node.css_pfx 
  else
    pfx = this.css_pfx;
  
  var htmnode = document.createElement("div");
  htmnode.id = id;
  htmnode.className = pfx + TVDefaultRenderer.CSS_NODE;
  var tbl = document.createElement("table");
  var tbdy = document.createElement("tbody");
  htmnode.appendChild(tbl);
  tbl.appendChild(tbdy);
  var tr = document.createElement("tr");
  tbdy.appendChild(tr);
  var td;
  for(var idx = level; idx >= 0; --idx)
  {
    td = document.createElement("td");
    tr.appendChild(td);
  }
  this.updateIdentPriv(td, node, level, true);
  td = document.createElement("td");
  td.className = pfx + TVDefaultRenderer.CSS_NODE_ICON;
  tr.appendChild(td);
  td = document.createElement("td");
  td.className = pfx + TVDefaultRenderer.CSS_NODE_NAME;
  node.content = td.appendChild((node.content));
  tr.appendChild(td);
  var pnode = document.getElementById(rid);
// alert('draw ' + rid + ' ' + id + ' ' + typeof(pnode));
  var sibnode = null;
  if(level > 0)
  {
    var n = node;
    var sib = n.nextSibling();
    while(sib == null)
    { 
      n = n.parent_node;
      if(!n)
        break;
      sib = n.nextSibling();
    }
    if(sib)
      sibnode = document.getElementById(rid + "_" + sib.id);
  }
  if(sibnode)
    pnode.insertBefore(htmnode, sibnode);
  else
    pnode.appendChild(htmnode);
}

// --------------------------------------------------------
TVDefaultRenderer.prototype.eraseNode = function(node)
{
  if(node.children)
    for(var c = node.children.length; c--;)
      this.eraseNode(node.children[c]);
  var elem = document.getElementById(this.node_id_pfx + this.id + "_" + node.id);
  elem.parentNode.removeChild(elem);
}

// --------------------------------------------------------
TVDefaultRenderer.prototype.showNode = function(node)
{
  var elem = document.getElementById(this.node_id_pfx + this.id + "_" + node.id);
  elem.style.display = "block";
}

// --------------------------------------------------------
TVDefaultRenderer.prototype.hideNode = function(node)
{
  var elem = document.getElementById(this.node_id_pfx + this.id + "_" + node.id);
  elem.style.display = "none";
}

// --------------------------------------------------------
TVDefaultRenderer.prototype.drawLeaf = function(leaf, level)
{
}

// --------------------------------------------------------
TVDefaultRenderer.prototype.eraseLeaf = function(leaf)
{
}

// --------------------------------------------------------
TVDefaultRenderer.prototype.showLeaf = function(leaf)
{
}

// --------------------------------------------------------
TVDefaultRenderer.prototype.hideLeaf = function(leaf)
{
}


//END class TVDefaultRenderer


//BEGIN class TVNode
TVNode = function(content)
{
  if(typeof(content) == "object")
    this.content = content;
  else
    this.content = document.createTextNode(content);
  this.renderers = new Array();
  this.children = new Array();
  this.parent_node = null;
  this.id = TVNode.next_node_id++;
  this.expand_hook = null;
}
TVNode.next_node_id = 0;

// --------------------------------------------------------
TVNode.prototype.appendChild = function(child)
{
  if(child.parent_node)
    child.parent_node.removeChild(child);
  var prev_sib = null;
  if(this.children.length > 0)
    prev_sib = this.children[this.children.length - 1];
  this.children.push(child);
  child.parent_node = this;
  for(var idx = this.renderers.length; --idx >= 0; )
    if(this.renderers[idx])
    {
      if(prev_sib != null && this.renderers[idx].rend_obj.rendered)
      {
        this.renderers[idx].rend_obj.updateIdent(prev_sib, this.renderers[idx].level + 1);
      }
      if(this.renderers[idx].expanded) {
        __DBG_OUT__(idx + " .. appendChild addRenderer " + child.id + " " + this.renderers[idx].level + 1);
        child.addRenderer(this.renderers[idx].rend_obj, this.renderers[idx].level + 1);
      }
    }
  return(this);
}

// --------------------------------------------------------
TVNode.prototype.insertBefore = function(child, pred)
{
  if(child.parent_node)
  {
    child.parent_node.removeChild(child);
  }
  for(var idx = this.children.length; --idx >= 0; )
    if(this.children[idx] == pred)
    {
      this.children.splice(idx, 0, child);
      break;
    }
  child.parent_node = this;
  for(var idx = this.renderers.length; --idx >= 0; )
    if(this.renderers[idx]) {
      __DBG_OUT__(idx + " .. insertBefore addRenderer " + child.id + " " + this.renderers[idx].level + 1);
      child.addRenderer(this.renderers[idx].rend_obj, this.renderers[idx].level + 1);
    }
  return(this);
}

// --------------------------------------------------------
TVNode.prototype.removeChild = function(child)
{
  assert(this == child.parent_node);
  for(var idx = this.children.length; --idx >= 0; )
    if(this.children[idx] == child)
    {
      for(var idx = this.renderers.length; --idx >= 0; )
        if(this.renderers[idx])
        {
          child.removeRenderer(this.renderers[idx].rend_obj);
          if(this.children.length == 1)
            this.renderers[idx].rend_obj.updateIdent(this, this.renderers[idx].level);
        }
      this.children.splice(idx, 1);
        
      return(this);
    }
  return(this);
}

// --------------------------------------------------------
TVNode.prototype.hasChildren = function()
{
  return(this.children.length > 0 || this.expand_hook != null);
}

// --------------------------------------------------------
TVNode.prototype.previousSibling = function()
{
  if(this.parent_node == null)
    return(null);
  var ret = null;
  for(var idx = this.parent_node.children.length; --idx > 0; )
  {
    if(this.parent_node.children[idx] == this)
    {
      ret = this.parent_node.children[idx - 1];
      break;
    }
  }
  return(ret);
}

// --------------------------------------------------------
TVNode.prototype.nextSibling = function()
{
  if(this.parent_node == null)
    return(null);
  var ret = null;
  for(var idx = this.parent_node.children.length - 1; --idx >= 0; )
  {
    if(this.parent_node.children[idx] == this)
    {
      ret = this.parent_node.children[idx + 1];
      break;
    }
  }
  return(ret);
}

// --------------------------------------------------------
TVNode.prototype.addRenderer = function(renderer, level)
{
  if(this.renderers[renderer.id])
    return;
    
  var ob = new Object();
  ob.rend_obj = renderer;
  ob.level = level;
  ob.showing = true;
  ob.expanded = !renderer.on_demand_rendering;
  while(this.renderers.length < renderer.id)
  	this.renderers.push(null);
  this.renderers.push(ob);
  if(!renderer.on_demand_rendering)
    for(var idx = 0, end = this.children.length; idx < end; ++idx)
      this.children[idx].addRenderer(renderer, level + 1);
  renderer.drawNode(this, level)
}

// --------------------------------------------------------
TVNode.prototype.removeRenderer = function(renderer)
{
  if(this.renderers[renderer.id] == null)
    return;
  
  for(var idx = this.children.length; --idx >= 0; )
    this.children[idx].removeRenderer(renderer);
  renderer.eraseNode(this);
  this.renderers[renderer.id] = null;
}

// --------------------------------------------------------
TVNode.prototype.hide = function(renderer, recursive)
{
  if(renderer)
  {
    if(!this.renderers[renderer.id])
    {
      assert(this.parent_node.renderers[renderer.id]);
      this.addRenderer(renderer, this.parent_node.renderers[renderer.id].level + 1);
    }
    if(recursive)
    {
      this.renderers[renderer.id].expanded = false;
      renderer.updateIdent(this, this.renderers[renderer.id].level);
    }
    for(var idx = this.children.length; --idx >= 0; )
      this.children[idx].hide(renderer, recursive);
    renderer.hideNode(this);
    this.renderers[renderer.id].showing = false;
  }
  else
  {
    for(var idx = this.renderers.length; --idx >= 0; )
      if(this.renderers[idx])
      {
        if(recursive)
        {
          this.renderers[idx].expanded = false;
          this.renderers[idx].rend_obj.updateIdent(this, this.renderers[idx].level);
        }
        for(var idx = this.children.length; --idx >= 0; )
          this.children[idx].hide(this.renderers[idx].rend_obj, recursive);
        
        this.renderers[idx].rend_obj.hideNode(this);
        this.renderers[idx].showing = false;
      }
  }
}

// --------------------------------------------------------
TVNode.prototype.show = function(renderer, recursive)
{
  if(renderer)
  {
    if(!this.renderers[renderer.id])
    {
      assert(this.parent_node.renderers[renderer.id]);
      this.addRenderer(renderer, this.parent_node.renderers[renderer.id].level + 1);
    }
    if(recursive && !(this.renderers[renderer.id].expanded))
    {
      this.renderers[renderer.id].expanded = true;
      renderer.updateIdent(this, this.renderers[renderer.id].level);
    }
    if(this.renderers[renderer.id].expanded)
    {
      for(var idx = this.children.length; --idx >= 0; )
        this.children[idx].show(renderer, recursive);
    }
    renderer.showNode(this);
    this.renderers[renderer.id].showing = true;
  }
  else
  {
    for(var idx = this.renderers.length; --idx >= 0; )
      if(this.renderers[idx])
      {
        if(recursive || this.renderers[idx].expanded)
        {
          this.renderers[idx].expanded = true;
          for(var idx = this.children.length; --idx >= 0; )
            this.children[idx].show(this.renderers[idx].rend_obj, recursive);
        }
        this.renderers[idx].rend_obj.showNode(this);
        this.renderers[idx].showing = true;
      }
  }
}

// --------------------------------------------------------
TVNode.prototype.toggleShowChildren = function(renderer, recursive)
{
  if(this.expand_hook != null)
  {
   __DBG_OUT__(renderer.id + " .. toggleShowChildren " + this.id );
  //alert("TVNode.prototype.toggleShowChildren " + renderer.id);
    var cont = this.expand_hook(renderer);
    if(!cont)
      return;
  }
  var expanded = this.renderers[renderer.id].expanded;
	if(expanded)
	{
		for(var idx = this.children.length; --idx >= 0; )
			this.children[idx].hide(renderer, recursive);
	}
	else
	{
		for(var idx = this.children.length; --idx >= 0; )
			this.children[idx].show(renderer, recursive);
	}
  this.renderers[renderer.id].expanded = !expanded;
  renderer.updateIdent(this, this.renderers[renderer.id].level);
//   alert('toggle ' + this.id + ' ' + renderer.id);
}

//END class TVNode

//BEGIN class TVLeaf
TVLeaf = function(content)
{
  this.content = content;
  
  this.renderers = new Array();
  this.children = new Array();
  this.parent_node = null;
  this.id = TVNode.next_node_id++;
}

// --------------------------------------------------------
TVLeaf.prototype.previousSibling = TVNode.prototype.previousSibling;

// --------------------------------------------------------
TVLeaf.prototype.nextSibling = TVNode.prototype.nextSibling;

// --------------------------------------------------------
TVLeaf.prototype.addRenderer = function(renderer, level)
{
  if(this.renderers[renderer.id])
    return;
    
  var ob = new Object();
  ob.rend_obj = renderer;
  ob.level = level;
  ob.showing = true;
  ob.expanded = false;
  this.renderers[renderer.id] = ob;
  renderer.drawLeaf(this, level)
}

// --------------------------------------------------------
TVLeaf.prototype.removeRenderer = function(renderer)
{
  if(this.renderers[renderer.id] == null)
    return;
  
  renderer.eraseLeaf(this);
  this.renderers[renderer.id] = null;
}

//END class TVLeaf

//BEGIN class TVAttribute
TVAttribute = function(nam, val)
{
  this.name = nam;
  this.value = val;
  
  this.renderers = new Array();
  this.children = new Array();
  this.parent_node = null;
  this.id = TVNode.next_node_id++;
}

//END class TVAttribute

