function AEnavigation(pAEid, pAE_HTML_Node, pAE_ParamList, pAE_Parent)
{
/*
This is the Navigation Active Element, which is responsible for generating almost every hyperlink which appears
in an AE enabled website.  This will generate a comprehensive range of linkbars, which will in most cases
result in screen updates without a full HTTP page refresh.

The website navigation structure is requested from the sever in Navigation.xml, which is read once and then
stored at the browser, making for fast updates to linkbars when the screen changes.
*/
    
    D.debugStartFunction("AEnavigation", arguments);
    
    this.superclass = activeElement;
    this.superclass(pAEid, "AEnavigation", "", pAE_HTML_Node, pAE_ParamList, pAE_Parent);
    D.debug("About to call aeGetAlgorithmObject from AE, id = " + this.aeGetAEid());
    this.AE_NavData = this.aeGetAlgorithmObject("Navigation");
    
    this.aeInitialise = function() // Overloads master aeInitialise function
    {
        /*
        Overrides aeInitialise from master activeElement class.
        
        Called after new AE has been created.  Populates HTML nodes with startup text and
        performs any other initialisation for this AE.
        
        Check status of navigation object, and only send request if not already sent.
        */
        
        D.debugStartFunction("AEnavigation:aeInitialise", arguments);

        var Header = new Array;
        var DataRequest;
        var RequestXML;
        var NavigationStatus;
        
        this.aeSetStatus("RequestingData");
        NavigationStatus = this.aeGetAlgorithmStatus("Navigation");
        
        D.debug("NavigationStatus = " + NavigationStatus);
        
        if (NavigationStatus && NavigationStatus == "Initialised")
        {
            D.debug("Sending Request");
            
            Header['msgseqnum']   = "01" // this.aeGetMsgSeqNum();
            Header['requesttype'] = 'datarequest';
            Header['loggingflag'] = 'true';
            Header['aetype']      = this.aeGetAEtype();
            Header['aeid']        = this.aeGetAEid();
    
            RequestXML = this.aeCreateRequestHeaderXML(Header);
            RequestXML += this.aeCreateDataRequestXML('NavXML');
            
            this.aeSetAlgorithmStatus("Navigation", "RequestingData");
            
            DataRequest = this.aeXMLnodeCreate('activeelementrequest', RequestXML);
            
            this.aeSendRequest(DataRequest);
        }
    
        // Propogate to any controlled AEs under this one
        
        for (var i=0; i<this.AE_controlledAE_Count; i++)
        {
            this.AE_controlledAE_List[i].aeInitialise();
        }
        
        D.debugEndFunction("aeInitialise");
    }
    
    this.aeScreenChange = function(pScreen)
    {
        D.debugStartFunction("AEnavigation.aeScreenChange", arguments);
        
        this.aeUpdateContent();
        // Cascade to all child AEs
        
        for (var i=0; i<this.AE_controlledAE_Count; i++)
        {
            D.debug("Cascading to AEid " + this.AE_controlledAE_List[i].AE_AEid);
            this.AE_controlledAE_List[i].aeScreenChange(pScreen);
        }
        D.debugEndFunction();
    }
    
    this.aeGetNavStatus = function()
    {
        if (!this.AE_NavData.NavStatus)
        {
            return false;
        }
        else
        {
            return this.AE_NavData.NavStatus;
        }
    }
    
    this.aeSetNavStatus = function(pStatus)
    {
        if (!this.AE_NavData.NavStatus)
        {
            return false;
        }
        else
        {
            return this.AE_NavData.NavStatus;
        }
    }
    
    this.aeUpdateContent = function()
    {
        /*
        This function creates a link bar according to the provided parameters. At the moment this only
        creates a link of children of the current screen.
        */
        
        D.debugStartFunction("AEnavigation:aeUpdateContent", arguments);
        
        var navLinkParameters = new Array;
        var screenInfoList = new Array;
        var HTML;
        var curScreen = this.aeGetScreenName();
        var emptyList = false;
        var status = true;
            
        if (this.aeGetAlgorithmStatus("Navigation") == "DataLoaded") // May need more sophisticated test here
        {
            // Firstly update title so browser window title is set correctly.
            
            document.title = this.AE_NavData.NavScreenDetails[this.aeGetScreenName()]['screenTitleLong'];
            navLinkParameters = this.aeParseNavParameters(this.AE_paramList);
            
            if (navLinkParameters['pageHeader'])
            {
                HTML = this.AE_NavData.NavScreenDetails[curScreen]['screenTitleLong'];
            }
            else
            {
                var List = this.AE_NavData.NavCreateInitialList(navLinkParameters, curScreen);
                if (false === List) status = false;
                if (!false === status) status = this.AE_NavData.NavModifyList(navLinkParameters, List, curScreen);
                if (!false === status) status = this.AE_NavData.NavAddClassInfo(navLinkParameters, List, curScreen);
                if (!false === status) status = this.AE_NavData.NavCalculateListHrefs(List, curScreen);
                if (false === status || List.length == 0)
                {
                    HTML = this.AE_NavData.NavAssembleHTMLLinkbar(false); // Calculate empty linkbar
                }
                else
                {
                    HTML = this.AE_NavData.NavAssembleHTMLLinkbar(List);
                }
            }
                D.debug("HTML = " + HTML);
                this.aeUpdateHTML_Nodes(HTML);
        }
        D.debugEndFunction();
    }
    
    this.aeParseNavParameters = function(pParamString)
    {
    /* ==============================================================================================================
    This function generates a linkbar according to a number of parameters encoded within the ID of the active
    element declaration within the HTML page or template associated with the current screen.

    The parameters are passed in a string extracted from the invocation of the AE within the page template.
    
    The form of the parameters list is as follows:

        "B(baseScreenSpecifier)-L(screenListSpecifier)-M(screenListModifiers)-C(classSpecifiers)"
        
    screenListModifiers and classSpecifiers can contain multiple entries, which are separated by "+"

    Each of the components of the string is described below:
    
    baseScreenSpecifier (optional, current screen used if omitted) :
    
        - "Bthis"            : Current page
        - "Bhome"            : Home page
        - "Bparent"          : Parent to current page
        - "BfirstChild"      : First child of current page
        - "BlastChild"       : Last child of current page
        - "BprevSibling"     : Previous sibling of current page
        - "BnextSibling      : Next sibling of current page
        - "BpreviousVisited" : Last page visited by user
        - "BnextPageVisited" : Next page visited by user (only valid if user has gone back at least one page)
    
   screenListSpecifier (optional, base screen alone used if omitted) :
   
        - "Lbase"                : The baseScreen itself (in general not the same page as in baseScreen parameter)
        - "Lhome"                : Home page
        - "Lparent"              : Parent to baseScreen
        - "LfirstChild"          : First child of baseScreen
        - "LlastChild"           : Last child of baseScreen
        - "LprevSibling"         : Previous sibling of baseScreen
        - "LSnextSibling"        : Next sibling of baseScreen
        - "Lsiblings"            : All siblings of baseScreen
        - "Lchildren"            : All children of baseScreen
        - "LchildrenFirstChild"  : All first children of children of baseScreen (but with text from children screens)
        - "Lregions"             : All region home pages (value of baseScreen is ignored for this)
        - "LsiteMap#"            : Site map to # levels from base screen. "smp0" implies full site map
        
    screenListModifiers (optional, none if omitted) :
    
        - "MprevNext"         : Add prevSibling and nextSibling (of base) at beginning and end of linkbar
        - "MfirstLast"        : Add firstSibling and lastSibling (of base) at beginning and end of linkbar
        - "Mhome"             : Add home page to beginning of linkbar
        - "Mup"               : Add parent (up) (of current screen not base) to beginning of linkbar
        - "MbackForward"      : Add previousVisitedPage at beginning and end of linkbar
        
   ClassSpecifier (optional, none if omitted):
   
        - "Cfirst"           : Place first link in list in class of "first"
        - "Clast"            : Place last link in list in class of "last"
        - "Calternate"       : Place odd links in class of "odd", even in "even"
        - "Cthis"            : Place current screen in class of "this"
    ============================================================================================================== */

    // Firstly, parse parameters to get four parts.
    
        D.debugStartFunction("aeParseNavParameters", arguments);

        var i, j;
        var paramArray = pParamString.split("-");
        var modifierArray, classArray;
        var paramData = new Array;
        var firstChar, parseString, validString;
        var base, listSpec, listMod, classSpec;
        
        // Validation arrays
        
        var validBase = new Array("this", "home", "parent", "firstChild", "lastChild", "prevSibling", "nextSibling", "previousVisited", "nextVisited");
        var validList = new Array("base", "home", "parent", "firstChild", "lastChild", "prevSibling", "nextSibling", "children", "siblings", "fchildOfChildren", "regions", "siteMap");
        var validMod = new Array("prevNext", "firstLast", "home", "up", "backForward");
        var validClass = new Array("first", "last", "alternate", "this");
        
        paramData['baseScreen'] = "this"; // Default value of current screen, overridden if specifier used
        paramData['screenListModifiers'] = new Array;
        paramData['classSpecifiers'] = new Array;
        
        if (pParamString == "")
        {
            paramData['baseScreen'] = "this";
            paramData['screenListSpecifier'] = "base";
        }
        else
        {
            D.debug("paramArray = " + dump(paramArray));
            for (i=0; i<paramArray.length; i++)
            {
                parseArray = paramArray[i].match("([HBLMC])([A-Za-z]*)");
                D.debug("parseArray = " + dump(parseArray));
                if (!parseArray)
                {
                    D.debug("Unrecognised specifier in : " + paramArray[i]);
                }
                else
                {
                    firstChar = parseArray[1];
                    parseString = parseArray[2];
                }
                                
                D.debug("firstChar = " + firstChar + ", parseString = " + parseString);
                
                switch(firstChar)
                {
                    case "H" :
                        // Page Header type, just set flag
                        
                        D.debug("This is a page header, setting flag...");
                        paramData['pageHeader'] = true;
                        D.debug("Flag value paramData['pageHeader'] = " + paramData['pageHeader']);
                    break;
                
                    case "B" :
                        if (!inArray(parseString, validBase))
                        {
                            D.warn("Can't match base specifer : " + parseString);
                            // Not valid string - do something!
                        }
                        else
                        {
                            D.debug("Matched base specifer " + parseString);
                            paramData['baseScreen'] = parseString;
                        }
                    break;
                
                    case "L" :
                        if (!inArray(parseString, validList))
                        {
                            D.warn("Can't match list specifer : " + parseString);
                            // Not valid string - do something!
                        }
                        else
                        {
                            D.debug("Matched list specifer " + parseString);
                            paramData['screenListSpecifier'] = parseString;
                        }
                    break;
                
                    case "M" : // Needs to work for a number of modifiers separated by '+'
                        modifierArray = parseString.split("+");
                        D.debug("No. list modifiers = " + modifierArray.length);
                        for (j=0; j<modifierArray.length; j++)
                        {
                            if (!inArray(modifierArray[j], validMod))
                            {
                                D.warn("Can't match list modifier : " + parseString);
                                // Not valid string - do something!
                            }
                            else
                            {
                                D.debug("Matched list modifier " + modifierArray[j]);
                                paramData['screenListModifiers'][modifierArray[j]] = true;
                            }
                        }
                    break;
                
                    case "C" : // Needs to work for a number of specifiers separated by '+'
                        classArray = parseString.split("+");
                        D.debug("No. class specifiers = " + classArray.length);
                        for (j=0; j<classArray.length; j++)
                        {
                            if (!inArray(classArray[j], validClass))
                            {
                                D.warn("Can't match list modifier : " + parseString);
                                // Not valid string - do something!
                            }
                            else
                            {
                                D.debug("Matched class specifier " + classArray[j]);
                                paramData['classSpecifiers'][classArray[j]] = true;
                            }
                        }
                    break;
                
                    default:
                        D.debug("Specifier not matched");
                    break;
                }
            }
        }
        D.debugEndFunction();
        return paramData;
    }
    
    this.aeCreateDataRequestXML = function(pRequestType)
    {
        var XML;
        
        XML = this.aeXMLnodeCreate('datarequesttype', pRequestType);
        XML = this.aeXMLnodeCreate('datarequest', XML);
        
        return XML;
    }
    
    this.aeProcessResponseData = function(pXML)
    {
        /*
        This function overrides that of the parent class.
         
        This function processes the XMLnextSibling data received from the server which contains the website structure.
        To begin with, just populate this manually.
        */
        
        D.debugStartFunction("aeProcessResponseData", arguments);

        // (IBH) find out what we're dealing with here
        D.debug("pXML = " + pXML);
        D.debug("pXML.nodeName = " + pXML.nodeName);
        D.debug("pXML.firstChild.nextSibling.nodeName = " + pXML.firstChild.nextSibling.nodeName);

        siteData = pXML.getElementsByTagName("siteDetails");
        D.debug("siteData.length = " + siteData.length);
        var siteDataArray = new Array;
        siteDataArray['siteName'] = siteData[0].getElementsByTagName("siteName")[0].firstChild.nodeValue;
        siteDataArray['siteHomePage'] = siteData[0].getElementsByTagName("siteHomePage")[0].firstChild.nodeValue;
        siteDataArray['siteCategories'] = (siteData[0].getElementsByTagName("siteCategories")[0].firstChild) ? siteDataArray['siteCategories'] = siteData[0].getElementsByTagName("siteCategories")[0].firstChild.nodeValue : "";
        siteDataArray['siteRegions'] = (siteData[0].getElementsByTagName("siteRegions")[0].firstChild) ? siteDataArray['siteRegions'] = siteData[0].getElementsByTagName("siteRegions")[0].firstChild.nodeValue : "";
        
        // put this in an associative array
        this.AE_NavData.AddWebsiteDetails( siteDataArray );
        
        screenData = pXML.getElementsByTagName("screen");
        // this gives us an array of screen data
        
        D.debug("There are " + screenData.length + " screens");
        for (i=0; i<screenData.length; i++)
        {
            this.AE_NavData.NavAddScreen({  Name: screenData[i].getElementsByTagName("screenName")[0].firstChild.nodeValue,
                                            Details:
                                              {screenTitleLong: screenData[i].getElementsByTagName("screenTitleLong")[0].firstChild.nodeValue,
                                               screenTitleShort: screenData[i].getElementsByTagName("screenTitleShort")[0].firstChild.nodeValue,
                                               screenTemplate: screenData[i].getElementsByTagName("screenTemplate")[0].firstChild.nodeValue,
                                               screenParent: (screenData[i].getElementsByTagName("screenParent")[0].firstChild) ? screenData[i].getElementsByTagName("screenParent")[0].firstChild.nodeValue : "",
                                               screenRegion: (screenData[i].getElementsByTagName("screenRegion")[0].firstChild) ? screenData[i].getElementsByTagName("screenRegion")[0].firstChild.nodeValue : "",
                                               screenCategories: (screenData[i].getElementsByTagName("screenCategories")[0].firstChild) ? screenData[i].getElementsByTagName("screenCategories")[0].firstChild.nodeValue : ""
                                              }
                                            });
        }
        this.aeSetStatus("DataLoaded");
        
        // Need to update content for everything in case other AEs waiting for the file to be loaded
        this.aeSetAlgorithmStatus("Navigation", "DataLoaded");
        this.aeGetMasterAE().aeScreenChange(this.aeGetScreenName());
        D.debugEndFunction();        
    }
    this.aeInitialise();
    this.aeUpdateContent();
    
    D.debugEndFunction("AEtext");
}