function cmp (opa, opb)
{
  if (typeof (opa) != typeof (opb))
  {
    throw "Type mismatch: Both operands sent to cmp must be of the same type.";
  }
  
  if (typeof (opa) == "number")
  {
    // Numeric comparrison
    return (opa-opb);
  }
  else if (typeof (opa) == "string")
  {
    if (opa.toLowerCase () < opb.toLowerCase ())
    {
      return -1;
    }
    else if (opa.toLowerCase () > opb.toLowerCase ())
    {
      return 1;
    }
    else
    {
      return 0;
    }
  }
  
  return 0;
};

function objectCount (obj)
{
  var count = 0;
  
  var x = 0;
  
  for (x in obj)
  {
    count += 1;
  }
  
  return count;
}

function CategoryContainer (tree)
{
  var one = null;
  this.rows = new Array ();
  this.catData = new Object ();
  this.table = null;
  this.nowShowingId = 0;
  this.toplevel = new Array ();
  this.currentRow = -1;
  this.currentCol = -1;
  this.crumbs = new Object ();
  this.crumbs.handle = null;
  this.message = new Object ();

  this.LoadTree(tree);
  return this;
};

/*
  This should be called from inside the calling page with the following code:
  catContainer.init (
    {
      placeholder : "PLACEHOLDER_DIV_ID",
      cols : 3,
      type : 'W',
      please_wait : true,
      width : "100%",
      cat_id : 0,
      select_callback : function_name,
      varname : "catContainer"
    });
 */
CategoryContainer.prototype.init = function (args)
{
  this.placeName = args.placeholder;
  this.placeHandle = document.getElementById (this.placeName);
  if (!this.placeHandle)
  {
    throw "Unable to obtain handle: " + placeName;
  }
  this.colLimit = args.cols;
  this.showType = args.type;
  this.pleaseWait = args.please_wait;
  this.tableWidth = args.width || "100%";
  this.nowShowingId = args.cat_id || this.nowShowingId;
  this.selectCallback = args.select_callback || false;
  this.varname = args.varname || "catContainer";

  if (!this.catData[this.nowShowingId])
  {
    this.nowShowingId = 0;
  }

  // Show the widget
  this.Show ();

  return this;
};

CategoryContainer.prototype.LoadTree = function (tree)
{
  this.tree = tree;
  this.catData = new Object ();
  this.toplevel = new Array ();
  if (typeof(tree) != "object") return;
  for (var one in this.tree)
  {
    this.toplevel[this.toplevel.length] = this.tree[one];
    MakeCatsFlat (this, this.tree[one]);
  }
}

CategoryContainer.prototype.DisplayMessage = function (msg)
{
  try
  {
    if (!this.message.handle)
    {
      this.message.handle = document.createElement ("span");
      this.message.handle.className = "_jscatMessage";
      this.message.handle.setAttribute ("id", "__ZSCategoryMessage");
      this.placeHandle.appendChild (this.message.handle);
      this.message.handle.innerHTML = msg;
    }
    else
    {
      this.HideMessage ();
      this.message.handle.innerHTML = msg;
      this.message.handle.style.display = "";
    }
  }
  catch (err)
  {
    throw "CategoryContainer.DisplayMessage(): " + err;
  }
  
  return;
};

CategoryContainer.prototype.HideMessage = function ()
{
  if (this.message.handle && this.message.handle.style.display == "")
  {
    this.message.handle.style.display = "none";
  }
  
  return;
};

CategoryContainer.prototype.DropBreadcrumbs = function ()
{
  var crumb = null;
  
  try
  {
    if (!this.crumbs.handle)
    {
      this.crumbs.handle = document.createElement ("span");
      this.crumbs.handle.setAttribute ("id", "__ZSCategoryBreadcrumbs");
      this.crumbs.list = new Array ();

      if (this.placeHandle.firstChild)
      {
        this.placeHandle.insertBefore (this.crumbs.handle, this.placeHandle.firstChild);
      }
      else
      {
        this.placeHandle.appendChild (this.crumbs.handle);
      }
      
      this.AddCrumb (this.nowShowingId, null);
    }
    else if (this.crumbs.handle.style.display == "")
    {
      this.SweepBreadcrumbs ();
      this.crumbs.handle.style.display = "";
      this.AddCrumb (this.nowShowingId, null);
    }
  }
  catch (err)
  {
    throw "CategoryContainer.DropBreadcrumbs(): " + err;
  }
  
  return;
};

CategoryContainer.prototype.SweepBreadcrumbs = function ()
{
  try
  {
    while (this.crumbs.handle.firstChild)
    {
      this.crumbs.handle.removeChild (this.crumbs.handle.firstChild);
    }
    this.crumbs.handle.display = "none";
  }
  catch (err)
  {
    throw "CategoryContainer.SweepBreadcrumbs(): " + err;
  }
  
  return;
};

CategoryContainer.prototype.AddCrumb = function (id, node)
{
  var a = null;
  var raquo = document.createTextNode (" \xBB ");
  
  try
  {
    if (this.nowShowingId == 0)
    {
      return;
    }
    else if (this.catData[id])
    {
      if (id == this.nowShowingId)
      {
        a = document.createElement ("span");
      }
      else
      {
        a = document.createElement ("a");
        a.setAttribute ("href", "javascript:"+this.varname+".SelectCategory('"+this.catData[id].id+"')");
      }
      a.appendChild (document.createTextNode (this.catData[id].name));
    }
    else if (id == 0)
    {
      a = document.createElement ("a");
      a.setAttribute ("href", "javascript:"+this.varname+".SelectCategory('0')");
      a.appendChild (document.createTextNode ("Start"));
    }
    else
    {
      a = document.createElement ("a");
      a.setAttribute ("href", "javascript:"+this.varname+".SelectCategory('0')");
      a.appendChild (document.createTextNode ("Start"));
    }
    
    if (node == null)
    {
      this.crumbs.handle.appendChild (a);
      if (id == this.nowShowingId)
      {
        this.crumbs.handle.appendChild (document.createElement ("br"));
      }
    }
    else
    {
      this.crumbs.handle.insertBefore (raquo, this.crumbs.handle.firstChild);
      this.crumbs.handle.insertBefore (a, this.crumbs.handle.firstChild);
      if (id == this.nowShowingId)
      {
        this.crumbs.handle.appendChild (document.createElement ("br"));
      }
    }
    
    if (id != 0 && this.catData[id])
    {
      this.AddCrumb (this.catData[id].parent, this.crumbs.handle.firstChild);
    }
  }
  catch (err)
  {
    throw "CategoryContainer.AddCrumb(): " + err;
  }
  
  return;
};

CategoryContainer.prototype.CreateTable = function ()
{
  var tbl = null;
  
  try
  {
    if (!this.placeHandle)
    {
      throw "CODE ERROR: No place-holder handle available for CategoryContainer.CreateTable().";
    }

    if (this.table == null)
    {
      // Create a new table because none exists.
      tbl = document.createElement ("table");
      tbl.setAttribute ("id", "__ZS_CategoryContainer");
      tbl.setAttribute ("border", "0");
      tbl.setAttribute ("width", this.tableWidth);
      tbl.setAttribute ("cellpadding", "0");
      tbl.setAttribute ("cellspacing", "2");
      tbl.className = "_jscatTable";
      this.table = document.createElement ("tbody");
      this.table.className = "_jscatTBody";
      tbl.appendChild (this.table);
      this.placeHandle.appendChild (tbl);
    }
    else
    {
      // If the table isn't cleared out, let's make it that way
      if (this.table.style.display == "")
      {
        this.Clear ();
      }
      
      // Display the table
      this.table.style.display = "";
    }
  }
  catch (err)
  {
    throw "CategoryContainer.CreateTable(): " + err;
  }
  
  return;
};

CategoryContainer.prototype.AddRow = function ()
{
  var row = null;
  
  try
  {
    // Make sure that we have a table to work with
    if (this.table == null)
    {
      this.CreateTable ();
    }
    
    if ((this.currentRow + 1) >= this.rows.length)
    {
      // We need to create a new row.
      row = document.createElement ("tr");
      row.className = "_jscatRow";
      this.table.appendChild (row);
      this.currentRow = this.rows.length;
      this.rows[this.currentRow] = new Object ();
      this.rows[this.currentRow].handle = row;
      this.rows[this.currentRow].cols = new Array ();
    }
    else
    {
      // We already have an existing row here.
      this.currentRow += 1;
      // Unhide the row for display.
      this.rows[this.currentRow].handle.style.display = "";
    }
  }
  catch (err)
  {
    throw "CategoryContainer.AddRow(): " + err;
  }
  
  return;
}

CategoryContainer.prototype.AddCell = function ()
{
  var cell = null;
  
  try
  {
    // Make sure that we have a row to work with.
    if (this.currentRow < 0 || !this.rows[this.currentRow])
    {
      this.AddRow ();
    }
    
    // Make sure we still have room in this row
    if ((this.currentCol + 1) >= this.colLimit)
    {
      // Add a new row to make room for this cell
      this.AddRow ();
      this.currentCol = -1;
    }
    
    if (!this.rows[this.currentRow].cols[this.currentCol+1])
    {
      // Create the cell node
      cell = document.createElement ("td");
      cell.setAttribute ("align", "left");
      cell.setAttribute ("valign", "top");
      cell.setAttribute ("width", (100/this.colLimit) + "%");
      cell.className = "_jscatCell";
      
      // Get the new column index
      this.currentCol = this.rows[this.currentRow].cols.length;
      
      // Add the new cell to the row
      this.rows[this.currentRow].handle.appendChild (cell);
      this.rows[this.currentRow].cols[this.currentCol] = new Object ();
      this.rows[this.currentRow].cols[this.currentCol].handle = cell;
    }
    else
    {
      this.currentCol += 1;
      this.rows[this.currentRow].cols[this.currentCol].handle.style.display = "";
    }
  }
  catch (err)
  {
    throw "CategoryContainer.AddCell(): " + err;
  }
}

CategoryContainer.prototype.AddCategory = function (obj)
{
  try
  {
    this.AddCell ();
    this.rows[this.currentRow].cols[this.currentCol].handle.appendChild (obj.handle);
    //this.rows[this.currentRow].cols[this.currentCol].handle.appendChild (document.createTextNode (this.currentRow + "x" + this.currentCol));
    this.rows[this.currentRow].cols[this.currentCol].category = obj;
  }
  catch (err)
  {
    throw "CategoryContainer.AddCategory(): " + err;
  }
  
  return;
}

CategoryContainer.prototype.Show = function ()
{
  var elm = null;
  var parentId = this.nowShowingId;
  var list = null;
  var x = 0;
  var len = 0;
  var colnum = 0;
  var currow = null;
  var cell = null;
  var cat = null;
  var nameList = new Array ();
  var one = null;

  if (typeof(this.tree) != "object") {
    this.Clear();
    this.DisplayMessage("<br/><br/>No currently active classifieds<br/><br/>");
    return;
  }
  this.HideMessage();

  this.currentRow = -1;
  this.currentCol = -1;

  try
  {
    // If the ID doesn't exist, default to zero.
    if (!this.catData[this.nowShowingId])
    {
      this.nowShowingId = 0;
    }

    // If we're drilled down as far as we can go, then show selected without
    // executing the callback
    if (this.catData[parentId] && objectCount(this.catData[parentId].children) == 0)
    {
      this.ShowSelected ();
      return;
    }
    
    // Clean up previous displays and create the table if it doesn't exist.
    this.Clear ();
    this.CreateTable ();
    
    // Show breadcrumbs
    this.DropBreadcrumbs ();
    
    // Loop through all necessary nodes
    if (parentId == 0)
    {
      list = this.toplevel;
    }
    else
    {
      if (this.catData[parentId])
      {
        list = this.catData[parentId].children;
      }
      else
      {
        list = new Array ();
      }
    }

    if (!list || list.length == 0)
    {
      throw "No Data.";
    }
    for (x in list)
    {
      nameList[nameList.length] = list[x];
    }
    nameList.sort (
      function (opa, opb) {
        if (typeof(opa.name) != "string")
        {
          return 1;
        }
        else if (typeof(opb.name) != "string")
        {
          return -1;
        }
        return (cmp (opa.name, opb.name))
      });
    for (x = 0, len = nameList.length; x < len; x++)
    {
      one = nameList[x];
      // Create a new Category
      if (one.type == this.showType && one.displayed)
      {
        var decorated_name = typeof(one.number) == "number" ? one.name+" ("+one.number+")" : one.name;
        // this.AddCategory (new Category (one.id, one.name, this.varname));
        this.AddCategory (new Category (one.id, decorated_name, this.varname));
      }
    }
  }
  catch (err)
  {
    throw "CategoryContainer.Show(): " + err;
  }
  
  return true;
}

CategoryContainer.prototype.Clear = function ()
{
  var x = 0;
  var y = 0;
  var len = 0;
  var len2 = 0;
  
  try
  {
    if (!this.table)
    {
      return;
    }
    
    // Loop through rows
    for (y = 0, len = this.rows.length; y < len; y++)
    {
      // Loop through columns
      for (x = 0, len2 = this.rows[y].cols.length; x < len2; x++)
      {
        // Remove all children nodes
        while (this.rows[y].cols[x].handle.firstChild)
        {
          this.rows[y].cols[x].handle.removeChild (this.rows[y].cols[x].handle.firstChild);
        }
        // Hide the cell element for future use
        this.rows[y].cols[x].handle.style.display = "none";
        this.rows[y].cols[x].category = null;
      }
      // Hide the row element for future use
      this.rows[y].handle.style.display = "none";
    }
    // Hide the table element for future use
    this.table.style.display = "none";
    
    // Reset the current coords values
    this.currentRow = -1;
    this.currentCol = -1;
    
    this.SweepBreadcrumbs ();
    this.HideMessage ();
  }
  catch (err)
  {
    throw "CategoryContainer.Clear(): " + err;
  }
  
  return;
};

CategoryContainer.prototype.ShowSelected = function (execute)
{
  var msg = "";
  
  try
  {
    this.Clear ();
    this.DropBreadcrumbs ();
    msg = '<hr/>Your current category is:<br/><span style="font-weight:bold;color:#CC0000;">' +
      this.catData[this.nowShowingId].name + '</span>';
    if (this.pleaseWait)
    {
      msg += "<br/><br/>Please wait...";
    }
    else
    {
      msg += '<br/><br/>To change your category, please click "Start".';
    }
    this.DisplayMessage (msg);

    // Call our callback
    if (this.selectCallback && execute)
    {
      this.selectCallback (this.nowShowingId);
    }
  }
  catch (err)
  {
    throw "CategoryContainer.ShowSelected(): " + err;
  }
  
  return;
};

CategoryContainer.prototype.SelectCategory = function (id)
{
  try
  {
    this.nowShowingId = id;
    if (this.catData[id] && objectCount (this.catData[id].children) == 0)
    {
      this.ShowSelected (true);
    }
    else
    {
      this.Show ();
    }
  }
  catch (err)
  {
    alert ("SelectCategory: " + err);
  }
  
  return;
};

function Category (id, name, varname)
{
  this.catId = id;
  this.catName = name;
  
  this.handle = document.createElement ("a");
  this.handle.setAttribute ("href", "javascript:"+varname+".SelectCategory('"+id+"')");
  this.handle.className = "_jscatCategory";
  this.textHandle = document.createTextNode (this.catName);
  this.handle.appendChild (this.textHandle);
  
  return this;
};

function MakeCatsFlat (container, cat)
{
  var x = 0;
  var len = 0;

  container.catData[cat.id] = cat;
  for (x in cat.children)
  {
    MakeCatsFlat (container, cat.children[x]);
  }
  
  return;
};

// Cat container
var catContainer = new CategoryContainer ();

var __headAry = document.getElementsByTagName ("head");
var __cssTag = document.createElement ("link");

function doOnload ()
{
  __cssTag.setAttribute ("rel", "stylesheet");
  __cssTag.setAttribute ("type", "text/css");
  __cssTag.setAttribute ("href", "/lib/suffixapp/jscat/jscat.css");
  __headAry[0].appendChild (__cssTag);

  return true;
}

if (window.addEventListener)
{
  window.addEventListener("load",doOnload,false);
}
else if (window.attachEvent)
{
  window.attachEvent("onload",doOnload);
}

