
// 
// CLASS:
// - Navigation
// 
// VERSION:
// - 1.0.0 - 20070816 - Core design
// - 1.0.1 - 20070820 - Fix bug in "responseHandle" method that previously only handles Firefox and IE browsers, which now handles IE and all other browsers
// - 1.1.0 - 20070921 - Updated "init" method to handle xml strings and/or xml file paths
// - 1.1.1 - 20070925 - Fixed "parseResponse" to handle <url> tags properly, and use hooks for setting behaviours
// - 1.2.0 - 20071026 - Added parameter to accept Navigation type (horizontal/vertical)
// 
// DEPENDANCIES:
// - Ajax handler
// - Element class
// - XML handler
// 
// NOTES:
// - This navigation is interfaced with XML. An example can be found 
//   below,
//
//     <navi>
//       <item>
//         <caption>Google</caption>
//         <name>google</name>
//         <url>http://google.com</url>
//         <item>
//           <caption>Email</caption>
//           <name>email</name>
//           <url>http://gmail.com</url>
//         </item>
//         <item>
//           <caption>Image</caption>
//           <name>image</name>
//           <url>http://images.google.com</url>
//         </item>
//       </item>
//       <item>
//         <caption>Yahoo</caption>
//         <name>yahoo</name>
//         <url>http://yahoo.com</url>
//       </item>
//     </navi>
// 
// - This navigation system uses "href" an anchor tags for links, it 
//   also sets ID values specified in the <name> tag as well serving 
//   as hooks if an "onclick" event is required for a link
// 

var Navigation = 
{
  // 
  // Constructor method that accepts 2 arguments. The first argument 
  // specifies the HTML element it will work with. The second 
  // argument specifies where the XML data is to build the 
  // navigation. 
  // 
  // The second argument can either be an XML string, or a path to 
  // an XML file.
  // 
  init: function (naviBlock, data, type)
  {
    // Check arguments
    if (typeof type == 'undefined') type = 2;
    
    // Save arguments
    this.naviBlock = naviBlock;
    this.data = data;
    this.type = type;
    
    // Set navi type
    this.setType();
    
    // Get data
    var regex = new RegExp('\.xml$');
    
    if (regex.test(data))
    {
      Ajax.send(data, {onComplete: this.responseHandle, onStatus: this.errorHandle});
    }
    
    else
    {
      this.responseHandle(Xml.string(data));
    }
  },
  
  addEvent: function (li, ul)
  {
    var timer = null;
    
    li.onmouseover = function ()
    {
      clearTimeout(timer);
      Element.removeClass(ul, 'hidden');
    }
    
    li.onmouseout = function ()
    {
      clearTimeout(timer);
      timer = setTimeout( function () { Element.addClass(ul, 'hidden'); }, 250);
    }
    
    ul.onmouseover = function ()
    {
      clearTimeout(timer);
      Element.removeClass(ul, 'hidden');
    }
    
    ul.onmouseout = function ()
    {
      clearTimeout(timer);
      timer = setTimeout( function () { Element.addClass(ul, 'hidden'); }, 250);
    }
  },
  
  errorHandle: function (httpCode)
  {
    if (httpCode != 200 && httpCode != 304)
    {
      // ...
    }
  },
  
  getBlock: function ()
  {
    return Element.getById(this.naviBlock);
  },
  
  parseResponse: function (curXml, parentBlock, level)
  {
    // Arguments
    if (typeof level == 'undefined')
    {
      level = 1;
    }
    
    // Initialize
    var dataA = {tag: 'a'};
    var dataLi = {tag: 'li'};
    var dataUl = {tag: 'ul'};
    
    var caption = '';
    var name = '';
    var url = '';
    
    // Check all item elements on the same level
    for (var i = 0; i < curXml.length; i++)
    {
      if (curXml.length > 0)
      {
        if (curXml[i].nodeType == 1 && curXml[i].nodeName == 'item')
        {
          // Get item data
          caption = curXml[i].childNodes[0].childNodes[0].nodeValue;
          name = curXml[i].childNodes[1].childNodes[0].nodeValue;
          
          if (typeof curXml[i].childNodes[2].childNodes[0] != 'undefined' && curXml[i].childNodes[2].childNodes[0] != null)
          {
            url = curXml[i].childNodes[2].childNodes[0].nodeValue;
            url_type = Element.getAttribute('type', curXml[i].childNodes[2]);
          }
          
          else
          {
            url = '#';
          }
          
          // Create item
          var a = Element.create(dataA);
          var li = Element.create(dataLi);
          
          // Prepare list item
          a.setAttribute('href', url);
          
          if (url_type)
          {
            a.className = url_type;
          }
          
          // Append elements
          a.appendChild(document.createTextNode(caption));
          li.appendChild(a);
          parentBlock.appendChild(li);
          
          // :BUG: IE Style fix for not support > css selector
          if (level == 1) { li.style.clear = 'none'; li.style.width = 'auto'; }
          // :BUG: IE Style fix for not support > css selector
          
          // Check for sub-items
          if (typeof curXml[i].childNodes[3] != 'undefined' && curXml[i].childNodes[3])
          {
            var ul = Element.create(dataUl);
            ul.className = 'subnav';
            parentBlock.appendChild(ul);
            
            // :BUG: Problems with how IE renders the page
            if (navigator.appName == 'Microsoft Internet Explorer' && level > 0) ul.style.width = '170px';
            // :BUG: Problems with how IE renders the page
            
            level++;
            this.parseResponse(curXml[i].childNodes, ul, level);
            level--;
            
            // Cleanup
            this.addEvent(li, ul);
            this.setPosition(ul, li, level);
            Element.addClass(ul, 'hidden');
          }
        }
      }
    }
  },
  
  responseHandle: function (xmlDoc)
  {
    // Initialize
    var dataUl = {tag: 'ul'};
    
    var block = Navigation.getBlock();
    var child = '';
    
    if (block)
    {
      var ul = Element.create(dataUl);
      ul.className = 'mainnav';
      block.appendChild(ul);
      
      child = xmlDoc.childNodes[0].childNodes;
      
      // :BUG: IE7 bug fix for not removing the xml doc type declaration
      if (navigator.appName == 'Microsoft Internet Explorer') {
        if (xmlDoc.childNodes[1] != null) child = xmlDoc.childNodes[1].childNodes;
        else child = xmlDoc.childNodes[0].childNodes;
      }
      // :BUG: IE7 bug fix for not removing the xml doc type declaration
      
      Navigation.parseResponse(child, ul);
    }
  },
  
  setPosition: function (ul, li, level)
  {
    switch (this.type)
    {
      case 1:
        if (level == 1)
        {
          ul.style.top = li.offsetHeight + 'px';
          ul.style.left = li.offsetLeft + 'px';
        }
        
        else if (level > 1)
        {
          Element.addClass(li, 'navarrow');
          
          ul.style.top = li.offsetTop + 'px';
          ul.style.left = li.offsetWidth + 'px';
        }
      break;
      case 2:
        if (level >= 1)
        {
          Element.addClass(li, 'navarrow');
        }
        
        ul.style.top = li.offsetTop + 'px';
        ul.style.left = li.offsetWidth + 'px';
      break;
    }
  },
  
  setType: function ()
  {
    switch (this.type)
    {
      case 1:
        Element.addClass(this.getBlock(), 'horizontal');
      break;
      case 2:
        Element.addClass(this.getBlock(), 'vertical');
      break;
    }
  }
}

