/*
* Copyright (c) 2006, 2008, 2011 Eric Wilde.
*
* PageMenus.js contains Javascript functions for displaying the standard page
* menus that are traditionally displayed at the bottom of a Web page.
*
* To use these functions, include PageMenus.js somewhere within your Web page.
*
* To display a page menu, invoke the function DisplayPageMenu, wherever you'd
* like to display the menu.  Note that the HTML generated by the function call
* is relative to the current formatting context so that menus can be positioned
* in various spots throughout the page, as needed.
*
* There is one global variable which can be set to affect the overall look and
* feel of the menus:
*
*      PgMenuFont    - The font type/size, boldface, etc. to use for the text
*                      used to draw the menus.  This may either be the name of
*                      a class in a CSS style sheet or it may be a set of style
*                      values (e.g. "font-family: arial; font-size: 12px;").
*                      The default is 12px, san-serif, regular.
*
*                      Note that, since page menus are drawn using normal text
*                      and tagged as links, if you wish to set the color and
*                      attributes of the links, you must use a class name for
*                      this variable and define a matching CSS class that has
*                      the ":link", ":visited" and possibly ":hover"
*                      pseudo-classes defined therein (see below).  The default
*                      is to use the same links properties as are defined for
*                      the rest of the page.
*
* If you'd like to set the colors and attributes of the page menu text, here's
* how to do it.  Begin by defining a class for the basic stuff like font
* information:
*
*      .PgMenu { font-family: Helvetica, Arial, san-serif; font-size: 10px; }
*
* Next, add the pseudo-classes for the link properties:
*
*      a.PgMenu:link { color: #006699; text-decoration: none; }
*      a.PgMenu:visited { color: #006699; text-decoration: none; }
*      a.PgMenu:hover { color: #333333; text-decoration: underline; }
*
* Specify the page menu font class somewhere in your Javascript before you
* invoke the page menu function:
*
*      var PgMenuFont = 'PgMenu';
*/

/*****************************************************************************/

var PgMenuFontInt;                      // Font to use for menu text

/*****************************************************************************/

function DisplayPageMenu(Choices, MenuPath, Extended)
/*
* Choices                               A two dimensional array that describes
*                                       the menu choices that are to be in the
*                                       menu.  There is one array element
*                                       (first index) for each choice in the
*                                       menu.  Each of these elements has
*                                       either two or four elements within it
*                                       (second index), depending on whether
*                                       the Extended flag is set, that give the
*                                       text to appear for the menu choice, an
*                                       image (which is ignored), a pointer to
*                                       a submenu array (if any) and the link
*                                       to be invoked when the choice is
*                                       clicked (see below).
*
* MenuPath                              A set of numbers (given as a string,
*                                       separated by periods) that give the
*                                       path (starting from the left at one and
*                                       numbering to the right) of the path
*                                       through the menu tree that is currently
*                                       selected (see below).  If this variable
*                                       is omitted, the entire menu tree will
*                                       be displayed and no choice will be
*                                       selected.
*
* Extended                              An optional, boolean flag that
*                                       indicates whether extended menus are to
*                                       be used or not.  If not supplied or set
*                                       to false, the regular menus are used.
*                                       If set to true, extended menus are used
*                                       (see below).
*
* This function will display a traditional page bottom menu, composed of a
* number of lines of links, separated by or bars, at the current location in
* the current formatting context (i.e. if there's a surrounding div block or
* table, it will be displayed at the current position within that block or
* table).
*
* This function is a member of the suite of menu functions that are provided by
* the likes of BlockMenus.js and TabMenus.js.  As such, it is meant to fit in
* with and mimic their behavior.  Consequently, it accepts the identical type
* of tree menu descriptions used by the block and tabbed menu routines,
* regardless of the fact that much of the information in these tree
* descriptions is irrelevent to this function, the point being that the same
* menu tree that is passed to the block menu routine (for example) can be
* passed to this routine to ensure that changes to the block menu will be
* reflected in the page menu.  This is especially handy, since page menus
* usually mirror the other menus on a page exactly, their point being to allow
* users who don't have advanced browsers (e.g. lynx) to still navigate all of
* the menus on a page via text links.
*
* You must pass a two dimensional array that describes the choices that are to
* be in the menu.  There is one array element (first index) for each choice in
* the menu.  Each of these elements has either two elements, for regular menus,
* within it (second index) that give the text to appear as the menu choice and
* the link to be invoked when the choice is clicked, or four elements, for
* extended menus, within it (second index) that give the text to appear as the
* menu choice, an image (which is ignored), a pointer to a submenu array (if
* any) and the link to be invoked when the menu choice is clicked.
*
* The text that is supplied via the text element is drawn between or bars,
* horizontally on a line, one line per menu level.  This is the traditional
* page bottom menu for Web pages.
* 
* For block menus or tab menus, it makes sense to have empty blocks/tabs or
* those that do not have links associated with them.  However, for page menus,
* it is largely superfluous to have menu choices that are blank or that go
* nowhere.  Consequently, any such empty menu choices will be omitted.
*
* For all the rest, you must pass a link element that is either a URL or a
* JavaScript function (preceded by "javascript:"), just as you would use within
* an HTML anchor tag.
*
* If you need double quotes within the link, you're probably going to have to
* do something like '\\"', since the value of the link element will be placed
* inside a double quoted string inside the href parameter of an anchor tag.
* However, it is unsure whether or not this will work under all browsers so it
* is best to use the single quote.
*
* For extended menus, the image element is ignored.  It is simply supported for
* the sake of compatibility with the other menu routines in this suite.  You
* may put anything you like in this element.  Presumably it is something that
* makes sense to one of the other menu functions.
*
* For extended menus, you may pass the name of a submenu array for the submenu
* element.  The array itself should appear as described below.  If this
* parameter is not needed, a value of zero can be passed instead.
*
* Here is an example of how to display a regular menu:
*
*      var MyMenuItems =
*        [
*        ['General', 'http://my.site.com/General.html'],
*        ['Specific', 'http://my.site.com/lower/level/Specific.html?parm=xyz'],
*        ['Missing', ''],
*        ['Function', 'javascript:Function(\'A parameter\');'],
*        ['Exit', 'javascript:Cleanup();ExitPage();']
*        ];
*
*      DisplayPageMenu(MyMenuItems);
*
* Here is an example of how to display an extended menu with one submenu:
*
*      var SubMenuItems =
*        [
*        ['page', '', '0,0', '20,150'],
*        ['Sub 1', '', 0, 'http://my.site.com/sub/menus/Sub1.html'],
*        ['Sub 2', '', 0, 'http://my.site.com/sub/menus/Sub2.html']
*        ];
*
*      var ExtMenuItems =
*        [
*        ['General', '', 0, 'http://my.site.com/General.html'],
*        ['Specific', '', 0,
*          'http://my.site.com/lower/level/Specific.html?parm=xyz'],
*        ['Submenu', '', SubMenuItems, ''],
*        ['Function', '', 0, 'javascript:Function(\'A parameter\');'],
*        ['Exit', '', 0, 'javascript:Cleanup();ExitPage();']
*        ];
*
*      DisplayPageMenu(ExtMenuItems, '', true);
*
* If you wish to attach submenus to any of the menu choices, you must choose
* extended menus and then pass the name of a submenu array to each menu choice
* that will have a submenu.  The submenus can be any of the menu types that are
* supported by this suite of modules.  The type is ignored and all submenus are
* simply displayed as another line in the page menu.
*
* In keeping with the rules for submenus followed by the other menu routines,
* the submenu array looks exactly the same as the menu array except for one
* small detail.  The first element of the submenu array must describe the type
* of submenu to be invoked.  Then, the second through nth elements describe the
* actual menu elements.  Here is a description of the four second-level
* elements of the first submenu element:
*
*      menutype, position, size, parms
*
* The first element, menutype, is a string that must start with one of the
* following, to select the submenu's type:
*
*      "page"       A page menu, as implemented by this module.
*      "tab"        A tabbed menu, as implemented by TabMenus.js.
*      "text"       A text menu, as implemented by TextMenus.js.
*      "tree"       A tree menu, as implemented by TreeMenus.js.
*      "vert"       A vertical menu, as implemented by BlockMenus.js.
*      ""           The default submenu type, which, in this case, is a page
*                   menu, as implemented by this module.
*
* In addition, where it is appropriate, you may add a menu subtype to the
* submenu's type by suffixing it with ',' and following that with the subtype.
* For example, when choosing a tabbed submenu, you might include a subtype of
* BrickCurve, as follows:
*
*      'tabbed,BrickCurve'
*
* You may omit the subtype if it is not required for the menu type you're
* choosing or to accept the default as set by the skin for that menu type.
*
* The second element, position, is ignored because this routine simply draws
* page menus in a straight line, across the page, with one line for each
* submenu and, therefore, needs not position information.  Likewise, the
* third element is also ignored.  Both of these elements are only supported
* to allow menu trees to be reused with one of the other menu types
* supported by this suite.
*
* The fourth element, parms, is a comma-separated list of any parameters that
* are appropriate to the selected submenu type.  Usually this element will be
* left blank but there is one parameter that can be used for page menus (see
* the other menu modules for the list of parameters that are valid for their
* menus):
*
*      PageBreak=n[;n;...]    A list of one or more submenu elements after
*                             which a line break will be inserted in the page
*                             menu, as it is drawn, instead of the usual or
*                             bar.  This can be used to wrap long page menus
*                             where you want them wrapped, instead of where
*                             this routine decides.  The line break will be
*                             inserted after the nth element is drawn.  If
*                             you wish to break the menu in multiple
*                             locations, all of the menu items that are to
*                             have breaks following them are listed,
*                             separated from one another by semi-colons.  Do
*                             not use any blanks.  For example:
*
*                                  PageBreak=4;9
*
*                             Also, the multiple menu items must be listed in
*                             ascending order.  So, for example, use "4;9",
*                             not "9;4".
*
* Because of the way that this parameter is parsed, there are certain rules
* that must be followed when supplying it.  The case of the parameter name is
* important and must be reproduced exactly as shown.  There must be no space
* between the equal sign and the parameter/value it separates.  Each parameter
* and value pair must be followed immediately by a comma, if there are
* additional parameters following it.  You may include whitespace after each
* comma, for readability.  Here is an example:
*
*      FontSize=10, PageBreak=4;9
*
* Note that the entire menu tree is displayed, line by line, by default.  This
* is probably not the behavior that you're interested in for a page menu.
* Rather, the page menu usually reflects the current point in the menu tree
* that the user has traversed to.  To show this path, if you will, the MenuPath
* parameter is passed to this function to indicate to it what path the user
* would have traversed through the tree to get to the current page.
*
* MenuPath is given as a string which contains a set of numbers, separated by
* periods, that specify the menu choice made at each level.  The menu choices
* are numbered, starting at one, from left to right.  For example, the string
* "1.3.2" would mean the first menu choice at the top level, the third menu
* choice at the 2nd level and the second menu choice at the 3rd level.
*
* Regardless of the depth of the tree, only the number of levels given by
* MenuPath are displayed.  The terminal node in the path is never a link.
* Thus, for example, the string "1.3.2" would show the top level as one line of
* links, the choice 1 submenu as a second line of links, the choice 3 submenu
* off of the choice 1 submenu as the third line of links and choice 2 within
* that line as regular text, not a link.
*
* Note that a choice may be blank or have no link but still have a submenu.  In
* that case, it will not be displayed as a menu choice but its submenu will
* still be displayed if it is part of the path given by MenuPath.  This
* behavior supports block menus with placeholder menu items.
*/
{
var MenuFont;

// Check the glow-balls and assign them defaults, if not set.
if (typeof(PgMenuFont) != 'undefined') MenuFont = PgMenuFont;
else MenuFont = 'font-family: Helvetica, Arial, san-serif; font-size: 12px;';

// Assign defaults to our optional parameters.
if (typeof(MenuPath) == 'number') MenuPath = ''+MenuPath;
else if (typeof(MenuPath) != 'string') MenuPath = '';

if (typeof(Extended) != 'boolean') Extended = false;

// Decide whether the font is a reference to a CSS class or not.
var StyleRE = new RegExp(':');

if (MenuFont.match(StyleRE)) PgMenuFontInt = 'style="'+MenuFont+'"';
else PgMenuFontInt = 'class="'+MenuFont+'"';

// Draw the page menu at the current location.
PgMenuDrawInt(Choices, MenuPath, Extended, 1);

// We're done.
return;
}

/*****************************************************************************/

function PgMenuDrawInt(Tree, MenuPath, Extended, Level)
/*
* Internal routine to draw one row of the page menu.  Invoked recursively to
* draw the entire tree.
*/
{
var TreeDepth = Tree.length;
var CurrElem = 0;
var PageBreakElems = new Array(0), CurrIndex = 0;
var PathElems;
var PathElem = -1
var TerminalLevel = false;
var ItemText;
var ItemTextRE = /\|/g;

// If we're not at the top level, process the parameter element.  Also, emit
// a break to put us on a new line.
if (Level > 1)
  {
  var Parms;
  var PageBreak = '';

  // All of the values assigned to variables must be quoted so that eval does
  // not crap out.
  Parms = Tree[0][3].replace(/=/g, '=\'');
  Parms = Parms.replace(/,/g, '\',');
  Parms += '\'';

  // Evaluate the parameters that the caller wants to set.
  eval(Parms);

  // Build an array of line break indexes from the PageBreak parameter, if it
  // is specified.
  if (PageBreak.length > 0)
    {
    var PageIndex = PageBreak.indexOf(';');

    if (PageIndex != -1)
      {
      while (PageIndex != -1)
        {
        PageBreakElems[CurrIndex++] = PageBreak.substr(0, PageIndex);
        PageBreak = PageBreak.substr(PageIndex+1);
        PageIndex = PageBreak.indexOf(';');
        }
      }

    PageBreakElems[CurrIndex] = PageBreak;
    }

  // We're done with the parameter element.
  CurrIndex = 0; CurrElem = 1; document.write('<br>');
  }

// See if we can figure out the path through the menu tree.
if (MenuPath.length > 0)
  {
  PathElems = MenuPath.split('.');

  if (Level <= PathElems.length)
    {
    PathElem = parseInt(PathElems[Level-1]);
    if (Level <= 1) PathElem--;

    if (Level == PathElems.length) TerminalLevel = true;
    }
  }

// Wrap this line of the menu with its attributes.  This causes the or bars to
// be displayed in the correct color/font.
document.write('<span '+PgMenuFontInt+'>');

// Loop through all of the elements in this tree level and draw a line of the
// page menu.
while (CurrElem < TreeDepth)
  {
  // Since we are required to accept menu trees that can be passed to the other
  // menu functions in our suite, we need to remove the tags that they can
  // apply to the beginning of the menu text.  We also need to watch out for
  // images.
  ItemText = Tree[CurrElem][0];

  if (ItemText.substr(0, 6) == 'image=')
    {
    ItemHeight = ItemText.indexOf(',');

    if (ItemHeight > 0)
      { ItemText = ItemText.substring(ItemText.indexOf('=')+1, ItemHeight-1); }
    else { ItemText = ItemText.substring(ItemText.indexOf('=')+1); }
    }

  else if (ItemText.substr(0, 5) == 'size=')
    {
    ItemHeight = ItemText.indexOf(',');

    if (ItemHeight > 0) { ItemText = ItemText.substring(ItemHeight+1); }
    else { ItemText = ''; }
    }

  // Check for empty nodes or those without links.
  if ((ItemText.length <= 0)
    || (((Extended) ? Tree[CurrElem][3].length : Tree[CurrElem][1].length)
      <= 0)) { CurrElem++; continue; }

  // Text menus use or bars to force line wrapping.  Since we must accept any
  // menu that can be fed to DisplayTextMenu, we must replace any or bars in
  // the menu text with blanks.
  ItemText = ItemText.replace(ItemTextRE, ' ');

  // If we're at the terminal level in the tree and this is the terminal node
  // of the path through the tree, it isn't a link.
  if (TerminalLevel && (CurrElem == PathElem))
    document.write(ItemText);
  
  // Otherwise, emit the menu choice as a link.
  else document.write(
    '<a '+PgMenuFontInt+' href="' +
      ((Extended) ? Tree[CurrElem][3] : Tree[CurrElem][1])+'">' +
      ItemText+'</a>');

  // If its not the last one on the line, emit an or bar.  Or is that a line
  // break.
  if (CurrElem < (TreeDepth-1))
    {
    if ((PageBreakElems.length > 0) && (CurrIndex < PageBreakElems.length))
      {
      if (CurrElem == PageBreakElems[CurrIndex])
        { CurrIndex++; document.write('<br>'); }
      else { document.write(' | '); }
      }
    else { document.write(' | '); }
    }

  CurrElem++;
  }

// Finish wrapping this line of the menu with its attributes.
document.write('</span>');

// If we're doing extended menus, we need to process all of the submenus.
if (Extended)
  {
  // Since sublevels appear on a new line, loop through all of the elements in
  // this tree level again and draw their submenus.
  if (Level > 1) CurrElem = 1;
  else CurrElem = 0;

  while (CurrElem < TreeDepth)
    {
    // If this isn't the terminal level and there's a submenu on the path, draw
    // it.
    if (((PathElem < 0) || (CurrElem == PathElem))
      && (typeof(Tree[CurrElem][2]) == 'object'))
      PgMenuDrawInt(Tree[CurrElem][2], MenuPath, true, Level+1);

    CurrElem++;
    }
  }

// All done.
return;
}

