// 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
// mPrimaryPathIds	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, primaryPathIds, begin_id, begin_class, debug )
{
   // Constants
    this.ID_KEY="title";
    var ELEMENT_NODE  = 1; // constant defined for html nodes that are considered elements. (li, ul, b, a, ... )

    // object variables with the default values.
    this.mBeginId = "";
    this.mBeginClass = "";
    this.mDebug = false;
    this.mSelectedNodesList;

    // set the local variables to the values from the ones passed in.
    // required parameters
    this.mNavName = tagname;
    this.mPrimaryPathIds = primaryPathIds;
    this.mSelectedId;
    
    // Parameters used to generate Nav
    this.mStartAt;
    this.mStopAt;
    this.mDisplaySiblingsAt;
    this.mDisplayChildren;
    this.mExpandAll;
    this.mExpandDescendants;
    this.mSelectedNavLevel = 0;
    
    // 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;
    }

    // TODO: Not sure if Debug makes sense anymore. How do I output the dom object as a text node?
    if ( debug != undefined || debug != null )
    {
        this.mDebug = debug;
    }

	/// *** Function definitions ***
	/// *                          *
	/// ***                      ***
	
	// "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.
	// expand_descendants	If true, all descendants of the selected node will display.
	// display_siblings_at	Start displaying siblings at this level. Only shows selected tree.
	// display_children		If false will not display children of the selected node.
	this.setParams = function( start_at, stop_at, expand_descendants, display_siblings_at, display_children, expand_all )
		{
		    this.mStartAt = start_at;
		    this.mStopAt = stop_at;
		    this.mExpandDescendants = expand_descendants;
		    this.mDisplaySiblingsAt = display_siblings_at;
		    this.mDisplayChildren = display_children;
		    this.mExpandAll = expand_all;
		};
		
    
    // Utility method that returns the correct class string for use on the node passed in.
    this.setClassName = function( liNode, isSelected, descending  )
		{
		    var className = this.mNavName;
		    if ( isSelected )
		    {
		        className += "_selected";
		    }
		
		    if ( descending )
		    {
		    	className += "_submenu";
	    	}
			// Set the class attribute for the li node and it's child <a> node
			liNode.setAttribute("class", className );
			liNode.firstChild.setAttribute("class", className );
			// Now do it for IE<8
			liNode.setAttribute("className", className );
			liNode.firstChild.setAttribute("className", className );
			
		};
		
	// Utility method that returns true if this node is selected or if it is a parent of one that is.
    this.isNodeSelected = function( 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.
    this.buildSelectionTree = function()
		{
		    // If we have already built the list just return it.
		    if ( typeof this.mSelectedNodesList != "undefined" )
		    {
		    	return;
		    }
		    
		    // Find the selected nodes.
		    var navULNode = document.getElementById("cssnavtree");
		    var liNodes = navULNode.getElementsByTagName( "LI" );
		    var selectedNode;
		    
		    // We now have the primary path of ID's stored in our mPrimaryPathIds
		    // We need to look through all of them.
		    var primaryPathIds = this.mPrimaryPathIds.split('.');
		    var foundSelected = false;
		    for ( j = 0; j < primaryPathIds.length; j++ )
		    {
			    for ( i = 0; i < liNodes.length; i++ )
			    {
			       var title = liNodes[i].getAttribute( this.ID_KEY );
			       //changed the below if loop condition by rsamudrala for the defect 1-8DPV9I 
			      if ( title == primaryPathIds[j] && !foundSelected)
			       //if (!foundSelected)
			       {
			            selectedNode = liNodes[i];
			            // The rest of the code expects the old behavior of mPrimaryPathIds
			            // being a single Id, so now that we have one. Set it to that.
			            this.mSelectedId = primaryPathIds[j];
			            foundSelected = true;
			            // break; We don't want to break we need to remove the unwanted nodes for all li
			       }
			    }
			    
		     for ( j = 0; j < primaryPathIds.length; j++ )
		     {
		     	    for ( i = 0; i < liNodes.length; i++ )
			    {
			    	if ( !foundSelected)
			    	{
			    		selectedNode = liNodes[i];
					this.mSelectedId = primaryPathIds[j];
					foundSelected = true;
					// break; We don't want to break we need to remove the unwanted nodes for all li
				}
            		    }
		     }
			    
		    };
		
		    this.mSelectedNodesList = new Array();
		    if ( selectedNode != undefined )
		    {
		        parentSelectedNode = selectedNode;
		        while ( parentSelectedNode != undefined )
		        {
		            if ( parentSelectedNode.tagName == "LI" )
		            {
		               this.mSelectedNodesList.push( parentSelectedNode );
		               this.mSelectedNavLevel++;
		            }
		            parentSelectedNode = parentSelectedNode.parentNode;
		        }
		    }
		};
 	
 	
 	// Utility Method used to determine if the nav should descend down the tree from the
	// node passed in.
    this.shouldDescend = function( node, nav_level )
		{
		    var shouldDescend = false;
		
		    // if we are less or equal to our stop level then display
		    if ( nav_level < this.mStopAt )
		    {
		        // If we are using levels to display and NOT expand_all
		        // we need to be selective about which items get expanded.
		        var expand = this.mExpandAll;
		        if ( this.mExpandDescendants && !this.mExpandAll )
		        {
		            // 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 <= this.mStartAt )
		                    {
		                        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 ( this.mDisplayChildren == false || nav_level < this.mStartAt )
		            {
		                var nodeId = node.getAttribute(this.ID_KEY);
		                if ( this.mSelectedId == nodeId )
		                {
		                    shouldDescend = false;
		                }
		            }
		        }
		    }
		    return shouldDescend;
		};
		
	// Copies a list item and any of it's children EXCEPT UL's
	// returns the new list item after removing the title attribute.
	this.copyListItem = function ( oldItem )
	{
		// Clone the li and any other children nodes that aren't UL items
		if ( typeof oldItem != "undefined")
		{ 
			newItem = oldItem.cloneNode(false);
			newItem.removeAttribute("title");
	  		for ( j=0; j<oldItem.childNodes.length; j++)
			{
				var child = oldItem.childNodes[j];
				
				// If it's not a ul add it to our newItem
				if ( child.nodeType == ELEMENT_NODE )
				{
					if ( child.nodeName.toLowerCase().indexOf("ul") == -1 )
					{
						newNode = child.cloneNode(true);
						newItem.appendChild( newNode );
					}
				}
			}
		}
		return newItem;
	};

    // buildNavUL creates the navigation UL from the navigation
	// source already included in the page. This function is only
	// used when we are doing an html rendition.
	// returns a new UL elelment that contains the nav specified by the
	// parameters..
	this.buildNavUL = function( selectedNode )
		{
		    if ( this.mExpandAll == undefined )
		    {
		        this.mExpandAll = false;
		    }
		    
		    if ( this.mExpandDescendants == undefined )
		    {
			    this.mExpandDescendants = false;
		    }
		    
		    // Build the selection tree. This will also find out how deep our selected node
		    // is in the nav structure. 
		    this.buildSelectionTree();
		    
		    // Adjust our start and stop if they have specified relative parameters.
		    if ( this.mStartAt < 1)
		    {
		    	// We are relative so adjust our start and stop levels
		    	this.mStartAt = this.mStartAt + this.mSelectedNavLevel;
		    	if ( this.mStartAt < 1) { this.mStartAt = 1;}
		    	
		    	this.mStopAt = this.mStopAt + this.mSelectedNavLevel;
		    	if ( this.mStopAt < 1 ) {this.mStopAt = 1;}
		    	
		    	this.mDisplaySiblingsAt = this.mDisplaySiblingsAt + this.mSelectedNavLevel;
		    	if ( this.mDisplaySiblingsAt < 1) {this.mDisplaySiblingsAt = 1;}
		    }
			
			var newNavUL = document.createElement("ul");
			newNavUL.setAttribute("id", this.mBeginId );
			newNavUL.setAttribute("class", this.mBeginClass );
			newNavUL.setAttribute("className", this.mBeginClass );
	
			// Get our this.mStartAt selected node and work down to stop at.
			// The list is backwards...
			var startLINode = this.mSelectedNodesList[this.mSelectedNodesList.length - this.mStartAt];
			if ( typeof startLINode != "undefined")
			{
				this.parseNavUL( newNavUL, startLINode.parentNode, this.mStartAt );
			}
	
			return newNavUL;
			
		};

	// parseNavUL is a recursive function that will call itself when a child
	// ul is found that we should be displaying on the page. 
	// newUL is the ul that will be modified to include the desired navigation information.
	// navULNode is the starting point in the nav source. Nav information will be cloned from here into the newUL
	// currentLevel is the current depth in the nav source. Begins at mStartAt.
	this.parseNavUL = function ( newUL, navULNode, currentLevel )
		{
			// Check to see if we need to clone our siblings.
			if ( typeof navULNode != "undefined" )
			{
				var children = navULNode.childNodes;
				var listItemIndex = 0;
				for ( ; listItemIndex < children.length; listItemIndex++ )
				{
					var child = children.item( listItemIndex );
					if ( child.nodeName.toLowerCase().indexOf("li") != -1 ) // only copy LI items
					{
						var isChildSelected = this.isNodeSelected( child );
						if ( this.mDisplaySiblingsAt > currentLevel )
						{
							// We aren't displaying siblings yet so skip this child
							// if it isn't a selected item.
							if ( !isChildSelected )
							{
								// We need to still display the children if we are past our selected nav level
								if ( currentLevel <= this.mSelectedNavLevel )
								{
									continue;
								}
							}
						}
						
						var newLI = this.copyListItem( child );
						var descending = this.shouldDescend( child, currentLevel );
						newUL.appendChild( newLI );
						var hasChildren = false;
						if ( descending )
						{
							// We are going to descend so we need to copy each ul that is contained in this li
							// Will there ever be more than one?
							var grandChildren = child.childNodes;
							var ulItemIndex = 0;
							for ( ; ulItemIndex < grandChildren.length; ulItemIndex++ )
							{
								// if the grandChild is a ul then we are going to clone it.
								var grandChild = grandChildren.item( ulItemIndex );
								if ( grandChild.nodeName.toLowerCase().indexOf("ul") != -1 )
								{
									hasChildren = true;
									var newChildUL = document.createElement("ul");
									newChildUL.setAttribute("class", this.mNavName + "_level" + (currentLevel + 1) );
									newChildUL.setAttribute("className", this.mNavName + "_level" + (currentLevel + 1) );
									var nextLevel = currentLevel + 1;
									this.parseNavUL( newChildUL, grandChild, nextLevel );
									newLI.appendChild( newChildUL );
								}
							}
						}
						this.setClassName(newLI, isChildSelected, hasChildren );
					}
				}
			}
		};

}

// 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()
        // TODO: What if they don't set an ID for this navTag? The tag sets the id to the name of the tag if one isn't specified.
        var navUL = document.getElementById(gSctNavObjects[numNavObj].mBeginId );
        var newUL = gSctNavObjects[numNavObj].buildNavUL();
        if ( typeof newUL != "undefined" )
        {
        	navUL.parentNode.replaceChild( newUL, navUL);
        }
    }
}

