// SctNav Class
// Main object used for interacting with the master Navlist.
// tagname		used to style elements. All elements will have at least tagname in their class
// selected_id	is the objectId of the currently selected page ( the page being rendered)
// begin_id		The string used for the id parameter in the base UL (*Optional)
// begin_class	The string used for the class parameter in the base UL (*Optional)
// debug		Will print out the menu as text instead of html for debug purposes (*Optional)
function SctNav ( tagname, selected_id, container_id, begin_id, begin_class, debug )
{
   // Constants
    this.ID_KEY="title";
    this.END_OF_NODE = "endofnode"
    this.SUB_PLACEHOLDER = "_SCT_SUBMENU";


    // object variables with the default values.
    this.mBeginId = "";
    this.mBeginClass = "";
    this.mDebug = false;
    this.mSelectedNodesList = new Array();
    this.mWriteStack = new Array();

    // set the local variables to the values from the ones passed in.
    // required parameters
    this.mNavName = tagname;
    this.mSelectedId = selected_id;
    this.mContainerId = container_id;
    this.mSelectedFound = false;

    // Parameters used to generate Nav
    this.start_at;
    this.stop_at;
    this.levels_to_display;
    this.display_siblings_at;
    this.display_children;
    this.expand_all;

    // String used to hold the parsed nav data or final output of the nav tag.
    this.mParsedNavData = new String();

    // Assign our object methods. See actual definitions below
    // for usage and parameter info.
    this.write = SCT_Write;
    this.getNodeClass = SCT_getNodeClass;
    this.isNodeSelected = SCT_isNodeSelected;
    this.buildSelectionTree = SCT_buildSelectionTree;
    this.parseNav = SCT_parseNav;
    this.shouldDescend = SCT_shouldDescend;
    this.writeStack = SCT_writeStack;
    this.setParams = SCT_setParams;
    this.getNavData = SCT_getNavData;

    // If they pass in the beginId then they must pass in the begin_class as well.
    // optional parameters
    if ( begin_id != undefined || begin_id != null )
    {
        this.mBeginId = begin_id;
        this.mBeginClass = begin_class;
    }

    if ( debug != undefined || debug != null )
    {
        this.mDebug = debug;
    }
}

//  "public" function used to set the parameters that should be used during the parsing of the navigation
// Sets the nav info to use while parsing the "master_nav" tree.
// start_at				The level to start this nav object at.
// stop_at				The level to stop displaying the nav.
// levels_to_display	Display all levels (even unselected ones) up to this level. Starts at 1 and is not tied to start_at. Overrides stop_at
// display_siblings_at	Start displaying siblings at this level. Overrides levels_to_display and only shows selected tree.
// display_children		If false will not display children of the selected node.
function SCT_setParams( start_at, stop_at, levels_to_display, display_siblings_at, display_children, expand_all )
{
    this.start_at = start_at;
    this.stop_at = stop_at;
    this.levels_to_display = levels_to_display;
    this.display_siblings_at = display_siblings_at;
    this.display_children = display_children;
    this.expand_all = expand_all;
}


// Parses the navigation and generates the desired section of the nav based on the
// parameters passed in setPrams. Also retuns the result
function SCT_parseNav( )
{
    var nodes = [];
    var nav_level = 1;
    var usingLTD = false;
    if ( this.expand_all == undefined )
    {
        this.expand_all = false;
    }

    //build the selection tree. We could just do this once for the page
    this.buildSelectionTree();
    if ( !this.mSelectedFound )
    {
        this.mSelectedId = this.mContainerId
        // the current page is not in the navigation, so we need to pretend
        // the webview that contains the nav is the currently selected node.
        this.buildSelectionTree()
    }

    // Figure out what level we are going to stop at.
    // levels_to_display overrides stop_at and expands all items down to that level.
    if ( this.levels_to_display != null )
    {
        this.stop_at = this.levels_to_display;
        usingLTD = true;
    }

    // if you start this iteration from cssnavtree instead of from
    // firstchild, you'll have an infinite loop, because we're adding siblings...
    var node = document.getElementById( 'cssnavtree' ).firstChild;

    while ( node != undefined )
    {

      if ( node == this.END_OF_NODE )
      {
        nav_level--;

        this.writeStack( "" );
        // We don't want any extra </ul>'s if we don't start at 1
        if ( nav_level >= this.start_at )
        {
            this.write("<\/li><\/ul>" );
        }
      }
      else if ( node.tagName == 'UL' )
      {
         nav_level++;
        // Make sure we should descend into this level of the nav
        if ( this.shouldDescend( node.parentNode, nav_level, this.start_at, this.stop_at, usingLTD, this.display_children, this.expand_all ))
        {
            // If we are not starting at 1 we don't want to write extra ul's
            if ( nav_level > this.start_at )
            {
                this.writeStack( "_submenu" );
                // If we don't start at 1 adjust the level back to 2 for css style issues. We want it to still
                // be written out in sequence starting at 1
                this.write("<ul class='"+ this.mNavName + "_level" + ( nav_level - this.start_at + 1) + "'>");
            }

            if ( node.nextSibling )
            {
                nodes.push( node.nextSibling )
            }

            nodes.push( this.END_OF_NODE )

            if ( node.firstChild )
            {
                nodes.push( node.firstChild )
            }
        }
        else // if we didn't push the child node then we need to decrement the level
        {
            this.writeStack( "" );
            nav_level--;
        }
      }
      else if ( nav_level < this.start_at && nav_level <= this.stop_at ) // make sure we are before our start_at
      {
          // Go to the next node...
          if ( node.nextSibling )
          {
              nodes.push( node.nextSibling )
          }
          if ( node.firstChild )
          {
              nodes.push( node.firstChild )
          }
      }
      else if ( node.tagName == 'LI' )
      {
        this.writeStack( "" );

        var displayNode = (this.isNodeSelected ( node ) || nav_level >= this.display_siblings_at);

        // If our parrent UL wasn't descended into we don't want to display this LI if it has children,
        if (displayNode)
        {
            // An inactive item would not have any attributes on the <A> tag                        
            var nodeHTML = node.innerHTML.toLowerCase();
            var aIndex = nodeHTML.indexOf("<a>");
            if ( aIndex > -1 && aIndex <= 3 ) // Starts with <a>
            {
                displayNode = false;
            }   

        }
        // Now reset the not descending flag because the next LI won't
        // care about it, only the first LI after a UL that doesn't get descended into.
        this.mNotDescending = false;

        if ( displayNode )
        {
            if ( (node.previousSibling != null && node.previousSibling.tagName == "LI") )
            {
                this.mWriteStack.push("<\/li>");
            }
            var className = this.getNodeClass( node );
            this.mWriteStack.push("<li class='" +  className + "'>" );
            if ( node.nextSibling )
            {
                nodes.push( node.nextSibling )
            }
            if ( node.firstChild )
            {
                nodes.push( node.firstChild )
            }
        }
        else
        {
            if ( node.nextSibling )
            {
                nodes.push( node.nextSibling )
            }
        }
      }
      else if ( node.tagName == 'A' )
      {
        var className = this.getNodeClass( node.parentNode );

        var nodeString = "<a class='" + className;
        if ( node.href != "" )
        {
            var nodeString = nodeString + "' href='" + node.href + "'>" +  node.innerHTML + "<\/a>";
        }
        else
        {
             // we have an item that should have an href. Just put out the text.
           nodeString= nodeString + "'>" + node.innerHTML + "<\/a>";

        }

        if ( node.nextSibling )
        {
            nodes.push( node.nextSibling )
        }

        this.mWriteStack.push( nodeString );
      }
      else if ( node.tagName == undefined || node.nodeType == node.TEXT_NODE )
      {
        // skip text nodes in nav tree, especially empty ones,
        // but get the next sibling for the real node that follows
        if ( node.nextSibling )
        {
            nodes.push( node.nextSibling )
        }
      }
      else
      {
        this.write( "Unknown type: " + node.tagName )
        if ( node.nextSibling )
        {
            nodes.push( node.nextSibling )
        }
      }
      node = nodes.pop()
    }
    this.writeStack( "" );

    return this.mParsedNavData;
}

// Returns the parsed data once the work has been done.
// Useful if you want to output the Navdata again without needing to
// do the parsing again.
function SCT_getNavData()
{
    return this.mParsedNavData;
}

// Writes the string as html unless mDebug is true, then it will
// write str as text.
function SCT_Write( str )
{
    if ( this.mDebug )
    {
        var rexp = new RegExp("<", "g");
        var str = str.replace(rexp, "&lt;")
    }
    this.mParsedNavData = this.mParsedNavData.concat(str);
}

// Utility method that returns the correct class string for use on the node passed in.
function SCT_getNodeClass( node )
{
    var nodeName = this.mNavName;

    if ( this.isNodeSelected( node ) )
    {
        nodeName += "_selected";
    }

    // Add a placeholder for the submenu if it does exist for this node.
    // It will either get replaced or removed depending on this nodes
    // children.
    nodeName += this.SUB_PLACEHOLDER;

    // Now combine the mNavName with the nodeName for our final class declaration
    return nodeName;
}

// Utility method that returns true if this node is selected or if it is a parent of one that is.
function SCT_isNodeSelected( node )
{
    var selected = false;
    if ( node != null)
    {
        for ( i = 0; i < this.mSelectedNodesList.length; i++ )
        {
            if ( node == this.mSelectedNodesList[i] )
            {
                selected = true;
                break;
            }
        }
    }

    return selected;
}

// Utility method that builds an array of nodes that are selected.
function SCT_buildSelectionTree()
{
    // Find the selected nodes.
    var liNodes = document.getElementsByTagName( "LI" );
    var selectedNode;
    for ( i = 0; i < liNodes.length; i++ )
    {
       var title = liNodes[i].getAttribute( this.ID_KEY );
       if ( title == this.mSelectedId )
       {
            selectedNode = liNodes[i];
            // break; We don't want to break we need to remove the unwanted nodes for all li
            this.mSelectedFound = true;
       }

       // Remove unwanted nodes that are end of lines and white space
       if ( liNodes[i].previousSibling && (liNodes[i].previousSibling.tagName == undefined || liNodes[i].previousSibling.tagName == liNodes[i].TEXT_NODE))
       {
           liNodes[i].parentNode.removeChild( liNodes[i].previousSibling );
       }
    }

    this.mSelectedNodesList = new Array();
    if ( selectedNode != undefined )
    {
        parentSelectedNode = selectedNode;
        while ( parentSelectedNode != undefined )
        {
            if ( parentSelectedNode.tagName == "LI" )
            {
               this.mSelectedNodesList.push( parentSelectedNode );
            }
            parentSelectedNode = parentSelectedNode.parentNode;
        }
    }
}

// Utility Method used to determine if the nav should descend down the tree from the
// node passed in.
function SCT_shouldDescend( node, nav_level, start_at, stop_at, usingLTD, display_children, expand_all )
{
    var shouldDescend = false;

    // if we are less or equal to our stop level then display
    if ( nav_level <= stop_at )
    {
        // If we are using levels to display and NOT expand_all
        // we need to be selective about which items get expanded.
        var expand = expand_all;
        if ( usingLTD && !expand_all )
        {
            // Levels to display is being used so we need to only
            // descend into UL's that have a parent that is selected.
            var pNode = node;
            var currentLevel = nav_level;
            while ( pNode != undefined )
            {
                if ( this.isNodeSelected( pNode ))
                {
                    expand = true;
                    break;
                }
                else
                {
                    if ( currentLevel <= start_at )
                    {
                        break;
                    }
                    // skip up two levels to get to the next <LI> the immediate parent is a <UL>
                    pNode = pNode.parentNode.parentNode;
                    currentLevel--;
                }
            }
        }

        // If we are expanding our level instead of just selected levels || this level is selected
        if ( expand || this.isNodeSelected(node) )
        {
            shouldDescend = true;
            // If we are not displaying children check the selectedId against our Key
            // if it is the end of the selection branch we don't display children
            if ( display_children == false)
            {
                var nodeId = node.getAttribute(this.ID_KEY);
                if ( this.mSelectedId == nodeId )
                {
                    shouldDescend = false;
                }
            }
        }
    }
    return shouldDescend;
}

// Utility method that writes out the items in the mWriteStack
function SCT_writeStack( subMenuText )
{
    if ( this.mWriteStack.length > 0 )
    {

        for ( i = 0; i < this.mWriteStack.length; i++ )
        {
            // Replace the SUBMENU placeholder
            var text = this.mWriteStack[i];
            text = text.replace( this.SUB_PLACEHOLDER, subMenuText );

            this.write( text );
        }
        // Empty the array once we are done writting it.
        this.mWriteStack = new Array();
    }
}

var gRequest;
function SCT_requestCallback()
{
    // ReadyState 4 is the request is complete
    // staus=200 is standard for no errors. 404 if not found etc.
    if (gRequest.readyState==4 && gRequest.status==200)
    {
        // Get the div that is to hold the master navlist
        navDiv = document.getElementById( 'sctNavSource' )

        navDiv.innerHTML = gRequest.responseText;
        parseAllNavObjects()

        // This is specific code for Nottingham. If the UDM (www.udm4.com) um variable
        // exists we need to call refresh to refresh the udm menus on the page. If we end up
        // with another like this we need to implement a way to register a callback.
        if(typeof(um) != "undefined"){ um.refresh(true);}
    }
}

// Parses all of the nav objects on the page. Called from the callback method
// as well as during edit mode wich doesn't use the AJAX way of including the data.
function parseAllNavObjects()
{
    for ( numNavObj = 0; numNavObj < gSctNavObjects.length; numNavObj++ )
    {
        // Grab the div for this current navObject based on its id field that is set to the prefix + the tag name and set
        // the innerHTML to the result of parseNav()
        document.getElementById(gSctNavObjects[numNavObj].mBeginId ).innerHTML = gSctNavObjects[numNavObj].parseNav();
    }
}

function requestNavFile(url)
{

    if ( window.XMLHttpRequest )
    {
        gRequest = new XMLHttpRequest();
    }
    else if( window.ActiveXObject)
    {
        gRequest = new ActiveXObject("Microsoft.XMLHTTP");
    }

    gRequest.onreadystatechange=SCT_requestCallback;

    gRequest.open("GET", url, true);
    gRequest.send(null);
}

