/*
* Copyright (c) 2004, 2006, 2008, 2009, 2011 Eric Wilde.
*
* TextMenus.js contains Javascript functions that draw a standard Web site
* menu, consisting of lines of text that expand/contract with hanging
* indentation, at the left side of the page.  It also takes care of actually
* expanding/contracting the menu, depending on where in the site navagation
* tree the user is browsing and marks the selected page with an arrow.
*
* None of this action is dynamic, however.  Each page is responsible for
* selecting the position in the menu tree to be shown/marked and that's all
* that happens.  If the user clicks on a menu item, it is simply a link to the
* associated page.  The page itself calls TextMenus.js and tells it which node
* to show/mark.
*
* To use these functions, include TextMenus.js somewhere within your Web page.
*
* To display a text menu, invoke the function DisplayTextMenu, 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 are several global configuration variables that can be used to
* configure the look and feel of the menus created by this script.  Normally,
* these variables are defined in a site-wide configuration file which is
* included by each page in the site.  The variables are:
*
*      TxMenuDir     - The directory where all the menu images can be found.
*                      The default is "/Controls/TextMenuImages/".  Note that
*                      an empty string may be set to use the current directory.
*
*      TxMenuArrow   - The type of arrow used to mark the current selection in
*                      the menu bar.  A name which is used to make up the
*                      actual file name of the arrow image.  Currently known
*                      choices are:
*
*                           Black - A black arrow.
*                           White - A white arrow.
* 
*                      A blank name can be used to cause the arrow not to be
*                      drawn.  The default is Black.
*
*      TxMenuArrowOffset - The offset above/below the calculated position where
*                      the arrow is to be drawn.  You may tweak this parameter
*                      if the arrow is not being drawn where you'd like it to
*                      go, relative to the menu item's text.  The default is
*                      -1.
*
*      TxMenuFont    - 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;
*                      font-weight: bold;").  The default is 12px, san-serif,
*                      bold, no text decoration.
*
*                      Note that, by default, text menus are drawn using normal
*                      text and tagged as links but with text-decoration set to
*                      "none" so that the links are not underlined.  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).  If you use a set of style values for this
*                      parameter, you cannot override the setting for
*                      text-decoration nor any of the other properties set by
*                      the ":link", ":visited" and ":hover" pseudo-classes.
*
*      TxMenuFontSize - Unfortunately, short of processing the style sheet
*                      itself, there is no way for Javascript code to know the
*                      font size set by a CSS class.  This being the case and
*                      since the size of the menu font, in pixels, is used for
*                      numerous calculations herein, you must indicate what
*                      font size is set by the class or set of style values
*                      defined by TxMenuFont.  The default is 12px.
*
*      TxMenuSpacing - The vertical spacing between menu items.  A nice value
*                      for this variable is 11, if the font size is 12, and 8,
*                      if the font size is 14.  These are the defaults, by the
*                      way.
*
*      TxMenuRules   - If the menu is to have horizontal rules, this variable
*                      should be set to the highest menu level that is to have
*                      rules (usually, ruling only the first level looks best).
*                      The default is to omit horizontal rules.  A value of
*                      zero has the same effect (i.e. omits horizontal rules).
*
*      TxMenuRuleAttr - The attributes to be applied to menu rules.  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. "border-collapse:
*                      collapse; border: 1px solid #808080;").  The default is
*                      a collapsed solid border, 1px wide, drawn in medium
*                      gray.
*
*                      Note that the horizontal rule which is used by this
*                      routine is produced by the <hr> tag.  It would seem
*                      that, under some browsers, the size of the rule
*                      generated by this tag is double the width set by the
*                      "border" parameter in CSS.  Unfortunately, there doesn't
*                      seem to be any obvious way around this problem.
*
*      TxMenuRuleSpacing - The amount of spacing to apply between the menu rule
*                      and the menu text.  The default, if not specified is
*                      a number that should make things look nice (we hope).
*
*                      This value can be used, in conjunction with the
*                      TxMenuSpacing value to space ruled menu items wider or
*                      closer than they normally would, to achieve any special
*                      look that you want.
*
*                      Note that, depending on what kind of rule spacing you
*                      use and what value is chosen for menu spacing by
*                      TxMenuSpacing, you may need to adjust the top position
*                      of the menu by passing a non-zero value for MenuTop to
*                      DisplayTextMenu.  This code could calculate the offset
*                      required but it was felt that not doing so provided more
*                      flexibility in menu positioning.
*
*      TxMenuRuleOffset - The amount of offset to apply at the left and right
*                      ends of the rule to move it in slightly from the edges
*                      of the menu bounding box.  The default, if not
*                      specified, is three.
*
*      TxMenuWidth   - The width of the menu in pixels.  This width is used to
*                      draw the menu bar, calculate the width of menu items,
*                      etc.  A nice value for this variable is 150 so this is
*                      the default.
*
* If you'd like to set the colors and attributes of the text menu text, here's
* how to do it.  Begin by defining a class for the basic stuff like font
* information:
*
*      .TxMenu { font-family: Helvetica, Arial, san-serif; font-size: 10px; }
*
* Next, add the pseudo-classes for the link properties:
*
*      a.TxMenu:link { color: #006699; text-decoration: none; }
*      a.TxMenu:visited { color: #006699; text-decoration: none; }
*      a.TxMenu:hover { color: #333333; text-decoration: underline; }
*      a.TxMenu:active { color: #333333; text-decoration: underline; }
*
* Specify the text menu font class somewhere in your Javascript before you
* invoke the text menu function:
*
*      var TxMenuFont = 'TxMenu';
*
* This script also sets several global variables that can be used by those who
* follow it.  The variables are:
*
*      NewsWidth     - The width (in pixels) of any news text that the user
*                      can draw in the menu bar.
*
*      NewsTop       - The top address (in pixels) where any news text that the
*                      user wishes to draw in the menu bar should be drawn.
*
*      NewsLeft      - The left-hand address (in pixels) where any news text
*                      that the user wishes to draw in the menu bar should be
*                      drawn.
*/

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

// Glow ball variables that need only be set once when this module is loaded.
// These variables determine the browser type, etc., which will never change,
// so there is no need to set them dynamically.
var TxMenuBrowserInt = navigator.userAgent.toLowerCase();
var TxMenuIsNavigatorInt = ((TxMenuBrowserInt.indexOf('mozilla') != -1)
  && (TxMenuBrowserInt.indexOf('compatible') == -1));

// Serially reusable glow balls that control our L & F.  These variables are
// set by DisplayTextMenu.  Thus, they can be reused by subsequent calls to
// DisplayTextMenu.
var TxMenuDirInt;                       // Directory for menu images

var TxMenuArrowInt;                     // Arrow type and offset
var TxMenuArrowOffsetInt;

var TxMenuFontInt;                      // Menu font and size
var TxMenuFontSizeInt;
var TxMenuSpacingInt;                   // Menu spacing

var TxMenuRulesInt;                     // Whether menu has rules or not
var TxMenuRuleAttrInt;                  // And how they look
var TxMenuRuleSpacingInt;
var TxMenuRuleOffsetInt;

var TxMenuWidthInt;                     // Menu width

// News text variables.
var NewsWidth;
var NewsTop;
var NewsLeft;

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

function DisplayTextMenu(Choices, MenuPath, DynamicText, MenuTop, MenuLeft,
                         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 gives the
*                                       path (starting from the left at one and
*                                       numbering to the right) through the
*                                       menu tree of the item that is currently
*                                       selected (see below).  If this variable
*                                       is omitted, the entire menu tree will
*                                       be displayed and no choice will be
*                                       selected.
*
*                                       Note that you can also pass an empty
*                                       value for this parameter to display the
*                                       entire menu tree (as noted above).
*                                       Either way, displaying the entire tree
*                                       can be used as an aid in debugging your
*                                       tree hierarchy before you implement it
*                                       on your Web pages.
*
* DynamicText                           The actual menu text that is to be used
*                                       for dynamic menu items.  If this text
*                                       is supplied and it is not an empty
*                                       string, it will be added to the menu
*                                       tree directly below the menu choice
*                                       that is chosen by MenuPath.
*
*                                       Note that you can always monkey with
*                                       the array passed in Choices to add
*                                       dynamic menu items to the menu tree.
*                                       However, this parameter provides an
*                                       easy way to add dynamic menu items for
*                                       terminal nodes without going through
*                                       all of the grief of array manipulation.
*                                       Since this is such a common operation,
*                                       this script does the work for you (how
*                                       nice).
*
*                                       Note that no links can be supplied for
*                                       dynamic menu items, hence their use
*                                       mainly for terminal nodes.
*
* MenuTop                               The top of the menu, in pixels.  The
*                                       menu will be drawn beginning at this
*                                       point in the menu bounding box.  The
*                                       default is zero, which draws the menu
*                                       right at the top of the menu box
*                                       (actually four pixels down but that's
*                                       as close as it gets).
*
* MenuLeft                              The left edge of the menu, in pixels.
*                                       The menu will be drawn this far in from
*                                       the left endge of the menu bounding
*                                       box.  The default is 20, which seems to
*                                       be a nice number.
*
* 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 text tree menu, composed of a
* vertical stack of links, possibly separated by line rules, 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).  The tree may have multiple levels, in which case, when a
* level is expanded it is displayed below the level above it, indented by a
* certain amount to form a hanging indent.
*
* 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 text menu.  This is especially handy, since text menus
* often mirror the other menus on a page exactly, their point being to allow
* users who don't understand advanced menuing techniques (e.g. popup block
* menus) 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 sub-elements within it
* (second index), for regular menus, that give the text to appear as the menu
* choice and the link to be invoked when the choice is clicked, or four
* sub-elements within it (again, second index), for extended menus, 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, one item per line,
* in a vertical column, with or without horizontal rules between each item.
* Lower levels are indented from the left to convey the hierarchy.  This is the
* traditional left hand tree menu for Web pages.
* 
* For text menus, it can make sense to have menu items that do not have links
* associated with them.  However it is largely superfluous to have menu choices
* that are blank.  Consequently, any such empty menu choices will be omitted.
*
* For menu items that have links, 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 for a particular menu item, 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();']
*        ];
*
*      DisplayTextMenu(MyMenuItems, '1');
*
* Here is an example of how to display an extended menu with one submenu:
*
*      var SubMenuItems =
*        [
*        ['', '', '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();']
*        ];
*
*      DisplayTextMenu(ExtMenuItems, '1.2', '', 0, 0, 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 submenu type can be any of the menu types that
* are supported by this suite of modules.  Essentially, the submenu type is
* ignored by this module and all submenus are drawn as text tree levels.
*
* In keeping with the rules for submenus followed by the other menu routines,
* the submenu array looks exactly the same as the top-level 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 can be anything, since it is
* ignored.  Ususally, if a menu that is defined as some other type of menu is
* being reused by the text tree, it will be one of the other valid menu types.
* Here is a list of menu types that are currently known:
*
*      "dia"        A dialog box, as implemented by DialogBox.js.
*      "page"       A page menu, as implemented by PageMenus.js.
*      "text"       A text menu, as implemented by this module.
*      "tab"        A tabbed menu, as implemented by TabMenus.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 text
*                   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 tree submenu, you might include a subtype of
* popup, as follows:
*
*      'tree,popup'
*
* 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 knows where to
* position submenus in relation to the previous level that encloses them.
* 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 here is a list of the parameters that can be used for text
* menus (see the other menu modules for the list of parameters that are valid
* for their menus):
*
*      Font=font              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; font-weight: bold;").
*                             However, if the font-family has multiple
*                             families, use colons to separate them, instead
*                             of commas (e.g. "font-family: helvetica: arial;").
*                             And, be sure to use the trailing ';' after the
*                             set of font families.
*      FontSize=num           The font size, in points, of menu text.
*      Indent=num             The amount to indent this submenu level by.  The
*                             default is 8 pixels.
*      MenuSpacing=num        The vertical spacing between menu items.
*
* Because of the way that these parameters are parsed, there are certain rules
* that must be followed when supplying them.  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:
*
*      Font=SubmenuFont, FontSize=10
*
* The parameters set for a submenu apply to all of the sub submenus that branch
* from it.  Often, they override the corresponding global values (which can be
* set before the menu is initially invoked by defining the appropriate
* variables in your code). If you don't like this behavior, you can always set
* the parameters back to what they were in the lower menus (e.g. if you only
* want a single submenu in the middle of a branch to have special properties).
*
* Note that the entire menu tree is displayed, with all the submenus expanded,
* by default.  This is probably not the behavior that you're interested in for
* a text menu.  Rather, the text 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 all of the items from the
* top level, vertically, down the menu bar with the least amount of
* indentation.  Underneath item 1, and indented by one level, would be its
* submenu.  Underneath item 3 in that submenu, and again indented by one level,
* would be its sub-submenu.  Item 2 within the sub-submenu would be marked as
* selected by the menu arrow.  This menu item, when clicked upon, would do
* nothing.
*
* Note that a choice may be blank, in which case it will not be displayed as a
* menu choice; or the choice may have no link, in which case it will be
* displayed as a placeholder for the submenu but without any link.  In either
* case these menu items can still have a submenu (how you get to the submenu is
* anybody's guess but not my problem) which can be displayed if it is part of
* the path given by MenuPath.  This behavior supports text menus with
* placeholder menu items.
*
* Many Web sites display dynamic content, for example pages from press reports
* or pages of documentation, in their menu bars.  It would be nice to include
* such dynamic menu items in the menu hierarchy but without incurring all of
* the overhead of fiddling with the menu description arrays.  The DynamicText
* parameter supports this goal.
*
* If a non-empty text string is passed in the optional DynamicText parameter,
* this text will be used to create a single menu item indented from and below
* the selected item in the menu tree.  In other words, the menu tree is drawn
* exactly as it would be drawn, according to MenuPath, and then one more menu
* item is added immediately below it.  There is no link associated with this
* menu item, as one would expect for a terminal node, and the number of options
* available for drawing this dynamic item is limited.  But, it may well prove
* useful for supporting most dynamic menu requirements without the need to
* alter the menu description arrays, thereby allowing them to be set up
* statically in a configuration file.
*
* A word about menu text handling is in order.  The length of each of the items
* on the tree menu is calculated from their actual text using a simple
* algorithm that takes font size but not kerning and proportionality into
* account.  In short, it is a best guess, given the fact that it needs to be
* fast and small.  Then, the browser gets involved and it decides where it
* would actually like to wrap the line based on all of the rules associated
* with the font being used.  Often, where it thinks it would like to break your
* menu lines is less than optimal from the aescetic standpoint.  The net result
* is that one often wishes to indicate a hard line break in a text menu item so
* that menu items are wrapped to the next line properly.  If this is the case,
* you can insert an or bar ('|') into the menu item to wrap it where you want
* it to be wrapped.  For example:
*
*      A Long Menu|Item
*
* There should be no effect on the menus, by breaking menu items like this,
* other than causing the menu item to wrap exactly where you want it to.  So,
* you should feel free to go ahead and use it wherever you wish to control how
* menu items break.
*/
{
// Current menu position and arrow position.
var CurrPos = 4;

// Work variables
var MenuFont, MenuRuleAttr;             // Temporary fonts/styles

// First, figure out where the menu arrow images are.
if (typeof(TxMenuDir) != 'undefined') { TxMenuDirInt = TxMenuDir; }
else { TxMenuDirInt = '/Controls/TextMenuImages/'; }

// Get the menu arrow type and offset.
if (typeof(TxMenuArrow) != "undefined") { TxMenuArrowInt = TxMenuArrow; }
else { TxMenuArrowInt = 'Black'; }

if ((typeof(TxMenuArrowOffset) != "undefined") && !isNaN(TxMenuArrowOffset))
  { TxMenuArrowOffsetInt = TxMenuArrowOffset; }
else { TxMenuArrowOffsetInt = -1; }

// Check the font parameter and assign the defaults, if not set.
if (typeof(TxMenuFont) != 'undefined') { MenuFont = TxMenuFont; }
else
  {
  MenuFont = 'font-family: Helvetica, Arial, san-serif; font-size: 12px; ' +
    'font-weight: bold; text-decoration: none;';
  }

// Get the font size and spacing.
if ((typeof(TxMenuFontSize) != "undefined") && !isNaN(TxMenuFontSize)
  && (TxMenuFontSize > 0)) { TxMenuFontSizeInt = TxMenuFontSize; }
else { TxMenuFontSizeInt = 12; }

if ((typeof(TxMenuSpacing) != "undefined") && !isNaN(TxMenuSpacing)
  && (TxMenuSpacing > 0)) { TxMenuSpacingInt = TxMenuSpacing; }
else
  {
  if (TxMenuFontSizeInt <= 12) { TxMenuSpacingInt = 11; }
  else { TxMenuSpacingInt = 8; }
  }

// Are we ruling the menu or not?
if ((typeof(TxMenuRules) != "undefined") && !isNaN(TxMenuRules)
  && (TxMenuRules > 0)) { TxMenuRulesInt = TxMenuRules; }
else { TxMenuRulesInt = 0; }

if (typeof(TxMenuRuleAttr) != 'undefined') { MenuRuleAttr = TxMenuRuleAttr; }
else { MenuRuleAttr = 'border-collapse: collapse; border: 1px solid #808080;'; }

if ((typeof(TxMenuRuleSpacing) != "undefined") && !isNaN(TxMenuRuleSpacing)
  && (TxMenuRuleSpacing >= 0)) { TxMenuRuleSpacingInt = TxMenuRuleSpacing; }
else { TxMenuRuleSpacingInt = TxMenuFontSizeInt; }

if ((typeof(TxMenuRuleOffset) != "undefined") && !isNaN(TxMenuRuleOffset)
  && (TxMenuRuleOffset >= 0)) { TxMenuRuleOffsetInt = TxMenuRuleOffset; }
else { TxMenuRuleOffsetInt = 3; }

// Get the menu width or take the default, if not set.
if ((typeof(TxMenuWidth) != "undefined") && !isNaN(TxMenuWidth)
  && (TxMenuWidth > 0)) { TxMenuWidthInt = TxMenuWidth; }
else { TxMenuWidthInt = 150; }

// If the user hasn't specified a menu path, just show the whole tree.
if (typeof(MenuPath) == 'number') { MenuPath = ''+MenuPath; }
else if (typeof(MenuPath) != 'string') { MenuPath = ''; }

// Get the menu position from the user.
if ((typeof(MenuTop) != "undefined") && !isNaN(MenuTop)
  && (MenuTop > 0)) { CurrPos += MenuTop; }

if ((typeof(MenuLeft) == "undefined") || isNaN(MenuLeft)
  || (MenuLeft <= 0)) { MenuLeft = 20; }

// Assign defaults to our optional parameters.
if (typeof(DynamicText) != 'string') { DynamicText = ''; }

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)) { TxMenuFontInt = 'style="'+MenuFont+'"'; }
else { TxMenuFontInt = 'class="'+MenuFont+'"'; }

// Decide whether the rule attribute is a reference to a CSS class or not.
if (MenuRuleAttr.match(StyleRE))
  { TxMenuRuleAttrInt = 'style="'+MenuRuleAttr+'"'; }
else { TxMenuRuleAttrInt = 'class="'+MenuRuleAttr+'"'; }

// Set the news text drawing parameters.
NewsWidth = TxMenuWidthInt - 35;
NewsLeft = MenuLeft;

// Draw a block that surrounds the menu to locate it within the page.
document.write('<div style="position: relative; top: 0px; left: 0px; ' +
  'width: '+TxMenuWidthInt+'px;">');

// Draw the text menu at the current location.
CurrPos += TxMenuDrawInt(Choices, MenuPath, DynamicText, CurrPos, MenuLeft,
  Extended, 1);

// Finish off with a horizontal rule, if the user wants one.
if (TxMenuRulesInt > 0)
  {
  document.write('<div style="height: '+TxMenuRuleSpacingInt+'px; ' +
      'width: '+(TxMenuWidthInt - (TxMenuRuleOffsetInt*2))+'px; ' +
      'position: absolute; ' +
      'top: '+(CurrPos - TxMenuRuleSpacingInt)+'px; ' +
      'left: '+TxMenuRuleOffsetInt+'px;">' +
    '<hr '+TxMenuRuleAttrInt+' ' +
      'width='+(TxMenuWidthInt - (TxMenuRuleOffsetInt*2))+' ' +
      'align=left></div>');
  }

// Set the position to draw news, if the Web page wants to.
NewsTop = CurrPos + TxMenuFontSizeInt + 3;

// Close off the menu block.
document.write('</div>');

// We're done.
return;
}

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

function TxMenuDrawInt(Tree, MenuPath, DynamicText, CurrPos, CurrIndent,
                       Extended, Level)
/*
* Internal routine to draw one level of the text menu.  Invoked recursively to
* draw the entire tree.
*/
{
var TreeDepth = Tree.length;
var OrigPos = CurrPos;
var CurrElem = 0;
var PathElems;
var PathElem = -1
var TerminalLevel = false;
var ItemHeight, ItemWidth;
var ItemText;
var ItemTextRE = /\|/g;
var ArrowPos;

// If we're not at the top level, skip the parameter element.
if (Level > 1) { CurrElem = 1; }

// 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; }
    }
  else { PathElem = 0; }
  }

// Calculate the width of all menu items at this level.
ItemWidth = TxMenuWidthInt - CurrIndent;

// Loop through all of the elements in this tree level and draw a level of the
// text 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.
  if (ItemText.length <= 0) { CurrElem++; continue; }

  // Calculate the height of this menu item (its just a guess).
  ItemHeight = TxMenuHeightInt(ItemText, ItemWidth);

  // Replace any or bars with line breaks.
  ItemText = ItemText.replace(ItemTextRE, '<br>');

  // The first thing we do is draw a horizontal rule if we're at any of the
  // menu levels that the user wanted horizontal rules drawn at.
  //
  // Note that we eyeball the rule size at two, for porpoises of doing the
  // spacing calculations herein.  If the user picks some number bigger than
  // that, they can compensate for it with TxMenuRuleSpacing.
  if (Level <= TxMenuRulesInt)
    {
    document.write('<div style="height: '+TxMenuRuleSpacingInt+'px; ' +
        'width: '+(TxMenuWidthInt - (TxMenuRuleOffsetInt*2))+'px; ' +
        'position: absolute; ' +
        'top: '+(CurrPos - TxMenuRuleSpacingInt)+'px; ' +
        'left: '+TxMenuRuleOffsetInt+'px;">' +
      '<hr '+TxMenuRuleAttrInt+' ' +
        'width='+(TxMenuWidthInt - (TxMenuRuleOffsetInt*2))+' ' +
        'align=left></div>');
    }

  // 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.  Similarly, if this is just
  // a placeholder node with no link, it isn't a link.
  if ((TerminalLevel && (CurrElem == PathElem) && (DynamicText == ''))
    || (((Extended) ? Tree[CurrElem][3].length : Tree[CurrElem][1].length)
      <= 0))
    {
    document.write('<div style="height: '+ItemHeight+'px; ' +
        'width: '+ItemWidth+'px; position: absolute; ' +
        'top: '+CurrPos+'px; '+'left: '+CurrIndent+'px;">' +
      '<span '+TxMenuFontInt+'>'+ItemText+'</span>' +
      '</div>');
    }

  // Otherwise, draw this node with a link.
  else
    {
    if (TxMenuIsNavigatorInt)
      {
      document.write('<a '+TxMenuFontInt+' ' +
        'href="'+((Extended) ? Tree[CurrElem][3] : Tree[CurrElem][1])+'">' +
      '<div style="height: '+ItemHeight+'px; ' +
        'width: '+ItemWidth+'px; position: absolute; ' +
        'top: '+CurrPos+'px; '+'left: '+CurrIndent+'px;">'+ItemText +
      '</div></a>');
      }
    else
      {
      document.write('<div style="height: '+ItemHeight+'px; ' +
          'width: '+ItemWidth+'px; position: absolute; ' +
          'top: '+CurrPos+'px; '+'left: '+CurrIndent+'px">' +
      '<a '+TxMenuFontInt+' ' +
        'href="'+((Extended) ? Tree[CurrElem][3] : Tree[CurrElem][1])+'">' +
        ItemText +
      '</a></div>');
      }
    }

  // If we're at the terminal level in the tree and this is the terminal node
  // of the path through the tree, we have some more work to do.
  if (TerminalLevel && (CurrElem == PathElem))
    {
    // First, we need to check whether this is a dynamic menu choice.  If so,
    // add the dynamic item's text at this point.
    if (DynamicText != '')
      {
      CurrPos += ItemHeight;

      // Calculate the height of the dynamic menu item (its just a guess).
      ItemHeight = TxMenuHeightInt(DynamicText, ItemWidth);

      // Replace any or bars with line breaks.
      DynamicText = DynamicText.replace(ItemTextRE, '<br>');

      // If if we're at any of the menu levels that the user wanted horizontal
      // rules drawn at, dynamic menu items are no exception.
      if (Level <= TxMenuRulesInt)
        {
        document.write('<div style="height: '+TxMenuRuleSpacingInt+'px; ' +
            'width: '+(TxMenuWidthInt - (TxMenuRuleOffsetInt*2))+'px; ' +
            'position: absolute; ' +
            'top: '+(CurrPos - TxMenuRuleSpacingInt)+'px; ' +
            'left: '+TxMenuRuleOffsetInt+'px;">' +
          '<hr '+TxMenuRuleAttrInt+' ' +
            'width='+(TxMenuWidthInt - (TxMenuRuleOffsetInt*2))+' ' +
            'align=left></div>');
        }

      // Now, we can draw the dynamic menu item.
      document.write('<div style="height: '+ItemHeight+'px; ' +
        'width: '+(ItemWidth-8)+'px; position: absolute; ' +
        'top: '+CurrPos+'px; '+'left: '+(CurrIndent+8)+'px;">' +
        '<span '+TxMenuFontInt+'>'+DynamicText+'</span>' +
        '</div>');
      }

    // If the user wants an arrow, draw it at this menu position.
    if (TxMenuArrowInt != '')
      {
      ArrowPos = CurrPos + Math.floor(ItemHeight / 2)
        - ((TxMenuFontSizeInt * 2) / 3) + TxMenuArrowOffsetInt;
      document.write('<div style="height: 9px; width: 5px; ' +
          'position: absolute; top: '+ArrowPos+'px; left: 1px;">' +
        '<img src="'+TxMenuDirInt+'TextArrow_'+TxMenuArrowInt+'.gif" ' +
          'height=9 width=5 alt="">' +
        '</div>');
      }
    }

  // Account for the height of this item.
  CurrPos += ItemHeight;

  // If this item has a submenu and there's no menu path or this item is on the
  // menu path, we need to recursively draw the submenu.
  if (Extended && ((PathElem < 0) || (CurrElem == PathElem))
    && (typeof(Tree[CurrElem][2]) == 'object'))
    {
    var HeightAdj = TxMenuSpacingInt;

    // Get the submenu parameters.
    var SubmenuParms = TxMenuParseSubmenuInt(Tree[CurrElem][2]);

    // Fix up the spacing, in case it has changed.
    HeightAdj -= TxMenuSpacingInt; CurrPos -= HeightAdj;

    // Draw the next level of the text menu at the current location.
    CurrPos += TxMenuDrawInt(Tree[CurrElem][2], MenuPath, DynamicText, CurrPos,
      CurrIndent+SubmenuParms[2], Extended, Level+1);

    // Restore any global overrides and fix up the spacing.
    if (SubmenuParms[3].length > 0) { eval(SubmenuParms[3]); }

    CurrPos += HeightAdj;
    }

  // Move on down the road.
  CurrElem++;
  }

// Return the increment to the current menu position.
return (CurrPos - OrigPos);
}

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

function TxMenuHeightInt(ItemText, ItemWidth)
/*
* Internal routine to estimate, in pixels, the height of a menu item,
* accounting for line breaks.
*/
{
var Items = ItemText.split('|');
var CurrItem = 0;
var ItemHeight = 0;

// Sum up the total height of all the lines in the item, including wrapped
// lines.
while (CurrItem < Items.length)
  {
  // Calculate the height of this piece of the menu item (its just a guess).
  // Note that the numbers 5 and 12 are used herein to scale the length by
  // 5/12.  You may need to play with these numbers but they seem to work
  // pretty good.
  ItemHeight += (Math.floor((Items[CurrItem].length
    * (TxMenuFontSizeInt * 5)) / (ItemWidth * 12)) + 1)
    * (TxMenuFontSizeInt + 2);
  CurrItem++;
  }

// Add in the fixed spacing amount and return it.
return (ItemHeight + TxMenuSpacingInt);
}

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

function TxMenuParseSubmenuInt(Submenu)
/*
* returns                               An array with the following elements:
*
*                                            [0] - menutype
*                                            [1] - menusubtype
*                                            [2] - indent
*                                            [3] - restorestr
*
* Internal routine to parse submenu parameters and return them to the caller in
* an array.
*/
{
// Separate the menu type and subtype.
var MenuType = Submenu[0][0];
var MenuSubtype = '';

if ((typeof(MenuType) != 'string') || (MenuType.length <= 0))
  { MenuType = 'text'; }
else if (MenuType.indexOf(',') != -1)
  {
  MenuSubtype = MenuType.substr(MenuType.indexOf(',')+1);
  MenuType = MenuType.substring(0, MenuType.indexOf(',')-1);
  }

// If the user has specified any extra parameters for the submenu, get them
// now.
var Indent = '8', Restore = '';
var ElementSelected = 0;

if ((typeof(Submenu[0][3]) == 'string') && (Submenu[0][3].length > 0))
  {
  var Parms;
  var Font = '', FontSize = '', MenuSpacing = '';

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

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

  // Save then override the glow balls.
  if (Font.length > 0)
    {
    Restore += ('TxMenuFontInt = \''+TxMenuFontInt+'\', ');

    if (Font.match(/:/))
      {
      var FamStart = Font.indexOf('font-family:');
      var FamEnd, Family;

      if (FamStart != -1)
        {
        Family = Font.substr(FamStart+12);

        if ((FamEnd = Family.indexOf(';')) != -1)
          {
          Family = Family.substr(0, FamEnd);
          TxMenuFontInt = 'style="'+Font.substr(0, FamStart+12) +
            Family.replace(/:/g, ',')+Font.substr(FamStart+FamEnd+12)+'"';
          }
        else
          {
          TxMenuFontInt = 'style="'+Font.substr(0, FamStart+12) +
            Family.replace(/:/g, ',')+'"';
          }
        }
      else { TxMenuFontInt = 'style="'+Font+'"'; }
      }
    else { TxMenuFontInt = 'class="'+Font+'"'; }
    }

  if (FontSize.length > 0)
    {
    Restore += ('TxMenuFontSizeInt = '+TxMenuFontSizeInt+', ');
    TxMenuFontSizeInt = parseInt(FontSize);
    }

  if (MenuSpacing.length > 0)
    {
    Restore += ('TxMenuSpacingInt = '+TxMenuSpacingInt+', ');
    TxMenuSpacingInt = parseInt(MenuSpacing);
    }

  // Rip off the trailing comma, since it offends eval.
  if (Restore.length > 0) { Restore = Restore.substr(0, Restore.length-2); }
  }

// Return all the stuff we parsed out of the submenu array as an array of
// parameters that our caller can put to good use.
return ([MenuType, MenuSubtype, parseInt(Indent), Restore]);
}

