// default node properties
var _cmNodeProperties =
{
// theme prefix
prefix: '',

  // main menu display attributes
  //
  // Note.  When the menu bar is horizontal,
  // mainFolderLeft and mainFolderRight are
  // put in <span></span>.  When the menu
  // bar is vertical, they would be put in
  // a separate TD cell.

  // HTML code to the left of the folder item
  mainFolderLeft: '',
  // HTML code to the right of the folder item
  mainFolderRight: '',
// HTML code to the left of the regular item
mainItemLeft: '',
// HTML code to the right of the regular item
mainItemRight: '',

// sub menu display attributes

// HTML code to the left of the folder item
folderLeft: '',
// HTML code to the right of the folder item
folderRight: '',
// HTML code to the left of the regular item
itemLeft: '',
// HTML code to the right of the regular item
itemRight: '',
// cell spacing for main menu
mainSpacing: 0,
// cell spacing for sub menus
subSpacing: 0,

// optional settings
// If not set, use the default

// auto disappear time for submenus in milli-seconds
delay: 500,

// 1st layer sub menu starting index
zIndexStart: 1000,
// z-index incremental step for subsequent layers
zIndexInc: 5,

// sub menu header appears before the sub menu table
subMenuHeader: null,
// sub menu header appears after the sub menu table
subMenuFooter: null,

// submenu location adjustments
//
// offsetHMainAdjust for adjusting the first submenu
// of a 'hbr' menu.
// offsetVMainAdjust for adjusting the first submenu
// of a 'vbr' menu.
// offsetSubAdjust for subsequent level of submenus
//
offsetHMainAdjust: [0, 0],
offsetVMainAdjust: [0, 0],
offsetSubAdjust: [0, 0],

// act on click to open sub menu
// not yet implemented
// 0 : use default behavior
// 1 : hover open in all cases
// 2 : click on main, hover on sub
// 3 : click open in all cases (illegal as of 1.5)
clickOpen: 1,

// special effects on open/closing a sub menu
effect: null
};

// Globals
var _cmIDCount = 0;
var _cmIDName = 'cmSubMenuID'; // for creating submenu id

var _cmTimeOut = null; // how long the menu would stay
var _cmCurrentItem = null; // the current menu item being selected;

var _cmNoAction = new Object (); // indicate that the item cannot be hovered.
var _cmNoClick = new Object (); // similar to _cmNoAction but does not respond to mouseup/mousedown events
var _cmSplit = new Object (); // indicate that the item is a menu split

var _cmMenuList = new Array (); // a list of the current menus
var _cmItemList = new Array (); // a simple list of items

var _cmFrameList = new Array (); // a pool of reusable iframes
var _cmFrameListSize = 0; // keep track of the actual size
var _cmFrameIDCount = 0; // keep track of the frame id
var _cmFrameMasking = true; // use the frame masking

// disable iframe masking for IE7
/*@cc_on
@if (@_jscript_version >= 5.6)
if (_cmFrameMasking)
{
var v = navigator.appVersion;
var i = v.indexOf ("MSIE ");
if (i >= 0)
{
if (parseInt (navigator.appVersion.substring (i + 5)) >= 7)
_cmFrameMasking = false;
}
}
@end
@*/

var _cmClicked = false; // for onClick

// flag for turning on off hiding objects
//
// 0: automatic
// 1: hiding
// 2: no hiding
var _cmHideObjects = 0;

// Utility function to do a shallow copy a node property
function cmClone (nodeProperties)
{
var returnVal = new Object ();
for (v in nodeProperties)
returnVal[v] = nodeProperties[v];
return returnVal;
}

//
// store the new menu information into a structure to retrieve it later
//
function cmAllocMenu (id, menu, orient, nodeProperties, prefix)
{
var info = new Object ();
info.div = id;
info.menu = menu;
info.orient = orient;
info.nodeProperties = nodeProperties;
info.prefix = prefix;
var menuID = _cmMenuList.length;
_cmMenuList[menuID] = info;
return menuID;
}

//
// request a frame
//
function cmAllocFrame ()
{
if (_cmFrameListSize > 0)
return cmGetObject (_cmFrameList[--_cmFrameListSize]);
var frameObj = document.createElement ('iframe');
var id = _cmFrameIDCount++;
frameObj.id = 'cmFrame' + id;
frameObj.frameBorder = '0';
frameObj.style.display = 'none';
frameObj.src = 'javascript:false';
document.body.appendChild (frameObj);
frameObj.style.filter = 'alpha(opacity=0)';
frameObj.style.zIndex = 99;
frameObj.style.position = 'absolute';
frameObj.style.border = '0';
frameObj.scrolling = 'no';
return frameObj;
}

//
// make a frame resuable later
//
function cmFreeFrame (frameObj)
{
_cmFrameList[_cmFrameListSize++] = frameObj.id;
}

//////////////////////////////////////////////////////////////////////
//
// Drawing Functions and Utility Functions
//
//////////////////////////////////////////////////////////////////////

//
// produce a new unique id
//
function cmNewID ()
{
return _cmIDName + (++_cmIDCount);
}

//
// return the property string for the menu item
//
function cmActionItem (item, isMain, idSub, menuInfo, menuID)
{
_cmItemList[_cmItemList.length] = item;
var index = _cmItemList.length - 1;
idSub = (!idSub) ? 'null' : ('\'' + idSub + '\'');

var clickOpen = menuInfo.nodeProperties.clickOpen;
var onClick = (clickOpen == 3) || (clickOpen == 2 && isMain);

var param = 'this,' + isMain + ',' + idSub + ',' + menuID + ',' + index;

var returnStr;
if (onClick)
returnStr = ' onmouseover="cmItemMouseOver(' + param + ',false)" onmousedown="cmItemMouseDownOpenSub (' + param + ')"';
else
returnStr = ' onmouseover="cmItemMouseOverOpenSub (' + param + ')" onmousedown="cmItemMouseDown (' + param + ')"';
return returnStr + ' onmouseout="cmItemMouseOut (' + param + ')" onmouseup="cmItemMouseUp (' + param + ')"';
}

//
// this one is used by _cmNoClick to only take care of onmouseover and onmouseout
// events which are associated with menu but not actions associated with menu clicking/closing
//
function cmNoClickItem (item, isMain, idSub, menuInfo, menuID)
{
// var index = _cmItemList.push (item) - 1;
_cmItemList[_cmItemList.length] = item;
var index = _cmItemList.length - 1;
idSub = (!idSub) ? 'null' : ('\'' + idSub + '\'');

var param = 'this,' + isMain + ',' + idSub + ',' + menuID + ',' + index;

return ' onmouseover="cmItemMouseOver (' + param + ')" onmouseout="cmItemMouseOut (' + param + ')"';
}

function cmNoActionItem (item)
{
return item[1];
}

function cmSplitItem (prefix, isMain, vertical)
{
var classStr = 'cm' + prefix;
if (isMain)
{
classStr += 'Main';
if (vertical)
classStr += 'HSplit';
else
classStr += 'VSplit';
}
else
classStr += 'HSplit';
return eval (classStr);
}

//
// draw the sub menu recursively
//
function cmDrawSubMenu (subMenu, prefix, id, nodeProperties, zIndexStart, menuInfo, menuID)
{
var str = '<div class="' + prefix + 'SubMenu" id="' + id + '" style="z-index: ' + zIndexStart + ';position: absolute; top: 0px; left: 0px;">';
if (nodeProperties.subMenuHeader)
str += nodeProperties.subMenuHeader;

str += '<table summary="sub menu" id="' + id + 'Table" cellspacing="' + nodeProperties.subSpacing + '" class="' + prefix + 'SubMenuTable">';

var strSub = '';

var item;
var idSub;
var hasChild;

var i;

var classStr;

for (i = 5; i < subMenu.length; ++i)
{
item = subMenu[i];
if (!item)
continue;

if (item == _cmSplit)
item = cmSplitItem (prefix, 0, true);
item.parentItem = subMenu;
item.subMenuID = id;

hasChild = (item.length > 5);
idSub = hasChild ? cmNewID () : null;

str += '<tr class="' + prefix + 'MenuItem"';
if (item[0] != _cmNoClick)
str += cmActionItem (item, 0, idSub, menuInfo, menuID);
else
str += cmNoClickItem (item, 0, idSub, menuInfo, menuID);
str += '>'

if (item[0] == _cmNoAction || item[0] == _cmNoClick)
{
str += cmNoActionItem (item);
str += '</tr>';
continue;
}

classStr = prefix + 'Menu';
classStr += hasChild ? 'Folder' : 'Item';

str += '<td class="' + classStr + 'Left">';

if (item[0] != null)
str += item[0];
else
str += hasChild ? nodeProperties.folderLeft : nodeProperties.itemLeft;

str += '</td><td class="' + classStr + 'Text">' + item[1];

str += '</td><td class="' + classStr + 'Right">';

if (hasChild)
{
str += nodeProperties.folderRight;
strSub += cmDrawSubMenu (item, prefix, idSub, nodeProperties, zIndexStart + nodeProperties.zIndexInc, menuInfo, menuID);
}
else
str += nodeProperties.itemRight;
str += '</td></tr>';
}

str += '</table>';

if (nodeProperties.subMenuFooter)
str += nodeProperties.subMenuFooter;
str += '</div>' + strSub;
return str;
}

//
// The function that builds the menu inside the specified element id.
//
// id id of the element
// orient orientation of the menu in [hv][ub][lr] format
// menu the menu object to be drawn
// nodeProperties properties for the theme
// prefix prefix of the theme
//
function cmDraw (id, menu, orient, nodeProperties, prefix)
{
var obj = cmGetObject (id);

if (!prefix)
prefix = nodeProperties.prefix;
if (!prefix)
prefix = '';
if (!nodeProperties)
nodeProperties = _cmNodeProperties;
if (!orient)
orient = 'hbr';

var menuID = cmAllocMenu (id, menu, orient, nodeProperties, prefix);
var menuInfo = _cmMenuList[menuID];

// setup potentially missing properties
if (!nodeProperties.delay)
nodeProperties.delay = _cmNodeProperties.delay;
if (!nodeProperties.clickOpen)
nodeProperties.clickOpen = _cmNodeProperties.clickOpen;
if (!nodeProperties.zIndexStart)
nodeProperties.zIndexStart = _cmNodeProperties.zIndexStart;
if (!nodeProperties.zIndexInc)
nodeProperties.zIndexInc = _cmNodeProperties.zIndexInc;
if (!nodeProperties.offsetHMainAdjust)
nodeProperties.offsetHMainAdjust = _cmNodeProperties.offsetHMainAdjust;
if (!nodeProperties.offsetVMainAdjust)
nodeProperties.offsetVMainAdjust = _cmNodeProperties.offsetVMainAdjust;
if (!nodeProperties.offsetSubAdjust)
nodeProperties.offsetSubAdjust = _cmNodeProperties.offsetSubAdjust;
// save user setting on frame masking
menuInfo.cmFrameMasking = _cmFrameMasking;

var str = '<table summary="main menu" class="' + prefix + 'Menu" cellspacing="' + nodeProperties.mainSpacing + '">';
var strSub = '';

var vertical;

// draw the main menu items
if (orient.charAt (0) == 'h')
{
str += '<tr>';
vertical = false;
}
else
{
vertical = true;
}

var i;
var item;
var idSub;
var hasChild;

var classStr;

for (i = 0; i < menu.length; ++i)
{
item = menu[i];

if (!item)
continue;

item.menu = menu;
item.subMenuID = id;

str += vertical ? '<tr' : '<td';
str += ' class="' + prefix + 'MainItem"';

hasChild = (item.length > 5);
idSub = hasChild ? cmNewID () : null;

str += cmActionItem (item, 1, idSub, menuInfo, menuID) + '>';

if (item == _cmSplit)
item = cmSplitItem (prefix, 1, vertical);

if (item[0] == _cmNoAction || item[0] == _cmNoClick)
{
str += cmNoActionItem (item);
str += vertical? '</tr>' : '</td>';
continue;
}

classStr = prefix + 'Main' + (hasChild ? 'Folder' : 'Item');

str += vertical ? '<td' : '<span';
str += ' class="' + classStr + 'Left">';

str += (item[0] == null) ? (hasChild ? nodeProperties.mainFolderLeft : nodeProperties.mainItemLeft)
: item[0];
str += vertical ? '</td>' : '</span>';

str += vertical ? '<td' : '<span';
str += ' class="' + classStr + 'Text">';
str += item[1];

str += vertical ? '</td>' : '</span>';

str += vertical ? '<td' : '<span';
str += ' class="' + classStr + 'Right">';

str += hasChild ? nodeProperties.mainFolderRight : nodeProperties.mainItemRight;

str += vertical ? '</td>' : '</span>';

str += vertical ? '</tr>' : '</td>';

if (hasChild)
strSub += cmDrawSubMenu (item, prefix, idSub, nodeProperties, nodeProperties.zIndexStart, menuInfo, menuID);
}
if (!vertical)
str += '</tr>';
str += '</table>' + strSub;
obj.innerHTML = str;
}

//
// The function builds the menu inside the specified element id.
//
// This function is similar to cmDraw except that menu is taken from HTML node
// rather a javascript tree.  This feature allows links to be scanned by search
// bots.
//
// This function basically converts HTML node to a javascript tree, and then calls
// cmDraw to draw the actual menu, replacing the hidden menu tree.
//
// Format:
// <div id="menu">
// <ul style="visibility: hidden">
// <li><span>icon</span><a href="link" title="description">main menu text</a>
// <ul>
// <li><span>icon</span><a href="link" title="description">submenu item</a>
// </li>
// </ul>
// </li>
// </ul>
// </div>
//
function cmDrawFromText (id, orient, nodeProperties, prefix)
{
var domMenu = cmGetObject (id);
var menu = null;
for (var currentDomItem = domMenu.firstChild; currentDomItem; currentDomItem = currentDomItem.nextSibling)
{
if (!currentDomItem.tagName)
continue;
var tag = currentDomItem.tagName.toLowerCase ();
if (tag != 'ul' && tag != 'ol')
continue;
menu = cmDrawFromTextSubMenu (currentDomItem);
break;
}
if (menu)
cmDraw (id, menu, orient, nodeProperties, prefix);
}

//
// a recursive function that build menu tree structure
//
function cmDrawFromTextSubMenu (domMenu)
{
var items = new Array ();
for (var currentDomItem = domMenu.firstChild; currentDomItem; currentDomItem = currentDomItem.nextSibling)
{
if (!currentDomItem.tagName || currentDomItem.tagName.toLowerCase () != 'li')
continue;
if (currentDomItem.firstChild == null)
{
items[items.length] = _cmSplit;
continue;
}
var item = new Array ();
var currentItem = currentDomItem.firstChild;
var hasAction = false;
for (; currentItem; currentItem = currentItem.nextSibling)
{
// scan for span or div tag
if (!currentItem.tagName)
continue;
if (currentItem.className == 'cmNoClick')
{
item[0] = _cmNoClick;
item[1] = getActionHTML (currentItem);
hasAction = true;
break;
}
if (currentItem.className == 'cmNoAction')
{
item[0] = _cmNoAction;
item[1] = getActionHTML (currentItem);
hasAction = true;
break;
}
var tag = currentItem.tagName.toLowerCase ();
if (tag != 'span')
continue;
if (!currentItem.firstChild)
item[0] = null;
else
item[0] = currentItem.innerHTML;
currentItem = currentItem.nextSibling;
break;
}
if (hasAction)
{
items[items.length] = item;
continue;
}
if (!currentItem)
continue;
for (; currentItem; currentItem = currentItem.nextSibling)
{
if (!currentItem.tagName)
continue;
var tag = currentItem.tagName.toLowerCase ();
if (tag == 'a')
{
item[1] = currentItem.innerHTML;
item[2] = currentItem.href;
item[3] = currentItem.target;
item[4] = currentItem.title;
if (item[4] == '')
item[4] = null;
}
else if (tag == 'span' || tag == 'div')
{
item[1] = currentItem.innerHTML;
item[2] = null;
item[3] = null;
item[4] = null;
}
break;
}

for (; currentItem; currentItem = currentItem.nextSibling)
{
// scan for span tag
if (!currentItem.tagName)
continue;
var tag = currentItem.tagName.toLowerCase ();
if (tag != 'ul' && tag != 'ol')
continue;
var subMenuItems = cmDrawFromTextSubMenu (currentItem);
for (i = 0; i < subMenuItems.length; ++i)
item[i + 5] = subMenuItems[i];
break;
}
items[items.length] = item;
}
return items;
}

//
// obtain the actual action item's action, which is inside a
// table.  The first row should be it
//
function getActionHTML (htmlNode)
{
var returnVal = '<td></td><td></td><td></td>';
var currentDomItem;
// find the table first
for (currentDomItem = htmlNode.firstChild; currentDomItem; currentDomItem = currentDomItem.nextSibling)
{
if (currentDomItem.tagName && currentDomItem.tagName.toLowerCase () == 'table')
break;
}
if (!currentDomItem)
return returnVal;
// skip over tbody
for (currentDomItem = currentDomItem.firstChild; currentDomItem; currentDomItem = currentDomItem.nextSibling)
{
if (currentDomItem.tagName && currentDomItem.tagName.toLowerCase () == 'tbody')
break;
}
if (!currentDomItem)
return returnVal;
// get the first tr
for (currentDomItem = currentDomItem.firstChild; currentDomItem; currentDomItem = currentDomItem.nextSibling)
{
if (currentDomItem.tagName && currentDomItem.tagName.toLowerCase () == 'tr')
break;
}
if (!currentDomItem)
return returnVal;
return currentDomItem.innerHTML;
}

//
// get the DOM object associated with the item
//
function cmGetMenuItem (item)
{
if (!item.subMenuID)
return null;
var subMenu = cmGetObject (item.subMenuID);
// we are dealing with a main menu item
if (item.menu)
{
var menu = item.menu;
// skip over table, tbody, tr, reach td
subMenu = subMenu.firstChild.firstChild.firstChild.firstChild;
var i;
for (i = 0; i < menu.length; ++i)
{
if (menu[i] == item)
return subMenu;
subMenu = subMenu.nextSibling;
}
}
else if (item.parentItem) // sub menu item
{
var menu = item.parentItem;
var table = cmGetObject (item.subMenuID + 'Table');
if (!table)
return null;
// skip over table, tbody, reach tr
subMenu = table.firstChild.firstChild;
var i;
for (i = 5; i < menu.length; ++i)
{
if (menu[i] == item)
return subMenu;
subMenu = subMenu.nextSibling;
}
}
return null;
}

//
// disable a menu item
//
function cmDisableItem (item, prefix)
{
if (!item)
return;
var menuItem = cmGetMenuItem (item);
if (!menuItem)
return;
if (item.menu)
menuItem.className = prefix + 'MainItemDisabled';
else
menuItem.className = prefix + 'MenuItemDisabled';
item.isDisabled = true;
}

//
// enable a menu item
//
function cmEnableItem (item, prefix)
{
if (!item)
return;
var menuItem = cmGetMenuItem (item);
if (!menuItem)
return;
if (item.menu)
menu.className = prefix + 'MainItem';
else
menu.className = prefix + 'MenuItem';
item.isDisabled = true;
}

//////////////////////////////////////////////////////////////////////
//
// Mouse Event Handling Functions
//
//////////////////////////////////////////////////////////////////////

//
// action should be taken for mouse moving in to the menu item
//
// Here we just do things concerning this menu item, w/o opening sub menus.
//
function cmItemMouseOver (obj, isMain, idSub, menuID, index, calledByOpenSub)
{
if (!calledByOpenSub && _cmClicked)
{
cmItemMouseOverOpenSub (obj, isMain, idSub, menuID, index);
return;
}

clearTimeout (_cmTimeOut);

if (_cmItemList[index].isDisabled)
return;

var prefix = _cmMenuList[menuID].prefix;

if (!obj.cmMenuID)
{
obj.cmMenuID = menuID;
obj.cmIsMain = isMain;
}

var thisMenu = cmGetThisMenu (obj, prefix);

// insert obj into cmItems if cmItems doesn't have obj
if (!thisMenu.cmItems)
thisMenu.cmItems = new Array ();
var i;
for (i = 0; i < thisMenu.cmItems.length; ++i)
{
if (thisMenu.cmItems[i] == obj)
break;
}
if (i == thisMenu.cmItems.length)
{
//thisMenu.cmItems.push (obj);
thisMenu.cmItems[i] = obj;
}

// hide the previous submenu that is not this branch
if (_cmCurrentItem)
{
// occationally, we get this case when user
// move the mouse slowly to the border
if (_cmCurrentItem == obj || _cmCurrentItem == thisMenu)
{
var item = _cmItemList[index];
cmSetStatus (item);
return;
}

var thatMenuInfo = _cmMenuList[_cmCurrentItem.cmMenuID];
var thatPrefix = thatMenuInfo.prefix;
var thatMenu = cmGetThisMenu (_cmCurrentItem, thatPrefix);

if (thatMenu != thisMenu.cmParentMenu)
{
if (_cmCurrentItem.cmIsMain)
_cmCurrentItem.className = thatPrefix + 'MainItem';
else
_cmCurrentItem.className = thatPrefix + 'MenuItem';
if (thatMenu.id != idSub)
cmHideMenu (thatMenu, thisMenu, thatMenuInfo);
}
}

// okay, set the current menu to this obj
_cmCurrentItem = obj;

// just in case, reset all items in this menu to MenuItem
cmResetMenu (thisMenu, prefix);

var item = _cmItemList[index];
var isDefaultItem = cmIsDefaultItem (item);

if (isDefaultItem)
{
if (isMain)
obj.className = prefix + 'MainItemHover';
else
obj.className = prefix + 'MenuItemHover';
}

cmSetStatus (item);
}

//
// action should be taken for mouse moving in to the menu item
//
// This function also opens sub menu
//
function cmItemMouseOverOpenSub (obj, isMain, idSub, menuID, index)
{
clearTimeout (_cmTimeOut);

if (_cmItemList[index].isDisabled)
return;

cmItemMouseOver (obj, isMain, idSub, menuID, index, true);

if (idSub)
{
var subMenu = cmGetObject (idSub);
var menuInfo = _cmMenuList[menuID];
var orient = menuInfo.orient;
var prefix = menuInfo.prefix;
cmShowSubMenu (obj, isMain, subMenu, menuInfo);
}
}

//
// action should be taken for mouse moving out of the menu item
//
function cmItemMouseOut (obj, isMain, idSub, menuID, index)
{
var delayTime = _cmMenuList[menuID].nodeProperties.delay;
_cmTimeOut = window.setTimeout ('cmHideMenuTime ()', delayTime);
window.defaultStatus = '';
}

//
// action should be taken for mouse button down at a menu item
//
function cmItemMouseDown (obj, isMain, idSub, menuID, index)
{
if (_cmItemList[index].isDisabled)
return;

if (cmIsDefaultItem (_cmItemList[index]))
{
var prefix = _cmMenuList[menuID].prefix;
if (obj.cmIsMain)
obj.className = prefix + 'MainItemActive';
else
obj.className = prefix + 'MenuItemActive';
}
}

//
// action should be taken for mouse button down at a menu item
// this is one also opens submenu if needed
//
function cmItemMouseDownOpenSub (obj, isMain, idSub, menuID, index)
{
if (_cmItemList[index].isDisabled)
return;

_cmClicked = true;
cmItemMouseDown (obj, isMain, idSub, menuID, index);

if (idSub)
{
var subMenu = cmGetObject (idSub);
var menuInfo = _cmMenuList[menuID];
cmShowSubMenu (obj, isMain, subMenu, menuInfo);
}
}

//
// action should be taken for mouse button up at a menu item
//
function cmItemMouseUp (obj, isMain, idSub, menuID, index)
{
if (_cmItemList[index].isDisabled)
return;

var item = _cmItemList[index];

var link = null, target = '_self';

if (item.length > 2)
link = item[2];
if (item.length > 3 && item[3])
target = item[3];

if (link != null)
{
_cmClicked = false;
window.open (link, target);
}

var menuInfo = _cmMenuList[menuID];
var prefix = menuInfo.prefix;
var thisMenu = cmGetThisMenu (obj, prefix);

var hasChild = (item.length > 5);
if (!hasChild)
{
if (cmIsDefaultItem (item))
{
if (obj.cmIsMain)
obj.className = prefix + 'MainItem';
else
obj.className = prefix + 'MenuItem';
}
cmHideMenu (thisMenu, null, menuInfo);
}
else
{
if (cmIsDefaultItem (item))
{
if (obj.cmIsMain)
obj.className = prefix + 'MainItemHover';
else
obj.className = prefix + 'MenuItemHover';
}
}
}

//////////////////////////////////////////////////////////////////////
//
// Mouse Event Support Utility Functions
//
//////////////////////////////////////////////////////////////////////

//
// move submenu to the appropriate location
//
function cmMoveSubMenu (obj, isMain, subMenu, menuInfo)
{
var orient = menuInfo.orient;

var offsetAdjust;

if (isMain)
{
if (orient.charAt (0) == 'h')
offsetAdjust = menuInfo.nodeProperties.offsetHMainAdjust;
else
offsetAdjust = menuInfo.nodeProperties.offsetVMainAdjust;
}
else
offsetAdjust = menuInfo.nodeProperties.offsetSubAdjust;

if (!isMain && orient.charAt (0) == 'h')
orient = 'v' + orient.charAt (1) + orient.charAt (2);

var mode = String (orient);
var p = subMenu.offsetParent;
var subMenuWidth = cmGetWidth (subMenu);
var horiz = cmGetHorizontalAlign (obj, mode, p, subMenuWidth);
if (mode.charAt (0) == 'h')
{
if (mode.charAt (1) == 'b')
subMenu.style.top = (cmGetYAt (obj, p) + cmGetHeight (obj) + offsetAdjust[1]) + 'px';
else
subMenu.style.top = (cmGetYAt (obj, p) - cmGetHeight (subMenu) - offsetAdjust[1]) + 'px';
if (horiz == 'r')
subMenu.style.left = (cmGetXAt (obj, p) + offsetAdjust[0]) + 'px';
else
subMenu.style.left = (cmGetXAt (obj, p) + cmGetWidth (obj) - subMenuWidth - offsetAdjust[0]) + 'px';
}
else
{
if (horiz == 'r')
subMenu.style.left = (cmGetXAt (obj, p) + cmGetWidth (obj) + offsetAdjust[0]) + 'px';
else
subMenu.style.left = (cmGetXAt (obj, p) - subMenuWidth - offsetAdjust[0]) + 'px';
if (mode.charAt (1) == 'b')
subMenu.style.top = (cmGetYAt (obj, p) + offsetAdjust[1]) + 'px';
else
subMenu.style.top = (cmGetYAt (obj, p) + cmGetHeight (obj) - cmGetHeight (subMenu) + offsetAdjust[1]) + 'px';
}

// IE specific iframe masking method
/*@cc_on
@if (@_jscript_version >= 5.5)
if (menuInfo.cmFrameMasking)
{
if (!subMenu.cmFrameObj)
{
var frameObj = cmAllocFrame ();
subMenu.cmFrameObj = frameObj;
}

var frameObj = subMenu.cmFrameObj;
frameObj.style.zIndex = subMenu.style.zIndex - 1;
frameObj.style.left = (cmGetX (subMenu) - cmGetX (frameObj.offsetParent)) + 'px';
frameObj.style.top = (cmGetY (subMenu)  - cmGetY (frameObj.offsetParent)) + 'px';
frameObj.style.width = cmGetWidth (subMenu) + 'px';
frameObj.style.height = cmGetHeight (subMenu) + 'px';
frameObj.style.display = 'block';
}
@end
@*/
if (horiz != orient.charAt (2))
orient = orient.charAt (0) + orient.charAt (1) + horiz;
return orient;
}

//
// automatically re-adjust the menu position based on available screen size.
//
function cmGetHorizontalAlign (obj, mode, p, subMenuWidth)
{
var horiz = mode.charAt (2);
if (!(document.body))
return horiz;
var body = document.body;
var browserLeft;
var browserRight;
if (window.innerWidth)
{
// DOM window attributes
browserLeft = window.pageXOffset;
browserRight = window.innerWidth + browserLeft;
}
else if (body.clientWidth)
{
// IE attributes
browserLeft = body.clientLeft;
browserRight = body.clientWidth + browserLeft;
}
else
return horiz;
if (mode.charAt (0) == 'h')
{
if (horiz == 'r' && (cmGetXAt (obj) + subMenuWidth) > browserRight)
horiz = 'l';
if (horiz == 'l' && (cmGetXAt (obj) + cmGetWidth (obj) - subMenuWidth) < browserLeft)
horiz = 'r';
return horiz;
}
else
{
if (horiz == 'r' && (cmGetXAt (obj, p) + cmGetWidth (obj) + subMenuWidth) > browserRight)
horiz = 'l';
if (horiz == 'l' && (cmGetXAt (obj, p) - subMenuWidth) < browserLeft)
horiz = 'r';
return horiz;
}
}

//
// show the subMenu w/ specified orientation
// also move it to the correct coordinates
//
function cmShowSubMenu (obj, isMain, subMenu, menuInfo)
{
var prefix = menuInfo.prefix;

if (!subMenu.cmParentMenu)
{
// establish the tree w/ back edge
var thisMenu = cmGetThisMenu (obj, prefix);
subMenu.cmParentMenu = thisMenu;
if (!thisMenu.cmSubMenu)
thisMenu.cmSubMenu = new Array ();
thisMenu.cmSubMenu[thisMenu.cmSubMenu.length] = subMenu;
}

var effectInstance = subMenu.cmEffect;
if (effectInstance)
effectInstance.showEffect (true);
else
{
// position the sub menu only if we are not already showing the submenu
var orient = cmMoveSubMenu (obj, isMain, subMenu, menuInfo);
subMenu.cmOrient = orient;

var forceShow = false;
if (subMenu.style.visibility != 'visible' && menuInfo.nodeProperties.effect)
{
try
{
effectInstance = menuInfo.nodeProperties.effect.getInstance (subMenu, orient);
effectInstance.showEffect (false);
}
catch (e)
{
forceShow = true;
subMenu.cmEffect = null;
}
}
else
forceShow = true;

if (forceShow)
{
subMenu.style.visibility = 'visible';
/*@cc_on
@if (@_jscript_version >= 5.5)
if (subMenu.cmFrameObj)
subMenu.cmFrameObj.style.display = 'block';
@end
@*/
}
}

if (!_cmHideObjects)
{
_cmHideObjects = 2; // default = not hide, may change behavior later
try
{
if (window.opera)
{
if (parseInt (navigator.appVersion) < 9)
_cmHideObjects = 1;
}
}
catch (e)
{
}
}

if (_cmHideObjects == 1)
{
if (!subMenu.cmOverlap)
subMenu.cmOverlap = new Array ();
cmHideControl ("IFRAME", subMenu);
cmHideControl ("OBJECT", subMenu);
}
}

//
// reset all the menu items to class MenuItem in thisMenu
//
function cmResetMenu (thisMenu, prefix)
{
if (thisMenu.cmItems)
{
var i;
var str;
var items = thisMenu.cmItems;
for (i = 0; i < items.length; ++i)
{
if (items[i].cmIsMain)
{
if (items[i].className == (prefix + 'MainItemDisabled'))
continue;
}
else
{
if (items[i].className == (prefix + 'MenuItemDisabled'))
continue;
}
if (items[i].cmIsMain)
str = prefix + 'MainItem';
else
str = prefix + 'MenuItem';
if (items[i].className != str)
items[i].className = str;
}
}
}

//
// called by the timer to hide the menu
//
function cmHideMenuTime ()
{
_cmClicked = false;
if (_cmCurrentItem)
{
var menuInfo = _cmMenuList[_cmCurrentItem.cmMenuID];
var prefix = menuInfo.prefix;
cmHideMenu (cmGetThisMenu (_cmCurrentItem, prefix), null, menuInfo);
_cmCurrentItem = null;
}
}

//
// Only hides this menu
//
function cmHideThisMenu (thisMenu, menuInfo)
{
var effectInstance = thisMenu.cmEffect;
if (effectInstance)
effectInstance.hideEffect (true);
else
{
thisMenu.style.visibility = 'hidden';
thisMenu.style.top = '0px';
thisMenu.style.left = '0px';
thisMenu.cmOrient = null;
/*@cc_on
@if (@_jscript_version >= 5.5)
if (thisMenu.cmFrameObj)
{
var frameObj = thisMenu.cmFrameObj;
frameObj.style.display = 'none';
frameObj.style.width = '1px';
frameObj.style.height = '1px';
thisMenu.cmFrameObj = null;
cmFreeFrame (frameObj);
}
@end
@*/
}

cmShowControl (thisMenu);
thisMenu.cmItems = null;
}

//
// hide thisMenu, children of thisMenu, as well as the ancestor
// of thisMenu until currentMenu is encountered.  currentMenu
// will not be hidden
//
function cmHideMenu (thisMenu, currentMenu, menuInfo)
{
var prefix = menuInfo.prefix;
var str = prefix + 'SubMenu';

// hide the down stream menus
if (thisMenu.cmSubMenu)
{
var i;
for (i = 0; i < thisMenu.cmSubMenu.length; ++i)
{
cmHideSubMenu (thisMenu.cmSubMenu[i], menuInfo);
}
}

// hide the upstream menus
while (thisMenu && thisMenu != currentMenu)
{
cmResetMenu (thisMenu, prefix);
if (thisMenu.className == str)
{
cmHideThisMenu (thisMenu, menuInfo);
}
else
break;
thisMenu = cmGetThisMenu (thisMenu.cmParentMenu, prefix);
}
}

//
// hide thisMenu as well as its sub menus if thisMenu is not
// already hidden
//
function cmHideSubMenu (thisMenu, menuInfo)
{
if (thisMenu.style.visibility == 'hidden')
return;
if (thisMenu.cmSubMenu)
{
var i;
for (i = 0; i < thisMenu.cmSubMenu.length; ++i)
{
cmHideSubMenu (thisMenu.cmSubMenu[i], menuInfo);
}
}
var prefix = menuInfo.prefix;
cmResetMenu (thisMenu, prefix);
cmHideThisMenu (thisMenu, menuInfo);
}

//
// hide a control such as IFRAME
//
function cmHideControl (tagName, subMenu)
{
var x = cmGetX (subMenu);
var y = cmGetY (subMenu);
var w = subMenu.offsetWidth;
var h = subMenu.offsetHeight;

var i;
for (i = 0; i < document.all.tags(tagName).length; ++i)
{
var obj = document.all.tags(tagName)[i];
if (!obj || !obj.offsetParent)
continue;

// check if the object and the subMenu overlap

var ox = cmGetX (obj);
var oy = cmGetY (obj);
var ow = obj.offsetWidth;
var oh = obj.offsetHeight;

if (ox > (x + w) || (ox + ow) < x)
continue;
if (oy > (y + h) || (oy + oh) < y)
continue;

// if object is already made hidden by a different
// submenu then we dont want to put it on overlap list of
// of a submenu a second time.
// - bug fixed by Felix Zaslavskiy
if(obj.style.visibility == 'hidden')
continue;

//subMenu.cmOverlap.push (obj);
subMenu.cmOverlap[subMenu.cmOverlap.length] = obj;
obj.style.visibility = 'hidden';
}
}

//
// show the control hidden by the subMenu
//
function cmShowControl (subMenu)
{
if (subMenu.cmOverlap)
{
var i;
for (i = 0; i < subMenu.cmOverlap.length; ++i)
subMenu.cmOverlap[i].style.visibility = "";
}
subMenu.cmOverlap = null;
}

//
// returns the main menu or the submenu table where this obj (menu item)
// is in
//
function cmGetThisMenu (obj, prefix)
{
var str1 = prefix + 'SubMenu';
var str2 = prefix + 'Menu';
while (obj)
{
if (obj.className == str1 || obj.className == str2)
return obj;
obj = obj.parentNode;
}
return null;
}

//
// A special effect function to hook the menu which contains
// special effect object to the timer.
//
function cmTimeEffect (menuID, show, delayTime)
{
window.setTimeout ('cmCallEffect("' + menuID + '",' + show + ')', delayTime);
}

//
// A special effect function.  Called by timer.
//
function cmCallEffect (menuID, show)
{
var menu = cmGetObject (menuID);
if (!menu || !menu.cmEffect)
return;
try
{
if (show)
menu.cmEffect.showEffect (false);
else
menu.cmEffect.hideEffect (false);
}
catch (e)
{
}
}

//
// return true if this item is handled using default handlers
//
function cmIsDefaultItem (item)
{
if (item == _cmSplit || item[0] == _cmNoAction || item[0] == _cmNoClick)
return false;
return true;
}

//
// returns the object baring the id
//
function cmGetObject (id)
{
if (document.all)
return document.all[id];
return document.getElementById (id);
}

//
// functions that obtain the width of an HTML element.
//
function cmGetWidth (obj)
{
var width = obj.offsetWidth;
if (width > 0 || !cmIsTRNode (obj))
return width;
if (!obj.firstChild)
return 0;
// use TABLE's length can cause an extra pixel gap
//return obj.parentNode.parentNode.offsetWidth;

// use the left and right child instead
return obj.lastChild.offsetLeft - obj.firstChild.offsetLeft + cmGetWidth (obj.lastChild);
}

//
// functions that obtain the height of an HTML element.
//
function cmGetHeight (obj)
{
var height = obj.offsetHeight;
if (height > 0 || !cmIsTRNode (obj))
return height;
if (!obj.firstChild)
return 0;
// use the first child's height
return obj.firstChild.offsetHeight;
}

//
// functions that obtain the coordinates of an HTML element
//
function cmGetX (obj)
{
if (!obj)
return 0;
var x = 0;

do
{
x += obj.offsetLeft;
obj = obj.offsetParent;
}
while (obj);
return x;
}

function cmGetXAt (obj, elm)
{
var x = 0;

while (obj && obj != elm)
{
x += obj.offsetLeft;
obj = obj.offsetParent;
}
if (obj == elm)
return x;
return x - cmGetX (elm);
}

function cmGetY (obj)
{
if (!obj)
return 0;
var y = 0;
do
{
y += obj.offsetTop;
obj = obj.offsetParent;
}
while (obj);
return y;
}

function cmIsTRNode (obj)
{
var tagName = obj.tagName;
return tagName == "TR" || tagName == "tr" || tagName == "Tr" || tagName == "tR";
}

//
// get the Y position of the object.  In case of TR element though,
// we attempt to adjust the value.
//
function cmGetYAt (obj, elm)
{
var y = 0;

if (!obj.offsetHeight && cmIsTRNode (obj))
{
var firstTR = obj.parentNode.firstChild;
obj = obj.firstChild;
y -= firstTR.firstChild.offsetTop;
}

while (obj && obj != elm)
{
y += obj.offsetTop;
obj = obj.offsetParent;
}

if (obj == elm)
return y;
return y - cmGetY (elm);
}

//
// extract description from the menu item and set the status text
//
function cmSetStatus (item)
{
var descript = '';
if (item.length > 4)
descript = (item[4] != null) ? item[4] : (item[2] ? item[2] : descript);
else if (item.length > 2)
descript = (item[2] ? item[2] : descript);

window.defaultStatus = descript;
}

//
// debug function, ignore :)
//
function cmGetProperties (obj)
{
if (obj == undefined)
return 'undefined';
if (obj == null)
return 'null';

var msg = obj + ':\n';
var i;
for (i in obj)
msg += i + ' = ' + obj[i] + '; ';
return msg;
}
