function Navigation()
{
/*
This is the Navigation class, which is updated and accessed through the AEnavigation Active Element.
At the moment the Navigation class is defined in the same file as the Navigation AE but as a separate
entity.
*/

    this.NavSiteDetails = new Array;
    this.NavScreenDetails = new Array;
    this.NavStatus = "begin";
    this.NavCurrentScreen = 'none';
    this.NavPreviousScreen = 'none';

    D.debug("Starting Navigation Constructor");

    this.AddWebsiteDetails = function(siteDetails)
    {
        D.debugStartFunction("AddWebsiteDetails", arguments);
        
        this.NavSiteDetails['siteName']       = siteDetails['siteName'];
        this.NavSiteDetails['siteHomePage']   = siteDetails['siteHomePage'];   // Screen name of home page
        this.NavSiteDetails['siteCategories'] = siteDetails['siteCategories']; // Probably an array
        this.NavSiteDetails['siteRegions']    = siteDetails['siteRegions'];    // Probably an array
        this.NavSiteDetails['rootDir']        = siteDetails['rootDir'];
        this.NavSiteDetails['dirStructure']   = siteDetails['dirStructure'];   // Array of main directories in website
        
        D.debugEndFunction();
    }
    
    this.NavAddScreen = function(screenDetails)
    {
        D.debugStartFunction("AddScreen", arguments);

        var newScreen = screenDetails['Name'];
        
        D.debug("Screen = " + newScreen);
        
        this.NavScreenDetails[newScreen] = new Array;
        
        this.NavScreenDetails[newScreen]['screenTitleLong']  = screenDetails['Details']['screenTitleLong'];
        this.NavScreenDetails[newScreen]['screenTitleShort'] = screenDetails['Details']['screenTitleShort'];
        this.NavScreenDetails[newScreen]['screenTemplate']   = screenDetails['Details']['screenTemplate'];
        this.NavScreenDetails[newScreen]['screenParent']     = screenDetails['Details']['screenParent'];
        this.NavScreenDetails[newScreen]['screenRegion']     = screenDetails['Details']['screenRegion'];
        this.NavScreenDetails[newScreen]['screenCategories'] = screenDetails['Details']['screenCategories'];
        
        D.debug("Screen Details : " + dump(this.NavScreenDetails[newScreen]));
        D.debugEndFunction();
    }
    
    this.NavGetHomePage = function()
    {
        return this.NavSiteDetails['siteHomePage'];
    }

    this.NavGetNumScreens = function()
    {
        return this.NavScreenDetails.length;
    }

    this.NavCreateLinkData = function(pLinkArray, pBaseScreen)
    {
        /*
        Takes an array of screen names and a base screen name and returns an
        array containing data which can be used to create an HTML linkbar, which is:
        - HREF
        - text
        - (others - may need target, title etc but not straight away)
         
        This function does NOT create the final HTML linkbar, as we need the
        flexibility to alter information before generating HTML (e.g. we may
        not wish to use the standard title for a page but use HOME, Previous, Next
        etc.
         
        linkArray is an array of screen names 
        baseScreen : is the screen from which HREFs are to be calculated (not always current screen!)
        */
        
        D.debugStartFunction("NavCreateLinkData", arguments);
        
        var i; // Using numerical indexes as we need to retain the order
        var linkData = new Array;
        
        for (i=0; i<pLinkArray.length; i++)
        {
            linkData[i] = new Array;
            linkData[i]['href'] = this.NavCreateHREF(pBaseScreen, pLinkArray[i]);
            linkData[i]['text'] = this.NavScreenDetails[pLinkArray[i]]['screenTitleShort'];
        }
        D.debugEndFunction();
        return linkData;
    }
    
    this.NavAddScreenInfo = function(pScreenName)
    {
        // Creates and returns entries for all information which might be required about a screen
        // in order to include it in a link bar.
        
        var element = new Array;
        
        element['screenName'] = pScreenName;
        element['text'] = "";
        element['class'] = new Array;
        element['href'] = "";
    }

    this.NavAssembleHTMLLinkbar = function(pLinkData)
    {
        D.debugStartFunction("NavAssembleHTMLLinkbar", arguments);
        
        var HTMLclass = "";
        var separator = "";
        var i, j;
        var HTML = "<ul>";
        var href = "";
        
        if (false === pLinkData)
        {
            // false so no data, just create well formed but empty linked list
            
            HTML = "<ul class=\"empty\"></ul>";
        }
        else
        {        
            for (i=0; i<pLinkData.length; i++)
            {
                HTMLclass = "";
                separator = "";
                if (pLinkData[i]['class'])
                {
                    for (j=0; j<pLinkData[i]['class'].length; j++)
                    {
                        HTMLclass += separator + pLinkData[i]['class'][j];
                        seperator = " ";
                    }
                    if (HTMLclass != "")
                    {
                        HTMLclass = " class=\"" + HTMLclass + "\"";
                    }
                }
                
                href = pLinkData[i]['href'] == "" ? "" : " href=\"" + pLinkData[i]['href'] + "\"";
                
                HTML +=  "<li" + HTMLclass + ">" +
                        "<a" + href +
                        " title=\"" + pLinkData[i]['text'] + "\">" +
                        pLinkData[i]['text'] +
                        "</a>" +
                        "</li>";
            }
            HTML += "</ul>";
            D.debugEndFunction();
        }
        return HTML;
    }
    
    this.NavCreateHref = function(pBaseScreen, pTargetScreen, pForceFlag)
    {
        /*
        This function calculates the href from BaseScreen to TargetScreen.
        This will normally work out whether the two screens use the same template
        in which case the href makes a call to update all AEs on that page, or if
        the templates are different, the page will reload and all AEs will be re-generated.
        
        However, sometimes we will want to force the link to be a full re-load, and
        this is indicated by the pForce flag.
        */
        
        D.debugStartFunction("NavCreateHref", arguments);
        
        var href;
        var baseTemplate;
        var targetTemplate;
        
        baseTemplate = this.NavScreenDetails[pBaseScreen]['screenTemplate'];
        targetTemplate = this.NavScreenDetails[pTargetScreen]['screenTemplate'];
        
        if (!pForceFlag && baseTemplate == targetTemplate)
        {
            href = "javascript:pageAE.aeScreenChange(" + "'" + pTargetScreen + "')";
        }
        else
        {
            href = "./" + targetTemplate + "?screen=" + "pTargetScreen";
        }
        D.debugEndFunction();
        return href;
    }
    
    this.NavCalculateListHrefs = function(pList, pCurScreen)
    {
        var i;
        var status = true;
       
        D.debugStartFunction("NavCalculateListHrefs", arguments);
        
        for (i=0; i<pList.length; i++)
        {
            if ("" != pList[i]['screenName'])
            {
                pList[i]['href'] = this.NavCreateHref(pCurScreen, pList[i]['screenName']);
            }
            else
            {
                pList[i]['href'] = "";
            }
        }
        
        D.debugEndFunction();
        return status;
    }

    this.NavCalculateBaseScreen = function(pBaseSpecifier, pCurScreen)
    {
        var baseScreen = pCurScreen;
        
        switch (pBaseSpecifier)
        {
            case "home" :
                baseScreen = this.NavGetHomePage();
            break;
        
            case "this" :
                baseScreen = pCurScreen;
            break;
        
            case "parent" :
                baseScreen = this.NavGetParent(pCurScreen);
            break;
        
            case "firstChild" :
                baseScreen = this.NavGetFirstChild(pCurScreen);
            break;
        
            case "lastChild" :
                baseScreen = this.NavGetLastChild(pCurScreen);
            break;
        
            case "prevSibling" :
                baseScreen = this.NavGetPrevSibling(pCurScreen);
            break;
        
            case "nextSibling" :
                baseScreen = pCurScreen;
            break;
        
            case "previousVisited" :
                baseScreen = pCurScreen;
            break;
        
            case "nextVisited" :
                baseScreen = pCurScreen;
            break;
        
            default:
                D.warn("base Specifer " + pBaseSpecifier + " not recognised");
                baseScreen = false;
            break;
        }
        return baseScreen;
    }

    this.NavCreateInitialList = function(pParamData, pCurScreen)
    {
        /*
        This function will create the list of screen names which will form the basis of the
        link bar, using the parameters to work out which screens to 
        */

        D.debugStartFunction("NavCreateInitialList", arguments);
        
        var initialList = new Array;
        var baseSpec = pParamData['baseScreen'];
        var baseScreen = this.NavCalculateBaseScreen(baseSpec, pCurScreen);
        
        D.debug("baseSpec = " + baseSpec);
        D.debug("baseScreen = " + baseScreen);
        
        if (false === baseScreen) { baseScreen = pCurScreen; }
        
        switch (pParamData['screenListSpecifier'])
        {
            case "base" :
                initialList[0] = new Array;
                initialList[0]['screenName'] = baseScreen;
            break;
    
            case "home" :
                initialList[0] = new Array;
                initialList[0]['screenName'] = this.NavGetHomePage();
            break;
    
            case "parent" :
                initialList[0] = new Array;
                initialList[0]['screenName'] = this.NavGetParent(baseScreen);
            break;
    
            case "firstChild" :
                initialList[0] = new Array;
                initialList[0]['screenName'] = this.NavGetFirstChild(baseScreen);
            break;
    
            case "lastChild" :
                initialList[0] = new Array;
                initialList[0]['screenName'] = this.NavGetLastChild(baseScreen);
            break;
    
            case "prevSibling" :
                initialList[0] = new Array;
                initialList[0]['screenName'] = this.NavGetPrevSibling(baseScreen);
            break;
    
            case "nextSibling" :
                initialList[0] = new Array;
                initialList[0]['screenName'] = this.NavGetNextSibling(baseScreen);
            break;
    
            case "children" :
                D.debug("Calling GetChildren with base = " + baseScreen);
                initialList = this.NavGetChildren(baseScreen, false);
            break;
        
            case "childrenFirstChild" :
                initialList = this.NavGetChildren(baseScreen, true);
            break;
    
            case "siblings" :
                initialList = this.NavGetSiblings(baseScreen);
            break;
        
            default:
            D.debug("Modifier not recognised" + pParamData['screenListSpecifier']);
            break;
        }
        D.debugEndFunction();
        return initialList;
    }

    /*
    The following are the various methods for calculating specified pages relative to the current
    page.
    */

    this.NavGetParent = function(pCurScreen)
    {
        var parent = this.NavScreenDetails[pCurScreen]['screenParent'];
        return (parent == "") ? false : parent;
    }

    this.NavGetFirstChild = function(pCurScreen)
    {
        var i;
        var ret = false;
        var screen_i;
        
        for (i=0; i<this.NavGetNumScreens() && (screen_i = this.NavScreenDetails[i]['screenParent']) != pCurScreen; i++)
        {
            if (i == this.NavGetNumScreens() )
            {
                ret = false;
            }
            else
            {
                ret = screen_i;
            }
        }
        return ret;
    }

    this.NavGetLastChild = function(pCurScreen)
    {
        /*
        This function works differently from GetFirstChild as we have to go through
        all the screens before we know we have the last child.  Therefore we just call
        GetChildren and then extract the last entry in the returned array
        */
        
        var children = this.NavGetChildren(pCurScreen, false);
        if (isArray(children))
        {
            ret = children[children.length-1];
        }
        else
        {
            ret = false;
        }
        return ret;
    }

    /*
    The following are the various methods for generating a list of screen names to
    be used as the basis for a link bar.
    */
    
    this.NavGetChildren = function(pScreen, pFirstChildFlag)
    {
        /*
        This function creates a list of screens which are the children of the passed in screen.
        Optionally, if pFirstChildFlag is set to true, what is returned is actually the first child
        of the child screens, which is sometimes more appropriate.
        */
    
        D.debugStartFunction("NavGetChildren", arguments);
        
        var children = new Array;
        var key;
        var i = 0;
        
        for (key in this.NavScreenDetails)
        {
            D.debug("key = " + key);
            if (this.NavScreenDetails[key]['screenParent'] == pScreen)
            {
                children[i] = new Array;
                D.debug("Adding " + key);
                if (!pFirstChildFlag)
                {
                    children[i]['screenName'] = key;
                    children[i]['text'] = this.NavScreenDetails[key]['screenTitleShort'];
                }
                else
                {
                    children[i]['screenName'] = this.NavGetFirstChild(key);
                    children[i]['text'] = this.NavScreenDetails[this.NavGetFirstChild(key)]['screenTitleShort'];
                }
                i++;
            }
        }
        D.debugEndFunction();
        D.debug("Dumping children " + dump(children));
        return children;
    }

    this.NavGetSiblings = function(pScreen)
    {
        D.debugStartFunction("NavGetSiblings", arguments);
        
        var thisParent = this.NavScreenDetails[pScreen]['screenParent'];
        
        D.debugEndFunction();
        
        return this.NavGetChildren(thisParent, false);
    }
    
    this.NavGetPrevSibling = function(pScreen)
    {
        D.debugStartFunction("NavGetPrevSibling", arguments);

        var siblings = this.NavGetSiblings(pScreen);
        var i;
        var ret = true;
        
        for (i=0; i<siblings.length && siblings[i]['screenName'] != pScreen; i++)
        {
            // No code - this is just to search for the element I want
        }
        if (i == siblings.length)
        {
            ret = false;
        }
        else
        {
            // found, so if not first element prev is in i-1            
            if (i > 0)
            {
                ret = siblings[i-1]['screenName'];
            }
            else
            {
                ret = false;
            }
        }
        D.debug("Returning " + ret);
        D.debugEndFunction();
        return ret;
    }

    this.NavGetNextSibling = function(pScreen)
    {
        D.debugStartFunction("NavGetNextSibling", arguments);

        var siblings = this.NavGetSiblings(pScreen);
        var i;
        var ret = true;
        
        for (i=0; i<siblings.length && siblings[i]['screenName'] != pScreen; i++)
        {
            // No code - this is just to search for the element I want
        }
        if (i == siblings.length)
        {
            ret = false;
        }
        else
        {
            // found, so if not last element prev is in i+1            
            if (i < siblings.length-1)
            {
                ret = siblings[i+1]['screenName'];
            }
            else
            {
                ret = false;
            }
        }
        D.debug("Returning " + ret);
        return ret;
    }

    this.NavAddScreenToList = function(pInsertFlag, pList, pScreenName, pText)
    {
        var index;
        
        if (pInsertFlag)
        {
            pList.unshift(new Array);
            index = 0
        }
        else
        {
            pList.push(new Array);
            index = pList.length-1;
        }
        pList[index]['screenName'] = pScreenName;
        pList[index]['text'] = pText;
    }
    
    this.NavAddClassToScreen = function( pListElement, pClass )
    {
        if (!pListElement['class'])
        {
            pListElement['class'] = new Array;
        }
        
        pListElement['class'].push([pClass]);
    }
        
    this.NavModifyList = function(pParamData, pList, pCurScreen)
    {
        /*
        This function add links to the start and end of the list based on the parameters set
        for the AE.  E.g. add a home link to the top, or prev next links to top and bottom.
        */
        
        D.debugStartFunction("NavModifyList", arguments);

        var addScreen = new Array;
        var status = true;
        var i;
        var insertCount = 0;
        var initialLength = pList.length;
        
        if (pParamData['screenListModifiers']['up'])
        {
            var upLinkScreen = this.NavGetParent(pCurScreen);
            if (!upLinkScreen) upLinkScreen = "";
            this.NavAddScreenToList(true, pList, upLinkScreen, "Up");
            insertCount++;
        }

        if (pParamData['screenListModifiers']['home'])
        {
            this.NavAddScreenToList(true, pList, this.NavGetHomePage(), "Home");
            insertCount++;
        }
        
        if (pParamData['screenListModifiers']['prevNext'])
        {
            this.NavAddScreenToList(true, pList, this.NavGetPrevSibling(pCurScreen), "Prev");
            insertCount++;
            this.NavAddScreenToList(false, pList, this.NavGetNextSibling(pCurScreen), "Next");
        }

        if (pParamData['screenListModifiers']['firstLast'])
        {
            this.NavAddScreenToList(true, pList, pList[insertCount]['screenName'], "First");
            insertCount++;
            this.NavAddScreenToList(false, pList, pList[insertCount+initialLength-1]['screenName'], "Last");
        }

        if (pParamData['screenListModifiers']['backForward'])
        {
            // add forward screen name, and 'Forward' text to preList
            // note that this is only applicable if we have gone 'back'
        }
        
        D.debugEndFunction();

        return status;
    }

    this.NavAddClassInfo = function(pParamData, pList, pCurScreen)
    {
        /* This function applies class modifiers to update the list entries to provide further
           information when the list is turned into XHTML, to provide hooks for
           CSS to use
         */
        
        var status = true;
        
        if (pParamData['classSpecifiers']['first'])
        {
            this.NavAddClassToScreen( pList[0], 'first' );
        }
        
        if (pParamData['classSpecifiers']['last'])
        {
            this.NavAddClassToScreen( pList[length-1], 'last');
        }
        
        /* if the current screen is in the list, append this fact to it can be treated
           differently (e.g made inactive, highlighted by colour)
          */
        for (i=0; i<pList.length-1; i++)
        {
            if (pList[i]['screenName'] == pCurScreen)
            {
                this.NavAddClassToScreen( pList[i], 'this');
            }
        }
        
        /* if we want to distinguish between adjacent items in a list, mark
           each item in the list alternately with 'odd' and 'even' to allow
           them to be coloured differently, for example
         */
        if (pParamData['classSpecifiers']['alternate'])
        {
            for (i=0; i < pList.length; i++)
            {
                if (i%2)
                {
                    this.NavAddClassToScreen( pList[i], 'even');
                }
                else
                {
                    this.NavAddClassToScreen( pList[i], 'odd');
                }
            }
        }
        return status;
    }
                                
        D.debug("Ending Navigation Constructor");
    }