/*
  Dynamically generates a nested UL navigation list from
  an XML file. Adds CSS class names to the LI elements.

  leafes get the menu_leaf_class
  open nodes get the menu_open_class
  closed nodes get the menu_closed_class
  the current node gets the menu_active_class

*/

//config
var menu_active_class = "current";
var menu_leaf_class = "leaf";
var menu_open_class = "open";
var menu_closed_class = "closed";

//the default page that is displayed if URL ends in /
var menu_default_page = "index.html";

//state
var menu_current; // XML menu node of the current location
var menu_totop;   // path to top folder from current location

// main function
// navfile : the URL to the navigation XML
// menu_id : id of the element into which to insert the navigation
function menu_main(navfile, menu_id) {
  var xml = menu_loadXml(navfile);
  if (!xml) return;
  
  menu_create(xml, menu_id);
}
  
// navfile : the URL to the navigation XML
function menu_loadXml(navfile) {
    var xmlHttp = false;
    // Mozilla, Opera, Safari, IE7
    if (typeof XMLHttpRequest != 'undefined') {
        xmlHttp = new XMLHttpRequest();
    }
    if (!xmlHttp) {
        // before IE 6
		  
        try {
            xmlHttp  = new ActiveXObject("Msxml2.XMLHTTP");
			
        } catch(e) {
            try {
                xmlHttp  = new ActiveXObject("Microsoft.XMLHTTP");
            } catch(e) {
                xmlHttp  = false;
            }
        }
		
		
    }
    if (!xmlHttp) {
      alert("Sorry, your browser does not support AJAX!");
      return false;
    }

    xmlHttp.open("GET", navfile, false);
    xmlHttp.send(null);
    // when on local file system browsers return 0 here
    if ((xmlHttp.status==0) || ((xmlHttp.status>=200) && (xmlHttp.status < 400))) {
      return xmlHttp.responseXML.getElementsByTagName("navigation")[0];
    } else {
      alert("AJAX error: "+ xmlHttp.status +" "+ xmlHttp.statusText);
      return false;
    }
}

// xml : the XML root node of the navigation XML
// menu_id : id of the element into which to insert the navigation
function menu_create(xml, menu_id) {
     var url = location.href;
	 
     if (url.lastIndexOf("/") == (url.length-1)) {
       url = url+menu_default_page;
	    
     }
	
     if (url.lastIndexOf("?") >= 0) {
       url = url.substring(0, url.lastIndexOf("?"));
	   
     }
     if (url.lastIndexOf("#") >= 0) {
       url = url.substring(0, url.lastIndexOf("#"));
	    
     }
	 
	
     menu_current = menu_getCurrentMenu(xml, url);
     menu_totop = menu_getPathToTop(menu_current);
     
     var main = document.getElementById(menu_id);
     if (!main) {
       alert("No element with id '"+ menu_id +"' found");
       return;
     }
	 
     var root = document.createElement("ul");
     menu_traverse(xml, root);
     main.appendChild(root);
}

/* Walks down the subtree and on the way back
   sets properties.
   element : the parent XML node
   root    : the DOM node of the enclosing UL
   returns bit set
           1: set = element is a node, unset = element is a leaf
           2: set = element contains the active node

*/
function menu_traverse(element, root) {
  var props = 0;
  var ihavechildren = false;
  var icontainactive = false;
  for (var i=0; i<element.childNodes.length; i++) {
    var child = element.childNodes[i];
    if (child.nodeType != 1) continue;
    if (child.nodeName != "menu") continue;
    ihavechildren = true;
    // create DOM
    var li = document.createElement("li");
    li.className = "";
    var a = document.createElement("a");
	var span = document.createElement("span");
    var url = child.getAttribute("url");
    if (!url) {
      var submenus = child.getElementsByTagName("menu");
      for (var j=0; j < submenus.length; j++) {
        url = submenus[j].getAttribute("url");
        if (url) break;
      }
    }
    if (!url) url="#"; // we are desperate now
    var iamactive = (child == menu_current);
    if (iamactive) {
      a.className = menu_active_class;
      icontainactive = true;
    }  
    a.setAttribute("href", menu_totop + url);
    var text = document.createTextNode(child.getAttribute("title"));
	span.appendChild(text);
    a.appendChild(span);
    li.appendChild(a);
    root.appendChild(li);

    // recurse
    var ul = document.createElement("ul");
    var subprops = menu_traverse(child, ul); // aggregate bits
    
    if (subprops & 1) {
      a.className += " "+ (iamactive || (subprops & 2) ? menu_open_class : menu_closed_class);
      li.appendChild(ul);
    } else {
      a.className += " "+menu_leaf_class;
    }
    props |= subprops;
  }
  if (ihavechildren) props |= 1;
  if (icontainactive) props |= 2;
  return props;
}

function menu_getPathToTop(menu) {
  var path = "";
  if (menu == null) return path;
  var url = menu.getAttribute("url");
  var a = url.split(/[\/]/);
  if (a.length == 1) return path;
  for (var i=0; i < a.length - 1; i++) {
    path += "../";
  }
  return path;
}

// determines the current location in the nav tree.
// its the longest URL that matches the location.
// xml : the XML root node of the navigation XML
// loc : the current URL location
function menu_getCurrentMenu(xml, loc) {
  var current = null;
  var maxlen = 0;
  var menus = xml.getElementsByTagName("menu");
  for (var i=0; i < menus.length; i++) {
    var menu = menus[i];
    var url = menu.getAttribute("url");
    if (!url) continue;
    if (url.length < maxlen) continue;    
    if (!menu_isSameUrl(loc, url)) continue;
    current = menu;
    maxlen = url.length;
  }
  return current;
}

//matches two URIs when href is the last part of url
//.. and . are correctly resolved
function menu_isSameUrl(url, href) {
  var a = url.split(/[?\/]/i);
  var b = href.split(/[?\/]/i);
  var i = a.length - 1;
  var j = b.length - 1;
  while ((i >= 0) && (j >= 0)) {
    if (b[j] == "..") { j-=2; continue; }
    if (a[i] == "..") { i-=2; continue; }
    if ((b[j] == ".") || (b[j] == "")) { j--; continue; }
    if ((a[i] == ".") || (a[i] == "")) { i--; continue; }
    if (! (a[i] == b[j])) return false;
    i--;
    j--;
  }
  return true;
}
