var AnalyzeManager = new analyzeManager();

AnalyzeManager.addAnalyzer( new link_analyzer(null, linkMenuActivator, 'bottom') );

window.onload = function ()
  {
  AnalyzeManager.analyzeAll();
  }

function menu( Element, ActivatorContext )
  {
  var DELAY_FOR_MOUSE_OUT_MENU = 300;

  Element = new baseElement( Element );
  var ParentMenu;
  var IsTopLevelMenu;

  var HideThread;

  if( document.layers != null )
    {
    window.getSize = function ()
      {
      return {Width: window.innerWidth, Height: window.innerHeight};
      }

    function cancel()
      {
      return false;
      }
    }
    else if( document.all != null )
    {
    window.getSize = function ()
      {
      return {Width: document.body.scrollWidth, Height: document.body.offsetHeight};
      }

    function cancel()
      {
      event.cancelBubble = true;
      return false;
      }

    Element.onmousedown = cancel;
    Element.onmouseup = cancel;
    }
    else return null;

  Element.getParentMenu = function ()
    {
      if( IsTopLevelMenu == null )
      {
        if( ActivatorContext != null && ActivatorContext.getParentMenu != null )
        {
        ParentMenu = ActivatorContext;
        IsTopLevelMenu = false;
        }
        else IsTopLevelMenu = true;
      }
    return ParentMenu;
    }

  function getContext()
    {
    var M = Element;
      while( M.getParentMenu() != null )
      {
      M = M.getParentMenu();
      }
    return M.parentLayer != null && M.parentLayer != window ? new baseElement( M.parentLayer ) : window;
    }

  function selectByPlacing( Size, AreaBegin, AreaSize, HoleBegin, HoleSize )
    {
    return HoleBegin + HoleSize + Size < AreaBegin + AreaSize ? HoleBegin + HoleSize : HoleBegin - Size;
    }

  function selectByAligning( Size, AreaBegin, AreaSize, HoleBegin, HoleSize )
    {
    return HoleBegin + Size < AreaBegin + AreaSize ? HoleBegin : HoleBegin + HoleSize - Size;
    }

  function selectByRestricting( Size, AreaBegin, AreaSize, HoleBegin, HoleSize )
    {
    return HoleBegin + Size < AreaBegin + AreaSize ? HoleBegin : AreaBegin + AreaSize - Size;
    }

  Element.place = function ( ActivatorPosition, ActivatorSize, Layout )
    {
    var Context = getContext();
    var Origin = Context.getAbsolutePosition != null ? Context.getAbsolutePosition() : {X: 0, Y: 0};
    var Area = Context.getSize();

    var Size = Element.getSize();

    var X;
    var Y;
      if( IsTopLevelMenu )
      {
        if( Layout == 'top' || Layout == 'bottom'  )
        {
        X = selectByRestricting( Size.Width, Origin.X, Area.Width, ActivatorPosition.X, ActivatorSize.Width );
        Y = selectByPlacing( Size.Height, Origin.Y, Area.Height, ActivatorPosition.Y, ActivatorSize.Height );
        }
        else if( Layout == 'left' || Layout == 'right' )
        {
        X = selectByPlacing( Size.Width, Origin.X, Area.Width, ActivatorPosition.X, ActivatorSize.Width );
        Y = selectByAligning( Size.Height, Origin.Y, ActivatorPosition.Y, ActivatorSize.Height );
        }
      }
      else
      {
      X = selectByPlacing( Size.Width, Origin.X, Area.Width, ParentMenu.getAbsolutePosition().X, ParentMenu.getSize().Width );
      Y = selectByAligning( Size.Height, Origin.Y, Area.Height, ActivatorPosition.Y, ActivatorSize.Height );
      }
    Element.moveToAbsolute( X, Y );
    }

  Element.hideAfter = function ( Waiting )
    {
    HideThread = setTimeout( Element.hide, Waiting )
    }

  Element.clearHide = function ()
    {
      if( HideThread != null ) clearTimeout( HideThread );
    HideThread = null;
    }

  var IsMicrosoft = document.all != null;//////////////////////////////////////////////////////

  Element.onmouseover = function onMouseOver( Evt )
    {
      if( !IsMicrosoft || !Element.contains(event.fromElement) )
      {
      var M = Element;
        while( M != null )
        {
        M.clearHide();
        M.show();
        M = M.getParentMenu();
        }
      }
    return cancel();
    }

  Element.onmouseout = function onMouseOut( Evt )
    {
      if( !IsMicrosoft || !Element.contains(event.toElement) )
      {
      Element.hide();
      var M = Element.getParentMenu();
        while( M != null )
        {
        M.hideAfter( DELAY_FOR_MOUSE_OUT_MENU );
        M = M.getParentMenu();
        }
      }
    return cancel();
    }

  return Element;
  }

function baseElement( Element )
  {
    if( document.layers != null )
    {
    Element.show = function show( Strongly )
      {
      Element.visibility = Strongly ? 'show' : 'inherit';
      }

    Element.hide = function hide()
      {
      Element.visibility = 'hide';
      }

    Element.getAbsolutePosition = function ()
      {
      return {X: Element.pageX, Y: Element.pageY};
      }

    Element.getSize = function ()
      {
      return {Width: Element.clip.width, Height: Element.clip.height};
      }
    }
    else if( document.all != null )
    {
    Element.show = function show( Strongly )
      {
      Element.style.visibility = Strongly ? 'visible' : 'inherit';
      }

    Element.hide = function hide()
      {
      Element.style.visibility = 'hidden';
      }

    Element.getAbsolutePosition = function ()
      {
      var PageX = 0;
      var PageY = 0;
      var E = Element;
        while( E != null )
        {
        PageX += E.offsetLeft;
        PageY += E.offsetTop;
        E = E.offsetParent;
        }
      return {X: PageX, Y: PageY};
      }

    Element.getSize = function ()
      {
      return {Width: (Element.style.posWidth != 0 ? Element.style.pixelWidth : Element.offsetWidth), Height: (Element.style.posHeight != 0 ? Element.style.pixelHeight : Element.offsetHeight)};
      }

    Element.moveToAbsolute = function ( X, Y )
      {
      var X0 = 0;
      var Y0 = 0;
      var P = Element.offsetParent;
        while( P != null )
        {
        X0 += P.offsetLeft;
        Y0 += P.offsetTop;
        P = P.offsetParent;
        }
      Element.style.pixelLeft = X - X0;
      Element.style.pixelTop = Y - Y0;
      }
    }
    else return null;

  return Element;
  }

function linkMenuActivator( Link, Container, Name, Layout )
  {
  var DELAY_FOR_MOUSE_OUT_ACTIVATOR = 120;

  var Menu = findLayer( Name, Container.window.document );
    if( Menu == null ) return null;

    if( document.layers != null )
    {
    Link.getAbsolutePosition = function ()
      {
        if( Container == Container.window ) return {X: Link.x, Y: Link.y};
        else return {X: Link.x + Container.pageX, Y: Link.y + Container.pageY};
      }

    function getValueOf( SizeString )
      {
      var M = SizeString.match( /([0-9]+\.?[0-9]*)([a-zA-Z]*)/ );
        switch( M[2] )
        {
        case 'px': return parseInt( M[1] );
        case 'pt': return parseInt( M[1] )*96/72;
        case 'mm': return parseInt( M[1] )*96/25.4;
        case 'cm': return parseInt( M[1] )*96/2.54;
        }
      }

    Link.getSize = function ()
      {
      var DEFAULT_FONT_SIZE = 16;
      var DEFAULT_TEXT_LENGTH = 8;
      var FontSize;
        if( document.tags['a'].fontSize != null ) FontSize = getValueOf( document.tags['a'].fontSize );
        else if( document.tags['body'].fontSize != null ) FontSize = getValueOf( document.tags['body'].fontSize );
        else FontSize = DEFAULT_FONT_SIZE;
      var W = FontSize*(0.5*(Link.text != null ? Link.text.length : DEFAULT_TEXT_LENGTH) + 0.2);
      var H = FontSize*1.5;
      return {Width: Math.round(W), Height: Math.round(H)};
      }

    Link.getContext = function ()
      {
      return Container;
      }

    function cancel()
      {
      return false;
      }
    }
    else if( document.all != null )
    {
    Link.getAbsolutePosition = function ()
      {
      var X = 0;
      var Y = 0;
      var E = Link;
        while( E != null )
        {
        X += E.offsetLeft;
        Y += E.offsetTop;
        E = E.offsetParent;
        }
      return {X: X, Y: Y};
      }

    Link.getSize = function ()
      {
      return {Width: Link.offsetWidth, Height: Link.offsetHeight};
      }

    Link.getContext = function ()
      {
        for( var P = Link.offsetParent ; P.tagName != 'BODY' ; P = P.offsetParent )
          if( P.tagName != 'TABLE' && P.tagName != 'TR' && P.tagName != 'TD' ) return P;
      return Container.window;
      }

    function cancel()
      {
      event.cancelBubble = true;
      return false;
      }
    }
    else return null;

  function findLayer( ID, Document )
    {
    var Ls = Document.layers;
      if( Ls == null ) return Document.all[ID];
      if( Ls[ID] != null ) return Ls[ID];
    var L;
      for( var i = 0 ; i < Ls.length ; i++ )
      {
      L = findLayer( ID, Ls[i].document );
        if( L != null ) return L;
      }
    return null;
    }

  Link.onmouseover = function onMouseOver( Evt )
    {
    Menu.clearHide();
    Menu.place( Link.getAbsolutePosition(), Link.getSize(), Layout );
    Menu.show();
    return cancel();
    }

  Link.onmouseout = function onMouseOut( Evt )
    {
    Menu.hideAfter( DELAY_FOR_MOUSE_OUT_ACTIVATOR );
    return cancel();
    }

  Link.href = Container.window.document.location.href;
//  Link.onclick = cancel;

  Menu = new menu( Menu, Link.getContext() );

  return Link;
  }

function link_analyzer( Marker, objectConstructor, ObjectData )
  {
  var Type = Marker != null ? Marker.toLowerCase() : null;

  this.getType = function ()
    {
    return Type;
    }

  this.findArray = function findArray( Container )
    {
      if( window.document.layers != null ) return Container.document.links;
      else if( document.all != null ) return (Container != window ? Container : document).all.tags( 'a' );
      else return null;
    }

  function getPath( Link )
    {
    var Ret = unescape( Link.href );
    var i = Ret.indexOf( '#' );
      if( 0 < i ) Ret = Ret.substring(0, i);
      if( Link.pathname == '/' && Ret.charAt(Ret.length - 1) != '/' ) Ret += '/';
    return Ret; 
    }

  function getHash( Link )
    {
    var i = Link.href.indexOf( '#' );
    return (0 < i && i + 1 < Link.href.length) ? Link.href.substring(i + 1) : null; 
    }

  this.accept = function accept( Element, Container )
    {
    var Hash = getHash( Element );
      if( Hash == null || getPath(Element) != getPath(Container.window.document.location) ) return false;

    var Anchors = Container.window.document.anchors;
      for( var i = 0 ; i < Anchors.length ; i++ )
        if( Anchors[i].name == Hash ) return false;

    return (Type == null || Hash.toLowerCase().search(Type) != -1);
    }

  this.createObject = function createObject( Element, Container )
    {
    var Object = new objectConstructor( Element, Container, getHash(Element), ObjectData );
    }
  }

function analyzeManager()
  {
  var Analyzers;
  var This = this;

  this.addAnalyzer = function addAnalyzer( New )
    {
      if( New.getType != null
      &&  New.findArray != null
      &&  New.accept != null
      &&  New.createObject != null )
      {
        if( Analyzers == null ) Analyzers = {};
      Type = New.getType();
        if( Type == null ) Type = 'type_' + Math.random();
      Analyzers[Type] = New;
      }
    }

  this.analyze = function ( Type, Container )
    {
    var Cont = Container != null ? Container : window;
    var An = Analyzers[Type];

    var Elements = An.findArray( Cont );
      for( var j = 0 ; j < Elements.length ; j++ )
      {
        if( An.accept(Elements[j], Cont) ) An.createObject( Elements[j], Cont );
      }

      if( Cont.document != null && Cont.document.layers != null )
      {
        for( var k = 0 ; k < Cont.document.layers.length ; k++ )
        This.analyze( Type, Cont.document.layers[k] );
      }
    }

  this.analyzeAll = function ( Container )
    {
      for( var each in Analyzers )
      This.analyze( each, Container );
    }
  }
