// Глобальные переменные
var _cmIDCount = 0;
var _cmIDName = 'cmSubID';              // ID для создания подменю

var _cmTimeOut = null;                  // задержка показа меню
var _cmCurrentItem = null;              // текущий элемент меню выбран

var _cmNoAction = new Object ();        // соответсвует элементу, котрый не имеет свойство hover
var _cmSplit = new Object ();           // элемент меню - разделитель

var _cmItemList = new Array ();         // список элементов меню

// настройки меню по умолчанию
var _cmNodeProperties = {
  // настройки отображения главного меню

  // Если тип меню - горизонтальный, то mainFolderLeft и mainFolderRight являются
  // аттрибутами <span></span>.
  // Когда же меню - вертикальное они вкладываются в отдельные ячейки TD.
  	
  mainFolderLeft:  '',  // HTML код слева от элемента типа "Folder"
  mainFolderRight: '',  // HTML код справа от элемента типа "Folder"
  mainItemLeft:    '',  // HTML код слева от элемента типа "Item"
  mainItemRight:   '',  // HTML код справа от элемента типа "Item"
                       
  // настройки отображения подменю

  folderLeft:  '',     // 0, HTML код слева от элемента типа "Folder"
  folderRight: '',     // 1, HTML код справа от элемента типа "Folder"
  itemLeft:    '',     // 2, HTML код слева от элемента типа "Item"
  itemRight:   '',     // 3, HTML код справа от элемента типа "Item"
  mainSpacing: 0,      // 4, разделитель ячеек для меню
  subSpacing: 0,       // 5, разделитель ячеек для подменю
  delay: 500           // 6, время задержки дял подменю в 'мс'
};

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

               Функции создания и отображения меню

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

// создание уникального ID
function cmNewID() {
   return _cmIDName + ( ++_cmIDCount );
}

// возвращает строку свойств для элемента меню
function cmActionItem( item, prefix, isMain, idSub, orient, nodeProperties, isSub ) {
  _cmItemList[ _cmItemList.length ] = item;

  var index = _cmItemList.length - 1;

  idSub  = ( !idSub ) ? 'null' : ( '\'' + idSub + '\'' );
  orient = '\'' + orient + '\'';
  prefix = '\'' + prefix + '\'';

  var eventMouseOver = ' onmouseover="cmItemMouseOver (this,' + prefix + ',' + isMain + ',' + idSub + ',' + orient + ',' + index + ', ' + ( item[5] && isSub ) + ')"';
  return eventMouseOver + ' onmouseout="cmItemMouseOut (this,' + nodeProperties.delay + ')" onmousedown="cmItemMouseDown (this,' + index + ')" onmouseup="cmItemMouseUp (this,' + index + ')"';
}

// для разделителя пунктов меню
function cmNoActionItem( item, prefix ) {
  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';

  var item = eval( classStr );

  return cmNoActionItem( item, prefix );
}

// рекурсивно создает подменю
function cmDrawSubMenu (subMenu, prefix, id, orient, nodeProperties) {

  var str = '\n<div class="' + prefix + 'SubMenu" id="' + id + '">\n';

  str += ' <table summary="sub menu" cellspacing="' + nodeProperties.subSpacing + '" class="' + prefix + 'SubMenuTable">\n';

  var strSub = '';

  var item;
  var idSub;
  var hasChild;

  var i;

  var classStr;

  for ( i = 6; i < subMenu.length; ++i ) {

     item = subMenu[ i ];

     if ( !item ) continue;

     // по кол-ву элементов в массиве определим есть ли вложения
     hasChild = ( item.length > 6 );

     idSub = hasChild ? cmNewID () : null;

     str += '  <tr class="' + prefix + 'SubItem"' + cmActionItem (item, prefix, 0, idSub, orient, nodeProperties, false) + '>\n';

     if ( item == _cmSplit ) {
        str += '    ';
        str += cmSplitItem ( prefix, 0, true );
        str += '\n  </tr>\n';
        continue;
     }

     if ( item[0] == _cmNoAction ) {
        str += '    ';
        str += cmNoActionItem (item, prefix);
        str += '\n  </tr>\n';
        continue;
     }

     classStr = prefix + 'Sub';
     classStr += hasChild ? 'Folder' : 'Item';

     str += '   <td class="' + classStr + 'Left">\n    ';

     if ( item[0] != null && item[0] != _cmNoAction ) str += item[0] + '\n   </td>\n';
     else str += hasChild ? nodeProperties.folderLeft : nodeProperties.itemLeft;

     str += '   <td class="' + classStr + 'Text">\n    <nobr>' + item[1] + '</nobr>\n   </td>\n';
     str += '   <td class="' + classStr + 'Right">\n    '

     if ( hasChild ) {    // елси есть вложения - то вызываем рекурсию
        str    += nodeProperties.folderRight + '\n';
        strSub += cmDrawSubMenu (item, prefix, idSub, orient, nodeProperties);
     }
     else {
        str += nodeProperties.itemRight;
     }

     str += '   </td>\n  </tr>\n';

  }

  str += ' </table>\n</div>\n\n' + strSub;

  return str;
}

/***************************************************************************
   Фукнция строит меню внутри указанного родительского ID

   @param id - ID элемента
	  orient - ориентация меню 9вертикальная или горизонтальная)
	  menu	 - массив с элементами меню
	  nodeProperties - объект с базовыми настройками (optional)
          prefix - префикс меню  (optional)
***************************************************************************/

function cmDraw( id, menu, orient, nodeProperties, prefix ) {

  // получим объект меню
  var obj = cmGetObject( id );

  if ( !nodeProperties ) nodeProperties = _cmNodeProperties;
  if ( !prefix )         prefix = '';
  if ( !orient )         orient = 'hbr';

  var strSub    = '';
  var orientStr = String ( orient );
  var orientSub;
  var vertical;

  var str = '<table summary="main menu" class="' + prefix + 'Menu" cellspacing="' + nodeProperties.mainSpacing + '">\n';

  // создание элементов основного меню
  if (orientStr.charAt(0) == 'h') {
     // горизонтальное меню
     orientSub = 'v' + orientStr.substr( 1, 2 );
     vertical  = false;
     str      += ' <tr>\n';
  }
  else {
     // вертикальное меню
     orientSub = 'v' + orientStr.substr( 1, 2 );
     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;

     str += vertical ? ' <tr' : ' <td';
     // str += ' class="' + prefix + 'MainItem"';
     str += ' class="' + prefix + ( item[5] ? 'ActiveLink' : '' ) + 'MainItem"';

     // если параметров более 5 => имеются вложенные меню (они тоже массивы)
     hasChild = ( item.length > 6 );

     // получим ID для подменю ( stringID++ )
     idSub    = hasChild ? cmNewID () : null;

     // получим строку событий onMouse
     str += cmActionItem (item, prefix, 1, idSub, orient, nodeProperties, true) + ( ( item == _cmSplit ) ? ' width="2" ' : ' width="25%" align="center" valign="middle" ' ) + '>\n';
     // str += cmActionItem (item, prefix, 1, idSub, orient, nodeProperties, true) + '>\n';

     // если элемент - разделитель
     if ( item == _cmSplit ) {
        str += '  ';
        str += cmSplitItem( prefix, 1, vertical );
        str += vertical ? '\n </tr>\n' : "\n<img src='/img/i.hms.gif' width='2' height='29'></td>\n";

	continue;
     }

//      <td width='25%' align='center' valign='middle'><a href='#'><img src='/img/i.hmi1.gif' width='62' height='29' border='0'></a></td>

     if ( item[0] == _cmNoAction ) {
        str += cmNoActionItem( item, prefix );
        str += vertical? '</tr>\n' : '</td>\n';

        continue;
     }

     // тип класса
     classStr = prefix + 'Main' + ( hasChild ? 'Folder' : 'Item' );

     str += vertical ? '  <td' : '  <span';
     str += ' class="' + classStr + 'Left">\n   ';

     // str += (item[0] == null) ? (hasChild ? nodeProperties.mainFolderLeft : nodeProperties.mainItemLeft) : item[0];
     str += hasChild ? nodeProperties.mainFolderLeft : nodeProperties.mainItemLeft;

     str += vertical ? '\n  </td>\n' : '\n  </span>\n';
     str += vertical ? '  <td' : '  <span';
     str += ' class="' + classStr + 'Text">\n   ';

     // str += item[1];
     str += item[ 0 ];

     str += vertical ? '\n  </td>\n' : '\n  </span>\n';

     str += vertical ? '  <td' : '  <span';
     str += ' class="' + classStr + 'Right">\n   ';

     str += hasChild ? nodeProperties.mainFolderRight : nodeProperties.mainItemRight;

     str += vertical ? '\n  </td>\n' : '\n  </span>\n';
     str += vertical ? ' </tr>\n' : ' </td>\n';

     // создание разделов подменю
     if ( hasChild ) strSub += cmDrawSubMenu (item, prefix, idSub, orientSub, nodeProperties);
  }

  if ( !vertical ) str += '</tr>\n';

  str += '</table>\n' + strSub;

  obj.innerHTML = str;

}

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

               функции обработки событий мыши

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

// событие onMouseOver

function cmItemMouseOver( obj, prefix, isMain, idSub, orient, index, isActive ) {
   clearTimeout ( _cmTimeOut );

   if ( !obj.cmPrefix ) {
      obj.cmPrefix = prefix;
      obj.cmIsMain = isMain;
   }

   var thisMenu = cmGetThisMenu( obj, prefix );

   // вставим obj внутрь массива cmItems
   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;
   }

   // спрячем предыдущее меню
   if ( _cmCurrentItem ) {

      // данный случай возникает тогда, когда пользователь медленно двигает мышку к границе
      if ( _cmCurrentItem == thisMenu ) return;

      var thatPrefix = _cmCurrentItem.cmPrefix;
      var thatMenu   = cmGetThisMenu( _cmCurrentItem, thatPrefix );

      if ( thatMenu != thisMenu.cmParentMenu ) {

         if ( _cmCurrentItem.cmIsMain && isActive ) { }
         else if ( _cmCurrentItem.cmIsMain ) { _cmCurrentItem.className = thatPrefix + 'MainItem'; }
         else                           { _cmCurrentItem.className = thatPrefix + 'SubItem'; }

         if ( thatMenu.id != idSub ) cmHideMenu( thatMenu, thisMenu, thatPrefix );
      }
   }

   // из obj запишем текущее меню
   _cmCurrentItem = obj;

   // сбросим все меню
   cmResetMenu ( thisMenu, prefix, isActive );

   var item = _cmItemList[index];
   var isDefaultItem = cmIsDefaultItem (item);

   if ( isDefaultItem ) {
      if ( isMain && isActive ) {}
      else if ( isMain ) obj.className = prefix + 'MainItemHover';
      else          obj.className = prefix + 'SubItemHover';
   }

   if ( idSub ) {
      var subMenu = cmGetObject (idSub);
      cmShowSubMenu( obj, prefix, subMenu, orient );
   }

   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;
}


// событие onMouseOut

function cmItemMouseOut ( obj, delayTime ) {
  if ( !delayTime ) delayTime = _cmNodeProperties.delay;

  _cmTimeOut = window.setTimeout ( 'cmHideMenuTime()', delayTime );
  window.defaultStatus = '';
}

// событие onMouseDown

function cmItemMouseDown ( obj, index ) {
  if ( cmIsDefaultItem ( _cmItemList[ index ] ) ) {
     if (obj.cmIsMain) obj.className = obj.cmPrefix + 'MainItemActive';
     else              obj.className = obj.cmPrefix + 'SubItemActive';
  }
}

// событие onMouseUp

function cmItemMouseUp ( obj, index ) {

   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 ) window.open ( link, target );

   var prefix   = obj.cmPrefix;
   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 + 'SubItem';
      }

      cmHideMenu (thisMenu, null, prefix);
   }
   else {
      if ( cmIsDefaultItem ( item ) ) {
         if ( obj.cmIsMain ) obj.className = prefix + 'MainItemHover';
         else obj.className = prefix + 'SubItemHover';
      }
   }
}

/********************************************************************/
/*                                                                  */
/*     Утилиты для работы фуекций обработчиков событий мышки        */
/*                                                                  */
/********************************************************************/

/********************************************************************/
/* перемещает подменю в относительное положение                     */
/*                                                                  */
/* @param  obj     - объект меню, из которого раскрываем подменю    */
/*	   subMenu - подменю, которое показывем                     */
/*	   orient  - ориентация подменю                             */
/********************************************************************/

function cmMoveSubMenu ( obj, subMenu, orient ) {
  var mode = String (orient);
  var    p = subMenu.offsetParent;

  if ( mode.charAt(0) == 'h' ) {

     if ( mode.charAt(1) == 'b' ) subMenu.style.top = ( cmGetYAt( obj, p ) + cmGetHeight( obj ) ) + 'px';
     else                         subMenu.style.top = ( cmGetYAt( obj, p ) - cmGetHeight( subMenu ) ) + 'px';

     if ( mode.charAt(2) == 'r' ) subMenu.style.left = ( cmGetXAt( obj, p ) ) + 'px';
     else                         subMenu.style.left = ( cmGetXAt( obj, p ) + cmGetWidth( obj ) - cmGetWidth( subMenu ) ) + 'px';

     try {
       var table = subMenu.getElementsByTagName( 'TABLE' )[ 0 ];
       table.style.width = obj.clientWidth + 'px';
     }
     catch ( ignore ) { alert( ignore.toString() ); }
  }
  else {
     if ( mode.charAt(2) == 'r' ) subMenu.style.left = ( cmGetXAt( obj, p ) + cmGetWidth( obj ) ) + 'px';
     else                         subMenu.style.left = ( cmGetXAt( obj, p ) - cmGetWidth( subMenu ) ) + 'px';

     if ( mode.charAt(1) == 'b' ) subMenu.style.top = ( cmGetYAt( obj, p ) ) + 'px';
     else                         subMenu.style.top = ( cmGetYAt( obj, p ) + cmGetHeight( obj ) - cmGetHeight( subMenu ) ) + 'px';
  }
}

/********************************************************************/
/* показывает подменю согласно указанному виду ориентации           */
/* также перемещает его в нужные координаты                         */
/*                                                                  */
/* @param  obj	   - объект меню, из которого раскрываем подменю    */
/*	   subMenu - подменю, которое показывем                     */
/*	   orient  - ориентация подменю                             */
/********************************************************************/

function cmShowSubMenu ( obj, prefix, subMenu, orient ) {

   if ( !subMenu.cmParentMenu ) {

      // присоединяет дерево по задней кромке
      var thisMenu = cmGetThisMenu( obj, prefix );
      subMenu.cmParentMenu = thisMenu;

      if ( !thisMenu.cmSubMenu ) thisMenu.cmSubMenu = new Array ();

      // thisMenu.cmSubMenu.push( subMenu );
      thisMenu.cmSubMenu[ thisMenu.cmSubMenu.length ] = subMenu;
   }

   // позицонирование подменю
   cmMoveSubMenu( obj, subMenu, orient );
   subMenu.style.visibility = 'visible';

   // Под IE, такие элементы как SELECT, OBJECT, IFRAME (до версии JS 5.5)
   // являются элементами окна (window). Поэтому, если подменю должно
   // показывться поверх одного их этих элементов, то эти элементы
   // должны быть скрыты перед этим, а после закрытия подменю снова показаны.

   // это IE
   if ( document.all ) {
      if ( !subMenu.cmOverlap )	subMenu.cmOverlap = new Array ();

      /*@cc_on @*/
      /*@if (@_jscript_version >= 5.5)
      @else @*/
      cmHideControl( "IFRAME", subMenu );
      /*@end @*/
      cmHideControl( "SELECT", subMenu );
      cmHideControl( "OBJECT", subMenu );
   }
}

// "сбрасывает" все элементы меню в className=SubItem в текущем меню

function cmResetMenu( thisMenu, prefix, isActive ) {
return;
if ( thisMenu.cmItems ) {
     var i;
     var str;
     var items = thisMenu.cmItems;

     for ( i = 0; i < items.length; ++i ) {
         if ( items[i].cmIsMain ) str = prefix + 'MainItem';
         else                     str = prefix + 'SubItem';

         if ( items[i].className != str && ! isActive ) items[i].className = str;
     }
  }
}

// вызывается по таймеру. скрывает меню

function cmHideMenuTime () {
  if ( _cmCurrentItem ) {
     var prefix = _cmCurrentItem.cmPrefix;
     cmHideMenu ( cmGetThisMenu ( _cmCurrentItem, prefix ), null, prefix );
  }
}

/********************************************************************/
/* скрывает меню и всех его потомков, если оно случайно "встретилось*/
/* с текущим меню"                                                  */
/********************************************************************/

function cmHideMenu( thisMenu, currentMenu, prefix ) {

  var str = prefix + 'SubMenu';

  // скрывает меню в порядке их раскрытия
  if ( thisMenu.cmSubMenu ) {
     var i;
     for ( i = 0; i < thisMenu.cmSubMenu.length; ++i ) cmHideSubMenu (thisMenu.cmSubMenu[i], prefix);
  }

  // скрывает меню в обратном порядке их раскрытию
  while ( thisMenu && thisMenu != currentMenu ) {
     cmResetMenu (thisMenu, prefix);

     if ( thisMenu.className == str ) {
        thisMenu.style.visibility = 'hidden';
        cmShowControl( thisMenu );
     }
     else break;

     thisMenu = cmGetThisMenu (thisMenu.cmParentMenu, prefix);
  }
}

/********************************************************************/
/* скрывает меню и его подменю, если меню еще не скрыто             */
/********************************************************************/

function cmHideSubMenu ( thisMenu, prefix ) {
   if ( thisMenu.style.visibility == 'hidden' ) return;
   if ( thisMenu.cmSubMenu ) {
      var i;
      for ( i = 0; i < thisMenu.cmSubMenu.length; ++i ) cmHideSubMenu (thisMenu.cmSubMenu[i], prefix);
   }
   cmResetMenu( thisMenu, prefix );
   thisMenu.style.visibility = 'hidden';
   cmShowControl( thisMenu );
}

// скрывает такие элементы как 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;

     // проверяем перекрывает ли подменю объект
 
     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.

     if(obj.style.visibility == "hidden") continue;

     // subMenu.cmOverlap.push (obj);
     subMenu.cmOverlap[subMenu.cmOverlap.length] = obj;
     obj.style.visibility = "hidden";
  }
}

// показывает элементы,находящиеся под выпадающем меню

function cmShowControl ( subMenu ) {

   if ( subMenu.cmOverlap ) {
      var i;
      for ( i = 0; i < subMenu.cmOverlap.length; ++i ) subMenu.cmOverlap[i].style.visibility = "";
   }

   subMenu.cmOverlap = null;
}

// возвращает объект меню, в котором только что смотрели подменю

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;
}

// возвращает истину, если выбранный элемент отличается от разделителя или элемента без действия

function cmIsDefaultItem ( item ) {
  if ( item == _cmSplit || item[0] == _cmNoAction ) return false;
  return true;
}

// возвращает объект по id

function cmGetObject( id ) {
   if ( document.all ) return document.all[ id ];
   return document.getElementById( id );
}

// функция, получающая ширину элемента HTML

function cmGetWidth( obj ) {
  var width = obj.offsetWidth;

  if ( width > 0 || !cmIsTRNode ( obj ) ) return width;

  if ( !obj.firstChild ) return 0;

  // использование ширины TABLE может привести к появлению зазора
  // return obj.parentNode.parentNode.offsetWidth;

  // поэтому используем левого и правого потомка
  return obj.lastChild.offsetLeft - obj.firstChild.offsetLeft + cmGetWidth( obj.lastChild );
}

// функция, получающая высоту элемента HTML

function cmGetHeight ( obj ) {

  var height = obj.offsetHeight;

  if ( height > 0 || !cmIsTRNode ( obj ) ) return height;
  if ( !obj.firstChild ) return 0;

  // используем высоту первого потомка
  return obj.firstChild.offsetHeight;
}

// функция получающая координаты HTML элемента

function cmGetX ( obj ) {
  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 cmGetX( elm ) - x;
}

function cmGetY ( obj ) {
  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";
}

// получает Y координату объекта. Не смотря на то, что первым элементом идет TR,
// мы пытаемся привестив порядок значение

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 cmGetY( elm ) - y;
}

// функция отладки

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;
}
