/* js
   
   This file is part of arena.

   arena is free software: you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 3 of the License or, at your
   option, any later version.

   arena is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABLITY
   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
   License for more details.

   You should have received a copy of the GNU General Public License along
   with arena.  If not, see <http://www.gnu.org/licenses/>.
*/

/* This file defines a utility class that organizes the channels in an arena
   site into a tree.
*/

subclass(ATree, AnObject); 

function ATree(treeName) {
  this.treeName = treeName;
  this.element = document.createElement("ul");
}

ATree.prototype.openIcon = ""
ATree.prototype.closedIcon = ""
ATree.prototype.itemIcon = ""
ATree.prototype.currentChannel = ""

// Gets the active node from the tree
ATree.prototype.getActiveLeaf = function() {
  // Retrieve the top of the list
  tree = document.getElementById(this.treeName);

  if (!tree) 
    return null;
    
  child = tree.firstChild;

  while (child) {
    if (child.className == "active") 
      return(child);

    child = child.nextSibling;
  }
  return(null);
}

// Shows or hides a channel list element
ATree.prototype.toggleVisibilityOf = function(id) {
  if (!id) return;
  // Change the icon that matches the element to show the list is expanded or collapsed
  icon = document.getElementById("icon" + id);
  element = document.getElementById("branch" + id);
  if (icon) {
    if (element.style.display != "none") {
      icon.title = "Expand this list";
    }
    else {
      icon.title = "Collapse this list";
    }
  }
  if (element) {
    // Show or hide the element
    element.style.display = (element.style.display != "none") ? "none":"block";
  }
}

// This function handles mouse clicks for individual leaves
ATree.prototype.pickLeaf = function(leafName) {
  // Retrieve the currently-selected leaf anchor
  var currentLeaf = this.getActiveLeaf();

  // Reset the leaf class name
  if (currentLeaf) 
    currentLeaf.className = "";

  // Highlight the new leaf
  leaf = document.getElementById(this.treeName + "/" + leafName + "$link");
  if (leaf) {
    leaf.className = "active";
    // Make sure the current channel always shows
    leaf = leaf.parentNode;
    // Work backwards
    while (leaf.id != this.treeName) {
      // Ensure the parent node (if it's a <li> element) and its associated children
      // are visible
      if ((leaf.tagName == "UL") && (leaf.style.display == "none")) 
        this.toggleVisibilityOf(leaf.id.split("branch")[1]);
      // Get the next node
      leaf = leaf.parentNode;
    }
  }
}

// This function attaches an icon to the specified leaf
ATree.prototype.attachIcon = function(leaf, icon) {
  if ( (leaf) && (icon != "") ) {
    newLeafIcon = document.createElement("IMG");
    newLeafIcon.id = leaf.id + "$icon";
    // Set the class name of the new icon
    newLeafIcon.className = "line_icon";
    // Set the icon image
    newLeafIcon.src = icon;
    // Attach the icon to the new leaf
    leaf.appendChild(newLeafIcon);
  }
}

// This function sets up a link for the specified leaf
ATree.prototype.attachLink = function(leaf, title, url) {
  if ( (leaf) && (url != "") ) {
    newLeafLink = document.createElement("A");
    newLeafLink.href = url;
    newLeafLink.title = title;
    newLeafLink.id = leaf.id + "$link";
    // Insert the link
    leaf.appendChild(newLeafLink);
    // Create a text node that contains the leaf name
    newLeafText = document.createTextNode(title);
    newLeafLink.appendChild(newLeafText);
  }
}

// This function inserts a leaf for the specified parent
ATree.prototype.sproutLeaf = function(branch, leafName, id, url, icon) {
  // Correct for the root node
  if (leafName == "/") 
    result = this.element;

  // See if the leaf already exists
  else 
    result = document.getElementById(id);

  // If the leaf already exists, great!
  if (result) 
    return(result);

  // Otherwise, create the leaf
  newLeaf = document.createElement("LI");
  newLeaf.id = id;
  // newLeaf.className = "none";
  //newLeaf.onmouseover = function() { this.className = "hover"; return true; }
  //newLeaf.onmouseout = function() { this.className = ""; return true; }

  // Create the leaf icon
  this.attachIcon(newLeaf, icon);

  // Create the leaf link
  this.attachLink(newLeaf, leafName, url);
  
  // Alter the prospective parent of the new node...
  // If we're at the top of the channel list, then the parent is the root node
  if (branch == this.element) 
    leafParent = branch;
    
  // Otherwise, if the branch has no children as yet, it must become a parent
  else if (branch.getElementsByTagName("ul").length == 0) {
    // Create a new unordered list
    leafParent = document.createElement("UL");
    leafParent.id = branch.id + "$branch";
    // Insert the unordered list underneath the parent
    branch.appendChild(leafParent);
    
    // Set the icon to toggle visibility of the new parent
    parentIcon = document.getElementById(branch.id + "$icon");
    if (parentIcon) {
      parentIcon.onclick = function() { 
        self.toggleVisibilityOf(branch.id);
        return true;
      }
    }
       
    // Toggle the visibility of the new parent
    this.toggleVisibilityOf(branch.id);
  }

  else {
    // We need to find the <ul> element that will act as the parent for our
    // new <li> element
    if (branch != this.element)
      leafParent = document.getElementById(branch.id + "$branch");
  }
  
  // Sprout a new leaf right on the branch
  leafParent.appendChild(newLeaf);
  // Return the leaf
  return(newLeaf);
}

// Sprout the named leaf
ATree.prototype.sprout = function(path, url, icon) {
  if ( (!path) || (path == "") )
    return;

  // Break out the node hierarchy
  if (path.indexOf("/") >= 0) {
    hierarchy = path.split("/");
    leafPath = this.treeName;

    for (var j=0; j < hierarchy.length; j++) {
      leafPath += "/" + hierarchy[j];

      if (!parentLeaf)
        parentLeaf = document.getElementById(leafPath);

      if (!parentLeaf)
        parentLeaf = this.element;
        
      parentLeaf = this.sproutLeaf(parentLeaf, hierarchy[j], 
          leafPath, url, icon);
    }
  }

  else {
    leafName = path;
    parentLeaf = this.element;
    leafPath = this.treeName + "/" + leafName;

    existingLeaf = document.getElementById(leafPath);
    if (!existingLeaf)
      this.sproutLeaf(parentLeaf, leafName, leafPath, url, icon);
            
  }
}

// Clears the channel tree
ATree.prototype.clear = function() {
  // See if the channel list exists
  channelTree = document.getElementById(this.treeName);
  // If it does, see if it has any children
  if (channelTree) {
    while (channelTree.hasChildNodes()) 
        channelTree.removeChild(channelTree.firstChild);
  }
}

// Builds the channel tree from an array of strings, where each element of the
// array has the form:
//   channelPath|channelURL|channelParent|channelIcon
ATree.prototype.buildFrom = function(data) {
  // Proceed only if we have data
  if (!data) 
    return;

  if (data.length == 0) 
    return;
    
  var channels = new Array();
  // Constants to make reading code easier
  var channelPath = 0;
  var url = 1;
  var icon = 2;

  // Clear the channel tree
  this.clear();
  // Break out the channel fields
  for (var i=1; i < data.length; i++) {
    channels.push(data[i].split("|"));
  }

  parentLeaf = false;

  // Make the list visible
  document.getElementById(this.treeName).appendChild(this.element);
  
  // Parse the channel records
  for (i = 0; i < channels.length; i++)
    this.sprout(channels[i][channelPath], channels[i][url],
      channels[i][icon]);

  // Highlight the current channel
  if (this.currentChannel != "") 
    this.pickLeaf(this.currentChannel);
}

// Requests the channel tree
ATree.prototype.fetch = function(url, blocking) {
  if (url == "")
    return;
  // Construct an HTTP request to retrieve the channel list
  var Request = new AnAjaxRequest("GET")
  // If we failed to create the request, we cannot fetch the list
  if (!Request) 
    return;
  Request.url = url;
  Request.queryString = "ajax=y";
  Request.caller = this;
  Request.callback = function () {
    // Check the server response
    if (Request.request.readyState == 4) {
      // Everything went well
      if (Request.request.status == 200) {
        // alert(Request.request.responseText)
        lines = Request.request.responseText.split("<br/>");
        // Pop the last, empty line from the array
        lines.pop();
        if (lines.length > 0) {
          if (lines[0] == "OK") {
            Request.caller.buildFrom(lines);
          }
        }
      }
    }
  }
  // Send the request
  Request.send(blocking);
}  

