var oc_toggle = {
  start : function()
  {
    if(this.timer == null)
      this.timer = setInterval('oc_toggle.update()',this.time);
  },
  stop : function()
  {
    if(this.timer != null)
      clearInterval(this.timer);
    this.timer = null;
  },
  addElement : function(id,anchorPoint,anchorElement,openClass,closeClass)
  {
    var el = document.getElementById(id);
    var item = Array();
    var anchor = Array();
    var classnames = Array();
    classnames.standard = el.className;
    classnames.opening  = el.className+' '+openClass;
    classnames.closeing = el.className+' '+closeClass;
    anchor.p = anchorPoint;
    anchor.e = anchorElement;

    item.parent = anchorElement.parentNode;
    item.anchor = anchor;
    item.cnames = classnames;
    item.count  = 0;
    item.childs = 0;
    item.el     = el;
    this.elements[id] = item;
    this.elements.push(id);
    
    if(item.anchor.p!=null) this.setPos(id,item.anchor.p,item.anchor.e);
    
    if(this.getStyle(el, 'display')=="block")
    {
      item.status = "opened";
      this.close(id);return;
    }
    else
    {
      if(this.elements[item.parent.id])
      {
        item.status = "wait_o";
      }
      else item.status = "open";
      this.open(id);return;
    }
  },
  toggle : function(id,anchorPoint,anchorElement,openClass,closeClass)
  {
    if(!this.elements[id]) this.addElement(id,anchorPoint,anchorElement,openClass,closeClass);
    else
    {
      var item = this.elements[id];
      // if opening or opened, call close
      if(item.status == 'open' || item.status == 'opened') {this.close(id);return;}
      // If waiting for close, stay opened
      if(item.status == 'wait_c') {item.status = "opened";return;}
      // if closing or closed, call open
      if(item.status=="close" || item.status=="closed") {this.open(id);return;}
      // if waiting to open, just stay closed
      if(item.status=="wait_o") {item.status = "closed";return;}
    }
  },
  open : function(id,anchorPoint,anchorElement,openClass,closeClass)
  {
    var item = Array();
    
    if(!this.elements[id])
    {
      this.addElement(id, anchorPoint, anchorElement, openClass, closeClass);
      return;
    }
    else
    {
      item = this.elements[id];
    }

    // if already opening or opened, do nothing
    if(item.status == 'open' || item.status == 'opened') return;
    // If waiting for close, stay opened
    if(item.status == 'wait_c') {item.status = "opened";return;}
    // If in closing process, stop and open again
    if(item.status == 'close') {item.status = "open";item.count  = this.countInvers(item.count);return;}
    // If closed
    if(item.status == 'closed')
    {
      // If parent Node Available, wait for opening
      if(this.elements[item.parent.id])
      {
        item.status = "wait_o";
        // Tell parent it has a child open
      }
      else
      {
        // If no parent, just open
        if(item.anchor.p!=null) this.setPos(id,item.anchor.p,item.anchor.e);
        item.status = "open";
        item.count  = 0;
      }
      return;
    }
    // If waiting for opening
    if(item.status == "wait_o")
    {
      if(this.elements[item.parent.id].status != "opened")
      {
        // parent must be in open state to get to 'opened'
        if(this.elements[item.parent.id].status != "open")
          this.elements[item.parent.id].status = "open";

        // wait until parent is opened
        this.elements[id].status = "wait_o";
      }
      // Open if parent is opened
      else
      {
        if(item.anchor.p!=null) this.setPos(id,item.anchor.p,item.anchor.e);
        this.elements[item.parent.id].childs++;
        item.status = "open";
      }
      return;
    }
  },
  close : function(id)
  {
    // if not exist => do nothing;
    if(!this.elements[id]) return;

    var item = this.elements[id];

    // if already closing or closed, do nothing
    if(item.status=="close" || item.status=="closed") return;
    // if waiting to open, just stay closed
    if(item.status=="wait_o") {item.status = "closed";return;}
    // if opening just toggle to close again
    if(item.status=="open")
    {
      // if childs still open, wait for closing
      if(item.childs>0) {item.status = "wait_c";return;}
      else
      {
        // no childs opened -> close
        item.status = "close";
        item.count  = this.countInvers(item.count);
      }
    }
    // if opened
    if(item.status=="opened")
    {
      // if childs still open, wait for closing
      if(item.childs>0) {item.status = "wait_c";return;}
      else
      {
        // no childs opened -> close
        item.status = "close";
        item.count  = 0;
      }
    }
    // if waiting to close
    if(item.status=="wait_c")
    {
      // if no childs open, close
      if(item.childs<=0)
      {
        item.count = 0;
        item.status = "close";
      }
      return;
    }
  },
  setPos : function(id,anchorPoint,anchorElement)
  {
    var el = document.getElementById(id);

    switch(anchorPoint)
    {
      case 'LL' :this.posLL(el,anchorElement);break;
      case 'UR' :this.posUR(el,anchorElement);
      default:break;
    }
  },
  posUR : function(el,anchorElement)
  {
    // UpperRight corner of the anchor element
    var pw,pt;

    pw = this.absLeft(anchorElement)+anchorElement.offsetWidth;
    pt = this.absTop(anchorElement);
    
    el.style.top = pt+'px';
    el.style.left = pw+'px';
  },
  posLL : function(el,anchorElement)
  {
    // lower left corner of the anchor element
    var pw,pt;

    pw = this.absLeft(anchorElement);
    pt = this.absTop(anchorElement)+anchorElement.offsetHeight;
    
    el.style.top = pt+'px';
    el.style.left = pw+'px';
  },
  absLeft : function(el)
  {
    var pos = this.getStyle(el, "position");

    if(pos=="absolute" || pos=="fixed") return el.offsetLeft;
    //else if(pos=="fixed") return 0;

    return (el.offsetParent) ? el.offsetLeft+this.absLeft(el.offsetParent) : el.offsetLeft;
  },
  absTop : function(el)
  {
    var pos = this.getStyle(el, "position");
    if(pos=="absolute" || pos=="fixed") return el.offsetTop;
    //else if(pos=="fixed") return 0;

    return (el.offsetParent) ? el.offsetTop+this.absTop(el.offsetParent) : el.offsetTop;
  },
  update : function()
  {
    for(var i=0;i<this.elements.length;i++)
    {
      var item = this.elements[this.elements[i]];
      switch(item.status)
      {
        case 'open' :this.show(i);break;
        case 'close' :this.hide(i);break;
        case 'wait_o' :this.open(item.el.id);break;
        case 'wait_c' :this.close(item.el.id);break;
        case 'closed' :
        case 'opened' :
        default :break;
      }
    }
  },
  show : function(id)
  {
    var item  = this.elements[this.elements[id]];

    var e = item.el;

    e.className = item.cnames.opening;

//    if(item.count==0) e.style.display = "block";

    if(item.count >= this.maxcount)
    {
      item.status = "opened";
      item.count  = 0;
//      e.className = item.cnames.standard;
    }
    else  item.count++;

  },
  hide : function(id)
  {
    var item  = this.elements[this.elements[id]];

    var e = item.el;

    // dont hide if childs available
    if(item.childs>0) {item.status = "open";return;}
    
    e.className = item.cnames.closeing;

    if(item.count >= this.maxcount)
    {
//      e.className = item.cnames.standard;
//      e.style.display = "none";
      item.status = "closed";
      item.count  = 0;
      // reduce child count of parent Node if available
      if(this.elements[item.parent.id])
        this.elements[item.parent.id].childs--;
    }
    else item.count++;
  },
  countInvers : function(count)
  {
    var a = Math.log(1-Math.pow(this.faktor, parseInt(count)));
    var b = Math.log(this.faktor);
    var c = Math.ceil(a/b);
    if(isNaN(c)) c = 0;
    return c;
  },
  elements : Array(),
  maxcount : 10,
  faktor : 0.9,
  time : 50,
  getStyle : function(id,style)
  {
    var e=document.getElementById(id);

    if(!e) e = id;

    if(window.getComputedStyle)
      return document.defaultView.getComputedStyle(e,null).getPropertyValue(style);
	else if(e.currentStyle)
      return e.currentStyle[style];
    else return false;
  }
}
oc_toggle.start();
