/**
 * @(#)navigation.js  1.1  2010-08-05
 *
 * Copyright (c) 2010 Werner Randelshofer
 * Hausmatt 10, CH-6405 Immensee, Switzerland
 * All rights reserved.
 *
 * The copyright of this software is owned by Werner Randelshofer. 
 * You may not use, copy or modify this software, except in  
 * accordance with the license agreement you entered into with  
 * Werner Randelshofer. For details see accompanying license terms. 
 */

/** Writes the main navigation bar and the side navigation bar into the document.
 *
 * "mainToc" and "sideToc" must be an object with the properties "base" and "items".
 *
 * The "base" property defines a base directory for all items items.
 * This must be an empty string or a string ending with a "/" character.
 *
 * The "items" property must be a nested array.
 * An array element is again an array element, with 1 to 3 elements:
 * 1. title (string)
 * 2. path to items   (string, optional, must end with slash)
 * 3. link   (string, optional, must not end with slash)
 * 4. items (array, optional)
 * 
 * Example:
 * var items = [
 * ["Home", "index.html", [
 *    ["Welcome", "index.html"],
 *    ["About", "about.html"],
 *    ["Links", "links.html"]
 * ]],
 * [false, "Fun", "fun/", "fun.html", [ // this node is hidden
 *    ["Overview", "fun.html"],
 *    ["Animations", "animations/index.html"],
 * ]],
 * ["Science", "science.html", [
 *    ["Overview", "fun.html"],
 *    ["Animations", "animations/index.html"],
 * ]],
 * ["Software", "software.html", [
 *    ["Overview", "software.html"],
 * ]]
 * ];
 *
 *
 * @param mainToc property object
 * @param sideToc (nullable) the toc of the sidebar
 * @param mainUrl String the selected URL in the mainToc
 * @param sideUrl (optional) String the selected URL in the sideToc
 * @param pageUrl (optional) The url of the page if different from the sideUrl.
 *
 * @version 
 * 1.1 2010-08-05 Added support for hidden nodes.
 * 1.0 2010-07-13 Created.
 */
function writeNav(mainToc, sideToc, mainUrl, sideUrl, pageUrl) {
  if (typeof(sideUrl)=='undefined') sideUrl=mainUrl;
  if (typeof(pageUrl)=='undefined' || pageUrl == null) pageUrl = sideUrl;
  
    var mainTree=convertTocArrayToTree(mainToc.items, mainToc.base);
    var sideTree=(sideToc==null) ? null : convertTocArrayToTree(sideToc.items, sideToc.base);
    writeMainNavTree(mainTree, sideTree, mainUrl, sideUrl, pageUrl);
  
  document.write('<div class="grid_4">');
  document.write('<header><div class="header1"><h1>&nbsp;</h1></div></header>');
  if (sideTree != null) {
    writeSideNavTree(sideTree, sideUrl, pageUrl);
  }
  document.write('</div>');
}

/**
 * Writes the main navigation bar into the document.
 *
 * @param mainToc property object
 * @param sideToc (nullable) the toc of the sidebar
 * @param mainUrl String the selected URL in the mainToc
 * @param sideUrl (optional) String the selected URL in the sideToc
 * @param pageUrl (optional) The url of the page if different from the sideUrl.
 */
function writeMainNav(mainToc, sideToc, mainUrl, sideUrl, pageUrl) {
  if (typeof(sideUrl)=='undefined') sideUrl = mainUrl;
  if (typeof(pageUrl)=='undefined' || pageUrl == null) pageUrl = sideUrl;
  
  var mainTree=convertTocArrayToTree(mainToc["items"], mainToc["base"]);
  var sideTree=convertTocArrayToTree(sideToc["items"], sideToc["base"]);
  writeMainNavTree(mainTree, sideTree, mainUrl, sideUrl, pageUrl);
}
function writeMainNavTree(mainTree, sideTree, mainUrl, sideUrl, pageUrl) {
  
  var html="";
  
  var mainPath=getPathToLink(mainTree, mainUrl);
  var sidePath=(sideTree==null)?[]:getPathToLink(sideTree, sideUrl);
  
  
  html += '<nav>';
  html += '<div class="mainNav">';
  html += '<div class="logo"><a href="http://www.randelshofer.ch">www.randelshofer.ch</a></div>';
  
  html += '<div class="nav1">';
  html += '<ul>';
  if (mainPath.length > 0) {
    for (var i=0;i<mainPath[0].children.length;i++) {
    var node = mainPath[0].children[i];
      html += '<li>';
      if (node.link != null) {
        html += '<a href="'+makeUrlRelativeTo(node.link,pageUrl)+'"'+(node==mainPath[1]?' class="selected"':'')+'>';
      html += node.title;
        html += '</a>';
    } else {
      html += node.title;
      }
      html += '</li>';
    }
  }
  html += '</ul>';
  html += '</div>';

  html += '<div class="nav2">';
  html += '<ul class="breadcrumbs">';
  if (mainPath.length > 1) {
    for (var i=0; i < mainPath[1].children.length; i++) {
    var node = mainPath[1].children[i];
      html += '<li>';
      if (node.link != null) {
        html += '<a href="'+makeUrlRelativeTo(node.link,pageUrl)+'"'+(node==mainPath[2]&&sidePath.length==0?' class="selected"':'')+'>';
      html += node.title;
        html += '</a>';
    } else {
      html += node.title;
      }
    html += '</li>';
    }
  }
  for (var i=0; i<sidePath.length; i++) {
    var node = sidePath[i];
    if (node.link != null) {
      html += '<li>';
      html += '<a href="'+makeUrlRelativeTo(node.link,pageUrl)+'"'+(i==sidePath.length-1?' class="selected"':'')+'>';
      html += node.title;
      html += '</a>';
      html += '</li>';
    }
  }
  html += '</ul>';
  html += '</div>';


  html += '</div>';
  html += '</nav>';
  
  document.write(html);
}
/**
 * Writes the side navigation bar into the document.
 *
 * @param sideToc the toc of the sidebar
 * @param sideUrl (optional) String the selected URL in the sideToc
 * @param pageUrl (optional) The url of the page if different from the sideUrl.
 */
/**
 * Writes the main navigation bar into the document.
 *
 * @param mainToc property object
 * @param sideToc (nullable) the toc of the sidebar
 * @param mainUrl String the selected URL in the mainToc
 * @param sideUrl (optional) String the selected URL in the sideToc
 * @param pageUrl (optional) The url of the page if different from the sideUrl.
 */
function writeSideNav(sideToc, sideUrl, pageUrl) {
  if (typeof(pageUrl)=='undefined' || pageUrl == null) pageUrl = sideUrl;
  
  var sideTree=convertTocArrayToTree(sideToc["items"], sideToc["base"]);
  writeSideNavTree(mainTree, sideTree, mainUrl, sideUrl, pageUrl);
}
function writeSideNavTree(sideTree, sideUrl, pageUrl) {
  var out=[];
  var isSelected = false;
  for (var i=0; i < sideTree.children.length; i++) {
    isSelected |= addNode(sideTree.children[i], sideUrl, pageUrl, false, 0, out);
  }
  
  var html="";
  html += '<nav>';
  html += '<div class="sideNav">';

  var boxHeight=208;
  var height=620;
  for (var i=0;i < out.length; i++) {
    html += '<div id="sideNav'+i+'" class="nav1" style="max-height:'
         +  ((i==out.length-1 && boxHeight<height)?height:boxHeight)
         +  'px;">';
    html += '<ul>';
    html += out[i];
    html += '</ul>';
    html += '</div>';
    height-=boxHeight;
  }
  html += '</div>';
  html += '</nav>';
  document.write(html);
  
  // For each side navigation box, scroll the selected element into view 
  for (var i=0; i < out.length; i++) {
    var navElem = document.getElementById('sideNav'+i);

    var selectedElem = document.getElementById('sideNavSelected'+i);
    if (navElem != null && selectedElem != null) {
      var selectedElemTop = selectedElem.offsetTop - navElem.offsetTop;
      if (selectedElemTop + selectedElem.offsetHeight > navElem.clientHeight) {
        navElem.scrollTop = selectedElemTop - selectedElem.offsetHeight;
      }
    }
  }

  // Same code as above, but waits with execution until page is fully loaded
/*  
  var fn = function() {
    for (var i=0; i < out.length; i++) {
      var navElem = document.getElementById('sideNav'+i);

      var selectedElem = document.getElementById('sideNavSelected'+i);
      if (navElem != null && selectedElem != null) {
        var selectedElemTop = selectedElem.offsetTop - navElem.offsetTop;
        if (selectedElemTop + selectedElem.offsetHeight > navElem.clientHeight) {
          navElem.scrollTop = selectedElemTop - selectedElem.offsetHeight;
        }
      }
    }
    
    if (window.removeEventListener) {
      window.removeEventListener('load', fn, false);
    } else { // for poor IE:
      window.detachEvent('onload', fn);
    }
  }
  
  if (window.addEventListener) {
    window.addEventListener('load', fn, false);
  } else { // for poor IE:
    window.attachEvent('onload', fn);
  }
*/
}

/** Recursively adds items to the output array 
 *
 * @param itm the toc item
 * @param base the base URL of the toc item
 * @param selectedUrl the URL to be selected in the toc
 * @param pageUrl the URL of the current page
 * @param isParentSelected whether the parent node is selected
 * @param level the index in the out array
 * @param out an array of html String objects
 * @return the selected item
 */
function addNode(node, selectedUrl, pageUrl, isParentSelected, level, out) {
  while (out.length <= level) {
    out.push("");
  }
  
  var isNodeSelected = (node.link == selectedUrl);
  
  // write subtree
  var subOut=[];
  var nextLevel = (node.link != null) ? level+1:level;
  var isSubtreeSelected = false;
  for (var i=0; i < node.children.length; i++) {
    isSubtreeSelected |= addNode(node.children[i], selectedUrl, pageUrl, isParentSelected|isNodeSelected, nextLevel, subOut);
  }
  // write the item
  var html = '<li>';
  if (node.link != null) {
    html += '<a href="'
       +makeUrlRelativeTo(node.link,pageUrl)+'"'
     +(isNodeSelected|isSubtreeSelected?' class="selected"':'')
     +(isNodeSelected|isSubtreeSelected?' id="sideNavSelected'+level+'"':'')
     +'>'+node.title+'</a>';
  } else {
    html += "<h1>"+node.title+"</h1>";
  }
  if (typeof(subOut[nextLevel])!='undefined') {
  if (nextLevel==level) {
      html += '<ul>';
      html += subOut[nextLevel];  
      html += '</ul>';
  } 
  if (isNodeSelected|isSubtreeSelected) {
    for (var i=level+1; i<subOut.length; i++) {
            while (out.length <= i) {
            out.push("");
            }
        out[i]=subOut[i];
    }
  }
  }
  html += '</li>';
	
	// We only want visible nodes and selected nodes
  if (node.visible || isNodeSelected || isSubtreeSelected) {
    out[level]+=html;
	}
    
  return isNodeSelected|isSubtreeSelected;
}


/** Converts the toc items array into a tree structure.
 * Each tree node has three properties: title, link, children.
 */
function convertTocArrayToTree(tocArray, base) {
  var root={title:null, link:null, children:[]};
  for (var i=0; i < tocArray.length; i++) {
    convertTocItemToTree(tocArray[i],base,root);
  }
  return root;
}
/** Converts a single toc item array into a tree structure.
 * Each tree node has the following properties: visible, title, base, link, children.
 *
 * visible:  boolean     (optional)
 * title:    string      (required)
 * base:     string (optional)
 * link:     string,null (optional)
 * children: array       (optional)
 */
function convertTocItemToTree(tocItem, base, parent) {
  if (typeof(tocItem) == "undefined" || tocItem == null) return; 
  
  var i=0; 
  var visible = (typeof(tocItem[i])=="boolean") ?tocItem[i++] : true;
  var title = tocItem[i++];
  var link = (tocItem[i]==null || typeof(tocItem[i])=="string") ?tocItem[i++] : null;
  if (link != null && link.substring(link.length - 1) == '/') {
    base += link;
    link = (tocItem[i]==null || typeof(tocItem[i])=="string") ? tocItem[i++] : null;
  }
  if (link != null) {
    link = base+link;
  }
  
  var node={
    "visible":visible,
    "title":title, 
      "link":link,
    "children":[]
  };
  parent.children.push(node);

  var childArray = tocItem[i];
  if (typeof(childArray)=="object") {
    for (var j=0; j < childArray.length; j++) {
     convertTocItemToTree(childArray[j], base, node);
    }
  }
}
/** Gets the path to the specified URL.
*
* @param node a toc tree. Each tree node has three properties: title, link, children.
* @param url the url
* @return an array of tree nodes
*/
function getPathToLink(node, url) {
  if (node.link == url) {
    return [node];
  }
  for (var i=0; i < node.children.length; i++) {
    var path = getPathToLink(node.children[i], url);
    if (path.length != 0) {
      path.unshift(node);
      return path;
    }
  }
  return [];
}


function makeUrlRelativeTo(aURL, bURL) {
  var a = aURL.split('/');
  var b = bURL.split('/');
  var relativeURL = '';
  
  var i=0;
  for (; i < a.length - 1 && i < b.length - 1 && a[i]==b[i]; i++) {
    // skip common path elements at the beginning
  }
  for (var j=i; j < a.length - 1; j++) {
    // insert path elements which descend into a
    relativeURL += a[j]+'/';
  }
  for (var j=i; j < b.length - 1; j++) {
    // insert .. elements to ascend from b
    relativeURL = '../'+relativeURL;
  }
  return relativeURL + a[a.length - 1];
}



