//
// Copyright(c) 2001-2002 BroadVision, Inc. All rights reserved.
//
// This software is copyrighted.  Under the copyright laws, this software
// may not be copied, in whole or in part, without prior written consent
// of BroadVision, Inc. or its assignees. This software is provided under
// the terms of a license between BroadVision and the recipient, and its
// use is subject to the terms of that license.
//
// This software may be protected by one or more U.S. and International
// patents. Certain applications of BroadVision One-To-One software are
// covered by U.S. patent 5,710,887.
//
// TRADEMARKS: BroadVision and the BroadVision logo are registered
// trademarks, and BroadVision One-To-One is a trademark of BroadVision,
// Inc. IONA and Orbix are trademarks of IONA Technologies, Ltd. RSA,
// MD5, and RC2 are trademarks of RSA Data Security, Inc. All other
// trademarks, service marks, and trade names belong to their respective
// owners. BroadVision, Inc. disclaims any proprietary interest in the
// marks and names of others.
//

//
// A drag 'n drop block is implemented as a "view", using a set of
// named DIV's, with an underlying "model" that extends the basic
// JavaScript document object model.
// 
//   view -  a set of <DIV> elements/layers that defines 
//           a "control/widget" area on the browser.
//
//             block
//             header
//             minimize, maximize, restore, close, edit, menu
//             body, text
//             horizontal scroll, vertical scroll, resize
//
// 
//   model - a set of client-side JavaScript "objects". These building
//           blocks provides a higher layer of abstraction that allows
//           for easier manipulation of the browser's real estate. 
//
//             Page
//             Column
//             Block
//             Div
//             Rect
//             Point
//             Browser
//

var IE_CLIENT = 0;   // Microsoft Internet Explorer
var NS_CLIENT = 1;   // Netscape Navigator/Communicator

//
// Block styles
//

var BS_MAXIMIZE  =    1 ;
var BS_MINIMIZE  =    2 ;
var BS_CLOSE     =    4 ;
var BS_VSCROLL   =    8 ;
var BS_HSCROLL   =   16 ;
var BS_VRESIZE   =   32 ;
var BS_HRESIZE   =   64 ;
var BS_BORDER    =  128 ;
var BS_EDIT      =  256 ;
var BS_SYSMENU   =  512 ;
var BS_MOVEABLE  = 1024 ;

var BS_DEFAULT   = BS_MINIMIZE | BS_CLOSE | BS_VSCROLL | BS_HSCROLL |
                   BS_VRESIZE | BS_HRESIZE | BS_BORDER | BS_EDIT |
                   BS_MOVEABLE;

var BS_REQUIRED  = BS_MINIMIZE | BS_VSCROLL | BS_HSCROLL |
		   BS_VRESIZE | BS_HRESIZE | BS_BORDER |
                   BS_MOVEABLE;
		   // For required blocks, don't show close or edit


//
// Block state
//

var BSS_NORMAL    =  0 ;   // normal/restored state
var BSS_MINIMIZED =  1 ;   // minimized state
var BSS_MAXIMIZED =  2 ;   // maximized state
var BSS_CLOSED    =  3 ;   // closed/hidden state

//
// Color - sets colors for the elements on the page
// Only used for Netscape 4
// It never needs to be instantiated--all its members are 
// static, set with setColors.
//
function Color() {
}

function _color__setColors(primaryColor, secondaryColor, backgroundColor) {
  Color.primaryColor = primaryColor;
  Color.secondaryColor = secondaryColor;
  Color.backgroundColor = backgroundColor;

  //Default colors
  //Color.primaryColor = "#CCCCFF";
  //Color.secondaryColor = "#9999CC";
  //Color.backgroundColor = "#FFFFFF";
}
Color.setColors = _color__setColors;


//
// Browser - Represents a browser object.
//
function Browser() {

  //
  this.client = _browser_getClient();
  this.version = _browser_getVersion();
  this.userAgent = _browser_getAgent();

  this.dom = document.getElementById ? 1 : 0;

  this.ns4 = (this.client == NS_CLIENT) && (this.version == 4);
  this.ns6 = (this.client == NS_CLIENT) && (this.version >= 5);
  this.ns7 = false;

  // Patch for Mozilla browser: force NS 7 behavior for Mozilla
  if((this.userAgent.indexOf("Gecko") > 0
     && this.userAgent.indexOf("Netscape") < 0)
     || this.userAgent.indexOf("Netscape/7") != -1 && this.ns6) {
       this.ns7 = true; this.ns6 = false;
  }

  this.ie4 = (this.client == IE_CLIENT) && (this.version == 4);
  this.ie5 = (this.client == IE_CLIENT) && 
               (navigator.appVersion.indexOf("MSIE 5") > -1);
  this.ie6 = (this.client == IE_CLIENT) && 
               (navigator.appVersion.indexOf("MSIE 6") > -1);
}

//
// Browser::getClient - Returns the type of browser.
//
function _browser_getClient() {

  //
  var appName = navigator.appName;

  if ( appName.indexOf( 'Microsoft' ) != -1 )
    return IE_CLIENT;
  else if ( appName.indexOf( 'Netscape' ) != -1 )
    return NS_CLIENT;

  return -1;
}

//
// Browser::getAgent
//
function _browser_getAgent() {
  return navigator.userAgent;
}

//
// Browser::getVersion - Returns the major version of the browser.
//
function _browser_getVersion() {
  var version = navigator.appVersion;
  return version.substring( 0, 1 );
}

//
// Div - Represents a DIV element.  A Div is an
//       extension of the DOM Element and provides
//       the "glue" between the HTML/view and the
//       object model.
//
function Div(name, nest, dnest, ddnest, id) {
  this.element = null;

  // Get the named DIV element from the DOM
  if (!browser.ns4) {
    if (browser.dom) {
      this.element = document.getElementById(name);
    }
    else if (browser.ie4) {
      this.element = document.all[name];
    }
  }
  else {
    var tmp = 0;
    if (ddnest) {
      tmp = document[nest].document[dnest].document[ddnest].document[name];
    }
    else if (dnest) {
      tmp = document[nest].document[dnest].document[name];
    }
    else if (nest) {
      tmp = document[nest].document[name];
    }
    else {
      tmp = document.layers[name];
    }	
    this.element =  tmp ? tmp : 0;
  }

  if (!this.element) {
    alert('Error: DIV(' + name + ') does not exist.')
    return;
  }

  this.css = browser.dom || browser.ie4 ? this.element.style : this.element;  
  this.ref = browser.dom || browser.ie4 ? document : this.css.document;

  // Define the "methods"
  this.moveTo = _div__moveTo;
  this.show = _div__show;
  this.clipTo = _div__clipTo;
  this.setBackgroundColor = _div__setBackgroundColor;

  this.x = 0;
  this.y = 0;
  this.width = 0;
  this.height = 0;

  this.clip = new Rect(0,0,0,0);

  this.id = id; 	
  this.obj = "g_DivObject" + name;

  eval(this.obj + " = this");

  return this;
}

//
// Div::moveTo - Move the DIV element.
//
// Parameters:
//
//   x
//   y
//
function _div__moveTo(x, y) {

  this.x = x;
  this.y = y;
  this.css.left = x;
  this.css.top = y;
}

//
// Div::show - Show/hide the DIV element.
//
// Parameters:
//
//   bVisible
//
function _div__show(bVisible) { 
  if (bVisible) {
    this.css.visibility = "visible";
  }
  else {
    this.css.visibility = "hidden";
  }
}

//
// Div::clipTo - Clip the DIV element.
//
// Parameters:
//
//   t top
//   r right
//   b bottom
//   l left
//
function _div__clipTo(t, r, b, l) {

  if (t < 0) t = 0;
  if (r < 0) r = 0;
  if (b < 0) b = 0;
  if (l < 0) l = 0;

  this.clip.top = t;
  this.clip.right = r;
  this.clip.bottom = b;
  this.clip.left = l;

  if (browser.ns4) {
    this.css.clip.top = t;
    this.css.clip.right = r;
    this.css.clip.bottom = b;
    this.css.clip.left = l;
  }
  else {
    this.css.clip = "rect(" + t + "," + r + "," + b +"," + l +")";
    this.css.width = r;
    this.css.height = b;
    this.width = r;
    this.height = b;
  }
}

//
// Div::setBackgroundColor
//
// Parameters:
//
//   rgb
//
function _div__setBackgroundColor(rgb) {
  if (browser.ns4) {
    this.css.bgColor = rgb;
  }
  else {
    this.css.backgroundColor = rgb;
  }
}

//
// Div::on_mouseover - onMouseOver event handler.
//
// 
//
//
// Parameters:
//
//   id
//   resize
//
function _div__on_mouseover(id, resize) {
  if (!resize) {
    g_page.dragover = id;
  }
  else {
    g_page.resizeover = id;
  }
}

//
// Div::on_mouseout
//
function _div__on_mouseout() {
  g_page.dragover = -1;
  g_page.resizeover = -1;
}

//
// Block - Represent a block.
//
function Block(id, blockId, style) {

  // internal UID - index into g_blocks
  this._id = id;

  // user defined block ID
  this._blockId = blockId;

  // block's current location and size
  this._left = 0;
  this._top = 0;
  this._width = 0;
  this._height = 0;

  // block's current style flag
  this._style = style;

  // size of the border for the block
  this._bordersize = (style & BS_BORDER) ? c_bordersize : 0;

  // height of the header for the block
  this._headerHeight = c_headerHeight;

  // index of the column the block is snapped to
  this.columnIndex = -1;

  //
  this._restoreWidth = 0;
  this._restoreHeight = 0;

  // clipping 
  this.clip = new Rect(0,0,0,0);

  // block's current state: minimized, maximized, normal
  this.state = BSS_NORMAL;

  //
  this._editPath = "";

  // position of the left-most button
  this._xLeftmostButton = 0;

  // Define the "methods"
  this.moveTo = _block__moveTo;
  this.clipTo = _block__clipTo;
  this.show = _block__show;
  this.resize = _block__resize;
  this.close = _block__close;
  this.minimize = _block__minimize;
  this.maximize = _block__maximize;
  this.restore = _block__restore;
  this.focus = _block__focus;
  this.checkScroll = _block__checkScroll;
  this.checkHeaderWrap = _block__checkHeaderWrap;
  this.scrollUp = _block__scrollUp;
  this.scrollDown = _block__scrollDown;
  this.scrollLeft = _block__scrollLeft;
  this.scrollRight = _block__scrollRight;
  this.edit = _block__edit;
  this.menu = _block__menu;
  this.setEditPath = _block__setEditPath;

  // DIV elements that make up a block

  this.container = new Div('divBlock' + id, "" , "" , "" , id);

  this.header = new Div('divBlockHeader' + id,'divBlock' + id);
  if (style & BS_MOVEABLE) {
    this.header.element.onmouseover = new Function("_div__on_mouseover(" + id + ")")
    this.header.element.onmouseout = new Function("_div__on_mouseout()")
  }

  this.btnMenu = new Div('divMenu' + id, 'divBlock' + id);
  this.btnMinimize = new Div('divMinimize' + id, 'divBlock' + id);
  this.btnMaximize = new Div('divMaximize' + id, 'divBlock' + id);
  this.btnRestore = new Div('divRestore' + id, 'divBlock' + id);
  this.btnClose = new Div('divClose' + id, 'divBlock' + id);
  this.btnEdit = new Div('divEdit' + id, 'divBlock' + id);

  this.body = new Div('divBody' + id, 'divBlock' + id);
  this.text = new Div('divBlockText' + id, 'divBlock' + id, 'divBody' + id);

  this.btnResize = new Div('divResize' + id, 'divBlock' + id);
  this.btnResize.element.onmouseover = new Function("_div__on_mouseover(" + id + ",true)")
  this.btnResize.element.onmouseout = new Function("_div__on_mouseout()")

  this.vscroll = new Div('divVScroll' + id, 'divBlock' + id); 
  this.hscroll = new Div('divHScroll' + id, 'divBlock' + id); 

  this.btnUp = new Div('divUp' + id, 'divBlock' + id); 
  this.btnDown = new Div('divDown' + id, 'divBlock' + id);

  this.btnLeft = new Div('divLeft' + id, 'divBlock' + id);
  this.btnRight = new Div('divRight' + id, 'divBlock' + id);

  var xOffset = this._bordersize;
  if (style & BS_SYSMENU) xOffset += g_btnSysMenu.width;

  this.header.moveTo(xOffset, this._bordersize);
  this.body.moveTo(this._bordersize, this._headerHeight + this._bordersize);

  if (browser.ns4) {
    this.container.setBackgroundColor(Color.primaryColor);

    this.btnMenu.setBackgroundColor(Color.secondaryColor);
    this.btnMinimize.setBackgroundColor(Color.secondaryColor);
    this.btnMaximize.setBackgroundColor(Color.secondaryColor);
    this.btnRestore.setBackgroundColor(Color.secondaryColor);
    this.btnClose.setBackgroundColor(Color.secondaryColor);
    this.btnEdit.setBackgroundColor(Color.secondaryColor);

    this.vscroll.setBackgroundColor("#CCCCCC");
    this.hscroll.setBackgroundColor("#CCCCCC");

    this.body.setBackgroundColor("#ffffff");
  }

  this.btnMenu.show(style & BS_SYSMENU ? true: false);
  this.btnMinimize.show(style & BS_MINIMIZE ? true : false);
  this.btnMaximize.show(style & BS_MAXIMIZE ? true : false);
  this.btnRestore.show(false);

  this.btnClose.show(style & BS_CLOSE ? true : false);
  this.btnEdit.show(style & BS_EDIT ? true : false);

  this.btnUp.show(false);
  this.btnDown.show(false);
  this.btnLeft.show(false);
  this.btnRight.show(false);

  this.obj = "g_BlockObject" + id;

  eval(this.obj + " = this");

  return this;
}

//
// Block::clipTo
//
// Parameters:
//
//   t
//   r
//   b
//   l
//
function _block__clipTo(t, r, b, l) {

  this.clip.top = t;
  this.clip.right = r;
  this.clip.bottom = b;
  this.clip.left = l;

  this.container.clipTo(t, r, b, l);
}

//
// Block::moveTo - move the block to a (x, y)
//
// Parameters:
//
//   x -
//   y -
//
function _block__moveTo(x, y) {

  this.container.moveTo(x, y);

  this._left = x;
  this._top = y;
}

//
// Block::show - show/hide the block
//
// Parameters:
//
//   bVisible - Visibility flag (boolean)
//                true -
//                false -
//
function _block__show(bVisible) {
  this.container.show(bVisible);
}

//
// Block::resize - Resize the block.
//
// Parameters:
//
//   cx - width
//   cy - height
//
function _block__resize(cx, cy) {

  // If the height passed in was 0, size block to content
  var sizeToContent = (cy <= 0) ? true : false;

  // Display the menu button in the upper left hand corner

  if (this._style & BS_SYSMENU) {
    this.btnMenu.moveTo(this._bordersize, this._bordersize);
  }

  //
  // Render the display of the buttons from right to left.
  //

  var x = cx - this._bordersize;

  // Display the minimize button if the block can be minimized and the
  // block currently is normal/maximized.

  if ((this._style & BS_MINIMIZE) && 
      ((this.state == BSS_MAXIMIZED) || (this.state == BSS_NORMAL))) {
    x -= g_btnMinimize.width;
    this.btnMinimize.moveTo(x, this._bordersize);
  }

  // Display the maximize button if the block can be maximized and 
  // the block is currently normal/minimized.

  if ((this._style & BS_MAXIMIZE) && 
      ((this.state == BSS_MINIMIZED) || (this.state == BSS_NORMAL))) {
    x -= g_btnMaximize.width;
    this.btnMaximize.moveTo(x, this._bordersize);
  }

  // Display the restore button if the block can be minimized/maximized
  // and the block is currently minimized/maximized.

  if (((this._style & BS_MINIMIZE) || (this._style & BS_MAXIMIZE)) &&
      ((this.state == BSS_MINIMIZED) || (this.state == BSS_MAXIMIZED))) {
    x -= g_btnRestore.width;
    this.btnRestore.moveTo(x, this._bordersize);
  }

  // Display the edit button if the block is editable.

  if (this._style & BS_EDIT) {
    x -= g_btnEdit.width + c_buttonspacing;
    this.btnEdit.moveTo(x, this._bordersize);
  }

  // Display the close button if the block can be closed.

  if (this._style & BS_CLOSE) {
    x -= g_btnClose.width + c_buttonspacing;
    this.btnClose.moveTo(x, this._bordersize);
  }

  // remember where we're at so we don't have to recompute
  this._xLeftmostButton = x;

  // resize the block's body
  this.body.clipTo(0, cx - this._bordersize*2, cy - this._bordersize*2 - this._headerHeight, 0);

  this.clipTo(0, cx, cy, 0);

  if (sizeToContent) {
    // Calculate the height of the block based on the actual
    // height of the text within the block, then add in the size 
    // of the header, bottom border and scroll bar.
    // offsetHeight is used in Netscape.  ref.height is used in IE
    // and refers to the DOM element (DIV) for the text.
    
    var textHeight = this.text.element.offsetHeight ||
                     this.text.css.pixelHeight      ||
                     this.text.ref.height           ||
                     0;
    cy = textHeight + c_bottomHeight + c_scrollHeight + 
       this._headerHeight;

    if (browser.ns4 || browser.ns6 || browser.ns7){
      // IE automatically adds a little padding, but we have to do
      // so explicitly on Netscape
      cy += 10;
    }

    // We have to redo the clipping because the height is
    // determined by the width, which was set by calling clipTo
    this.body.clipTo(0, cx - this._bordersize*2, cy - this._bordersize*2 - this._headerHeight, 0);

    this.clipTo(0, cx, cy, 0);
  }

  this._width = cx;
  this._height = cy;

  var xOffset = this._bordersize;
  if (this._style & BS_SYSMENU) xOffset += g_btnSysMenu.width;

  this.header.clipTo(0, x - xOffset, this._headerHeight, 0);
  this.text.moveTo(2, 0);
  this.btnResize.moveTo(cx - 10, cy - 10);

  this.checkScroll();
}

//
// Block::close - Hide/close the block.
//
function _block__close() {

  // Hide the parent and children Div's

  this.container.show(false);
  this.state = BSS_CLOSED;

  this.btnUp.show(false);
  this.btnDown.show(false);
  this.btnLeft.show(false);
  this.btnRight.show(false);
  this.vscroll.show(false);
  this.hscroll.show(false);

  this.btnMinimize.show(false);
  this.btnMaximize.show(false);
  this.btnRestore.show(false);
  this.btnClose.show(false);
  this.btnEdit.show(false);

  // If the block is snapped to a column, we need
  // to "unlink" the block from the column and refresh
  // the display of the column.

  if (g_page.snapToGrid) {

    // Get the Column the block is linked to.

    var column = g_page._columns[this.columnIndex];  // block's column
    var blocks = column._blocks;                     // column's blocks
    var numberOfBlocks = blocks.length;              // number of blocks in the column

    // Find the block in the list of linked blocks.

    for (var blockIndex = 0; blockIndex < numberOfBlocks; blockIndex++) {
      if (blocks[blockIndex]._id == this._id) break;
    }

    // If the block is not found, do nothing

    if (blockIndex == numberOfBlocks) return;

    // The block was found. Remove the block
    // and update the column's display.

    blocks.splice(blockIndex, 1);                    // remove the block
    column.arrangeBlocks();                          // refresh the column display
  }
}

//
// Block::minimize - Minimize/collapse the block.
//
function _block__minimize() {

  if (this.state != BSS_MINIMIZED) { 
    if (this.state != BSS_MAXIMIZED) {
      this.lastX = this.x;
      this.lastY = this.y;
    }
  
    this.state = BSS_MINIMIZED;
    this._restoreWidth = this._width;
    this._restoreHeight = this._height;

    this.btnMinimize.show(false);
    this.btnRestore.show(true);
    this.btnResize.show(false);

    // If the block is snapped to a column, we need
    // to resize the block based on the width of the column
    // and refresh the display of the column.

    if (g_page.snapToGrid) {

      // Get the Column the block is linked to

      var column = g_page._columns[this.columnIndex];

      // Resize the block
      this.resize(column._width, this._headerHeight + 2*this._bordersize);
      column.arrangeBlocks();
    }
    else {
      this.resize(c_minimizeWidth, this._headerHeight);
    }
  }
  this.checkScroll();
}

//
// Block::maximize
//
function _block__maximize() {

  this.state = BSS_MAXIMIZED;
  this.btnRestore.show(false);

  if (this._style & BS_MINIMIZE) this.btnMinimize.show(true);

  this.btnResize.show(false);

  this.resize(100, 75);
  this.checkScroll();
}

//
// Block::restore
//
function _block__restore() {

  this.btnRestore.show(false);
  this.state = BSS_NORMAL;

  if (this._style & BS_MINIMIZE) this.btnMinimize.show(true);
  if (this._style & BS_MAXIMIZE) this.btnMaximize.show(true);

  this.btnResize.show(true);

  if (g_page.snapToGrid) {
    var column = g_page._columns[this.columnIndex];
    this.resize(column._width, this._restoreHeight);
  }
  else {
    this.resize(this._restoreWidth, this._restoreHeight);
  }

  if (g_page.snapToGrid) {
    var column = g_page._columns[this.columnIndex];
    column.arrangeBlocks();
    g_page.arrangeFooter();
  }
  this.checkScroll();
}

//
// Block::focus - Bring the block to the foreground.
//
//
//
function _block__focus() {

  //
  g_page.zIndex++;
  this.container.css.zIndex = g_page.zIndex;
}

//
// Block::checkScroll
//
function _block__checkScroll() {

  //
  var cx = this.container.clip.right;
  var cy = this.container.clip.bottom;

  this.checkHeaderWrap(cx, cy);
                
  var vertScroll = 1;

  this.text.height = this.text.element.offsetHeight || 
                     this.text.css.pixelHeight      ||
                     this.text.ref.height           ||
                     0;
  if (this.text.height > cy - c_bottomHeight - this._headerHeight && this.state != BSS_MINIMIZED) {

    this.body.clipTo(0, cx - c_scrollWidth - this._bordersize*2, cy - c_bottomHeight - this._headerHeight - 2*this._bordersize, 0); 

    this.vscroll.moveTo(cx - c_scrollWidth - this._bordersize, this._headerHeight + this._bordersize);
    this.vscroll.clipTo(0, c_scrollWidth, cy - c_bottomHeight - g_btnResize.height - this._headerHeight - this._bordersize, 0);
    this.vscroll.show(true);

    this.btnUp.moveTo(cx - c_scrollWidth - this._bordersize, this._headerHeight + this._bordersize);
    this.btnDown.moveTo(cx - c_scrollWidth - this._bordersize, cy - c_bottomHeight - g_btnResize.height - c_scrollHeight);

    this.btnUp.show(true)
    this.btnDown.show(true);
    vertScroll = 1;
  }
  else {
    this.vscroll.show(false)
    this.btnUp.show(false); 
    this.btnDown.show(false);
    vertScroll = 0;
  }

  this.text.width = this.text.element.offsetWidth ||
                    this.text.css.pixelWidth      ||
                    this.text.ref.width           ||
                    0;

  if (this.text.width > cx - ((vertScroll == 1) ? c_scrollWidth : 0) - this._bordersize*2 && this.state != BSS_MINIMIZED) {
    var clipWidth = cx - this._bordersize*2;
    if (vertScroll == 1) {
       clipWidth -= c_scrollWidth;
    }

    this.body.clipTo(0, clipWidth, cy - c_bottomHeight - c_scrollHeight - this._headerHeight - 2*this._bordersize, 0); 

    this.btnLeft.moveTo(this._bordersize, cy - c_bottomHeight - c_scrollHeight - this._bordersize);

    this.hscroll.moveTo(this._bordersize, cy - c_bottomHeight - c_scrollHeight - this._bordersize);
    this.hscroll.clipTo(0, cx - 2*this._bordersize - g_btnResize.width, c_scrollHeight, 0);
    this.hscroll.show(true);

    var xOffset = c_scrollWidth;
    xOffset += (vertScroll == 0) ? g_btnResize.width : c_scrollWidth;

    this.btnRight.moveTo(cx - xOffset, cy - c_bottomHeight - c_scrollHeight - this._bordersize);
    this.btnDown.moveTo(cx - c_scrollWidth - this._bordersize, cy - c_bottomHeight - c_scrollHeight - c_scrollHeight);
    this.btnLeft.show(true);
    this.btnRight.show(true);
  }
  else {
    this.hscroll.show(false)
    this.btnLeft.show(false);
    this.btnRight.show(false);
  }
}

//
// Block::checkHeaderWrap
//
function _block__checkHeaderWrap(cx, cy) {

  var xOffset = this._bordersize;
  if (this._style & BS_SYSMENU) xOffset += g_btnSysMenu.width;

//  this.header.clipTo(0, this._xLeftmostButton - xOffset, c_headerHeight, 0);

  this.header.height = this.header.element.offsetHeight ||
                       this.header.css.pixelHeight      ||
                       this.header.ref.height           ||
                       0;

  this.header.width = this.header.element.offsetWidth ||
                      this.header.css.pixelWidth      ||
                      this.header.ref.width           ||
                      0;

  if (this.header.height != this._headerHeight) {
    this._headerHeight = (this.header.height < c_headerHeight) ? c_headerHeight : this.header.height;
    this.body.moveTo(this._bordersize, this._headerHeight + this._bordersize);
    this.header.clipTo(0, this._xLeftmostButton - xOffset, this._headerHeight, 0);

    var offset = Math.abs(this.header.height - this._headerHeight);
  }
}

//
// Block::scrollUp - Scroll the block's content up.
//
function _block__scrollUp() {
  clearTimeout(g_page._scrollTimeoutId);
  if (this.text.y >= this.body.clip.bottom - this.text.height - 10 && G_Page._scroll) {
    // scroll up by moving the text layer down
    this.text.moveTo(this.text.x, this.text.y-8);
    setTimeout(this.obj + ".scrollUp()", c_scrollTimeout);
  }
}

//
// Block::scrollDown - Scroll the block's content down.
//
function _block__scrollDown() {
  clearTimeout(g_page._scrollTimeoutId);
  if (this.text.y <= 0 && g_page._scroll) {
    // scroll down by moving the text layer up
    this.text.moveTo(this.text.x, this.text.y+8);
    setTimeout(this.obj + ".scrollDown()", c_scrollTimeout);
  }
}

//
// Block::scrollLeft - Scroll the block's content to the left.
//
function _block__scrollLeft() {
  clearTimeout(g_page._scrollTimeoutId);
  if (this.text.x <= 0 && g_page._scroll) {
    // scroll left by moving the text layer right
    this.text.moveTo(this.text.x+8, this.text.y);
    setTimeout(this.obj + ".scrollLeft()", c_scrollTimeout);
  }
}

//
// Block::scrollRight - Scroll the block's content to the right.
//
function _block__scrollRight() {
  clearTimeout(g_page._scrollTimeoutId);
  if (this.text.x >= this.body.clip.right - this.text.width && g_page._scroll) {
    // scroll right by moving the text layer left
    this.text.moveTo(this.text.x-8, this.text.y);
    setTimeout(this.obj + ".scrollRight()", c_scrollTimeout);
  }
}

//
// Block::edit - 
//
function _block__edit() {
  window.location = this._editPath;
}

//
// Block::menu -
//
function _block__menu() {
  alert("Add your code here...");
}

//
// Block::setEditPath -
//
function _block__setEditPath(newEditPath) {
  this._editPath = newEditPath;
}

//
// Column - represents a column on a page.
//
function Column(name, x, y, cx, cy) {

  this._name = name;

  this._left = x;
  this._top = y;
  this._width = cx;
  this._height = cy;
  this._blocks = new Array();

  this.bottomBlock = y;  // y-coordinate of the last block

  // define Column methods
  this.addBlock = _column__addBlock;
  this.insertBlock = _column__insertBlock;
  this.removeBlock = _column__removeBlock;
  this.moveBlock = _column__moveBlock;
  this.arrangeBlocks = _column__arrangeBlocks;

  return this;
}

//
// Column::addBlock - Add/link a block to the column.
//
// Appends the block to the column's list of blocks and
// updates bottomBlock.
//
// Parameters:
//
//   block 
//
function _column__addBlock(block) {

  // get the block list
  var blocks = this._blocks;

  // append the block to the list
  blocks[blocks.length] = block;

  this.bottomBlock += block._height + c_blockspacing;
}

//
// Column::insertBlock - Insert a block into the column.
//
// Parameters:
//
//   id
//   y
//
function _column__insertBlock(id, y) {

  var blocks = this._blocks;
  var numberOfBlocks = blocks.length;
  var by = this._top;
  var block = g_blocks[id];

  block.resize(this._width, block._height);

  //
  // Get the index of the block's destination.
  //

  for (var destIndex = 0; destIndex < numberOfBlocks; destIndex++) {
    if (y < by) break;
    by += blocks[destIndex].container.clip.bottom + c_blockspacing;
  }

  if (destIndex == numberOfBlocks) {
    blocks[numberOfBlocks] = block;
    return;
  }

  // Check if the target is on a non-draggable block
  //
  // Note: the following code handles the case if fixed
  //       blocks are displayed first. The code needs to
  //       enhanced if fixed blocks are interspersed.
  var tmpBlock = blocks[destIndex];
  while (!(tmpBlock._style & BS_MOVEABLE)) {
    destIndex++;
    if (destIndex == numberOfBlocks) break;
    tmpBlock = blocks[destIndex];
  }

  // shift the blocks to the right
  for (var blockIndex = numberOfBlocks; blockIndex > destIndex; blockIndex--) {
      blocks[blockIndex] = blocks[blockIndex-1];
  }
  blocks[destIndex] = block;
}

//
// Column::removeBlock
//
function _column__removeBlock(id) {
  var blocks = this._blocks;
  var numberOfBlocks = blocks.length;
  var by = this._top;

  //
  // Get the index of the block to be removed.
  //

  for (var srcIndex = 0; srcIndex < numberOfBlocks; srcIndex++) {
    // Iterate through the column's block list to match a block's id with 'id'.
    if (blocks[srcIndex]._id == id) break;
  }

  //
  // The block to remove is the last element.
  //
  if (srcIndex == numberOfBlocks - 1) {
    blocks.pop();
    return;
  }

  blocks.splice(srcIndex, 1);
}

//
// Column::moveBlock - Move a block up/down within the column.
//
// Parameters:
//
//   id 
//   y
//
function _column__moveBlock(id, y) {
  var blocks = this._blocks;
  var numberOfBlocks = blocks.length;
  var by = this._top;

  //
  // Get the index of the block to be moved.
  //

  for (var srcIndex = 0; srcIndex < numberOfBlocks; srcIndex++) {
    // Iterate through the column's block list to match a block's id with 'id'.
    if (blocks[srcIndex]._id == id) break;
  }

  var block = blocks[srcIndex];

  //
  // Get the index of the block's destination.
  //

  for (var destIndex = 0; destIndex < numberOfBlocks; destIndex++) {
    by += blocks[destIndex].container.clip.bottom + c_blockspacing;
    if (y < by) break;
  }

  //
  // If the block is being moved up, the destination index
  // is off by one.
  //

  if (srcIndex > destIndex && y > this._top) destIndex++;

  if (destIndex < 0) destIndex = 0;
  if (destIndex == numberOfBlocks) destIndex--;

  if (numberOfBlocks > 1) {
    // Check if the target is on a non-draggable block 
    //
    // Note: the following code handles the case if fixed
    //       blocks are displayed first. The code needs to
    //       enhanced if fixed blocks are interspersed.
    var tmpBlock = blocks[destIndex];
    while (!(tmpBlock._style & BS_MOVEABLE)) {
      destIndex++;
      if (destIndex == numberOfBlocks) break;
      tmpBlock = blocks[destIndex];
    }
  }

  if (srcIndex < destIndex) {
    // move the block to the right (downwards)
    for (var blockIndex = srcIndex; blockIndex < destIndex; blockIndex++) {
      blocks[blockIndex] = blocks[blockIndex+1];
    }
    blocks[destIndex] = block;
  }
  else if (srcIndex > destIndex) {
    // move the block to the left (upwards)
    for (var blockIndex = srcIndex; blockIndex > destIndex; blockIndex--) {
      blocks[blockIndex] = blocks[blockIndex-1];
    }
    blocks[destIndex] = block;
  }
}

//
// Column::arrangeBlocks - Arrange the blocks from top to bottom.
//
function _column__arrangeBlocks() {

  var x = this._left;
  var y = this._top;

  var blocks = this._blocks;
  var numberOfBlocks = blocks.length;

  for (var blockIndex = 0; blockIndex < numberOfBlocks; blockIndex++) {
    blocks[blockIndex].moveTo(x, y);
    if (browser.ie6) {
      // On IE6, call Block::resize() to force the block to be
      // rendered.  In certain instances, IE6 fails to display
      // the block icons (min, max, etc) 
      var clip = blocks[blockIndex].clip;
      var cx = clip.right - clip.left;
      var cy = clip.bottom - clip.top;
      blocks[blockIndex].resize(cx, cy);
    }
    y += blocks[blockIndex].clip.bottom + c_blockspacing;
  }

  if (browser.ns4) {
    if (numberOfBlocks > 0) {
      var bottom = blocks[numberOfBlocks-1]._top +
                   blocks[numberOfBlocks-1]._height;

      if (bottom > document.height) {
        document.height = bottom;
      }
    }
  }
}

//
// Page - represents the display page.
//
function Page() {

  // Columns defined on the page, if any
  this._columns = new Array;
  this._numberOfColumns = 0;

  // ID's of 
  this.dragover = -1;
  this.resizeover = -1;

  // block ID 
  this.dragobj = -1;
  this.resizeobj = -1;

  // mouse offset of drag
  this.clickedX = 0;
  this.clickedY = 0;

  // page boundaries
  this.top = 5;
  this.left = 5;

  // snap blocks to grid/column
  this.snapToGrid = true;

  // last z-index
  this.zIndex = 200;

  // 
  this._scroll = false;

  this._scrollTimeoutId = 9999;

  // global block attributes
  this.minBlockWidth = 75;
  this.minBlockHeight = 15;

  // Define the "methods"
  this.initialize = _page__initialize;
  this.createBlock = _page__createBlock;
  this.addColumn = _page__addColumn;
  this.snapBlockToGrid = _page__snapBlockToGrid;
  this.getBlockLayout = _page__getBlockLayout;
  this.scroll = _page__scroll;
  this.arrangeFooter = _page__arrangeFooter;

  this.initialize();

  return this;
}

//
// Page::initialize - 
//
function _page__initialize() {

  if (document.layers) {
    document.captureEvents(Event.MOUSEMOVE | Event.MOUSEDOWN | Event.MOUSEUP | Event.DBLCLICK)
    document.ondblclick = on_dblclick;
  }

  document.onmousemove = on_mousemove;
  document.onmousedown = on_mousedown;
  document.onmouseup = on_mouseup;

  window.onresize = on_resize;
}

//
// Page::createBlock - Create block snapped to a column.
//
// Parameters:
//
//  colIndex   Index (0-based) of the column to snap to
//  cy         Height of the block
//  style      Block style bit flag
//  editPath   Url of the page to open when the edit button is clicked
//  isExpanded true if the block is expanded, false if the block is collapsed
//
function _page__createBlock(blockId, colIndex, cy, style, editPath, isExpanded) {

  //
  var column = this._columns[colIndex];
  var cx = column._width;

  var id = g_blocks.length;

  var block = new Block(id, blockId, style);

  block.resize(cx, cy);
  block.show(true);

  g_blocks[id] = block;
  column.addBlock(block);
  block.columnIndex = colIndex;

  if (editPath != null && editPath != "") {
    block.setEditPath(editPath);
  }
  if (isExpanded == false) block.minimize();
}

//
// Page::createBlockEx
//
// Parameters:
//
//   x
//   y
//   cx
//   cy
//   style
//
function _page__createBlockEx(blockId, x, y, cx, cy, style) {

  //
  var id = g_blocks.length;
  var block = new Block(id, blockId, style);

  block.moveTo(x, y);
  block.resize(cx, cy);
  block.show(true);

  g_blocks[id] = block;
}

//
// Page::addColumn
//
// Parameters:
//
//   name
//   nest
//   dnest
//   ddnest
//
function _page__addColumn(name, nest, dnest, ddnest) {
  
  //
  // Find the DIV element from the DOM that represents the named column.
  //

  var element = getElementByName(name, nest, dnest, ddnest);
  var thisElement = element;

  //
  // Get the dimensions of the element.
  //

  var left = !browser.ns4 ? element.offsetLeft : element.pageX;
  var top = !browser.ns4 ? element.offsetTop : element.pageY;
  var width = !browser.ns4 ? element.offsetWidth : element.clip.width;
  var height = !browser.ns4 ? element.offsetHeight : element.clip.height;
  
  if (!browser.ns4 && !browser.ns6) {
    while (element.offsetParent != null) {
      left += element.offsetLeft;
      top += element.offsetTop;
      element = element.offsetParent;
    }
  }

  //
  // Create the corresponding Column object and append
  // to the list of columns.
  //
  
  var column = new Column(name, left, top, width, height);

  column._element = thisElement;
  this._columns[this._numberOfColumns++] = column;
}

//
// Page::snapBlockToGrid
//
//  id   ID of the block to be snapped
//  x    x-coordinate of the block's upper left corner
//  y    y-coordinate of the block's upper left corner
//
function _page__snapBlockToGrid(id, x, y) {

  var block = g_blocks[id];                       // get the block
  var srcColumnIndex = block.columnIndex;         // get the column it's in now
  var srcColumn = this._columns[srcColumnIndex];

  var targetColumn;
  var targetColumnIndex = -1;

  //
  // Check which column is the drop target
  //

  var columns = this._columns;
  var numberOfColumns = this._numberOfColumns;

  //
  // Loop through all the columns to see which one the block
  // falls between.
  //

  //
  // Move the hot-point of the block from the block's
  // left-side to the center.
  //
  var xCenter = x + Math.round(block._width / 2);

  for (var colIndex = 0; colIndex < numberOfColumns; colIndex++) {
    if (xCenter < columns[colIndex]._left + columns[colIndex]._width) {
      targetColumn = columns[colIndex];
      targetColumnIndex = colIndex;
      break;
    }
  }

  // check if the block has been dragged past the last column
  if (colIndex == numberOfColumns) {
    targetColumn = columns[colIndex-1];
    targetColumnIndex = colIndex-1;
  }

  if (srcColumnIndex == targetColumnIndex) {
    // Target column is the same as the source. Just move
    // the block up/down within the column.
    targetColumn.moveBlock(id, y);
    targetColumn.arrangeBlocks();
  }
  else {
    // It's a different column.  Remove the block from the
    // column it's currently in and insert the block into the
    // target column.  And be sure to rearrange both source
    // and target columns since there's bound to be some 
    // shuffling of blocks up/down.
    srcColumn.removeBlock(id);
    targetColumn.insertBlock(id, y);
    srcColumn.arrangeBlocks();
    targetColumn.arrangeBlocks();
    block.columnIndex = targetColumnIndex;
    g_page.arrangeFooter();
  }
}

//
// Page::getBlockLayout - Return information about the user's block layout.
//
function _page__getBlockLayout() {

  var str = "";

  var columns = this._columns;
  var numColumns = columns.length;

  for (var colIndex = 0; colIndex < numColumns; colIndex++) {
    var column = columns[colIndex];
    var blocks = column._blocks;
    var numBlocks = blocks.length;

    for (var blockIndex = 0; blockIndex < numBlocks; blockIndex++) {
      var block = blocks[blockIndex];
      str += block._blockId + "," + colIndex + "," + blockIndex;
      if (block.state == BSS_NORMAL) str += ",1";     // expanded
      if (block.state == BSS_MINIMIZED) str += ",0";  // collapsed
      str += ",";
    }
  }

  // Strip off last comma
  return str.substring(0, str.length-1);
}

//
// Page::scroll - Scroll state.
//
function _page__scroll(bScroll) {

  //
  if (bScroll) {
    this._scroll = true;
  }
  else {
    this._scroll = false;
    clearTimeout(this._scrollTimeoutId);
  }
}

//
// Page::arrrangeFooter - Move the footer so that it "sinks" to 
// the bottom of the page.
//
function _page__arrangeFooter() {

  var footer = new Div('divFooter');
  var element = footer.element;
  var footerHeight = !browser.ns4 ? element.offsetHeight : element.clip.height;
  var left = !browser.ns4 ? element.offsetLeft : element.pageX;
  var top = !browser.ns4 ? element.offsetTop : element.pageY;
  var width = !browser.ns4 ? element.offsetWidth : element.clip.width;
  var height = !browser.ns4 ? element.offsetHeight : element.clip.height;
  var columns = this._columns;
  var numColumns = columns.length;
  var documentHeight = 550;

  if (browser.ns4 || browser.ns6 || browser.ns7) documentHeight = document.height;
  if (browser.ie5 || browser.ie6) documentHeight = top + height + 5;  //document.body.clientHeight;

  if (numColumns == 0) {
    if (browser.ns4) {
      document.height = documentHeight;
      footer.moveTo(0, documentHeight-footerHeight);
    }
    return;
  }

  var maxBottom = 0;

  for (var columnIndex = 0; columnIndex < numColumns; columnIndex++) {
    var column = this._columns[columnIndex];
    var blocks = column._blocks;
    var numBlocks = blocks.length;
    var bottom = 0;
    if (numBlocks > 0) {
      var block = blocks[numBlocks-1];
      bottom = block._top + block._height;
    }
    if (bottom > maxBottom) maxBottom = bottom;
  }

  if (maxBottom < documentHeight) {
    if (browser.ns4) {
      document.height = documentHeight;
      footer.moveTo(0, documentHeight-footerHeight); 
    }
    return;
  }

  var x = 0;
  var y = maxBottom + 5;

  if (browser.ns4) {
    if (y + footerHeight > document.height) {
      document.height = y + footerHeight;
    }
  }

  footer.moveTo(x, y);
}

//
// on_dblclick - onDblClick event handler.
//
function on_dblclick() {
}

//
// on_mousemove - onMouseMove event handler.
//
function on_mousemove(e, y, rresize) {

  // Get the mouse position

  var x = (browser.ns4 || browser.ns6 || browser.ns7) ? e.pageX : event.x || event.clientX;
  var y = (browser.ns4 || browser.ns6 || browser.ns7) ? e.pageY : event.y || event.clientY;

  if (browser.ie5 || browser.ie6) y += document.body.scrollTop;

  if (g_page.resizeobj > -1) { // Resize

    // get the block to be resized
    var block = g_blocks[g_page.resizeobj];

    var cx = x - block._left;  // new width (delta between block's left side and mouse's x-pos)
    var cy = y - block._top;   // new height (delta between block's top and mouse's y-pos)

    if (g_page.snapToGrid) cx = block.clip.right;
                
    if (cx < g_page.minBlockWidth) cx = g_page.minBlockWidth;
    if (cy < g_page.minBlockHeight) cy = g_page.minBlockHeight;

    block.resize(cx, cy);
  }
  else if (g_page.dragobj > -1) { //Move

    var nx = x - g_page.clickedX;
    var ny = y - g_page.clickedY;

    if (ny < g_page.top) ny = g_page.top;

    if (g_page.clickedX > 0) {
      g_blocks[g_page.dragobj].moveTo(nx, ny);
    }
  }

  if (!browser.ns4) return false      
}

//
// on_mousedown - onMouseDown event handler.
//
// Parameters:
// 
//   e event object for onMouseDown event
//
function on_mousedown(e) {

  var x = (browser.ns4 || browser.ns6 || browser.ns7) ? e.pageX : event.x || event.clientX;
  var y = (browser.ns4 || browser.ns6 || browser.ns7) ? e.pageY : event.y || event.clientY;

  if (browser.ie5 || browser.ie6) y += document.body.scrollTop;
        
  // dragover and resizeover are set in the div__on_mouseover and div__on_mouseout functions,
  // when the mouse moves over or out of either the resize DIV or the 
  // block header.  They identify which block is affected.

  var id1 = g_page.dragover;
  var id2 = g_page.resizeover;

  if (id2 > -1) {
    g_page.resizeobj = id2;
    g_blocks[id2].focus() // bring this block to the foreground                
  }
  else if (id1 > -1) { 
    g_page.dragobj = id1;
    g_page.clickedX = x - g_blocks[id1]._left; 
    g_page.clickedY = y - g_blocks[id1]._top;
    g_blocks[id1].focus() // bring this block to the foreground                
  }

  if (browser.ns4) routeEvent(e);

  if (browser.ns6 || browser.ns7) return true;

  if (!browser.ns4) return false;    
}

//
// on_mouseup - onMouseUp event handler.
//
function on_mouseup(e) {

  var x = (browser.ns4 || browser.ns6 || browser.ns7) ? e.pageX : event.x || event.clientX;
  var y = (browser.ns4 || browser.ns6 || browser.ns7) ? e.pageY : event.y || event.clientY;

  if (browser.ie5 || browser.ie6) y += document.body.scrollTop;

  if (g_page.resizeobj > -1) { //Resize
    // get the block to be resized
    var block = g_blocks[g_page.resizeobj];

    var cx = x - block._left;
    var cy = y - block._top;

    if (g_page.snapToGrid) cx = block.clip.right;

    if (cx < g_page.minBlockWidth) cx = g_page.minBlockWidth;
    if (cy < g_page.minBlockHeight) cy = g_page.minBlockHeight;
 
    block.resize(cx, cy);

    // If we're snapping to grid, we need to refresh
    // the display of the block's column the height
    // of the block has now been changed.

    if (g_page.snapToGrid) {
      var column = g_page._columns[block.columnIndex];
      column.arrangeBlocks();
    }

    block.checkScroll();
  }
  else if (g_page.dragobj > -1) { //Move
    // get the block to be moved
    var block = g_blocks[g_page.dragobj];

    var drop = new Point;
    drop.x = x - g_page.clickedX;
    drop.y = y - g_page.clickedY;

    if (drop.y < g_page.top) drop.y = g_page.top;

    if (g_page.snapToGrid) {
      g_page.snapBlockToGrid(g_page.dragobj, drop.x, drop.y);
    }
    else {
      block.moveTo(drop.x, drop.y);
    }

    block.lastX = block._left;
    block.lastY = block._top;
  }

  g_page.dragobj = -1 ;
  g_page.dragover = -1;

  g_page.clickedX = 0;
  g_page.clickedY = 0;

  g_page.resizeobj = -1;
  g_page.resizeover = -1;

  if (browser.ns4) routeEvent(e);
}

//
// on_resize - onResize event handler
//
function on_resize() {
  if (!g_page.snapToGrid) return;

  var columns = g_page._columns;
  var numColumns = columns.length;

  for (var colIndex = 0; colIndex < numColumns; colIndex++) {
    var column = columns[colIndex];
    var name = column._name;
    var element = column._element;

    //
    // Get the dimensions of the element.
    //

    var left = !browser.ns4 ? element.offsetLeft : element.pageX;
    var top = !browser.ns4 ? element.offsetTop : element.pageY;
    var width = !browser.ns4 ? element.offsetWidth : element.clip.width;
    var height = !browser.ns4 ? element.offsetHeight : element.clip.height;
  
    if (!browser.ns4 && !browser.ns6) {
      while (element.offsetParent != null) {
        left += element.offsetLeft;
        top += element.offsetTop;
        element = element.offsetParent;
      }
    }

    column._left = left;
    column._top = top;
    column._width = width;
    column._height = height;

    var blocks = column._blocks;
    var numBlocks = blocks.length;

    for (var blockIndex = 0; blockIndex < numBlocks; blockIndex++) {
      var block = blocks[blockIndex];

      //block.moveTo(left, top);
      block.resize(width, block._height);
    }

    column.arrangeBlocks();
  }
}

//
// Button - Represents a button image
//
function Button(p_width, p_height, p_imagePath, p_imageText) {
  //
  this.width = p_width;
  this.height = p_height;
  this.imagePath = p_imagePath;
  this.imageText = p_imageText;
}

//
// Rect - Represents a rectangle.
//
// Parameters:
//
//   t -
//   r -
//   b -
//   l -
//
function Rect(t, r, b, l) {

  //
  this.top = t;
  this.right = r;
  this.bottom = b;
  this.left = l;
}

//
// Point - Represents a point/coordinate.
//
function Point(p_x, p_y) {

  //
  this.x = p_x;
  this.y = p_y;
}

//
// Get the named DIV element from the DOM.
//
function getElementByName(name, nest, dnest, ddnest) {

  var element;

  if (!browser.ns4) {
    if (browser.dom) {
      element = document.getElementById(name);
    }
    else if (browser.ie4) {
      element = document.all[name];
    }
  }
  else {
    var tmp = 0;

    if (ddnest) {
      tmp = document[nest].document[dnest].document[ddnest].document[name];
    }
    else if (dnest) {
      tmp = document[nest].document[dnest].document[name];
    }
    else if (nest) {
      tmp = document[nest].document[name];
    }
    else {
      tmp = document.layers[name];
    }	
    element =  tmp ? tmp : 0;
  }

  return element;
}

//
// openColumnDiv
//
function openColumnDiv(id) {

  //
  document.write("<div id=" + id); 
  document.write(!browser.ns4 ? "" : " class=clColumn");
  document.write(">");
}

//
// openBlock - Open a block "tag" <block>.
//
// Enclose the content to be rendered in a block
// between openBlock() ... closeBlock().
//
// See closeBlock.
//
function openBlock(title) {

  //
  var str = "";
  var num = g_blocks.length;

  str += '<div id="divBlock' + num + '" class="clBlock">\n';

  str += '<div id="divMenu' + num + '" class="clMenu">\n';
  str += '<a href="#" onclick="g_blocks[' + num + '].menu(); return false"><img src="' + g_btnSysMenu.imagePath + '" width="' + g_btnSysMenu.width + '" height="' + g_btnSysMenu.height + '" border="0" vspace="0" hspace="0" alt="' + g_btnSysMenu.imageText + '"></a>';
  str += '</div>\n';

  // create a DIV for the header
  str += '<div id="divBlockHeader' + num + '" class="clHeader">' + title + '</div>\n';

  // create a DIV for each of the header buttons

  str += '<div id="divMinimize' + num + '" class="clMinimize">\n';
  str += '<a href="#" onclick="g_blocks[' + num + '].minimize(); return false"><img src="' + g_btnMinimize.imagePath + '" width="' + g_btnMinimize.width + '" height="' + g_btnMinimize.height + '" border="0" vspace="0" hspace="0" alt="' + g_btnMinimize.imageText + '"></a>';
  str += '</div>\n';

  str += '<div id="divMaximize' + num + '" class="clMaximize">\n';
  str += '<a href="#" onclick="g_blocks[' + num + '].maximize(); return false"><img src="' + g_btnMaximize.imagePath + '" width="' + g_btnMaximize.width + '" height="' + g_btnMaximize.height + '" border="0" vspace="0" hspace="0" alt="' + g_btnMaximize.imageText + '"></a>';
  str += '</div>\n';

  str += '<div id="divRestore' + num + '" class="clRestore">\n';
  str += '<a href="#" onclick="g_blocks[' + num + '].restore(); return false"><img src="' + g_btnRestore.imagePath + '" width="' + g_btnRestore.width + '" height="' + g_btnRestore.height + '" border="0" vspace="0" hspace="0" alt="' + g_btnRestore.imageText + '"></a>';
  str += '</div>\n';

  str += '<div id="divClose' + num + '" class="clClose">\n';
  str += '<a href="#" onclick="g_blocks[' + num + '].close(); return false"><img src="' + g_btnClose.imagePath + '" width="' + g_btnClose.width + '" height="' + g_btnClose.height + '" border="0" vspace="0" hspace="0" alt="' + g_btnClose.imageText + '"></a>';
  str += '</div>\n';

  str += '<div id="divEdit' + num + '" class="clEdit">\n';
  str += '<a href="#" onclick="g_blocks[' + num + '].edit(); return false"><img src="' + g_btnEdit.imagePath + '" width="' + g_btnEdit.width + '" height="' + g_btnEdit.height + '" border="0" vspace="0" hspace="0" alt="' + g_btnEdit.imageText + '"></a>';
  str += '</div>\n';

  // create a DIV for the resize
  str += '<div id="divResize'+num+'" class="clResize">\n';
  str += '</div>\n';

  str += '<div id="divBody' + num + '" class="clBody">\n';
  str += '<div id="divBlockText' + num + '" class="clText">';

  document.write(str);
}

//
// closeBlock - Closes a block "tag" </block>.
//
// See openBlock.
// 
function closeBlock() {

  //
  var str = "";
  var num = g_blocks.length;

  str  = '\n';
  str += '</div>\n';  // close DIV block text
  str += '</div>\n';  // close DIV block body

  str += '<div id="divVScroll' + num + '" class="clVScroll"></div>\n';
  str += '<div id="divHScroll' + num + '" class="clHScroll"></div>\n';
  str += '<div id="divUp' + num + '" class="clUp"><a href="#" onclick="return false" onmousedown="g_page.scroll(true); g_blocks[' + num + '].scrollDown();" onmouseup="g_page.scroll(false);"><img src="' + g_btnUp.imagePath + '" width="' + g_btnUp.width + '" height="' + g_btnUp.height + '" alt="" border="0"></a></div>\n';
  str += '<div id="divDown' + num + '" class="clDown"><a href="#" onclick="return false" onmousedown="g_page.scroll(true); g_blocks[' + num + '].scrollUp();" onmouseup="g_page.scroll(false);"><img src="' + g_btnDown.imagePath + '" width="' + g_btnDown.width + '" height="' + g_btnDown.height + '" alt="" border="0"></a></div>\n';
  str += '<div id="divLeft' + num + '" class="clLeft"><a href="#" onmousedown="g_page.scroll(true); g_blocks[' + num + '].scrollLeft();" onclick="return false;" onmouseup="g_page.scroll(false);"><img src="' + g_btnLeft.imagePath + '" width="' + g_btnLeft.width + '" height="' + g_btnLeft.height + '" alt="" border="0"></a></div>\n';
  str += '<div id="divRight' + num + '" class="clRight"><a href="#" onmousedown="g_page.scroll(true); g_blocks[' + num + '].scrollRight();" onclick="return false;" onmouseup="g_page.scroll(false);"><img src="' + g_btnRight.imagePath + '" width="' + g_btnRight.width + '" height="' + g_btnRight.height + '" alt="" border="0"></a></div>\n';

  str += '</div>';    // close DIV block

  return str;
}

//
// Globals
//

  var browser = new Browser;
  var g_blocks = new Array;
  var g_page = new Page;

  //
  // Buttons
  //

  var g_btnMinimize = new Button(16, 16, "/ep/images/buttons/block/bbtn_collapse.gif", "Collapse");
  var g_btnMaximize = new Button(16, 16, "/ep/images/buttons/block/bbtn_expand.gif", "Maximize");
  var g_btnRestore  = new Button(16, 16, "/ep/images/buttons/block/bbtn_expand.gif", "Expand");
  var g_btnClose    = new Button(16, 16, "/ep/images/buttons/block/bbtn_hide.gif", "Hide");
  var g_btnEdit     = new Button(26, 16, "/ep/images/buttons/block/bbtn_edit.gif", "Edit");

  var g_btnUp       = new Button(16, 16, "/ep/images/buttons/block/bbtn_arrow_up.gif", "");
  var g_btnDown     = new Button(16, 16, "/ep/images/buttons/block/bbtn_arrow_down.gif", "");
  var g_btnLeft     = new Button(16, 16, "/ep/images/buttons/block/bbtn_arrow_left.gif", "");
  var g_btnRight    = new Button(16, 16, "/ep/images/buttons/block/bbtn_arrow_right.gif", "");

  var g_btnSysMenu  = new Button(16, 16, "/ep/images/buttons/block/bbtn_hide.gif", "");
  var g_btnResize   = new Button( 6,  6, "/ep/images/buttons/block/bbtn_resize.gif", "");

  var c_bordersize = 1;                           // border size
  var c_bottomHeight = 0;
  var c_headerHeight = g_btnMinimize.height + 1;  // header height
  var c_blockspacing = 5;                         // spacing between blocks
  var c_buttonspacing = 1;                        // spacing between buttons

  var c_scrollWidth = g_btnUp.width;              // width of the vertical scrollbar
  var c_scrollHeight = g_btnLeft.height;          // height of the horizontal scrollbar

  var c_minimizeWidth = 40;                       // 

  var c_scrollTimeout = 60;                       //


