//list_control.class.js

var s_listControlHash;
s_listControlHash = new CHash();

function FindListControl( strBlockId)
{
  return s_listControlHash.Item( strBlockId);
}

function CListControl( strBlockId, bVisible, bFixedPosition)
{
  // What is rasion for these var declaration?
  //var m_strBlockId;
  //var m_nLineId;
  //var m_strNextFunction;
  //var m_strPreviousFunction;
  //var m_bPreviousExists;
  //var m_bNextExists;
  //var m_block;
  //var m_ul;
  //var m_nMaxLineId;
  //var m_insideDiv;
  //var m_strOnSelectFunction;
  //var m_focusKeeper;
  //var m_bBlockButtons;
  //var m_timeOut;
  //var m_hashSelectedLines;
  //var m_nPixelsForLineVisible;
  ////var m_nInnerBlockHeight;
  
  this.m_nMaxLineId = 0;
  this.m_strBlockId = strBlockId;
  this.m_strNextFunction = '';
  this.m_strPreviousFunction = '';
  this.m_bPreviousExists = false;
  this.m_bNextExists = false;
  this.m_nActiveLineId = -1;
  this.m_block = document.getElementById( this.m_strBlockId);
  this.m_ul = null;
  this.m_insideDiv = null;
  this.m_bMousePressed = false;
  this.m_strOnSelectFunction = '';
  this.m_focusKeeper = null;
  this.m_bBlockButtons = false;
  //this.m_hashSelectedLines = new CIdHash();
  this.m_hashSelectedLines = new CHash();
  this.m_nPixelsForLineVisible = 10;
  //this.m_nInnerBlockHeight = 0;
  this.m_bSingleSelection = false

  this.m_strDblClickFunction = false;
  this.m_strReturnFunction = false;

  this.m_bVisible = false;
  this.m_bFixedPosition = false;

  this.m_nScrollTopPosition = -1;

  this.IsSingleSelection = function()
  {
    return this.m_bSingleSelection;
  }

  this.SetSingleSelection = function( bIs)
  {
    this.m_bSingleSelection = bIs;
  }

  this.IsFixedPosition = function()
  {
    return this.m_bFixedPosition;
  }

  this.IsVisible = function()
  {
    return this.m_bVisible;
  }

  this.SetVisible = function()
  {
    this.m_bVisible = true;
    this.m_block.style.display = 'block';
    this.ResizeInnerDiv();
    this.CreateFocusKeeper();
    this.ShowState();
    if ( this.m_nScrollTopPosition >= 0)
      this.m_insideDiv.scrollTop = this.m_nScrollTopPosition;
  }

  this.SetNotVisible = function()
  {
    this.m_bVisible = false;
    this.m_nScrollTopPosition = this.m_insideDiv.scrollTop;
    this.m_block.style.display = 'none';
  }

  this.GetScrollTopPosition = function()
  {
    var nPosition = this.m_nScrollTopPosition;
    if ( this.IsVisible())
    {
      nPosition = this.m_insideDiv.scrollTop;
    }
    return nPosition;
  }

  this.SetScrollTopPosition = function( nScrollTopPosition)
  {
    this.m_nScrollTopPosition = nScrollTopPosition;
    if ( this.IsVisible())
    {
      this.m_insideDiv.scrollTop = nScrollTopPosition;
    }
  }

   
  this.Init = function( bVisible, bFixedPosition)
  {
    this.m_bVisible = bVisible;

    if ( !IsOldIE())
      this.m_bFixedPosition = bFixedPosition;

    var nInnerDivLength; 
    var nInnerDivHeight;
    
    this.m_insideDiv = document.createElement( 'div');
    this.m_bMousePressed = false;
    
    //if ( window.opera )  
    //{
    //  this.m_focusKeeper = document.createElement( 'textarea');
    //  this.m_focusKeeper.setAttribute( 'rows', 0);
    //  this.m_focusKeeper.setAttribute( 'cols', 0);
    //}
    //else
    //{
    //  this.m_focusKeeper = document.createElement( 'input');
    //  this.m_focusKeeper.setAttribute( 'type', 'text');
    //}
    
    this.ResizeInnerDiv();
     
    if ( document.all && ( ! window.opera))
    {
      //Dumb IE
      this.m_insideDiv.attachEvent( 'onclick',  ListControlIEFocusElement);
      this.m_insideDiv.attachEvent( 'onmouseout',  ListControlIEMouseOut);
      //this.m_focusKeeper.attachEvent( 'onkeydown',  ListControlIEKeyDown);
      //this.m_focusKeeper.attachEvent( 'onkeyup',  ListControlIEKeyUp);
     }
    else
    {
      this.m_insideDiv.setAttribute( 'onmouseout', 'ListControlNotIEMouseOut( "'+this.m_strBlockId+'", event)');
      //this.m_focusKeeper.setAttribute( 'onkeyup', 'ListControlKeyUp( "' +this.m_strBlockId+'"); return false;');
      //if ( window.opera || ( navigator.platform != 'Win32'))
      //{
      //  this.m_focusKeeper.setAttribute( 'onkeypress', 'ListControlKeyPressed( "' +this.m_strBlockId+'", event); return false;');
      //}
      //else
      //{
      //  this.m_focusKeeper.setAttribute( 'onkeydown', 'ListControlKeyPressed( "' +this.m_strBlockId+'", event); return false;');
      //}
    }
    
    this.m_block.appendChild( this.m_insideDiv);
    this.CreateFocusKeeper();

    //var arInnerBlockPostion = FindPosition( this.m_insideDiv, false);

    //this.m_focusKeeper.style.position = 'absolute';
    //this.m_focusKeeper.style.left = arInnerBlockPostion[ 0]+'px';
    //this.m_focusKeeper.style.top = arInnerBlockPostion[ 1]+'px';
    //this.m_focusKeeper.className = 'list_control_keeper';
    //this.m_block.appendChild( this.m_focusKeeper);
  }

  this.CreateFocusKeeper = function()
  {
    if ( this.m_focusKeeper)
    {
    }
    else
    if ( this.IsVisible())
    {
      if ( IsOldOpera() || IsChrome())  
      {
        this.m_focusKeeper = document.createElement( 'textarea');
        this.m_focusKeeper.setAttribute( 'rows', 0);
        this.m_focusKeeper.setAttribute( 'cols', 0);
      }
      else
      {
        this.m_focusKeeper = document.createElement( 'input');
        this.m_focusKeeper.setAttribute( 'type', 'text');
      }

      if ( document.all && ( ! window.opera))
      {
        //Dumb IE
        this.m_focusKeeper.attachEvent( 'onkeydown',  ListControlIEKeyDown);
        this.m_focusKeeper.attachEvent( 'onkeyup',  ListControlIEKeyUp);
       }
      else
      {
        this.m_focusKeeper.setAttribute( 'onkeyup', 'ListControlKeyUp( "' +this.m_strBlockId+'"); return false;');
        if ( window.opera || ( navigator.platform != 'Win32'))
        {
          this.m_focusKeeper.setAttribute( 'onkeypress', 'ListControlKeyPressed( "' +this.m_strBlockId+'", event); return false;');
        }
        else
        {
          this.m_focusKeeper.setAttribute( 'onkeydown', 'ListControlKeyPressed( "' +this.m_strBlockId+'", event); return false;');
        }
      }

      var arInnerBlockPostion = FindPosition( this.m_insideDiv, false);

      if (this.IsFixedPosition())
        this.m_focusKeeper.style.position = 'fixed';
      else
        this.m_focusKeeper.style.position = 'absolute';
      this.m_focusKeeper.style.left = arInnerBlockPostion[ 0] + 'px';
      
      var nTopOffsetFocusKeeper = 0;
      if ( window.opera)
        nTopOffsetFocusKeeper = 10;

      this.m_focusKeeper.style.top = nTopOffsetFocusKeeper + arInnerBlockPostion[ 1] + 'px';
      this.m_focusKeeper.className = 'list_control_keeper';

      //if ( document.all && ( ! window.opera))
        this.m_block.parentNode.insertBefore( this.m_focusKeeper, this.m_block);
      //else
      //  this.m_block.appendChild( this.m_focusKeeper);
    }
  }

  this.SetOnDblClickFunction = function( strDblClickFunction)
  {
    this.m_strDblClickFunction = strDblClickFunction;
  }
  this.SetOnReturnFunction = function( strReturnFunction)
  {
    this.m_strReturnFunction = strReturnFunction;
  }

  this.OnDblClick = function()
  {
    if ( this.m_strDblClickFunction)
    {
      eval( this.m_strDblClickFunction+"( '"+this.m_strBlockId+"')");
    }
  }
  this.OnReturn = function()
  {
    //alert( 'OnReturn');
    if ( this.m_strReturnFunction)
    {
      eval( this.m_strReturnFunction+"( '"+this.m_strBlockId+"')");
    }
  }
  
  this.ListSize = function()
  {
    return this.m_nMaxLineId;
  }
    
  this.SetOnSelectFunction = function( strFunction)
  {
     this.m_strOnSelectFunction = strFunction;
  }
  this.OnSelect = function()
  {
    if ( this.m_strOnSelectFunction)
      eval( this.m_strOnSelectFunction);
  }
  
  this.ResizeInnerDiv = function()
  {
    var ieBug;
    try
    {
      //if ( this.m_nInnerBlockHeight > 0 )
      //{
      //  this.m_insideDiv.style.height = this.m_nInnerBlockHeight + 'px';  
      //}
      //else
      //{
        if ( this.IsVisible())
          this.m_insideDiv.style.height =  this.m_block.offsetHeight - 2 + 'px';  
      //}  
    }
    catch ( ieBug)
    {

    }
  }

  //this.SetInnerBlockHeight = function( nInnerBlockHeight)
  //{
  //  ////Should be called only if "ResizeInnerDiv" unable to determine this size automatically
  //  //this.m_nInnerBlockHeight = nInnerBlockHeight;
  //  //this.ResizeInnerDiv();
  //}
  
  this.CreateUl = function()
  {
    var elements = this.m_insideDiv.getElementsByTagName( 'ul');
    if ( elements.length > 0)
    {
      this.m_insideDiv.removeChild( elements[ 0]);
    }
       
    this.m_ul = document.createElement( 'ul'); 
    this.m_ul.setAttribute( 'id', 'ul_' + this.m_strBlockId);
    this.m_insideDiv.appendChild( this.m_ul);
    if ( ! document.all)
    {
      this.m_ul.setAttribute( 'style', '-moz-user-select: none;');
    }
    this.m_nMaxLineId = 0;
    this.m_bPreviousExists = false;
    this.m_bNextExists = false;
  }

  this.ClearControl = function()
  {
    var arElements = this.m_ul.childNodes;
    var nLength = this.m_ul.childNodes.length;
    var i;
    
    for ( i = 0; i < nLength; i++)
    {
      this.m_ul.removeChild( this.m_ul.childNodes[ 0]);
    }
    this.m_nMaxLineId = 0;
    this.m_bPreviousExists = false;
    this.m_bNextExists = false;
  }
  
  this.AddNext = function( strFunction)
  {
    var ul = this.m_ul;
    var li = document.createElement( 'li');
    li.setAttribute( 'class', 'arrow_href');
    if ( document.all && ( ! window.opera))
    {
     li.attachEvent( 'onclick', ListControlIENextFunction);
    }
    else
    {
      li.setAttribute( 'onclick', strFunction + '; return false;');
    }
    
    li.innerHTML = ' &darr; Next &darr; ';
    
    if ( window.opera)
    {
      li.setAttribute( 'onfocus', 'ListControlOperaFocusBug( "'+this.m_strBlockId+'", event);' );
    }
    
    li.setAttribute( 'id', this.GetLiId( this.m_nMaxLineId));
    
    ul.appendChild( li);
    this.m_bNextExists = true;
    this.m_strNextFunction = strFunction;
  }

  this.CallNext = function()
  {
    if ( this.m_bNextExists)
      eval( this.m_strNextFunction);
  }

  
  this.AddPrevious = function( strFunction)
  {
    var ul = this.m_ul;
    var li = document.createElement( 'li');
    li.setAttribute( 'class', 'arrow_href');
    if ( document.all && ( ! window.opera))
    {
     li.attachEvent( 'onclick', ListControlIEPreviousFunction);
    }
    else
    {
      li.setAttribute( 'onclick', strFunction + '; return false;');
    }
    li.setAttribute( 'id', this.GetLiId( '-1'));
    li.innerHTML = ' &uarr; Previous &uarr; ';
    if ( window.opera)
    {
      li.setAttribute( 'onfocus', 'ListControlOperaFocusBug( "'+this.m_strBlockId+'", event);' );
    }
    ul.insertBefore( li, ul.getElementsByTagName( 'li')[ 0]);
    this.m_strPreviousFunction = strFunction;
    this.m_bPreviousExists = true;
  }

  this.CallPrevious = function()
  {
    if ( this.m_bPreviousExists)
      eval( this.m_strPreviousFunction);
  }
  
  
  this.AddLine = function( strKey, strValue)
  {
    var li = document.createElement( 'li');
    var input = document.createElement( 'input');
    
    li.setAttribute( 'id', this.GetLiId( this.m_nMaxLineId));
    
    li.innerHTML = strValue;
    
    input.setAttribute( 'type', 'hidden');
    input.setAttribute( 'name', this.m_strBlockId + '_hiddens[ ' + strKey + ']');
    input.setAttribute( 'id',  this.GetHiddenId( this.m_nMaxLineId));
    input.setAttribute( 'value', 0);
    
    if ( document.all && ( ! window.opera))
    {
      //Dumb IE
      //li.attachEvent( 'onclick',  ListControlIELineClicked);
      li.attachEvent( 'onselectstart', ListControlIEDisableSelect);
      li.attachEvent( 'onmouseover',  ListControlIEMouseOver);
      li.attachEvent( 'onmousedown',  ListControlIEMouseDown);
      li.attachEvent( 'onmouseup',  ListControlIEMouseUp);
      li.attachEvent( 'ondblclick',  ListControlIEDblClick);
    }
    else
    {
      //li.setAttribute( 'onclick', 'ListControlLineClicked( "'+this.m_strBlockId+'", '+this.m_nMaxLineId+', event)' );
      li.setAttribute( 'onmouseover', 'ListControlMouseOver( "'+this.m_strBlockId+'", '+this.m_nMaxLineId+', event)');
      li.setAttribute( 'onmousedown',  'ListControlMouseDown( "'+this.m_strBlockId+'", '+this.m_nMaxLineId+', event)');
      li.setAttribute( 'onmouseup',  'ListControlMouseUp( "'+this.m_strBlockId+'")');
      li.setAttribute( 'ondblclick', 'ListControlDblClick( "' +this.m_strBlockId+'")');
    }
    
    //Dumb Opera bug work-around
    if ( window.opera)
    {
      li.setAttribute( 'onfocus', 'ListControlOperaFocusBug( "'+this.m_strBlockId+'", event); return false;' );
      li.setAttribute( 'onkeyup', 'ListControlKeyUp( "'+this.m_strBlockId+'"); ListControlFocusElement( "'+this.m_strBlockId+'"); return false;');
    }
    
    li.appendChild( input);
    
    
    this.m_ul.appendChild( li);
 
    this.m_nMaxLineId++;
  }
  
  this.LineClicked = function( nLineId, event)
  {
    var bSelectLine = true;
    if ( event.ctrlKey || event.metaKey)
    {
      if ( this.m_hashSelectedLines.Item( nLineId)) // don't use IsLineSelected() here
      {
        bSelectLine = false;
      }
    }
    else if ( event.shiftKey)
    {
      this.SelectSlice( nLineId);
    }
    else
    {
      this.UnselectAll();
    }
    if ( bSelectLine)
    {
      this.SelectLine( nLineId);
    }
    else
    {
      this.UnselectLine( nLineId);
    }
    this.ActivateLine( nLineId);
    this.ShowState();
  }
  
  this.SelectSlice = function( nLineId)
  {
    var i;
    var nActiveLineId = parseInt( this.m_nActiveLineId);
    nLineId = parseInt( nLineId);
    
    if ( nActiveLineId == -1)
    {
     return false; 
    }
    
    if ( parseInt( nLineId) > parseInt( nActiveLineId))
    {
      
      for ( i = nLineId; i > nActiveLineId; i--)
      {
        this.SelectLine( i);
      }
    }
    else if ( parseInt( nLineId) < parseInt( nActiveLineId))
    {
      
      for ( i = nLineId; i < nActiveLineId; i++)
      {
        this.SelectLine( i);
      }
    }
    return true;
  }
  
  this.SelectLine = function( nLineId)
  {
    if ( !this.IsSingleSelection())
    {
      //alert( 'SelectLine( '+nLineId+')');
      //this.m_hashSelectedLines.Mem( nLineId);
      this.m_hashSelectedLines.Set( nLineId, true);
      this.OnSelect();
      //eval( this.m_strOnSelectFunction);
    }
  }

  this.IsLineSelected = function( nLineId)
  {
    var bSelected = false;
    if ( this.IsSingleSelection())
    {
      if ( nLineId == this.m_nActiveLineId)
        bSelected = true;
    }
    else
    {
      if ( this.m_hashSelectedLines.Item( nLineId))
        bSelected = true;
    }
    return bSelected;
  }

  this.GetActiveLine = function()
  {
    return this.m_nActiveLineId;
  }

  this.ActivateLine = function( nLineId)
  {
    //alert( 'ActivateLine( '+nLineId+')');
    this.m_bBlockButtons = false;
    this.m_nActiveLineId = parseInt( nLineId);
    if ( this.IsSingleSelection())
    {
      this.OnSelect();
      //eval( this.m_strOnSelectFunction);
    }
  }
  
  this.UnselectLine = function( nLineId)
  {
    if ( !this.IsSingleSelection())
    {
      //this.m_hashSelectedLines.Remove( nLineId);
      this.m_hashSelectedLines.Del( nLineId);
      {
        this.OnSelect();
        //eval( this.m_strOnSelectFunction);
      }
    }
  }
  
  this.UnselectAll = function()
  {
    if ( !this.IsSingleSelection())
    {
      //this.m_hashSelectedLines = new CIdHash();
      this.m_hashSelectedLines.Clear();
      this.OnSelect();
      //eval( this.m_strOnSelectFunction);
    }
  }
  
  this.ShowState = function( bNoScrollBlock)
  {
    var i;
    //var classNames = new CMultipleClassNames( );
    
    for ( i = 0; i < this.m_nMaxLineId; i++)
    {
      var itemLi = GetIdItem( this.GetLiId( i));
      if ( itemLi)
      {
        var idHashClassName = FillMultiNameIdHash( GetClassName( itemLi));
        //classNames.Load( this.GetLiId( i));  
        if ( this.m_nActiveLineId == i)
        {
          idHashClassName.Mem( 'active');
          //classNames.Mem( 'active');
        }
        else
        {
          idHashClassName.Remove( 'active');
          //classNames.Remove( 'active');
        }
        
        if ( this.IsLineSelected( i))
        {
          idHashClassName.Mem( 'selected');
          //classNames.Mem( 'selected');
          document.getElementById( this.GetHiddenId( i)).value = 1;
        }
        else
        {
          idHashClassName.Remove( 'selected');
          //classNames.Remove( 'selected');
          document.getElementById( this.GetHiddenId( i)).value = 0;
        }
        SetClassName( itemLi, idHashClassName.Value());
      }
      
      //classNames.Save();
    }
    if ( !bNoScrollBlock)
      this.ScrollBlock();
  }
    
  this.GetHiddenId = function( nLineId)
  {
    return 'hidden_' + this.m_strBlockId + '_' + nLineId;
  }
  
  this.GetLiId = function( nLineId)
  {
    return 'li_' + this.m_strBlockId + '_N_' + nLineId;
  }
  
  this.KeyPressed = function( event)
  {
    if ( !this.m_bBlockButtons)
    {
      var nEventCode = event.keyCode;
      if ( nEventCode == 0)
      if ( event.charCode)
      {
        // For FireFox 3.5.7 for MacOS X - 50 and 56 are in charCode
        nEventCode = event.charCode;
      }
      
      var nLineId;
      var nNextLine;
        
      var nUp = 38;
      var nDown = 40;
      var nPgUp = 33;
      var nPgDown = 34;
      // Safary has different value for these codes
      //alert( 'Code = '+nEventCode);
      
      nCurrentLineId = this.m_nActiveLineId;
      
      var bNavigationKey = true;
      switch ( nEventCode)
      {
        case 63232:
        case 56:
        case 104:
        case nUp:
        {
          nNextLine = this.GetLineUp();
          break;
        }
        case 63233:
        case 50:
        case 98:
        case nDown:
        {
          nNextLine = this.GetLineDown();
          break;
        }
        case 63276:
        case nPgUp:
        {
          nNextLine = this.GetPageUp();
          break;
        }
        case 63277:
        case nPgDown:
        {
          nNextLine = this.GetPageDown();
          break;
        }
        default :
        {
          bNavigationKey = false;
        }
      }

      if ( bNavigationKey)
      {
        if ( nNextLine >= this.m_nMaxLineId )
        {
          if ( this.m_bNextExists)
          {
            nNextLine = this.m_nMaxLineId;
            //alert( 'Next: nCurrentLineId = '+nCurrentLineId+' nNextLine = '+nNextLine);
            if ( nCurrentLineId < nNextLine)
              this.CallNext();
              //eval( this.m_strNextFunction);
            //return false;    
          }
          else
          {
            nNextLine = ( this.m_nMaxLineId - 1);
          }
        }
        
        if ( nNextLine <= -1 )
        {
          if ( this.m_bPreviousExists)
          {
            nNextLine = -1;
            //alert( 'Prev: nCurrentLineId = '+nCurrentLineId+' nNextLine = '+nNextLine);
            if ( nCurrentLineId > nNextLine)
              this.CallPrevious();
              //eval( this.m_strPreviousFunction);
            //return false;    
          }
          else
          {
            nNextLine = 0;
          }
        }
        
        if ( event.shiftKey)
        {
          this.SelectSlice( nNextLine);
        }
        else if ( event.ctrlKey || event.metaKey)
        {
          //Do nothing
        }
        else
        {
           this.UnselectAll();
           this.SelectLine( nNextLine);
        }
          
        this.ActivateLine( nNextLine);
        this.ShowState();
      }
      else
      {
        if ( nEventCode == 13)
        {
          this.OnReturn();
        }
      }
    }
    return false;
  }
  
  this.GoToLine = function( nLineIndex, bSetForcus)
  {
    if ( nLineIndex < 0)
      nLineIndex = this.m_nMaxLineId -1;
    this.ActivateLine( nLineIndex);
    this.SelectLine( nLineIndex);
    this.ShowState();
    this.ScrollBlock();
    if ( bSetForcus)
      this.SetFocus();
  }

  this.GoToFirstElement = function()
  {
    this.GoToLine( 0, true);
  }
  
  this.GoToLastElement = function()
  {
    this.GoToLine( -1, true);
  }
 
  this.OperaFocusBug = function( event)
  {
    this.SetFocus();
    this.KeyPressed( event);
    
  }

  this.ScrollBlock = function()
  {
    var nBlockTop = TopPosition( this.m_insideDiv) + this.m_insideDiv.scrollTop;
    var nBlockHeight = this.m_insideDiv.offsetHeight;
    var nBlockBottom = ( nBlockTop + nBlockHeight);

    if( this.m_insideDiv.scrollHeight <= this.m_insideDiv.offsetHeight)
    {
      //If there is no scroll..
      return false;
    }
    
    //Dumb opera fix
    this.m_nActiveLineId = parseInt( this.m_nActiveLineId);
    
    var nNearnessToNeibor = 1;
    var topNeibor = document.getElementById( this.GetLiId( this.m_nActiveLineId - nNearnessToNeibor));
    var bottomNeibor = document.getElementById( this.GetLiId( this.m_nActiveLineId + nNearnessToNeibor));
    
    if ( topNeibor)
    {
      var nTopNeiborTop = TopPosition( topNeibor);
      var nTopNeiborHeight = topNeibor.offsetHeight;
      var nTopNeiborBottom = ( nTopNeiborTop + nTopNeiborHeight);
    }
    
    
    if ( bottomNeibor)
    {
      var nBottomNeiborTop = TopPosition( bottomNeibor);
      var nBottomNeiborHeight = bottomNeibor.offsetHeight;
      var nBottomNeiborBottom = ( nBottomNeiborTop + nBottomNeiborHeight);
    }
    
    if (( topNeibor) && ( ( nTopNeiborTop + this.m_nPixelsForLineVisible) < nBlockTop ))
    {
      this.m_insideDiv.scrollTop = ( nTopNeiborBottom - TopPosition( this.m_insideDiv));
    }
    else if ( ! topNeibor)
    {
      this.m_insideDiv.scrollTop = 0;
    }
    
    if (( bottomNeibor) && ( ( nBottomNeiborTop + this.m_nPixelsForLineVisible) > nBlockBottom ))
    {
      this.m_insideDiv.scrollTop = (nBottomNeiborTop -  nBlockHeight - TopPosition( this.m_insideDiv));
    }
    else if ( ! bottomNeibor)
    {
      this.m_insideDiv.scrollTop = TopPosition( this.m_ul) + this.m_ul.offsetHeight;
    }
    return true;
  }
  
  this.SelectAll = function()
  {
     var i;
     for ( i = 0; i < this.m_nMaxLineId; i++)  
     {
       this.SelectLine( i);
     }
     this.ShowState();
  }
  
  this.GetFirstElementNum = function( bShowLast)
  {
    var i;
    var nLineId;
    var nTop = TopPosition( this.m_insideDiv) + this.m_insideDiv.scrollTop;
    var nCurrent = this.m_nActiveLineId;
    for ( i = 1; i < this.m_nMaxLineId; i++)
    {
      var element = document.getElementById( this.GetLiId( i));
      if ( TopPosition( element) > nTop)
      { 
        nLineId = i;
        break;
      }
    }
    if (( nLineId >= this.m_nActiveLineId) || ( bShowLast))
    {
      if ( ! this.m_bNextExists)
      {
        return 0;
      }
      else
      {
        return -1;
      }
    }
    return nLineId;
  }
  
  this.GetLineUp = function()
  {
    return ( this.m_nActiveLineId - 1);
  }
  
  this.GetLineDown = function()
  {
    return ( this.m_nActiveLineId + 1);
  }
  
  this.GetPageUp = function()
  {
     //Scrolling to make current line at the bottom
    this.ScrollToElementBottom( document.getElementById( this.GetLiId( this.m_nActiveLineId)));
    return this.GetTopLineId();
  }
    
  this.GetPageDown = function()
  {
    //Scrolling to make current line at the top
    this.ScrollToElementTop( document.getElementById( this.GetLiId( this.m_nActiveLineId)));
    return this.GetBottomLineId();
  }
   
   this.GetTopLineId = function()
   {
    //return top element id
    var i;
    var nBlockTop = (TopPosition( this.m_insideDiv) + this.m_insideDiv.scrollTop - this.m_nPixelsForLineVisible);
    for ( i = this.GetMinLineIdForLoops(); i < this.m_nMaxLineId; i++ )
    {
      var element = document.getElementById( this.GetLiId( i));
      if ((TopPosition( element)) >= nBlockTop)
      {
        break;
      }
    }
    return i;
   }
   
   
   this.GetBottomLineId = function()
   {
      var i;
      var nBlockBottomLine = (parseInt( TopPosition( this.m_insideDiv)) + this.m_insideDiv.offsetHeight + this.m_insideDiv.scrollTop - this.m_nPixelsForLineVisible);
      
      for ( i = this.GetMaxLineIdForLoops(); i > 0; i--)
      {
        var element = document.getElementById( this.GetLiId( i));
        if ( (TopPosition( element)) <= nBlockBottomLine)
        {
         break;
        }
      }
      return i;
   }
  
  this.ScrollToElementBottom = function ( element)
  {
    var nDivOffsetBottom = TopPosition( this.m_insideDiv) + this.m_insideDiv.offsetHeight;
    var nCurrentScroll = this.m_insideDiv.scrollTop;
    var i;
    
    for ( i = nCurrentScroll; i > 0; i--)
    {
      this.m_insideDiv.scrollTop = i;
      if (( TopPosition( element) + element.offsetHeight) >= ( nDivOffsetBottom + i))
      {
        break;
      }
    }
  }
   
  this.ScrollToElementTop = function ( element)
  {
    var nDivOffset = TopPosition( this.m_insideDiv);
    var nElementOffset = TopPosition( element);
    this.m_insideDiv.scrollTop = ( nElementOffset - nDivOffset);
  }
  
  this.GetMaxLineIdForLoops = function()
  {
    if ( this.m_bNextExists)
    {
      return this.m_nMaxLineId;
    }
    else
    {
      return (this.m_nMaxLineId - 1);
    }
  }
  
  this.GetMinLineIdForLoops = function()
  {
    if ( this.m_bPreviousExists)
    {
      return -1;
    }
    else
    {
      return 0;
    }
  }
  
  this.GetListOfSelectedItems = function( bFullInfo)
  {
    var arData;
    var arResult;
    var reg = /^(.+)<input[^>]+>$/i;
    var arReturn = new Array();
    
    var list = this.m_ul.getElementsByTagName( 'input');
    
    for ( i = 0; i < list.length; i++)
    {
      if ( list[ i].getAttribute( 'value') == 1)
      {
        if ( bFullInfo)
        {
          arData = new Array();
          arData[ 'key'] = this.GetKeyByLineId( i);
          arResult = reg.exec( document.getElementById( this.GetLiId( i)).innerHTML);
          arData[ 'value'] = arResult[ 1];
          arReturn.push( arData); 
        }
        else
        {
          arReturn.push( this.GetKeyByLineId( i)); 
        }
      }
    }
    return arReturn;
  }
  
 this.GetKeyByLineId = function( nLineId)
  {
    var reg = /_hiddens\[ (.+)\]/i;
    var arResult = reg.exec( document.getElementById( this.GetHiddenId( nLineId)).getAttribute( 'name'));
    return arResult[ 1];
  }
  
  this.MouseMovedOver = function( nLineId)
  {
    if ( this.m_bMousePressed)
    {
     this.SelectSlice( nLineId);
     this.ShowState();
    }
  }
  
  this.SetFocus = function()
  {
    if ( this.IsVisible())
    {
      this.m_focusKeeper.focus();
      return true;
    }
    return false;
  }
  
  this.MouseOut = function ( element, x, y)
  {
    if ( element == this.m_insideDiv)
    {
       this.m_bMousePressed = false;
    }
    else
    if ( !this.IsOverInsideDiv( x, y))
    {
      this.m_bMousePressed = false;
    }
  }

  this.IsOverInsideDiv = function( x, y)
  {
    var bOver = false;
    var arPos = FindPosition( this.m_insideDiv, !this.IsFixedPosition());
    var nLeftReserv = 5;
    var nRightReserv = 0;
    var nTopReserv = 5;
    var nBottomReserv = 1;
    if ( arPos[0] + nLeftReserv < x)
    if ( arPos[0] + this.m_insideDiv.offsetWidth - nRightReserv > x)
    if ( arPos[1] + nTopReserv < y)
    if ( arPos[1] + this.m_insideDiv.offsetHeight - nBottomReserv > y)
    {
      bOver = true;
    }
    return bOver;
  }
  
  this.MousePressed = function( nLineId, event)
  {
   this.m_bMousePressed = true; 
   this.LineClicked( nLineId, event);
  }
  
  this.MouseReleased = function()
  {
   this.SetFocus();
   this.m_bMousePressed = false; 
  }
  
  this.KeyUp = function()
  {
    /*if ( this.m_strOnSelectFunction != '')
    {
      eval( this.m_strOnSelectFunction);
    }
    */
  }
  
  this.Init( bVisible, bFixedPosition);
  s_listControlHash.Set( this.m_strBlockId, this);
  this.CreateUl();
}

//Wrappers
function ListControlKeyPressed( strBlockId, event)
{
  var listControl = FindListControl( strBlockId);
  if ( window.opera)
  {
    listControl.SetFocus();
  }
  listControl.KeyPressed( event);
}

function ListControlLineClicked(  strBlockId, nLineId, event)
{
  var listControl = FindListControl( strBlockId);
  listControl.LineClicked( nLineId, event)
}

function ListControlKeyUp( strBlockId)
{
  var listControl = FindListControl( strBlockId);
  listControl.KeyUp();
}


function ListControlMouseOver( strBlockId, nLineId, event)
{
  var listControl = FindListControl( strBlockId);
  listControl.MouseMovedOver( nLineId);
}

function ListControlMouseUp( strBlockId)
{
  var listControl = FindListControl( strBlockId);
  listControl.MouseReleased();
  listControl.KeyUp();
}

function ListControlMouseDown(  strBlockId, nLineId, event)
{
  var listControl = FindListControl( strBlockId);
  listControl.MousePressed(  nLineId, event);
}

function ListControlFocusElement( strBlockId)
{
  var listControl = FindListControl( strBlockId);
  listControl.SetFocus();
}



//IE wrappers
function ListControlIsIdPrefix( obj, strPrefix)
{
  var bIs = false;
  if ( obj)
  if ( obj.getAttribute)
  {
    var strId = obj.getAttribute( 'id');
    if ( strId)
    {
      if ( strId.substring( 0, strPrefix.length) == strPrefix)
        bIs = true;
    }
  }
  return bIs;
}

function ListControlIeGetLiId( obj)
{
  //while ( obj.getAttribute( 'id').substring(0, 3) != 'li_')
  while ( !ListControlIsIdPrefix( obj, 'li_'))
  {
    obj = obj.parentElement;
    if ( obj.parentElement == null)
    {
     return false;
    }
  }
  return obj.getAttribute( 'id');
}

function ListControlIeGetBlockId( obj)
{
  var classes;
  while ( obj.parentNode != null)
  {
    classes = obj.className.split( ' ');
    if ( classes)
    {
      var i;
      for ( i = 0; i < classes.length; i++)
      {
        if ( classes[ i] == 'list_control')
        {
          return obj.getAttribute( 'id');
        }
      }
    }
    obj = obj.parentNode;
  }
  return '';
}



function ListControlIEFocusElement( event)
{
  var strBlockId = ListControlIeGetBlockId( event.srcElement);
  ListControlFocusElement( strBlockId);
}

function ListControlIEKeyDown( event)
{
  //var strBlockId = event.srcElement.parentElement.getAttribute( 'id');
  var strBlockId = event.srcElement.nextSibling.getAttribute( 'id');
  ListControlKeyPressed( strBlockId, event);
  return false;
}

function ListControlIEKeyUp( event)
{
  //var strBlockId = event.srcElement.parentElement.getAttribute( 'id');
  var strBlockId = event.srcElement.nextSibling.getAttribute( 'id');
  ListControlKeyUp( strBlockId);
}

function ListControlIELineClicked( event)
{
  var strBlockId = ListControlIeGetLiId( event.srcElement);
  var arRes = ListControlGetDataById( strBlockId);
  
  ListControlLineClicked(  arRes[ 1], arRes[ 2], event);
  
}

function ListControlIEPreviousFunction( event)
{
  var strBlockId = event.srcElement.parentElement.parentElement.parentElement.getAttribute( 'id');
  var listControl = FindListControl( strBlockId);
  listControl.CallPrevious();
  //if ( listControl.m_bPreviousExists)
  //{
  //  eval( listControl.m_strPreviousFunction);
  //}
}

function ListControlIENextFunction( event)
{ 
  var strBlockId = event.srcElement.parentElement.parentElement.parentElement.getAttribute( 'id');
  var listControl = FindListControl( strBlockId);
  listControl.CallNext();
  //if ( listControl.m_bNextExists)
  //{
  //  eval( listControl.m_strNextFunction);
  //}
}

function ListControlIEDisableSelect()
{
  return false;
}

function ListControlIEMouseOut( event)
{
  var listControl = FindListControl( ListControlIeGetBlockId( event.srcElement));
  listControl.MouseOut( event.srcElement, event.clientX, event.clientY);
}

function ListControlIEMouseOver( event)
{
  var arResult = ListControlGetDataById( ListControlIeGetLiId( event.srcElement));
  var nLineId = arResult[ 2];
  var strBlockId = ListControlIeGetBlockId( event.srcElement);
  
  ListControlMouseOver( strBlockId, nLineId);
}

function ListControlIEMouseDown( event)
{
  var li = document.getElementById( ListControlIeGetLiId( event.srcElement));
  var strBlockId = li.parentElement.parentElement.parentElement.getAttribute( 'id');
  var arResult = ListControlGetDataById( li.getAttribute( 'id'));
  var nLineId = arResult[ 2];
  
  ListControlMouseDown(  strBlockId, nLineId, event);
}

function ListControlIEMouseUp( event)
{
  var strBlockId = ListControlIeGetBlockId( event.srcElement);
  ListControlMouseUp( strBlockId);
}

function ListControlGetDataById( strId)
{
  var reg = /^li_(.+)_N_(\d+)$/i;
  var arRes = reg.exec( strId);
  return arRes;
}

function ListControlNotIEMouseOut( strBlockId, event)
{
  var listControl = FindListControl( strBlockId);
  listControl.MouseOut( event.target, event.clientX, event.clientY);
}

//Opera bugs

function ListControlOperaFocusBug( strBlockId, event)
{
  var listControl = FindListControl( strBlockId);
  listControl.OperaFocusBug( event);
}


// Additional events
function ListControlIEDblClick( event)
{
  var strBlockId = ListControlIeGetBlockId( event.srcElement);
  ListControlDblClick( strBlockId);
}

function ListControlDblClick( strBlockId)
{
  var listControl = FindListControl( strBlockId);
  listControl.OnDblClick();
}

function ListControlSetFocus( strBlockId)
{
  var listControl = FindListControl( strBlockId);
  if ( listControl)
    return listControl.SetFocus();
  return false;
}
