// Note: The HTML id tag may contain A-Z, a-z, 0-9, -, _, :, .
//       Thus the best choice for the path seperator is ':' or '.' in
//       most cases

function jhmenu(objname, idprefix, pathsep, animstep, animinterval,
                imgwidth, imgheight, img_expand, img_collapse, img_blank) {

  this.objname=objname;
  this.idprefix=idprefix;
  this.pathsep=pathsep;
  this.animstep=animstep;
  this.animinterval=animinterval;
  this.imgwidth=imgwidth;
  this.imgheight=imgheight;
  this.img_expand=img_expand;
  this.img_collapse=img_collapse;
  this.img_blank=img_blank;
  this.maxwidth=0;
  this.clicklist=new Array();

  // get_childs_div:
  // - For a given menu item id (e.g. 1:1), this function will return an
  //   reference to an HTML div node which contains all subitems (e.g.
  //   1:1:1, 1:1:2, 1:1:2:1, 1:1:2:2, 1:1:3)
  // - The returned node can be used to hide/display the subitems by
  //   setting style.display accordingly
  // - If such a div node does not exist yet, it will be created on the fly,
  //   by creating it with document.createElement() and moving all subitems
  //   into it
  // - If the subitems contain further subitems (i.e. subsubitems), this
  //   function will recursively call itself so that a div node hierarchy
  //   is formed

  this.get_childs_div=function(id) {
    var c;
    var submenuidstr, submenudiv;
    var childidstr, all_tables, tables;
    var subitemid, subitem;
    var removed_items;

    // Check if the requested div node already exists
    // (from a preceding call)
    submenuidstr=this.idprefix+'submenus'+this.pathsep+id;
    submenudiv=document.getElementById(submenuidstr);

    if (submenudiv!=null) {
      return submenudiv;
    }

    // There is no div node yet for the child menu items, so we
    // create it now

    childidstr=this.idprefix+id+this.pathsep;
    all_tables=document.getElementsByTagName("table");
    tables=new Array();
    for (c=0;c<all_tables.length;c++) {
      // Get immediate childs, i.e. menu items which are one level
      // below the current one
      if (all_tables[c].id.indexOf(childidstr)==0 &&
          all_tables[c].id.substr(childidstr.length).indexOf(this.pathsep)==-1) {
        tables.push(all_tables[c]);
      }
    }

    // Remember the removed items, so we can put them back later
    removed_items=new Array();

    for (c=0;c<tables.length;c++) {
      // Create a new div right before the first child menu item
      if (submenudiv==null) {
        submenudiv=document.createElement("div");
        submenudiv.id=submenuidstr;
        tables[c].parentNode.insertBefore(submenudiv, tables[c]);
      }

      // Store the subitem's id before it gets removed, we need
      // it to check if the subitem has itself subitems (i.e. subsubitems)
      subitemid=tables[c].id.substr(this.idprefix.length);
      subitem=this.get_childs_div(subitemid);

      // Remove the child menu item
      removed_items.push(tables[c].parentNode.removeChild(tables[c]));

      // If there were subsubitems, remove them as well
      if (subitem!=null) {
        removed_items.push(subitem.parentNode.removeChild(subitem));
      }
    }

    // Now put the child menu items back again, but under the newly
    // created div node this time
    for (c=0;c<removed_items.length;c++) {
      submenudiv.appendChild(removed_items[c]);
    }

    // If there are no child menu items, we will return null!
    return submenudiv;
  }

  this.set_status=function(id, status) {
    var itemtable, itemrow;

    itemtable=document.getElementById(this.idprefix+id);
    itemrow=itemtable.rows[0];

    switch (status) {
    case 0:
      link='<img width="'+this.imgwidth+'" height="'+this.imgheight+'" '+
           'src="'+this.img_expand+'" onclick="'+this.objname+'.handle_click(\''+id+'\');" />';
      break;

    case 1:
      link='<img width="'+this.imgwidth+'" height="'+this.imgheight+'" '+
           'src="'+this.img_collapse+'" onclick="'+this.objname+'.handle_click(\''+id+'\');" />';
      break;

    case 2:
      link='<img width="'+this.imgwidth+'" height="'+this.imgheight+'" '+
           'src="'+this.img_blank+'" />';
      break;
    }

    itemrow.cells[1].innerHTML=link;
  }

  this.handle_click=function(id) {
    var submenudiv, origheight;

    if (this.animactive) {
      return;
    }

    submenudiv=this.get_childs_div(id);

    if (submenudiv==null) {
      // There are no subitems!
      this.set_status(id, 2);
      return;
    }

//    submenudiv.style.display='block';
    submenudiv.style.overflow='hidden';

    this.animactive=1;

    if (submenudiv.style.display=='none') {
      origheight=parseInt(submenudiv.style.height);
      submenudiv.style.height='1px'; // Helps for IE
      submenudiv.style.display='block';
      this.expand(id, 1, origheight);
    } else {
      this.collapse(id, submenudiv.offsetHeight, submenudiv.offsetHeight);
    }
  }

  this.collapse=function(id, height, origheight) {
    var submenudiv;
    var nextheight;

    submenudiv=this.get_childs_div(id);

    if (submenudiv==null) {
      // There are no subitems!
      this.set_status(id, 2);
      return;
    }

    if (height<0) {
      height=0;
    }

    submenudiv.style.height=''+height+'px';

    if (height>0) {
      nextheight=height-this.animstep;
      window.setTimeout(this.objname+".collapse('"+id+"', "+nextheight+", "+origheight+")",
                        this.animinterval);
    } else {
      submenudiv.style.display='none';
      this.animactive=0;
      submenudiv.style.height=''+origheight+'px';
      this.set_status(id, 0);

      if (this.clicklist.length>0) {
        this.handle_click(this.clicklist.shift());
      }
    }
  }

  this.expand=function(id, height, origheight) {
    var submenudiv;
    var nextheight;

    submenudiv=this.get_childs_div(id);

    if (submenudiv==null) {
      // There are no subitems!
      this.set_status(id, 2);
      return;
    }

    submenudiv.style.height=''+height+'px';

    if (height<origheight) {
      nextheight=height+this.animstep;
      window.setTimeout(this.objname+".expand('"+id+"', "+nextheight+", "+origheight+")",
                        this.animinterval);
    } else {
      this.animactive=0;
      submenudiv.style.height='';
      this.set_status(id, 1);

      if (this.clicklist.length>0) {
        this.handle_click(this.clicklist.shift());
      }
    }
  }

  this.has_child_items=function(id) {
    var tables, c;

    tables=document.getElementsByTagName("table");
    for (c=0;c<tables.length;c++) {
      if (tables[c].id.indexOf(this.idprefix+id+this.pathsep)==0) {
        return true;
      }
    }

    return false;
  }

  this.get_maxwidth=function() {
    return this.maxwidth;
  }

  this.collapse_level1_except=function(except_id) {
    var tables, c, menuid, submenudiv;

    tables=document.getElementsByTagName("table");

    for (c=0;c<tables.length;c++) {
      if (tables[c].id.substr(0, this.idprefix.length)!=this.idprefix) {
        continue;
      }

      menuid=tables[c].id.substr(this.idprefix.length);

      if (menuid.indexOf(this.pathsep)!=-1) {
        // Only level 1!
        continue;
      }

      submenudiv=this.get_childs_div(menuid);

      if (submenudiv==null) {
        // There are no subitems!
        continue;
      }

      if (menuid!=except_id && submenudiv.style.display!='none') {
        // We cannot call handle_click() directly here, because we have to wait
        // for each expand/collapse animation to complete before we
        // can start the next one (it's locked via this.animactive)
        this.clicklist.push(menuid);
      }
    }

    // Now handle the first collapse, the rest of the clicklist will be handled by
    // expand/collapse
    this.handle_click(this.clicklist.shift());
  }

  this.init=function() {
    var tables, c, menuid, row, link;

    tables=document.getElementsByTagName("table");

    for (c=0;c<tables.length;c++) {
      if (tables[c].id.substr(0, this.idprefix.length)!=this.idprefix) {
        continue;
      }

      if (tables[c].offsetWidth>this.maxwidth) {
        this.maxwidth=tables[c].offsetWidth;
      }

      menuid=tables[c].id.substr(this.idprefix.length);

      row=tables[c].rows[0];

      if (this.has_child_items(menuid)) {
        link='<img width="'+this.imgwidth+'" height="'+this.imgheight+'" src="'+this.img_collapse+'" '+
              'onclick="'+this.objname+'.handle_click(\''+menuid+'\');" />';
      } else {
        link='<img width="'+this.imgwidth+'" height="'+this.imgheight+'" src="'+this.img_blank+'">';
      }

      row.cells[1].innerHTML=link;

      this.animactive=0;
    }
  }

  this.init();
}

