Window system (Desktop) in JavaScript (IE only) : Desktop « GUI Components « JavaScript DHTML






Window system (Desktop) in JavaScript (IE only)

<HTML>
<HEAD>
<TITLE>Fortis Demo</TITLE>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">

</HEAD>

<BODY BGCOLOR="white" TEXT="black" LEFTMARGIN="0" TOPMARGIN="0" MARGINWIDTH="0" MARGINHEIGHT="0">

<SCRIPT LANGUAGE="JavaScript">
  var MAXWIN = 32;
  for(var i = 0; i < MAXWIN; i++)
    document.write('<DIV ID="FORTIS_W'+ i +'" STYLE="position: absolute"></DIV>');
</SCRIPT>
  
  <!-- fortis.js -->
  <SCRIPT>
//  File:          fortis.js
//  Description:  Core Fortis functionalities
//  Version:      0.34 alpha

/////////////////////////////////////////////////////////////////////////////////////////
/*
  Project: Fortis 

  Version: 0.34 alpha

  Copyright  2001-2002 Daniele Pagano
  
  What is it: Fortis is a windowing environment that runs in a single
              dynamic IE DHTML page. It can be used by itself or with
              other page components as it is minimally intrusive. It boasts
              a window bar (like in MS Windows 9x), global menu and status
              bar, and windows that can contain other (arbitrary) web pages.
              It can be used for easily creating web applications, either
              by generating API calls and windows content on the fly from a
              server, for static client-server applicatins, or for 
              client-only solutions. For more information and documentation 
              visit www.esaurito.com/fortis.

  Requirements: Microsoft Internet Explorer 5 or later.

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  You can contact the author by email at fortis@esaurito.com. 
  
  Installation: add these elements to your page anywhere within your BODY 
    tag and before your code.


        var MAXWIN = 100;
        for(var i = 0; i < MAXWIN; i++)
          document.write('<DIV ID="FORTIS_W'+ i +'" STYLE="position: absolute"></DIV>');



      The value of MAXWIN determines the maximum number of windows that
    can be created. Change it at will, but a very large number will slow
    down page loading.
      You may want to adjust the URL for the modules if they reside in a
    different directory than your page.

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

// FORTIS CLASS SINGLETON 
// !!NOT CODE-ENFORCED!! This is the ONLY instance of the environment, DO NOT change the
// variable name because plenty of code relies on it.
window.fortis = new FortisEnvironment();

// You should leave this here as well
fortis.init();

// Fortis Environment class
//  The environment. Contains and handles windows and settings.
//
//  Params: none (the constructor is called above for you)
//
//  Properties/Objects for public use:
//     statusbar:  a status bar at the bottom of the window
//
//  Exported classes for publis use: 
//      Window:   for window creation and handling
//      WinStyle: for window styling
//
//  Exported constants: see Constants section in constructor
//
//  Methods for public use:
//      setStyle:         sets default style for new windows
//      setMenu:          sets system menu
//      setBackground:    sets background image
//      showAll:          shows all windows
//      hideAll:          hides all winodws
//      minimizeAll:      minimizes all open windows
//      restoreAll:       restores all minimized winodws
//      endModal:         comes out of any modal windows state
//
function FortisEnvironment() 
{
  // Containers and counters:
  this.winArray = [];         //  Holds all window objects created
  this.divCount = 0;          //  Keeps count of layers with id's created 
                              //    (to generate unique id's)
  this.winCount = 0;          //  Holds position of last DIV that was
                              //    a window;
  this.zidx = 1;              //  Z-index tracker
  this.shadowDivs = [];       //  Holds ID's of divs used in dragging

  this.maxiwin = [];          //  Holds all maximized windows

  this.topUsed = 0;           // Pixels used on the top of the page by system windows
  this.botUsed = 0;           // Pixels used on the bottom of the page by system windows

  // Constants

  // Window Style masks
  this.WS_IFRAME  = 1;     // prepares an IFRAME as the contnent of the window
  this.WS_TITLE   = 2;     // adds a draggable title bar
  this.WS_CLOSE   = 4;     //  adds a close button*
  this.WS_MAX     = 8;     // adds a maximize button*
  this.WS_MIN     = 16;    //  adds a minimize button*
  this.WS_SIZE_NS = 32;    //  allows the window to be resized North-South**
  this.WS_SIZE_EW = 64;    // allows the window to be resized East-West**
  this.WS_WORKS   = 127;   // IFRAME, close, minimize, maximize, both size (DEFAULT)
  this.WS_BLANK   = 128;   // Blank window
    //*  : if selected, title bar is also created
    //** : if selected, IFRAME is also created

  // Environment classes

  // -- Window class --
  //  Manages windows.
  //
  //  Params:
  //    title:  the title of the window
  //    src:    the URL of the page this window will contain
  //    left:    the position in pixels from the left margin (or 'center')
  //    top:    the position in pixels from the top margin (or 'center')
  //    width:  the width of the window in pixels
  //    height:  the width of the window in pixels
  //    style:  the style of the window (a fortis.WinStyle object)
  //      -  default: use Environment settings.
  //
  //  Properties for public use:
  //    winN:    the position in the array of of DIV's of the page.
  //    div:    reference to the DIV object in the DOM. Can be used
  //            to manipulate the window arbitrarly.
  //    winId:  the string that, using fortis.winArray, can be evaluated
  //            anywhere to return a reference to the Window object. Used
  //            to refer to the window in generic event programming.
  //    title:  the HTML contained in the title bar.
  //    style:  the style of the class (a fortis.WinStyle object)
  //
  //  Methods for public use:
  //      (see at the top of each declaration for details)
  //
  //    addComponent:   adds a component to the window
  //    setStyle:       sets the window style
  //    applyStyle:      applies the style to the window
  //    hide:            hides the window
  //    show:            show the window
  //    refresh:        refreshes the IFRAME and focuses
  //    lock:           does not allow window closing
  //    unlock:         inverts lock effect
  //    moveTo:          moves the window to a coordinate or position
  //    moveBy:          moves the window by some amount
  //    setSize:        sets the size of the window
  //    min:            minimizes the window
  //    maxrestore:     maximizes or restores the maximized window
  //    goModal:        makes the window modal
  //    addButton:      adds a custom button to the window

    function Window(title, src, left, top, width, height, style)
    {
      //  Finds the next DIV to take over
      var d = document.all.tags('DIV');
      this.winN = fortis.winCount++;
      this.div = d['FORTIS_W' + this.winN];

      //  Registers in the winArray
      var winPos = fortis.winArray.length;
      fortis.winArray[winPos] = this;
      this.winId = "fortis.winArray[" + winPos + "]";

      // Sets basic properties
      this.title = title;
      this.setSize(width, height);
      this.moveTo(left, top);
      
      // Brings it to top
      this.div.style.zIndex = fortis.zidx++;
                
      // if no style use a blank one (overridden by environment)
      if(!style)
        this.style = fortis.style;
      else
        this.style = style;

      this.div.style.visibility = 'hidden';      // hidden by default
      this.div.style.backgroundColor = this.style.bgcolor;
      this.div.style.color = this.style.fgcolor;
      this.div.style.overflow = this.style.over;
      this.div.style.borderStyle = 'outset';
      this.div.style.borderWidth = 'thin';
      this.div.style.borderColor = this.style.bgcolor;

      this.styleX = 0;
      this.styleY = 0;

      // Sets the windows content (SRC)
      if(src)
        this.src = src;

      // Applies style
      this.applyStyle();

  }

  
  // -- Window method: setStyle --
  //  sets the window style
  //
  //  Params:    style: a valid fortis.WinStyle structure instance
  //
  //  Returns:  undefined

  function Window.prototype.setStyle(style)
  {
      this.style = style;
  }


  // -- Window method: applyStyle --
  //  applies the current style (including IFRAME) to the window.
  //  useful to recall on window resize.
  //
  //  Params:    none
  //
  //  Returns:  undefined

  function Window.prototype.applyStyle()
  {

   // Clears previous content first
      this.div.innerHTML = '';

   // Checks if title bar was implied
      if( (this.style.winstyle & fortis.WS_CLOSE) ||
          (this.style.winstyle & fortis.WS_MIN)   ||
          (this.style.winstyle & fortis.WS_MAX)
         )
         this.style.winstyle = this.style.winstyle | fortis.WS_TITLE;

   // Checks if IFRAME was implied
      if( (this.style.winstyle & fortis.WS_SIZE_EW) ||
          (this.style.winstyle & fortis.WS_SIZE_NS)
         )
         this.style.winstyle = this.style.winstyle | fortis.WS_IFRAME;

   // Creates title bar
      if(this.style.winstyle & fortis.WS_TITLE)
      {
        this.TB = this.addComponent();
        this.TB.style.backgroundColor = this.style.bgcolor;
        this.TB.style.color = this.style.fgcolor;
        this.TB.style.fontFamily = this.style.font;
        this.TB.style.fontWeight = 'bold';
        this.TB.style.fontSize = this.style.fsize + 'pt';
        this.TB.style.textIndent = '1pt';
        this.TB.style.borderStyle = 'solid';
        this.TB.style.borderWidth = 'thin';
        this.TB.style.borderColor = this.style.bgcolor;
        this.TB.style.padding = '1';
        this.TB.style.pixelLeft = 0;
        this.TB.style.pixelTop = 0;
        this.TB.style.pixelHeight = 22;
        this.TB.style.pixelWidth = this.width-4;
        if(this.style.barimg)
          this.TB.style.backgroundImage = 'url(' + this.style.barimg + ')';
        this.TB.style.cursor = 'default';
        this.TB.onselectstart="return false;";
        var title = '<DIV STYLE="position:absolute; left:23px; top:2px;"> ' + this.title + '</DIV>';
        if(this.style.icon)
          title = '<DIV style="position: absolute;"><IMG SRC="' + this.style.icon + 
                   '" WIDTH="16" HEIGHT="16"></DIV>' + title;
        this.TB.innerHTML = title;

        // Sets dragging event
        if(!this.isMaximized)
          this.TB.onmousedown = this.winId + '.startDrag();';

        this.styleX = 2;
        this.styleY = 22;
      }
      
      var fromleft = 27;

      // Creates close button
      if((this.style.winstyle & fortis.WS_CLOSE)  && !this.isModal)
      {
        var clsbtn = "<DIV ALIGN='center' STYLE='font-weight:900; font-family: sans-serif; font-size: 9px'>X</FONT><DIV>";
        this.CB = this.addButton(clsbtn, this.width - fromleft,  3 - this.styleY, 18, 16, 
          this.winId + ".hide();", '#CCCCCC', 'black');
        fromleft += 20;
      }

      // Creates maximize/restore button
      if((this.style.winstyle & fortis.WS_MAX) && !this.isModal)
      {
        if(this.isMaximized)
          var mb = '8';
        else
          var mb = '0';

        var maxbtn = "<DIV ALIGN='center' STYLE='font-weight:900; font-family: sans-serif; font-size: 9px'>" + mb + "</FONT><DIV>";
        this.MXB = this.addButton(maxbtn, this.width - fromleft,  3 - this.styleY, 18, 16, 
          this.winId + ".maxrestore();", '#CCCCCC', 'black');
        fromleft += 20;
      }

      // Creates minimize button
      if((this.style.winstyle & fortis.WS_MIN) && fortis.winbar  && !this.isModal)
      {
        var minbtn = "<DIV ALIGN='center' STYLE='font-weight:900; font-family: sans-serif; font-size: 9px'>_</FONT><DIV>";
        this.MNB = this.addButton(minbtn, this.width - fromleft,  3 - this.styleY, 18, 16, 
          this.winId + ".min();", '#CCCCCC', 'black');
      }

      // Creates sizing handle
      if( ((this.style.winstyle & fortis.WS_SIZE_NS) || (this.style.winstyle & fortis.WS_SIZE_EW)) 
          && !this.isMaximized)
      {
        this.DC = this.addComponent();
        this.DC.style.color = this.style.bgcolor;
        this.DC.style.borderStyle = 'none';
        this.DC.style.pixelLeft = this.width - 14;
        this.DC.style.pixelTop = this.height - 14;
        this.DC.style.pixelHeight = 12;
        this.DC.style.pixelWidth = 12;
        this.DC.style.overflow = 'hidden';
        
        if((this.style.winstyle & fortis.WS_SIZE_NS) && (this.style.winstyle & fortis.WS_SIZE_EW))
        {
          this.DC.style.cursor = 'nw-resize';
          this.sizable = 'nw';
        }
        else if(this.style.winstyle & fortis.WS_SIZE_EW)
        {
          this.DC.style.cursor = 'w-resize';
          this.sizable = 'ew';
        }
        else
        {
          this.DC.style.cursor = 'n-resize';
          this.sizable = 'ns';
        }
        
        this.DC.style.zIndex = 2;
        this.DC.onselectstart="return false;";
        this.DC.innerHTML = '//';
        this.DC.onmousedown = this.winId + '.startResize();';
      }

      // Creates IFRAME (and shadow div for dragging)
      if(this.style.winstyle & fortis.WS_IFRAME)
      {
        this.sdid = 'FORTIS_SD' + fortis.divCount++;
        this.ifid = 'FORTIS_IF' + fortis.divCount++;
        this.div.innerHTML += '<DIV id="' + this.sdid + '" style="position: absolute;"></DIV><IFRAME id="' + 
                              this.ifid + '" style=" position: absolute;"></IFRAME>';
            
        var c = this.SD = this.div.all.tags("DIV")[this.sdid];
        c.style.pixelLeft = 0;
        c.style.pixelTop = this.styleY;
        c.style.pixelWidth = this.width - this.styleX*2;
        c.style.pixelHeight = this.height - this.styleY-4;
        c.style.zIndex = 0;

        if(!this.sdplace)
          this.sdplace = fortis.shadowDivs.length;

        fortis.shadowDivs[this.sdplace] = c;

        c = this.IF = this.div.all.tags("IFRAME")[this.ifid];
        c.style.pixelLeft = 0;
        c.style.pixelTop = this.styleY;
        c.style.pixelWidth = this.width - this.styleX*2;
        c.style.pixelHeight = this.height - this.styleY-4;
        c.style.zIndex = 1;    
        if(this.src)
          c.src = this.src;
      }

  }

  // -- Window method: addComponent --
  //  Adds a component to the Window
  //
  //  Params:    none
  //
  //  Returns:  reference to the blank DIV just created

    function Window.prototype.addComponent()
    {
      var cid = 'FORTIS_C' + fortis.divCount++;
      
      this.div.innerHTML += '<DIV ID="' + cid + '" STYLE="position: absolute;"></DIV>'; 
      
      var c = this.div.all.tags("DIV")[cid];

      return c;
    }    

  // function Window.prototype.startDrag
  //  This function is meant to be called by an event handler and
  //    enables window dragging. It is not meant to be called by itself.

    function Window.prototype.startDrag() 
    {
      if(event.button == 1 && !event.altKey && !event.ctrlKey && !event.shiftKey)
      { 
        this.dragStartX = event.offsetX + 
          event.srcElement.style.pixelLeft + 6;
          
        this.dragStartY = event.offsetY +
          event.srcElement.style.pixelTop + 6;
          
        if(!this.isModal)
          this.div.style.zIndex = fortis.zidx++;

        for(var i = 0; i < fortis.shadowDivs.length; i++)
          fortis.shadowDivs[i].style.zIndex = 2;
 
        if (window.FMS_menuCount)
        {
          window.FMS_menuLock = true;
        }

        document.onmousemove = fortis.doDrag;
        document.onmouseup = fortis.endDrag;
        fortis.dw = this;
            
        event.returnValue = false;
      }
    }

  // function Window.prototype.startResize
  //  This function is meant to be called by an event handler and
  //    enables window resiziong. It is not meant to be called by itself.

    function Window.prototype.startResize() 
    {
      if(event.button == 1 && !event.altKey && !event.ctrlKey && !event.shiftKey && this.sizable)
      {         
        if(this.sizable == 'nw' || this.sizable == 'ew')
          this.dragStartX = event.offsetX + event.srcElement.style.pixelLeft + 6;
        else
          this.dragStartX = 0;
          
        if(this.sizable == 'nw' || this.sizable == 'ns')
          this.dragStartY = event.offsetY + event.srcElement.style.pixelTop + 6;
        else
          this.dragStartY = 0;

        if(!this.isModal)
          this.div.style.zIndex = fortis.zidx++;

        for(var i = 0; i < fortis.shadowDivs.length; i++)
          fortis.shadowDivs[i].style.zIndex = 2;
 
        if (window.FMS_menuCount)
        {
          window.FMS_menuLock = true;
        }

        document.onmousemove = fortis.doResize;
        document.onmouseup = fortis.endResize;
        fortis.dw = this;
            
        event.returnValue = false;
      }
    }


  // -- Window method: lock --
  //  Does not allow a window to be closed and executes some other
  //  code instead
  //
  //  Params:    code: JScript code to be executed instead of closing
  //
  //  Returns:  undefined

    function Window.prototype.lock(code) 
    {
      this.isLocked = true;
      this.lockedCode = code;
    }

  // -- Window method: unlock --
  //  Allows closing of a previosuly locked window
  //
  //  Params:    none
  //
  //  Returns:  undefined

    function Window.prototype.unlock() 
    {
      this.isLocked = false;
    }


  // -- Window method: hide --
  //  Hides the window and removes it from the window bar
  //
  //  Params:    none
  //
  //  Returns:  undefined

    function Window.prototype.hide() 
    {
      if(this.isLocked)
        eval(this.lockedCode)
      else
      {
        this.div.style.visibility = 'hidden';
        if(fortis.winbar)
          fortis.winbar.remove(this);
      }
    }

    
  // -- Window method: show --
  //  Shows the window and adds it to the window bar
  //  or brings it to focus if it is already visible.
  //
  //  Params:    none
  //
  //  Returns:  undefined

    function Window.prototype.show() 
    {
      if(this.div.style.visibility == 'visible')
        this.div.style.zIndex = fortis.zidx++;
      else
      {
        if(fortis.winbar && !this.isMinimized)
          fortis.winbar.add(this);
        this.div.style.visibility = 'visible';
        this.div.style.zIndex = fortis.zidx++;
        this.isMinimized = false;
      }
    }

  // -- Window method: refresh --
  // Refreshes the IFRAME of the window with the specified title
  // (if it exists) with the specified URL. The window must already
  // contain IFRAME capabilities in its style.
  //
  //  Params:    url:   URL to set the IFRAME's SRC at
  //
  //  Returns:  undefined

  function Window.prototype.refresh(url)
  {
    this.src = url;
    this.IF.src = url;
    this.show();
  }

  // -- Window method: min --
  //  Minimizes the window (hides it but keeps it on the window bar)
  //
  //  Params:    none
  //
  //  Returns:  undefined

    function Window.prototype.min() 
    {
      this.div.style.visibility = 'hidden';
      this.isMinimized = true;
    }

  // -- Window method: maxrestore --
  //  Maximizes the window or restores it if maximized
  //
  //  Params:    none
  //
  //  Returns:  undefined

    function Window.prototype.maxrestore() 
    {
      // Restores
      if(this.isMaximized)
      {
        this.isMaximized = false;;

        this.moveTo(this.restX, this.restY);
        this.setSize(this.restW, this.restH);

        fortis.maxiwin[this.winId] = 0;
      }
      else // Maximizes
      {
        var clw = document.body.clientWidth;
        var clh = document.body.clientHeight;

        this.isMaximized = true;
        this.restX = this.x;
        this.restY = this.y;
        this.restH = this.height;
        this.restW = this.width;

        this.moveTo(0, fortis.topUsed+4);
        this.setSize(clw, clh - fortis.topUsed - fortis.botUsed - 2);

        fortis.maxiwin[this.winId] = this;
      }
    }


  // -- Window method: moveTo --
  //  Moves the window to the indicated client co-ordinates
  //
  //  Params:    x, y:  position to move to. Each can be
  //                  - a positive number : pixel coordinates
  //                  - 'center': centered in the browser window on 
  //                    the corresponding axis. Position is not enforced
  //                    after this function call.
  //
  //  Returns:  undefined

    function Window.prototype.moveTo(x,y) 
    {
      if(isNaN(x))
      {
        if(x.toLowerCase() == 'center')
          this.div.style.pixelLeft = this.x = document.body.clientWidth/2  - this.width/2;
      }
      else
      {
        if(x >= 0)
          this.div.style.pixelLeft = this.x = + x;
        else
          this.div.style.pixelLeft = this.x = 0;
      }

      if(isNaN(y))
      {
        if(y.toLowerCase() == 'center')
          this.div.style.pixelTop = this.y = document.body.clientHeight/2 - this.height/2;
      }
      else
      {
        if(y >= 0)
          this.div.style.pixelTop = this.y = + y;
        else
          this.div.style.pixelTop = this.y = 0;
      }
    }


  // -- Window method: moveBy --
  //  Moves the window by the indicated pixel amounts
  //
  //  Params:    x, y:  pixel amounts to move by
  //
  //  Returns:  undefined

    function Window.prototype.moveBy(x,y) 
    {
      var newX = this.div.style.pixelLeft + (+ x); // Signs to insure proper casting
      var newY = this.div.style.pixelTop + (+ y);  //  (CSS values are strings)
      
      this.moveTo(newX, newY);
    }
    

  // -- Window method: setSize --
  //  Changes the window size to the indicated pixel amounts
  //    and refreshes the style and IFRAME
  //
  //  Params:    w, h:  new width and height of the window
  //
  //  Returns:  undefined

    function Window.prototype.setSize(w,h) 
    {
      this.div.style.pixelWidth = this.width = w;
      this.div.style.pixelHeight = this.height = h;

      if(this.style)
        this.applyStyle();
    }


  // -- Window method: goModal --
  // Gives modal state to the window. The window cannot be moved, 
  // resized, or closed, and no other object can be activated
  // until fortis.endModal() is called.
  //
  //  Params:    none
  //
  //  Returns:  undefined
  function Window.prototype.goModal()
  {
    fortis.endModal();

    fortis.modwin = this;
    
    this.show();

    fortis.lockdiv.style.visibility = 'visible';
    window.FMS_menuLock = true;
    fortis.winbar.wnd.div.style.visibility = 'hidden';
    this.div.style.zIndex = fortis.lockdiv.style.zIndex + 1;

    this.isModal = true;
    this.applyStyle();
  }


  // -- Window method: addButton --
  //  Adds an DIV with CSS properties that works and looks like a button.
  //  Used to create buttons on the title bar;
  //
  //  Params:    inner:      HTML content of the button (text, images...)
  //            x, y, w, h:  position and size of button
  //            handler:    JScript to call when button pressed
  //            bgc, fgc:   backround and foreground color of the button
  //
  //  Returns:  reference to button

    function Window.prototype.addButton(inner, x, y, w, h, handler, bgc, fgc)
    {
      var c = this.addComponent();
      c.style.pixelLeft = x + this.styleX;
      c.style.pixelTop = y + this.styleY;
      c.style.pixelHeight = h;
      c.style.pixelWidth = w;
      c.style.borderStyle = 'outset';
      c.style.borderWidth = 'thin';
      var bc = 'white';
      c.style.borderColor = bc;
      c.style.cursor = 'default';
      c.style.fontFamily = 'sans-serif';
      c.style.fontSize = '14';
      c.style.padding = '1';
      c.style.verticalAlign = 'middle';
      c.style.color = fgc;
      c.onmousedown = "style.borderStyle = 'inset'; event.returnValue = false";
      c.onmouseout = "style.borderStyle = 'outset'";
      c.style.backgroundColor = bgc;
      c.innerHTML = inner;
      c.onclick = handler + ";style.borderStyle = 'outset'" ;
      c.onselectstart = 'event.returnValue = false;';

      return c;
    }

  // END OF WINDOW CLASS
  this.Window = Window; // Set as environment property

  // -- WinStyle Sructure --
  //  Holds window attributes. It's passed to the Window
  //    constructor optionally.
  //
  //  Params:
  //    style:    the style of the window created. As supported by Window.setStyle();  
  //            - default: fortis.WS_WORKS
  //    bgcolor:  the  background color of the window
  //            - default: white
  //    fgcolor:  the foregorund color of the window
  //            - default: black
  //    font:     the fontlist for the window
  //            - default: sans-serif
  //    fsize:    the font size (in points)
  //            - default: 11
  //    icon:     an icon to be placed at the corner of the window (URL)
  //            - default: none
  //    barimg:   a background image for the title bar (URL)
  //            - default: none
  //    over:      the overflow mode of the window as in CSS
  //            - default: hidden
  //
  //  Properties:    same as Params

    function WinStyle(winstyle, bgcolor, fgcolor, font, fsize, icon, barimg, over) 
    {
      this.winstyle = winstyle?winstyle:127;
      this.bgcolor = bgcolor?bgcolor:'#FFFFFF';
      this.fgcolor = fgcolor?fgcolor:'#000000';
      this.font = font?font:'sans-serif';
      this.fsize = fsize?fsize:'11';
      this.icon = icon?icon:'0';
      this.barimg = barimg?barimg:'0';
      this.over = over?over:'hidden';
    }
 
  // END OF WINSTYLE CLASS
  this.WinStyle = WinStyle;     // Sets as environment property


  // -- WinBar class --
  //  The bar with buttons in the environment to access windows
  //
  //  Params:
  //    -none
  //
  //  Properties for public use:
  //    -none
  //
  //  Methods for public use:
  //      (see at the top of each declaration for details)
  //    -> This class is handled by the environment and should not
  //       be handled manually.
  //
  //    update:   refreshes the bar
  //    add:      adds a window
  //    remove:   removes a window

  function WinBar()
  {
    this.wins = [];
    this.isLocked = false;

    var clw = document.body.clientWidth;    
    var clh = document.body.clientHeight;

    this.wnd = new Window('', 0, 0, clh-26-fortis.botUsed, clw, 26, new fortis.WinStyle(fortis.WS_BLANK));
    this.wnd.div.style.visibility = 'hidden';
    this.wnd.div.style.zIndex = Number.MAX_VALUE - 9999;
    this.wnd.div.style.borderStyle = 'none';
    this.wnd.div.style.backgroundColor = fortis.style.bgcolor;
    this.wnd.sysW = true;
    fortis.botUsed += 26;
  }

  // -- WinBar method: update --
  //  Updates the bar and resizes buttons accordingly
  //
  //  Params:    none
  //
  //  Returns:  undefined

  function WinBar.prototype.update()
  {
    var titles = []; // The real windows to show
    this.wnd.div.style.visibility = 'visible';
    for(var i = 0; i < this.wins.length; i++)
      if(this.wins[i])
        titles[titles.length] = this.wins[i];

    // Outer frame
    var out = '<TABLE WIDTH="100%" BORDER="0" CELLSPACING="0" CELLPADDING="0" BGCOLOR="' +
              fortis.syscolor + '"><TR>';

    var clw = document.body.clientWidth;
    if (titles.length > 4)
    {
      var lpc = 100/titles.length;
      var lpc2 = 0;
      var tn = titles.length;
    }
    else
    {
      var lpc = 20;
      var lpc2 = lpc;
      var tn = 5;
    }

    // Each button
    for(var i = 0; i < titles.length; i++)
    {
      if(titles[i].title.length < clw/(tn * 11))
        var ttl = titles[i].title;
      else
        var ttl = titles[i].title.slice(0, clw/(tn * 11) ) + '...';

      if(titles[i].style.icon)
        var icn = '<IMG SRC="' + titles[i].style.icon + '" WIDTH="16" HEIGHT="16">';
      else
        var icn = '';

      var txt = '<TABLE WIDTH="100%" BORDER="0" CELLSPACING="0" CELLPADDING="0">'+
                '<TR><TD WIDTH="20" ALIGN="LEFT" VALIGN="MIDDLE">' + icn +
                '</TD><TD><SPAN STYLE="font-size:'+
                titles[i].style.fsize + 'pt; font-weight:bold; color:' +
                titles[i].style.fgcolor + ';">' + ttl + '<SPAN></TD></TR></TABLE>';


      out += '<TD WIDTH="' + lpc + '%"><DIV ONMOUSEDOWN="style.borderStyle =' + 
             "'inset'; event.returnValue = false;" + 
             '" ONMOUSEOUT="style.borderStyle =' + "'outset';" + 
             '" ONCLICK="if(!fortis.winbar.isLocked) ' + titles[i].winId + '.show();' + 
             'style.borderStyle =' + "'outset';" + 
             '" ONSELECTSTART="event.returnValue = false;' + 
             '"ID="FORTIS_WB' + i + '">' + txt + '</DIV></TD>';
    }

    out += '<TD WIDTH="' + lpc2 + '%"></TD><TD WIDTH="' + lpc2 + '%"></TD></TR></TABLE>';

    this.wnd.div.innerHTML = out;
    
    var cid = '';

    // Makes it look proper
    for(var i = 0; i < titles.length; i++)
    {
      cid = 'FORTIS_WB' + i;
      c = this.wnd.div.all.tags("DIV")[cid];

      c.style.cursor = 'default';
      c.style.fontFamily = titles[i].style.font;
      c.style.borderStyle = 'outset';
      c.style.borderWidth = 'thin';
      c.style.borderColor = titles[i].style.bgcolor;
      if(titles[i].style.barimg)
        c.style.backgroundImage = 'url(' + titles[i].style.barimg + ')';
      c.style.backgroundColor = titles[i].style.bgcolor;
      c.style.padding = 3;
      c.style.verticalAlign = 'middle';
      c.style.pixelHeight = 26;
    }

  }


  // -- WinBar method: add --
  //  Adds a window and refreshes the bar
  //
  //  Params:    win: a valid fortis.Window class instance
  //
  //  Returns:  undefined

  function WinBar.prototype.add(win)
  {
    this.wnd.div.innerHTML = '';

    var wincount = this.wins.length;
      this.wins[wincount] = win;

    this.update();

  }

  // -- WinBar method: remove --
  //  Removes a window from the bar and refreshes it
  //
  //  Params:    win: a valid fortis.Window instance added before
  //
  //  Returns:  undefined
  function WinBar.prototype.remove(win)
  {
    this.wnd.div.innerHTML = '';
    
    for(var i = 0; i < this.wins.length; i++)
    {

      if(this.wins[i].winN == win.winN)
        {
          this.wins[i] = 0;
          break;
        }
    
    }

    this.update();
  }


  // END OF WINBAR CLASS
  this.WinBar = WinBar;         // Sets as environment property

  // -- StatusBar class --
  //  Easy to use global status bar 
  //
  //  Params:
  //    -none
  //
  //  Properties for public use:
  //    -none
  //
  //  Methods for public use:
  //      (see at the top of each declaration for details)
  //
  //    on:       displays the bar
  //    off:      hides the bar
  //    addMsg:   adds a message
  //    delMsg:   removes a message
  function StatusBar()
  {
    var clw = document.body.clientWidth;    
    var clh = document.body.clientHeight;

    this.msgs = [];
    this.size = 30; // Defualt size (can be changed)

    this.wnd = new Window('', 0, 0, clh-this.size-fortis.botUsed, clw, this.size, new fortis.WinStyle(fortis.WS_BLANK));
    this.wnd.div.style.visibility = 'hidden';
    this.wnd.div.style.overflow = 'auto';
    this.wnd.div.style.borderStyle = 'inset';
    this.wnd.div.style.borderColor = fortis.style.bgcolor;
    this.wnd.div.style.backgroundColor = fortis.style.bgcolor;
    this.tcolor = fortis.style.fgcolor;
    this.wnd.div.style.zIndex = Number.MAX_VALUE - 9999; // On top
    this.wnd.sysW = true;

    this.oldBU = fortis.botUsed;
  }


  // -- StatusBar method: on --
  //  Shows the bar and locates it properly within the environment
  //
  //  Params:    size: the height of the bar in pixles
  //             - default: previous or default value is retained
  //
  //  Returns:  undefined

  function StatusBar.prototype.on(size)
  {
    this.isOn = true;
    if(size)
      this.size = size;

    var clw = document.body.clientWidth;    
    var clh = document.body.clientHeight;

    this.wnd.div.style.visibility = 'visible';
    fortis.botUsed = this.oldBU + this.size;

    this.wnd.moveTo(0, clh-this.size-fortis.botUsed);
    this.wnd.setSize(clw, this.size);

    var out = '';

    // Adds messages to the top, to add them to the bottom
    // use this line instead:
    //     for(var i = 0; i < this.msgs.length; i++)
    for(var i = this.msgs.length-1; i >= 0; i--)
      if(this.msgs[i])
        out += this.msgs[i].out;

    this.wnd.div.innerHTML = out;

    fortis.winRefresh();
  }

  // -- StatusBar method: off --
  //  Hides the bar rearranges the environment properly
  //
  //  Params:    none
  //
  //  Returns:  undefined

  function StatusBar.prototype.off()
  {
    this.isOn = false;

    this.wnd.div.style.visibility = 'hidden';
    fortis.botUsed = this.oldBU;
    this.wnd.setSize(1, 1);


    fortis.winRefresh();
  }

  // -- StatusBar method: addMsg --
  //  Adds a message to the bar.
  //   -> The message is added ON TOP the others, see on method code to change
  //      the behavior to add it to the bottom
  //
  //  Params:    id: message id string, used to remove the message later
  //             - optional: if omitted, message cannot be removed
  //            type: type of message (only for look of side):
  //              'i'   :  information (green)
  //              'w'   :  warning     (orange)
  //              'e'   :  error       (red)
  //              other : unspecified (white)
  //            text: text of the message (HTML)
  //
  //  Returns:  undefined

  function StatusBar.prototype.addMsg(id, type, text)
  {
    var col = '';
    var ico = '';

    switch(type)
    {
      case 'i' : col = 'green'; ico = ''; break;
      case 'w' : col = 'orange'; ico = ''; break;
      case 'e' : col = 'red'; ico = ''; break;
      default: out += 'white';
    }

    var out = '<TABLE WIDTH="100%" BORDER="1" BORDERCOLOR="' + this.tcolor +
    '" CELLSPACING="0" CELLPADDING="1"><TR><TD BGCOLOR="' +
    col + '" VALIGN="TOP" ALIGN="CENTER" WIDTH=20>' + ico +
    '</TD><TD><FONT FACE="sans-serif" COLOR="' + this.tcolor +
    '" SIZE="-2">' + text + '</FONT></TD></TR></TABlE>';

    this.wnd.div.innerHTML = out + this.wnd.div.innerHTML;

    var mr = new Object;
    mr.id = id;
    mr.out = out;

    this.msgs[this.msgs.length] = mr;
  }

  // -- StatusBar method: delMsg --
  //  Remove a message from the bar by id.
  //
  //  Params:    id: message id string
  //
  //  Returns:  undefined

  function StatusBar.prototype.delMsg(id)
  {
    var out = '';

    for(var i = this.msgs.length-1; i >= 0; i--)
      if(this.msgs[i])
        if(this.msgs[i].id == id)
          this.msgs[i] = 0;
        else
          out += this.msgs[i].out;

     this.wnd.div.innerHTML = out;
  }

  // END OF STATUSBAR CLASS
  this.StatusBar = StatusBar;   // Sets as environment property
 
  this.style = new WinStyle();  // Sets default style
  this.syscolor = '#AAAAAA';

  var clw = document.body.clientWidth;
  var clh = document.body.clientHeight;

  var d = document.all.tags('DIV');
  var winN = this.winCount++;
  this.lockdiv = d['FORTIS_W' + winN];

  this.lockdiv.style.pixelTop = 0;
  this.lockdiv.style.pixelLeft = 0;
  this.lockdiv.style.pixelHeight = clh;
  this.lockdiv.style.pixelWidth = clw;
  this.lockdiv.style.backgroundImage = "URL(transparent.gif)";
  this.lockdiv.style.zIndex = Number.MAX_VALUE - 999;
  this.lockdiv.style.visibility = 'hidden';
}

// -- FortisEnvironment method: init --
//  Initializes environment. Called for you earlier on.
//
//  Params:    none
//
//  Returns:  undefined

function FortisEnvironment.prototype.init()
{
  
  // Desktop objects: you can comment out the ones you don't need,
  // but otherwise keep at most one copy (and with the same name) of each.

  // Wallpaper - off until inizialized
  this.wp_wnd = new fortis.Window('', 0, 0, 0, 0, 0, new fortis.WinStyle(fortis.WS_BLANK));
  this.wp_wnd.div.style.borderStyle = 'none';
  this.wp_wnd.div.style.overflow = 'visible';
  this.wp_wnd.div.style.zIndex = 0;
  this.wp_wnd.div.style.visibility = 'visible';
  this.wp_wnd.sysW = true;

 
  // Windows button bar - on by default. Not creating one does not allow creation of
  //  minimize buttons, but the rest works the same.
  this.winbar = new fortis.WinBar();

  // Status bar - invisible (but working) by defualt. Use its methods to show or hide.
  this.statusbar = new fortis.StatusBar();
}


// -- FortisEnvironment method: setStyle --
//  Sets the default style for all new windows created
//
//  Params:    a valid fortis.WinStyle structure instance
//
//  Returns:  undefined
function FortisEnvironment.prototype.setStyle(style)
{
  this.style = style;
}


// -- FortisEnvironment method: setMenu --
//  Initializes environment. Called for you earlier on.
//
//  Params:    menu: a valid FMS_Menu (or compatible) class instance
//
//  Returns:  undefined
function FortisEnvironment.prototype.setMenu(menu)
{
  if(!this.menu_wnd) // Creates containing window once
  {
    this.menu_wnd = new fortis.Window('', 0, fortis.topUsed, 0, 0, 0, new fortis.WinStyle(fortis.WS_BLANK));
    this.menu_wnd.div.style.borderStyle = 'none';
    this.menu_wnd.div.style.overflow = 'visible';
    this.menu_wnd.div.style.visibility = 'visible';
    this.menu_wnd.div.style.zIndex = Number.MAX_VALUE - 9999;
    this.menu_wnd.sysW = true; 
  }

  this.menu_wnd.div.innerHTML = menu.toString();
  this.menu = menu;
  this.menu_wnd.div.style.pixelWidth = document.body.clientWidth;
  this.menu_wnd.div.style.height = this.menu.height;
  this.menu_wnd.div.style.backgroundColor = this.syscolor = this.menu.bgcolor;
  this.statusbar.tcolor = this.menu.fgcolor;

  this.topUsed += this.menu.height;

  fortis.winRefresh();
}


// -- FortisEnvironment method: showAll --
//  Shows all created windows in the environment
//
//  Params:    none
//
//  Returns:  undefined
function FortisEnvironment.prototype.showAll()
{
  for(var i = 0; i < this.winArray.length; i++)
    if(!this.winArray[i].sysW)
      this.winArray[i].show();
}


// -- FortisEnvironment method: hideAll --
//  Hides all windows in the environment, except system ones.
//
//  Params:    none
//
//  Returns:  undefined
function FortisEnvironment.prototype.hideAll()
{
  for(var i = 0; i < this.winArray.length; i++)
    if(!this.winArray[i].sysW)
      this.winArray[i].hide();
}

// -- FortisEnvironment method: minimizeAll --
//  Minimizes all open windows in the environment
//
//  Params:    none
//
//  Returns:  undefined
function FortisEnvironment.prototype.minimizeAll()
{
  for(var i = 0; i < this.winArray.length; i++)
    if(!this.winArray[i].sysW && this.winArray[i].div.style.visibility == 'visible')
      this.winArray[i].min();
}


// -- FortisEnvironment method: restoreAll --
//  Restores all minimized windows in the environment.
//
//  Params:    none
//
//  Returns:  undefined
function FortisEnvironment.prototype.restoreAll()
{
  for(var i = 0; i < this.winArray.length; i++)
    if(!this.winArray[i].sysW && this.winArray[i].isMinimized)
      this.winArray[i].show();
}


// -- FortisEnvironment method: winRefresh --
//  Refreshes various parts of the environnment. Associate with broswer
//  window resize event, but also called manually when necessary.
//
//  Params:    none
//
//  Returns:  undefined

function FortisEnvironment.prototype.winRefresh()
{
  var clw = document.body.clientWidth;
  var clh = document.body.clientHeight;
  
  if(fortis.lockdiv)
  {
    fortis.lockdiv.style.pixelHeight = clh;
    fortis.lockdiv.style.pixelWidth = clw;
  }

  // Sets wallpaper
  if(fortis.wp_wnd && fortis.wp_wnd.isSet)
  {
    document.body.background = '';

    var url = fortis.wp_wnd.url;
    var x = fortis.wp_wnd.wp_x;
    var y = fortis.wp_wnd.wp_y;

    var cx = document.body.clientWidth;
    var cy = document.body.clientHeight;
    
    var px = 0;
    var py = 0;

    switch(fortis.wp_wnd.wp_mode)
    {
      case 'center' :  px = cx/2 - x/2;
                      py = cy/2 - y/2;

                      if(px < 0) px = 0;
                      if(py < 0) py = 0;
                      break;

      case 'tile'    :  document.body.background = url;
                      break;

      case 'none'    :  break;

      case 'stretch':  x = cx;  
                      y = cy;
                      break;

      case 'fit'    :
      default        :  if(cx/x < cy/y)
                          var zoom = cx/x;
                      else
                          var zoom = cy/y;

                      x *= zoom;
                      y *= zoom;

                      px = cx/2 - x/2;
                      py = cy/2 - y/2;
    }
    
    fortis.wp_wnd.div.style.pixelLeft = px;
    fortis.wp_wnd.div.style.pixelTop = py;
    fortis.wp_wnd.div.style.pixelWidth = x;
    fortis.wp_wnd.div.style.pixelHeight = y;
    fortis.wp_wnd.div.innerHTML = '<IMG SRC="' + url + '" WIDTH="' + x + '" HEIGHT="' + y + '">';
  }

  // Positions menu
  if(fortis.menu_wnd)
    fortis.menu_wnd.div.style.pixelWidth = clw;
  
  // Positions windows bar
  if(fortis.winbar)
  {
    fortis.winbar.wnd.moveTo(0, clh-fortis.botUsed);
    fortis.winbar.wnd.div.style.pixelWidth = clw;
    if(fortis.syscolor)
      fortis.winbar.wnd.div.style.backgroundColor = fortis.syscolor;
    fortis.winbar.update();
  }

  // Position status bar
  if(fortis.statusbar && fortis.statusbar.isOn)
  {
    fortis.statusbar.wnd.moveTo(0, clh-fortis.botUsed+fortis.statusbar.oldBU);
    if(fortis.syscolor)
    {
      fortis.statusbar.wnd.div.style.backgroundColor = fortis.syscolor;
      fortis.statusbar.wnd.div.style.borderColor = fortis.syscolor;
    }
    fortis.statusbar.wnd.div.style.pixelWidth = clw;
  }

  // Restores all maximized windows
  //  (they are not resized with window for efficency reasons)
  if(fortis.maxiwin)
  {
    for(w in fortis.maxiwin)
    {
      if(fortis.maxiwin[w])
      {
        fortis.maxiwin[w].maxrestore();
        fortis.maxiwin[w] = 0;
      }
    }
  }

}

// Ensures calls of function on resize
window.onresize = fortis.winRefresh;

// -- FortisEnvironment method: setBackground --
//  Sets the background image of the environment
//
//  Params:
//    URL:  the URL of the image
//    x:    the width in pixels of the image
//    y:    the height in pixels of the image
//    opts:  the display mode of the image
//      - supported modes:
//        -  'center':    the image is centered and left unchanged
//        - 'tile':      the image is tiled (uses standard HTML)
//        - 'none':      any image is removed
//        -  'stretch':  the image is stretched to fit the window
//        - 'fit':      the image fits the window but keeps its proportions
//      - default:  'fit'
//
//  Returns:  undefined

function FortisEnvironment.prototype.setBackground(url, x, y, mode)
{

  this.wp_wnd.isSet = true;
  this.wp_wnd.url = url;
  this.wp_wnd.wp_x = x;
  this.wp_wnd.wp_y = y;
  this.wp_wnd.wp_mode = mode;

  this.wp_wnd.div.innerHTML = '';

  this.winRefresh();
}

// -- FortisEnvironment method: doDrag --
//  This method is meant to be called by an event handler and
//    enables window dragging. It is not meant to be called by itself.

function FortisEnvironment.prototype.doDrag() 
{
  fortis.dw.div.style.pixelLeft = event.x + 
        document.body.scrollLeft - fortis.dw.dragStartX;

  fortis.dw.div.style.pixelTop = event.y + 
        document.body.scrollTop - fortis.dw.dragStartY;

  if(fortis.dw.div.style.pixelTop > document.body.clientHeight - 25 - fortis.botUsed)
    fortis.dw.div.style.pixelTop = document.body.clientHeight - 25 - fortis.botUsed;

  if(fortis.dw.div.style.pixelTop < fortis.topUsed + 6)
    fortis.dw.div.style.pixelTop = fortis.topUsed + 6;

  event.returnValue = false;
}

// -- FortisEnvironment method: endDrag --
//  This method is meant to be called by an event handler and
//    enables window dragging. It is not meant to be called by itself.

function FortisEnvironment.prototype.endDrag() 
{
  document.onmousemove = null;
  document.onmouseup = null;

  fortis.dw.SD.style.zIndex = 0;
  fortis.dw.x = fortis.dw.div.style.pixelLeft;
  fortis.dw.y = fortis.dw.div.style.pixelTop;

  if (window.FMS_menuCount && !fortis.modwin)
  {
    window.FMS_menuLock = false;
  }

  event.returnValue = false;
}


// -- FortisEnvironment method: doResize --
//  This method is meant to be called by an event handler and
//    enables window resizing. It is not meant to be called by itself.

function FortisEnvironment.prototype.doResize() 
{
  if(fortis.dw.dragStartX)
  {
    fortis.dw.div.style.pixelWidth =  event.x - fortis.dw.x +
          document.body.scrollLeft - fortis.dw.dragStartX + fortis.dw.width;

    if(fortis.dw.div.style.pixelWidth < 100)
        fortis.dw.div.style.pixelWidth = 100;
  }

  if(fortis.dw.dragStartY)
  {
    fortis.dw.div.style.pixelHeight = event.y - fortis.dw.y +
          document.body.scrollTop - fortis.dw.dragStartY + fortis.dw.height;

    if(fortis.dw.div.style.pixelHeight < 25)
      fortis.dw.div.style.pixelHeight = 25;
  }

  event.returnValue = false;
}


// -- FortisEnvironment method: endResize --
//  This method is meant to be called by an event handler and
//    enables window resizing. It is not meant to be called by itself.

function FortisEnvironment.prototype.endResize() 
{
  document.onmousemove = null;
  document.onmouseup = null;

  fortis.dw.SD.style.zIndex = 0;
  fortis.dw.width = fortis.dw.div.style.pixelWidth;
  fortis.dw.height = fortis.dw.div.style.pixelHeight;
  if(fortis.dw.style)
    fortis.dw.applyStyle();

  if (window.FMS_menuCount && !fortis.modwin)
  {
    window.FMS_menuLock = false;
  }

  event.returnValue = false;
}


// -- FortisEnvironment method: endModal --
// Restores system state after the Window.goModal method was called.
//
//  Params:    none
//
//  Returns:  undefined
function FortisEnvironment.prototype.endModal()
{
  if(this.modwin)
  {
    this.lockdiv.style.visibility = 'hidden';
    this.modwin.div.style.zIndex = fortis.zidx++;
    window.FMS_menuLock = false;
    this.winbar.wnd.div.style.visibility = 'visible';

    this.modwin.isModal = false;
    this.modwin.applyStyle();

    this.modwin = 0;
  }
}

//EOF: fortis.js  
  </SCRIPT>
  <!-- fms.js -->  
  <SCRIPT LANGUAGE="JavaScript">
/*
  Fortis Menu System (FMS) version 0.22 (Alpha)
  Copyright (C) 2001 Daniele Pagano 

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  You can contact the author by email at fortis@esaurito.com. 
  
  Requirements: Microsoft Internet Explorer 4 or later.

  Info: with FortisMS you can easily create complex menus that
        use the power of Dynamic HTML to contain any text, image, or
        other HTML and execute any JavaScript code when clicked.
        Also available from the same author is Fortis, a fully 
        dynamic windowing environment which includes this menu 
        system as a subset.

   Website: for more information, latest versions, and code examples
            for FortisMS and Fortis, visit www.esaurito.com/fortis.

   Use: To create a menu, just create a FMS_Menu object with the
        default or a custom FMS_MenuStyle, and populate it with FMS_SubMenu 
        and FMS_MenuEntry objects. You can either pass the constructor an
        array with these elements or add them one by one. Then use one 
        of the installation techniques as follows to deploy and use the 
        menu. You can then enable and disable menu entries (as long as every
        submenu has a different name), and perform other operations during the 
        menu life. For more information on how to create FMS_Menu, FMS_MenuStyle, 
        FMS_SubMenu, and FMS_MenuEntry objects, and all the available options, 
        refer to the code documentation later in this file.

    Installation: there are different ways to deploy FortisMS:

    1) Dynamic: menus are created on-the-fly by the client browser from your code. 
       All the code is visible, and it's processed after the page and the code
       are loaded. To deploy follow these steps:

       a) add this file (or paste the code) to your page anywhere 
          before your code, i.e.


       b) add code to create the menus, then use something like 
          document.write(menu_name) to output them anywhere in your page, or
          use a dynamic environment like Fortis to insert the menu in another
          component.

    2) Static: menus are created beforehand and their HTML is manually
       inserted into the page. This hides all the generating code, and requires
       minimal post-load processing. If you want to use multiple menus on 
       one page, you can do so as long as they are created from the same run of
       your code (to keep the id's unique); you can then scatter the menus on
       the page as you wish. You cannot use static deployment in an page that
       renders the code available to the browser after the page is parsed (i.e. in
       response to an event: that's the typical use of the other option), since 
       the SCRIPT tags required for the static menus to work are then ignored. 
       To deploy follow these steps:

       a) use the steps for case 1 on a test page, and output the text somewhere 
          you can copy from (i.e. create an  HTML text area and assign its value to 
          the created menu).

       b) put the HTML from step (a) anywhere you want it on your actual page.
*/

//  GLOBALS:
  window.FMS_menuCount = 0;                //  To generate unique menu id's.

// Copyright and warranty disclaimer to appear in every menu HTML
  window.FMS_notice = "Created with FortisMS 0.22 (Alpha), Copyright (C) 2001-02 Daniele Pagano.\n" +
                      "FortisMS comes with ABSOLUTELY NO WARRANTY. It is also free software,\n" +
                      "and you are welcome to redistribute it under certain conditions; for\n" +
                      "details on these conditions and on the warranty, see fms.js or\n" +
                      "visit www.esaurito.com/fortis.\n";

/* -- CODE FOR BETTER COMPATIBILITY WITH DYNAMIC ENVIRONMENTS --
  Since some dynamic environments (like Fortis) include the menus within
  other dynamic page components, the SCRIPT tags outputted by the toString
  functions are ignored, since they are created after the page is parsed.
  For these cases, you need code that enables that JavaScript in a dynamic manner. 
  In such environments you will obviously want to use dynamic deployment anyway. This
  code does not affect the output for static deployment. If you find yourself in a
  situation where the SCRIPT tags do not work and you can only use static deployment, 
  you can paste this code before your menu to avoid errors, but you will not be able
  to use these functions because there would be no way to register the menus in the
  arrays (you can try at most pasting the SCRIPT tags somewhere else before your menu).
*/

// Assocative arrays for dynamic menu enabling/disabling.

  window.FMS_disabledMenus = new Array();    // Disabled submenus register a true on the value associated 
                                            // with their id.
  window.FMS_menuIds = new Array();          // Associated submenu title with menu id's. If two submenus
                                            // have the same title, only the last one register will be
                                            // accessible. Hint: if you need submenus with the same name
                                            // displayed, use HTML witespace (like spaces) since it's
                                            // ignored in HTML rendering but is recorded as a different
                                            // entry in the arrays. Just keep track of what is what.
  window.FMS_menuColsD = new Array();        // Associates submenu titles with their disabled entry color.
  window.FMS_menuColsE = new Array();        // Associates submenu titles with their enabled entry color.


// Arrays use to unlock and close menus

  window.FMS_lockedMenus = new Array();      // Locked submenus (HTML entries) register here.
  window.FMS_closerArray = new Array();      // Every menu registers its closing Java Script
                                            // code in this. For mass closing.

// Total menu lock

  window.FMS_menuLock = false;              // When true, no menu can be opened

// FMS_closeMenus()
//  Closes all open menus and submenus in the page. Use to unlock HTML that 
//  contain items like forms that remain open but are closed on command.
//
//  Parameters: none.

  function FMS_closeMenus()
  {
    window.FMS_lockedMenus = new Array();
    for(m in window.FMS_closerArray)
      eval(window.FMS_closerArray[m]);
  }


// FMS_disable()
//  Disables a submenu entry. Call this after the menu is rendered.
//
//  Parameters:
//    menu: the name of the menu in which the entry to disable is.
//    entry: the position of the entry from the top of the menu (starting from 1)
  
function window.FMS_disable(menu, entry)
{
  var sid = window.FMS_menuIds[menu] + '_' + (entry - 1);  
  if(eval('document.all.' + sid))
  {
    eval('window.FMS_disabledMenus[\'' + sid + '\'] = 1');
    eval('document.all.' + sid + '.style.color = \'' + window.FMS_menuColsD[menu] + '\';');
  }
}


// FMS_enable()
//  Enables a previously disabled submenu entry. Call this after the menu is rendered.
//
//  Parameters:
//    menu: the name of the menu in which the entry to enable is.
//    entry: the position of the entry from the top of the menu (starting from 1)

function window.FMS_enable(menu, entry)
{
  var sid = window.FMS_menuIds[menu] + '_' + (entry - 1);  
  if(eval('document.all.' + sid))
  {
    eval('window.FMS_disabledMenus[\'' + sid + '\'] = 0');
    eval('document.all.' + sid + '.style.color = \'' + window.FMS_menuColsE[menu] + '\';');
  }
}
// -- END OF CODE FOR BETTER COMPATIBILITY WITH DYNAMIC ENVIRONMENTS --


/* -- FMS_MenuStyle --
  This function generates an object that can be passed to one or more FMS_Menu objects
  to achieve custom styling of your menu. It can be used for several menus.

  Parameters (all optional) & properties:
    bgColor: specifies the background color of your menu. You can use either
        HTML color codes (i.e. #12BC4F) or recognized color mnemonics (i.e. 'red');
        Default: 'white'
    fgColor: specifies the foreground color of your menu. Same format as the other.
        Default: 'black'
    font: the font face name (or list thereof) for your menu. You can specify a
        list of fonts that are tried in order like in HTML or CSS.
        Default: 'sans-serif'
    fontSize: the font size (in points) of yuor font. This uses CSS to avoid
        font resizing due to browser settings.
        Default: 9
    disabledColor = specifies the color of your menu's disabled entries and dividers. 
        Same format as the other color parameters.
        Default: 'gray'
    separator: what to insert between your topmost menu entries. You can add spaces
        to separate the entries more, since they are ignored in HTML, but the length
        of the string you provide is used to space the menus, or a separator like '|'.
        Default: '' (empty string, minimal separation)
    subind: the little indicator (usually and arrow of sorts) used to distinguish
        a submenu entry that contains another submenu. It can be any string or HTML,
        includin image tags, provided is within 20 pixel in width, and your submenu 
        element in height (when rendered) for a decent output. If you do not specify 
        the color, the menu color will be used, and it will even be inverted with 
        the rest of the entry when highlighted. Same for the background. If you do
        specify a color and/or a background color, or if it's a image tag or such,
        the colors will remain unchanged when the entry is highlighted (the element
        surrondings will behave normally in any case).
        Default: '>' (close angle bracket; crude, but portable)
    menuh: correction of positioning of your drop-down menus. If you use a font
        or font size particularly tall or short, you may want to adjust this
        value to prevent covering or too much distance (causes closing) between
        the menu bar and the drop-down menus. The amount indicates how many points
        up you have to move the drop-down menus. You can use a negative value to
        move the menu down and avoid covering.
        Default: 0;
    popup: set to true if you like a pop-up menu behavior for your menu. This is indicated
        for menus with a single item in them, since the menu will not close when mouse
        moves out of the menu pane. Also, you will need to click on the item to close the
        menu. Clicking again anywhere in it will close it and require another click
        to open it, and that includes clicking on a nested submenu (which will still 
        open when the mouse moves over it), so this is indicated for simple, 
        one-level menus that behave like a button that pops up a list of options. 
        Default: 0;
*/
  function FMS_MenuStyle(bgColor, fgColor, font, fontSize, disabledColor, separator, subind, menuh, popup)
  {
    this.bgcolor = bgColor?bgColor:'white';
    this.fgcolor = fgColor?fgColor:'black';
    this.font = font?font:'sans-serif';
    this.fontSize = fontSize?fontSize:9;
    this.disabledColor = disabledColor?disabledColor:'gray';
    this.separator = separator?separator:'';
    this.subind = subind?subind:'>';
    this.menuh = menuh?menuh:0;
    this.popup = popup?popup:0;
  }

/* -- FMS_Menu --
  Use this constructor to generate menu objects that return the HTML that
  is the menus. Each menu can only be used once, since multiple instances will
  create multiple id's that will confuse the broswer. If you need multiple
  instances, you can loop on the code that generates the menu (and the submenus)
  and the call to the 'new' operator will generate copies you can use independently.
  Same for FMS_SubMenu and FMS_MenuEntry objects.

  Parameters (all optional):
    els: an array of FMS_SubMenu and/or FMS_MenuEntry objects to populate the menu.
        Default: an empty array is created for later use.
    style: an FSM_MenuStyle object to customize the menu appearance,
        Default: a style with default valuse is used.
    width: the width of the menu bar in pixels. The menu bar is behind the menu 
        elements, which  are visible with any size of it, but this can wrap them 
        nicely. It is important  to use when you rely on HTML to position this 
        element, since this is the value that the broswer can see. Also of use 
        when different fonts in menus give an  irregularly bottomed edge to the menu; 
        this can make it even.
        Default: 0;

  Properties for public use: none.

  Methods (see the later for more info):
    NOTE: these methods only make sense if used before the HTML is placed in 
    the page,  afterwards they have no effect.

    add: allows you to add an element to the menu after the constructor is invoked.
    setStyle: allows you to change the style after the constructor is invoked.
    setWidth: allows you to change the width after the constructor is invoked.
    toString: called automatically when evaluating the object itself as a string
      (i.e. in document.write(menu_object) ), it returns the HTML of the working
      menu. It can also be called manually.
*/
  function FMS_Menu(els, style, width)
  {
    this.id = 'FMS_MENU_ID' + window.FMS_menuCount++;
    this.submenus = new Array();

    if(!els)
      this.els = new Array();
    else
      this.els = els;

    if(!style)
      this.setStyle(new FMS_MenuStyle());
    else
      this.setStyle(style);
    
    if(!width)
      this.setWidth(0);
    else
      this.setWidth(width);
    
    for(el in this.els)
      this.add(this.els[el]);
  }


// FMS_Menu.add
//  Adds an FMS_SubMenu or FMS_MenuEntry to the menu.
//
//  Parameters:
//    sub: reference to an FMS_SubMenu or FMS_MenuEntry object.
  function FMS_Menu.prototype.add(sub)
  {
    var pos = this.submenus.length;
    this.submenus[pos] = sub;
  }


// FMS_Menu.setStyle
//  Applies an FMS_MenuStyle to the menu.
//
//  Parameters:
//  style: reference to an FMS_MenuStyle object.
  function FMS_Menu.prototype.setStyle(style)
  {
    this.style = 'position:relative; ';
    this.style += 'height: ' + (style.fontSize*1.8) + 'pt; ';
    this.style += 'background-color: ' + style.bgcolor + '; ';
    this.style += 'color: ' + style.fgcolor + '; ';
    this.style += 'border-width: none; ';
    this.style += 'border-style: none; ';
    this.style += 'overflow: visible; ';
    this.style += 'visibility: inherit; ';
    this.style += 'cursor: default; ';
    this.style += 'font-family: ' + style.font + '; ';
    this.style += 'font-size: ' + style.fontSize + 'pt; ';

    this.height = style.fontSize*1.8;
    this.bgcolor = style.bgcolor;
    this.fgcolor = style.fgcolor;
    this.font = style.font;
    this.fontSize = style.fontSize;
    this.separator = style.separator;
    this,subind = style.subind;
    this.menuH = style.menuh;
    this.disColor = style.disabledColor;
    this.popup = style.popup;
  }


// FMS_Menu.setWidth
//  Sets the width of the menu
//
//  Parameters:
//    w: the width in pixel of the menu.
  function FMS_Menu.prototype.setWidth(w)
  {
    this.width = w;
  }
  
// FMS_Menu.toString
//  Returns the menu HTML to be placed in the page. Check the
//  note before the constructor for multiple instances.
//
//  Parameters: none
  function FMS_Menu.prototype.toString()
  {
    var html = '';
    var thecloser = '';
    this.cursor = 0;

    for(var i = 0; i < this.submenus.length; i++)
    {
      var subref = 'document.all.' + this.submenus[i].id;
      var subcnt = this.submenus.length;
      var titleid = this.id + '_' + i;
      thecloser += "document.all." + titleid + ".style.color = \\'" + this.fgcolor + "\\'; " +
        "document.all." + titleid + ".style.backgroundColor = \\'" + this.bgcolor + "\\'; ";

      if(this.popup)
      {
        var open = "if(window.FMSopen" + this.id + " && !window.FMS_menuLock) " + 
                    subref + ".style.visibility = 'visible';";
        var close = ""; 
        var toggle = "if(!window.FMSopen" + this.id + " && !window.FMS_menuLock) {window.FMSopen" + 
                     this.id + " = true;" + open +  "} else { window.FMSopen" + this.id + 
                     " = false; if(!window.FMS_lockedMenus['" + titleid + "']) " + 
                     subref + ".style.visibility = 'hidden'; }";
      }
      else
      {
        var open = "if(!window.FMS_menuLock) " + 
                    subref + ".style.visibility = 'visible';";
        var close = "if(!window.FMS_lockedMenus['" + titleid + "']) " + 
                    subref + ".style.visibility = 'hidden';"; 
        var toggle = "";
      }

      if(this.fgcolor == 'black' || this.fgcolor == '#000000') 
        bcolor = 'DDDDDD'
      else
        bcolor = this.fgcolor;
      
      var hilite = "if(!FMS_menuLock) {style.color = '" + this.bgcolor + "';" +
        "style.backgroundColor = '" + this.fgcolor + "';}";
      var normal = "style.color = '" + this.fgcolor + "';" +
        "style.backgroundColor = '" + this.bgcolor + "';";

      this.submenus[i].bgcolor = this.bgcolor;
      this.submenus[i].fgcolor = this.fgcolor;
      this.submenus[i].font = this.font;
      this.submenus[i].fontSize = this.fontSize;
      this.submenus[i].menuH = this.menuH;
      this.submenus[i].subind = subind;
      this.submenus[i].disColor = this.disColor;
      this.submenus[i].rootId = titleid;

      if(!this.submenus[i].width)
        var mw = this.submenus[i].title.getFMSLen();
      else
        var mw = this.submenus[i].width;

      if(this.submenus[i].ownFont)
        var font = 'font-family: ' + this.submenus[i].ownFont + ';';
      else
        var font = '';

      html += '\n<DIV TITLE="' + this.submenus[i].caption + '" ALIGN="center" ID="' + titleid + 
        '" onclick="' + toggle + this.submenus[i].action + 
        '" onmouseover="' + hilite + open +
        '" onmouseout="' + normal + close + 
        '" STYLE="position: absolute; '+ font +' background-color: ' +
        this.bgcolor + '; color: ' + this.fgcolor + '; padding: 3px; width:' + mw + 'pt; left:' + this.cursor + 'pt">\n' + 
        this.submenus[i].title + this.submenus[i] + '</DIV>\n';
      
      this.cursor += mw;          
      
      if(i != (this.submenus.length - 1))
      {
        var sepw = this.separator.getFMSLen();

        html += '\n<DIV STYLE="position: absolute; background-color: ' +
        this.bgcolor + '; color: ' + this.fgcolor + '; padding 3px; width:' + sepw + 'pt; left:' + this.cursor + 'pt">\n' + 
        this.separator + '</DIV>\n';

        this.cursor += sepw;
      }
      
      if(this.submenus[i].closestr) thecloser += this.submenus[i].closestr;
    }


  var disable = "function window.FMS_disable(menu, entry)\n{\n\tvar sid = window.FMS_menuIds[menu] + '_' + (entry - 1);  \n\tif(eval('document.all.' + sid))\n\t{\n\t\teval('window.FMS_disabledMenus[\\'' + sid + '\\'] = 1');\n\t\teval('document.all.' + sid + '.style.color = \\'' + window.FMS_menuColsD[menu] + '\\';');\n\t}\n}\n\n";

  var enable = "function window.FMS_enable(menu, entry)\n{\n\tvar sid = window.FMS_menuIds[menu] + '_' + (entry - 1);  \n\tif(eval('document.all.' + sid))\n\t{\n\t\teval('window.FMS_disabledMenus[\\'' + sid + '\\'] = 0');\n\t\teval('document.all.' + sid + '.style.color = \\'' + window.FMS_menuColsE[menu] + '\\';');\n\t}\n}\n\n";

  var close = "\nfunction window.FMS_closeMenus()\n{\n\twindow.FMS_lockedMenus = new Array();\n\tfor(m in window.FMS_closerArray)\n\t\teval(window.FMS_closerArray[m]);\n}\n";

    var globals = "if(!window.FMS_disabledMenus)\n\twindow.FMS_disabledMenus = new Array();\n\nif(!window.FMS_menuIds)\n\twindow.FMS_menuIds = new Array();\n\nif(!window.FMS_menuColsD)\n\twindow.FMS_menuColsD = new Array();\n\nif(!window.FMS_menuColsE)\n\twindow.FMS_menuColsE = new Array();\n\nif(!window.FMS_closerArray)\n\twindow.FMS_closerArray = new Array();\n\nwindow.FMS_lockedMenus = new Array();\n" + close + enable + disable;

    var closeme = "\nwindow.FMS_closerArray['" + 
      this.id + "'] =\n'" + thecloser + "';";

    eval(closeme);  // For compatibility with dynamic environments

    var prehtml = '<!'+'-- START OF FortisMS Menu HTML \n' + window.FMS_notice + 
    '--> \n<'+'SCRIPT>\n' + globals + closeme + '\n<'+'/SCRIPT>\n';

    prehtml += '\n<SPAN ID="' + this.id + 
      '" STYLE="' + this.style + ' width: ' + this.width + 'px;">\n';

    return prehtml + html + '</SPAN>\n<!'+'-- END OF FortisMS Menu HTML -->';
  }


// END OF FMS_Menu

/* -- FMS_SubMenu --
  
  Use this constructor to generate submenus at any level under the main menu.
    SubMenus can be nested, just like in normal windowing systems.

  Parameters (all optional):
    title: the HTML to display in the menu. If you do not supply a plain string,
        make sure you specify a width value for nicer output.
        Default: the object id.
    width: the width (in points) of the item in the menu bar. It does not apply
        for submenus of lower levels, where the innerWidth of the parent menu
        is used instead.
        Default: an automatically calculated value based on an average font and size
        calculated by analazing the characters in the string. This includes any tag,
        so make sure you specify the width for strange fonts or titles.
    innerWidth: the width (in points) of the collapsable submenu that opens on mouseover.
        Default: an automatically calculated value based on an average font and size
        calculated by analazing the characters in the title of the longest title string
        of the elements of the submenus. This includes any tag,  so make sure you specify 
        your value for strange fonts or entry titles.
    ownFont: the font face name (or list thereof) of this submenu. You can specify a
        list of fonts that are tried in order like in HTML or CSS.
        Default: the normal menu font
    caption: a message that appears when the mouse stays on the submenu title for a couple
        of seconds. Useful for giving explanations or instructions. This is implemented
        with the TITLE property in HTML and therefore handled by the browser (it's like 
        an ALT property for an IMG tag).
        Default: no caption.

  Properties for public use: none.

  Methods:
    add: use it to add an entry to the submenu.
    addSub:  use it to nest submenus.
    addHTML: use it to add an HTML component that resides in its own submenu and
      stays locked open on mouseover until you call FSM_CloseMenus();
    toString: called automatically when evaluating the object itself as a string.
      It's used bu FMS_Menu.toString and it's not useful when called manually.
*/
  function FMS_SubMenu(title, width, innerWidth, ownFont, caption)
  {
      this.id = 'FMS_' + window.FMS_menuCount++;
      this.title = title?title:this.id;
      this.width = width;
      this.innerWidth = innerWidth;
      this.ownFont = ownFont;
      this.caption = caption?caption:'';
      
      this.action = ';';

      this.SubItem = function(name, content, isSub, icon, elh, caption, isHtml)
      {
        this.name = name;
        this.content = content;
        this.isSub = isSub;
        this.icon = icon;
        this.elh = elh;
        this.caption = caption?caption:'';
        this.isHtml = isHtml;
      }

      this.items = new Array();
      this.sub = new Array();
      this.subchain = new Array();
  }

/* - FMS_SubMenu.add
  Adds an entry to the submenu.

  Parameters (all optional):
    title: the HTML to display in the menu. If you do not supply a plain string,
          make sure you specify a width value for nicer output. If you specify
          'DIVIDER', or nothing at all, as the name, you will get a menu divider. 
          Default: 'DIVIDER';
    action: the JavaScript to execute when the item is clicked.
          Default: ';' (no action, useful for self-behaving elements like links).
    icon: the icon to place to the left of the entry. Its size is automatically
          calculated making the image a square with the side equal to the
          entry's height. The paramenters contains the URL of the image. All 
          the other elements are indented to the size of the largest
          icon. If you specify the character '*' the icon is not placed, the
          space to the left zeroed, and the item does not highlight or respond to 
          clicks. Useful if you want to place some raw HTML in the menu. Check also
          the addHTML method to see which one fits your needs.
          Default: no icon.
    elh:  overrides the automatic size mesurment and allows you to set an arbitraty
          item hight (and consequently icon size and indentation). In conjunction
          with the '*' icon and the innerWidth property, allows you to reserve an
          arbitrary space for any HTML you want to display.
          Default: normal item height,
    caption: a message that appears when the mouse stays on the element for a couple
          of seconds. Useful for giving explanations or instructions. This is implemented
          with the TITLE property in HTML and therefore handled by the browser (it's like 
          an ALT property for an IMG tag).
          Default: no caption
*/
function FMS_SubMenu.prototype.add(name, action, icon, elh, caption)
  {
    var pos = this.items.length;
    name = name?name:'DIVIDER';
    action = action?action:';';
    this.items[pos] = new this.SubItem(name, action, 0, icon, elh, caption);
  }


/* - FMS_SubMenu.addSub
  Adds a submenu to the submenu.

  Parameters:
    sub: reference to an FMS_SubMenu object.
    icon: the icon to place to the left of the entry. Its size is automatically
          calculated making the image a square with the side equal to the
          entry's height. All the other elements are indented to the size of the largest
          icon. The paramenters contains the URL of the image.
          Default: no icon.
    elh:  overrides the automatic size mesurment and allows you to set an arbitraty
          item hight (and consequently icon size and indentation).
          Default: normal item height,
*/
function FMS_SubMenu.prototype.addSub(sub, icon, elh)
  {
    var pos = this.items.length;
    this.items[pos] = new this.SubItem(sub.title, sub, 1, icon, elh);
    this.sub[sub.title] = sub;
  }


/* - FMS_SubMenu.addHTML
  Adds an HTML component that opens like a submenu of the submenu.
  This submenu has the special property that once the user goes over
  it with the mouse, it stays open togheter with the whole menu
  structure that preceded it until the function FMS_closeMenus() is called.
  This allows to use forms and other interactive components. If you do
  not desire this behavior, use a normal submenu entry with a '*' icon
  instead. 

  WARNING: One side-effect of the submenu behavior is that, while other
  submenus (from the root) work normally, the submenus from the same root
  that are open while the menu is locked, stay open as well. If this really
  bothers you, you can disable such entries and then enable then back later.
  Such submenus always stay behind the HTML menus to avoid input problems.

  Parameters (all optional):
    name: the HTML to display as the title of the submenu that contains the HTML.
          Default: the id of the submenu created to allocate the HTML
    html: the HTML to render. 
          Default: no HTML;
    width: the width in points of the submenu that contains the HTML.
          Default: a width equal to the normal item height.
    height: the height in points of the submenu that contains the HTML.
          Default: the heigth of a normal menu entry.
    icon: the icon to place to the left of the entry. Its size is automatically
          calculated making the image a square with the side equal to the
          entry's height. The paramenters contains the URL of the image. All 
          the other elements are indented to the size of the largest icon.
          The paramenters contains the URL of the image.
          Default: no icon.
    elh:  overrides the automatic size mesurment and allows you to set an arbitraty
          item hight (and consequently icon size and indentation).
          Default: normal item height,
    caption: a message that appears when the mouse stays on the element for a couple
          of seconds. Useful for giving explanations or instructions. This is implemented
          with the TITLE property in HTML and therefore handled by the browser (it's like 
          an ALT property for an IMG tag).
          Default: no caption
*/
function FMS_SubMenu.prototype.addHTML(name, html, width, height, icon, elh, caption)
  {
    var pos = this.items.length;
    html = html?html:'';
    width = width?width:1;
    var thesub =  new FMS_SubMenu(name, 0, width, '', caption);
    thesub.add(html, '', '*', height);
    this.items[pos] = new this.SubItem(thesub.title, thesub, 0, icon, elh, '', 1);
  }


//- FMS_SubMenu.toString
//  Returns the submenu HTML. It's supposed to be called by FMS_Menu.toString.
//
//  Parameters: none
  function FMS_SubMenu.prototype.toString()
  {
    var selfref = 'document.all.' + this.id;
    
    this.cursor = 0;
    var elh = this.fontSize * 1.4;

    var closeparents = 'FMS_closeMenus();';

    this.closestr = "document.all." + 
      this.id + 
      ".style.visibility = \\'hidden\\'; ";

    if(!this.innerWidth)
    {
      var maxlen = 0;
      for(var i = 0; i < this.items.length; i++)
        maxlen = Math.max(maxlen, (this.items[i].name.getFMSLen())); 
        maxlen += elh;
    }
    else
      var maxlen = this.innerWidth

    if(!this.menuH)
      var mh = (this.fontSize * 1.4);
    else
      var mh = (this.fontSize * 1.4) - this.menuH;
    
    maxlen += 6;

    var indent = elh;
    var menuh = 6;

    for(var i = 0; i < this.items.length; i++)
    {
      if(this.items[i].elh)
        if(this.items[i].icon != '*')
          indent = Math.max(this.items[i].elh, indent);

      if(this.items[i].name == 'DIVIDER') 
        menuh += elh / 2;
      else
        menuh += elh;

      if(this.items[i].elh)
        menuh += this.items[i].elh - elh;
    }

    maxlen += indent;

// For compatibility with dynamic environments
    window.FMS_menuIds[this.title] = this.id;
    window.FMS_menuColsE[this.title] = this.fgcolor;
    window.FMS_menuColsD[this.title] = this.disColor; 

    var disreg = "window.FMS_menuIds['" + this.title + "'] = '" + this.id + 
    "'; window.FMS_menuColsE['" + this.title + "'] = '" + this.fgcolor + 
    "'; window.FMS_menuColsD['" + this.title + "'] = '" + this.disColor +
    "'; ";

    var html = '\n<'+'SCRIPT>\n'+disreg+'\n</'+'SCRIPT>\n\n<DIV ALIGN="left" ID="' + this.id +
      '" STYLE="position: absolute; overflow:visible; visibility:hidden; background-color: ' +
      this.bgcolor + '; color: ' + this.fgcolor + '; height:' + 
      menuh + 'pt; width:' + maxlen + 'pt; top:' + 
      + mh + 'pt; left: 2px; border: 1px ' + this.bgcolor + ' outset">\n';

    this.cursor = 2;
    maxlen -= 2;

    for(var i = 0; i < this.items.length; i++)
    {
      var elh = this.fontSize * 1.4;
      var item = this.items[i].name;
      var sid = this.id + '_' + i;

      var hilite = "style.backgroundColor = '" + 
        this.fgcolor + "'; if(!window.FMS_disabledMenus['" + 
          sid + "']) style.color = '" + 
        this.bgcolor + "';";
      var normal = "style.backgroundColor = '" + 
        this.bgcolor + "'; if(!window.FMS_disabledMenus['" + 
          sid + "']) style.color = '" + 
        this.fgcolor + "'; else style.color = '" + this.disColor + "';";

      if(this.items[i].elh)
        elh = this.items[i].elh;

      var themiddle = elh/2 - (this.fontSize*1.4)/2;

      var arrow = '\n<DIV ID="FMS_ARROW" STYLE="position:absolute; height:' + elh + 
        'pt; top:' + themiddle + 'pt; width:' + 30 + 'pt; text-indent:' + 
        indent + 'pt; left:'+  (maxlen - indent - 14) + 'pt">\n' + this.subind + '</DIV>\n';

      if(this.items[i].icon)
      {
        if(this.items[i].icon == '*')
        {
          var icon = '';
          themiddle = 0;
          hilite = '';
          normal = '';
          closeparents = '';
        }
        else
        {
          var icon = '\n<DIV ID="FMS_ICON" STYLE="position: absolute; border: none; height:' + elh + 
          'pt; top:1pt; width:' + elh + 'pt; text-indent:0pt; left:1pt">' +
            '<IMG WIDTH=' + (elh*1.25) + ' HEIGHT=' + (elh*1.25) + ' SRC="' + this.items[i].icon + '">\n' + 
            '</DIV>\n';
        }
      }
      else
        var icon = '';

      var entry = '\n<DIV ID="FMS_MENUENTRY" STYLE="position: absolute; border: none; height:' + elh + 
          'pt; top:' + themiddle + 'pt; width:' + maxlen + 'pt; text-indent:' + 
          indent + 'pt; left:1pt">\n' +  item + '</DIV>\n';

      if(item == 'DIVIDER')
      {
        var bdrcolor = this.disColor;
        
        html += '\n<DIV ID="' + sid + '" STYLE="position: absolute; background-color: ' +
        this.bgcolor + '; color: ' + this.fgcolor + '; height:2px; top:' + 
        (this.cursor + elh/4) + 'pt; border-top-style: inset; height:' + 
        elh/2 + 'pt; border-top-width: 2px; border-top-color: ' +
        bdrcolor + '; width:' + (maxlen - 4) + 'pt; text-indent:0px; left:2pt"></DIV>\n';  

        this.cursor += elh/2;
      }
      else if(this.items[i].isSub) 
      {
        this.idx = i;
        var subref = 'document.all.' + this.items[i].content.id;

        var open = "if(!window.FMS_disabledMenus['" + sid + "'] && !window.FMS_menuLock) {" + 
          subref + ".style.visibility = 'visible';" +
          subref + ".style.left = '" + (maxlen-1) + "pt';" +
          subref + ".style.pixelTop = 0;}";
        var close  = "if(!window.FMS_lockedMenus['" + this.rootId + "']) " + subref + ".style.visibility = 'hidden';";

        this.items[i].content.bgcolor = this.bgcolor;
        this.items[i].content.fgcolor = this.fgcolor;
        this.items[i].content.parentMenu = this;
        this.items[i].content.font = this.font;
        this.items[i].content.fontSize = this.fontSize;
        this.items[i].content.subind = this.subind;
        this.items[i].content.disColor = this.disColor;
        this.items[i].content.rootId = this.rootId;

        html += '\n<DIV TITLE="' + this.items[i].content.caption + '" ID="' + sid + 
          '"; onmouseover="' + hilite + open +
          '" onmouseout="' + normal + close +
          '" STYLE="position: absolute; overflow:visible; height:' + elh + 'pt; top:' + 
        this.cursor + 'pt; z-index: 0; width:' + maxlen + 'pt; text-indent:' + indent + 'pt; left:0pt">\n' + 
        icon + entry + arrow + this.items[i].content + '</DIV>\n' ;
        
        this.cursor += elh;
        if(this.items[i].content.closestr) this.closestr += this.items[i].content.closestr;

      }
      else if(this.items[i].isHtml) 
      {
        this.idx = i;
        var subref = 'document.all.' + this.items[i].content.id;

        var open = "if(!window.FMS_disabledMenus['" + sid + "'] && !window.FMS_menuLock) {" + 
          subref + ".style.visibility = 'visible';" +
          subref + ".style.left = '" + (maxlen-1) + "pt';" +
          subref + ".style.pixelTop = 0;}";

        var close  = "if(!window.FMS_lockedMenus['" + this.rootId + "']) " + subref + ".style.visibility = 'hidden';";
        
        this.items[i].content.bgcolor = this.bgcolor;
        this.items[i].content.fgcolor = this.fgcolor;
        this.items[i].content.parentMenu = this;
        this.items[i].content.font = this.font;
        this.items[i].content.fontSize = this.fontSize;
        this.items[i].content.isHtml = true;
        this.items[i].content.rootId = this.rootId;

        html += '\n<DIV TITLE="' + this.items[i].content.caption + '" ID="' + sid + 
          '"; onmouseover="' + hilite + open +
          '" onmouseout="' + normal + close +
          '" STYLE="position: absolute; overflow:visible; height:' + elh + 'pt; top:' + 
        this.cursor + 'pt; z-index: 1; width:' + maxlen + 'pt; text-indent:' + indent + 'pt; left:0pt">\n' + 
        icon + entry + arrow + this.items[i].content + '</DIV>\n' ;
        
        this.cursor += elh;
        if(this.items[i].content.closestr) this.closestr += this.items[i].content.closestr;
      }
      else
      {
        if(!this.isHtml)
        {
          var actioner = "if(!window.FMS_disabledMenus['" + sid + "'] && !window.FMS_menuLock) " +
            this.items[i].content;

          html += '\n<DIV TITLE="' + this.items[i].caption + '" ID="' + sid + 
          '" onclick="' + normal + closeparents + actioner + 
          '"; onmouseover="' + hilite + 
          '" onmouseout="' + normal + 
          '" STYLE="position: absolute; height:' + elh + 'pt; top:' + 
          this.cursor + 'pt; width:' + maxlen + 'pt; text-indent:' + indent + 'pt; left:0pt">\n' + 
          icon + entry + '</DIV>\n';  

          this.cursor += elh;
        }
        else
        {
          var lock = "window.FMS_lockedMenus['" + this.rootId + "'] = 1;";

          html += '\n<DIV TITLE="' + this.items[i].caption + '" ID="' + sid + 
          '"; onmouseover="' + lock + 
          '" STYLE="position: absolute; height:' + elh + 'pt; top:' + 
          this.cursor + 'pt; width:' + maxlen + 'pt; text-indent:' + indent + 'pt; left:0pt">\n' + 
          icon + entry + '</DIV>\n';  

          this.cursor += elh;
        }

      }    
  
    }
    
    return html += '</DIV>\n';
  }


// END OF FMS_SubMenu
  
/* -- FMS_MenuEntry --
  
  Use this constructor to generate menu entries that reside on the menu bar, and
    not in a submenu, but can be clicked to generate behavior instead of opening
    a submenu. It's essentially  a FMS_SubMenu with no content an action property, 
    so it cannot be  disabled.

  Parameters (all optional):
    title: the HTML to display in the menu. If you do not supply a plain string,
        make sure you specify a width value for nicer output.
        Default: the object id.
    action: the JavaScript to execute when the item is clicked.
        Default: ';' (no action, useful for self-behaving elements).
    width: the width (in points) of the item in the menu bar.
        Default: an automatically calculated value based on an average font and size
        calculated by analazing the characters in the string. This includes any tag,
        so make sure you specify the width for strange fonts or titles.
    ownFont: the font face name (or list thereof) this entry. You can specify a
        list of fonts that are tried in order like in HTML or CSS.
        Default: the normal menu font
    caption: a message that appears when the mouse stays on the element for a couple
        of seconds. Useful for giving explanations or instructions. This is implemented
        with the TITLE property in HTML and therefore handled by the browser (it's like 
        an ALT property for an IMG tag).
        Default: no caption.

  Properties for public use: none.

  Methods:
    toString: called automatically when evaluating the object itself as a string.
      It's used bu FMS_Menu.toString and it's not useful when called manually.
*/
  function FMS_MenuEntry(title, action, width, ownFont, caption)
  {
    this.id = 'FMS_' + window.FMS_menuCount++;
    this.title = title?title:this.id;
    this.action = action?('if(!FMS_menuLock) {' + action + '}'):';';
    this.width = width;
    this.ownFont = ownFont;
    this.caption = caption?caption:'';
  }

//- FMS_MenuEntry.toString
//  Returns the submenu HTML. It's supposed to be called by FMS_Menu.toString
//
//  Parameters: none
  function FMS_MenuEntry.prototype.toString()
  {
    var html = '<DIV ID="' + this.id +
      '" STYLE="position: absolute; height:0px; width:0px;"></DIV>';

    return html;
  }

// END OF FMS_MenuEntry

// -- String.prototype.getFMSLen --
//  This function is used to estimate the length of a string as it's
//  displayed with a variable width font. It's quite crude, since it
//  does not not take into consideration font family or size, but it works
//  well enough on normal fonts size 9 to 12 points. You can specify
//  every width you need, so you don't even have to use it if you don't want
//  to (except for the width of the separator). You can easily modify 'amt' 
//  for a higher or lower staring value, and 'r' to change the multiplication 
//  factor and make it work for smaller or larger fonts, or provide your own,
//  improved, version. The result is interpreted in points.
  function String.prototype.getFMSLen()
  {
    var amt = 0;
    var r = 1;

    for(var i = 0; i < this.length; i++)
    {
      switch(this.charAt(i))
      {
        case ' '  : amt += 2*r;
  
        case 'i'  :
        case 'l'  : amt += 4*r;
                    break;

        case 'j'  :
        case 'f'  : 
        case '.'  : 
        case ','  : amt += 5*r;
                    break;

        case '|'  :
        case 'I'  : 
        case 'J'  : amt += 6*r;
                    break;

        case 'c'  :  
        case 'k'  :  
        case 'r'  :  
        case 's'  :
        case 't'  : 
        case 'v'  :
        case 'x'  :
        case 'y'  :
        case 'z'  :  amt += 8*r;
                    break;

        case 'A'  :  
        case 'D'  :  
        case 'G'  :  
        case 'H'  :  
        case 'N'  : 
        case 'U'  : 
        case 'V'  : amt += 10*r;
                    break;


        case 'M'  : 

        case 'O'  : 
        case 'Q'  : amt += 11*r;
                    break;

        case 'm'  :
        case 'w'  : amt += 12*r;
                    break;

        case 'W'  : 
        case '@'  : amt += 14*r;
                    break;

        default    : amt += 9*r;
      }
    }

    return amt;

  }

//EOF: fms.js  
  </SCRIPT>
  <!-- fortis_demo.js -->  
  <SCRIPT LANGUAGE="JavaScript">
//  File:          fortis_demo.js
//  Description:  A demo for Fortis
//  Version:      0.4

/////////////////////////////////////////////////////////////////////////////////
//
//  Project: Fortis Demo
//
//  Uses modules:
//
//      -- NAME --      -- LAST KNOWN VERSION --
//    fortis.js                     0.34
//    fms.js                      0.22
//
//  Copyright  2001-2002 Daniele Pagano
//
//  This software can be used, modified, and distributed under 
//    the terms of the GNU General Public License.
//
//  For further information on Fortis refer to 'fortis.js'.
//
/////////////////////////////////////////////////////////////////////////////////

// Main menu - the demo main menu

  var MainMenu = new Array();

    MainMenu['Web'] = new FMS_SubMenu('Internet', 40, 75);
      MainMenu['Web'].add('Hotmail', "web.refresh('http://www.hotmail.com')");
      MainMenu['Web'].add('Google', "web.refresh('http://www.google.com')");
      MainMenu['Web'].add("My Yahoo", "web.refresh('http://my.yahoo.com')");
      
    MainMenu['Opts'] = new FMS_SubMenu('Options', 47, 111);
      MainMenu['Opts'].addSub(new FMS_SubMenu('Wallpaper', 0, 105, '', 'Change the wallpaper'), '', 20);

        MainMenu['Opts'].sub['Wallpaper'].add('Fortis Logo', "fortis.setBackground('fortis.jpg', 300, 300, 'center')", 'fortis.jpg', 30);

        var cust = '<DIV ALIGN="center"><INPUT TYPE="file" SIZE="12" NAME="cust"></INPUT> ' +
        '<INPUT TYPE="button" VALUE="Set Custom Tile" ONCLICK="fortis.setBackground(document.all.cust.value, 0, 0, '+
        "'tile'"+ ');"></DIV>';

        MainMenu['Opts'].sub['Wallpaper'].add(cust, ";", '*', 40);
        MainMenu['Opts'].sub['Wallpaper'].add('No Wallpaper', "fortis.setBackground('', 0, 0, 'none')");

      MainMenu['Opts'].addSub(new FMS_SubMenu('Status Bar', 0, 105, '', ''), '', 20);
        MainMenu['Opts'].sub['Status Bar'].add('Show', "fortis.statusbar.on(80)");
        MainMenu['Opts'].sub['Status Bar'].add('Hide', "fortis.statusbar.off()");

    MainMenu['Wins'] = new FMS_SubMenu('Windows', 50, 45);

      MainMenu['Wins'].add('Minimize all', 'fortis.minimizeAll()');
      MainMenu['Wins'].add('Restore all', 'fortis.restoreAll()');
      MainMenu['Wins'].add();
      MainMenu['Wins'].add('Open all', 'fortis.showAll()');
      MainMenu['Wins'].add('Close all', 'fortis.hideAll()');

    var aboutstr = "alert('Fortis Demo ver. 0.4 \\nCopyright  2001-2002 Daniele Pagano')";
    MainMenu['About'] = new FMS_MenuEntry('About', aboutstr, 40, 'Tempus Sans ITC', 'About this demo');

// Demo colors
var thebg = '#DEE9F3';
var thefg = 'black';

  var mms = new FMS_MenuStyle(thebg, thefg, 'tahoma', 9, '#A4A098', ' ', '<FONT FACE="webdings"> 4 </FONT>');
  var mymenu = new FMS_Menu(MainMenu, mms);


// The fun starts
fortis.setMenu(mymenu);
fortis.setStyle(new fortis.WinStyle(0, thebg, thefg, 'Tahoma', 9, 'fortis_ico.jpg', 'fortis_bar.jpg'));

new fortis.Window('Report: Monthly Stuff', '', 5, 100, 300, 300);
var cw = new fortis.Window('Customers', '', 10, 130, 300, 300);
cw.lock("alert('Remember: the customer is always right!'); cw.unlock(); cw.hide()");
new fortis.Window('Invoices', '', 20, 160, 300, 300);
new fortis.Window('Report: Daily Stuff', '', 40, 190, 300, 300);
var mod = new fortis.Window('A modal window', 'nomod.htm', 'center', 'Center', 200, 100);
fortis.showAll();
var web = new fortis.Window('Web', '', 50, 50, 600, 400);

fortis.statusbar.addMsg('', 'i', 'System Ready');
fortis.statusbar.addMsg('', 'w', 'Reports created: <BR> - Monthly Stuff<BR> - Daily Stuff');
fortis.statusbar.addMsg('', 'e', 'Error: this is a test error');

fortis.setBackground('fortis.jpg', 300, 300, 'center')

mod.goModal();

//EOF: fortis_demo.js  
  </SCRIPT>
</BODY>
</HTML>

           
       








DesktopDemo.zip( 27 k)

Related examples in the same category

1.Desktop Demo code